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/dist/index.d.cts CHANGED
@@ -1,3 +1,5 @@
1
+ export { AgentContext, AgentEvent, AgentLoopConfig, AgentTool, ApiOptionsMap, AssistantMessageEvent, AssistantMessageEventStream, KnownProvider, OptionsForApi, Api as PiApi, AssistantMessage as PiAssistantMessage, Context as PiContext, ImageContent as PiImageContent, Message as PiMessage, Model as PiModel, Provider as PiProvider, StreamOptions as PiStreamOptions, ToolResultMessage as PiToolResultMessage, Usage as PiUsage, UserMessage as PiUserMessage, QueuedMessage, ReasoningEffort, SimpleStreamOptions, StopReason, StreamFunction, TextContent, ThinkingContent, ToolCall, agentLoop, calculateCost, getApiKey, getModel as getPiModel, getModels as getPiModels, getProviders as getPiProviders, complete as piComplete, completeSimple as piCompleteSimple, stream as piStream, streamSimple as piStreamSimple, setApiKey } from '@mariozechner/pi-ai';
2
+
1
3
  type Modality = 'llm' | 'image' | 'video' | 'tts' | 'stt' | 'music' | 'embedding';
2
4
  type ModelStatus = 'installed' | 'available' | 'downloading' | 'running' | 'error';
3
5
  interface LocalModelInfo {
@@ -302,6 +304,59 @@ declare class OllamaProvider implements NoosphereProvider {
302
304
  getRunningModels(): Promise<OllamaRunningModel[]>;
303
305
  }
304
306
 
307
+ type TokenCountProvider = 'openai' | 'google' | 'anthropic' | 'mistral' | 'groq' | 'xai' | 'cerebras' | 'openrouter' | 'ollama' | 'unknown';
308
+ interface TokenCountResult {
309
+ tokens: number;
310
+ model: string;
311
+ provider: TokenCountProvider;
312
+ method: 'tiktoken' | 'api' | 'estimate';
313
+ }
314
+ interface TokenCountOptions {
315
+ messages: Array<{
316
+ role: string;
317
+ content: string;
318
+ }>;
319
+ model?: string;
320
+ provider?: string;
321
+ }
322
+ declare function countTokensOpenAI(messages: Array<{
323
+ role: string;
324
+ content: string;
325
+ }>, model?: string): number;
326
+ declare function countTokensGoogle(messages: Array<{
327
+ role: string;
328
+ content: string;
329
+ }>, apiKey: string, model?: string): Promise<number>;
330
+ declare function countTokensAnthropic(messages: Array<{
331
+ role: string;
332
+ content: string;
333
+ }>, apiKey: string, model?: string): Promise<number>;
334
+ declare function countTokensGroq(messages: Array<{
335
+ role: string;
336
+ content: string;
337
+ }>, model?: string): number;
338
+ declare function countTokensMistral(messages: Array<{
339
+ role: string;
340
+ content: string;
341
+ }>, model?: string): number;
342
+ declare function countTokensXai(messages: Array<{
343
+ role: string;
344
+ content: string;
345
+ }>, model?: string): number;
346
+ declare function countTokensCerebras(messages: Array<{
347
+ role: string;
348
+ content: string;
349
+ }>, model?: string): number;
350
+ declare function countTokensOpenRouter(messages: Array<{
351
+ role: string;
352
+ content: string;
353
+ }>, model?: string): number;
354
+ declare function countTokensOllama(messages: Array<{
355
+ role: string;
356
+ content: string;
357
+ }>, model?: string): number;
358
+ declare function countTokens(options: TokenCountOptions, apiKeys?: Record<string, string>): Promise<TokenCountResult>;
359
+
305
360
  declare class Noosphere {
306
361
  private config;
307
362
  private registry;
@@ -320,6 +375,7 @@ declare class Noosphere {
320
375
  getModel(provider: string, modelId: string): Promise<ModelInfo | null>;
321
376
  syncModels(modality?: Modality): Promise<SyncResult>;
322
377
  getUsage(options?: UsageQueryOptions): UsageSummary;
378
+ countTokens(options: TokenCountOptions): Promise<TokenCountResult>;
323
379
  installModel(name: string): Promise<AsyncGenerator<OllamaPullProgress>>;
324
380
  uninstallModel(name: string): Promise<void>;
325
381
  getHardware(): Promise<{
@@ -414,7 +470,10 @@ declare class OpenAIMediaProvider implements NoosphereProvider {
414
470
  readonly modalities: Modality[];
415
471
  readonly isLocal = false;
416
472
  private modelsCache;
473
+ private voicesCache;
417
474
  constructor(apiKey: string);
475
+ /** Auto-fetch available TTS voices by sending an invalid voice and parsing the error. */
476
+ private fetchVoices;
418
477
  ping(): Promise<boolean>;
419
478
  listModels(modality?: Modality): Promise<ModelInfo[]>;
420
479
  image(options: ImageOptions): Promise<NoosphereResult>;
@@ -430,6 +489,7 @@ declare class GoogleMediaProvider implements NoosphereProvider {
430
489
  readonly modalities: Modality[];
431
490
  readonly isLocal = false;
432
491
  private modelsCache;
492
+ private voicesCache;
433
493
  constructor(apiKey: string);
434
494
  ping(): Promise<boolean>;
435
495
  listModels(modality?: Modality): Promise<ModelInfo[]>;
@@ -456,4 +516,4 @@ declare function getProviderLogo(providerId: string | undefined | null): Provide
456
516
  declare function getAllProviderLogos(): Record<string, ProviderLogo>;
457
517
  declare const PROVIDER_LOGOS: Record<string, ProviderLogo>;
458
518
 
459
- export { AudioCraftProvider, type BaseOptions, type ChatOptions, GoogleMediaProvider, HfLocalProvider, type ImageOptions, type LocalModelInfo, type LocalServiceConfig, type Modality, type ModelInfo, type ModelStatus, type MusicOptions, Noosphere, type NoosphereConfig, NoosphereError, type NoosphereErrorCode, type NoosphereProvider, type NoosphereResult, type NoosphereStream, type OllamaModelDetail, OllamaProvider, type OllamaPullProgress, type OllamaRunningModel, type OpenAICompatConfig, OpenAICompatProvider, OpenAIMediaProvider, PROVIDER_IDS, PROVIDER_LOGOS, type ProviderInfo, type ProviderLogo$1 as ProviderLogo, type SpeakOptions, type StreamEvent, type SyncResult, type TranscriptionOptions, type TranscriptionResult, type UsageEvent, type UsageQueryOptions, type UsageSummary, type VideoOptions, WhisperLocalProvider, detectOpenAICompatServers, getAllProviderLogos, getProviderLogo };
519
+ export { AudioCraftProvider, type BaseOptions, type ChatOptions, GoogleMediaProvider, HfLocalProvider, type ImageOptions, type LocalModelInfo, type LocalServiceConfig, type Modality, type ModelInfo, type ModelStatus, type MusicOptions, Noosphere, type NoosphereConfig, NoosphereError, type NoosphereErrorCode, type NoosphereProvider, type NoosphereResult, type NoosphereStream, type OllamaModelDetail, OllamaProvider, type OllamaPullProgress, type OllamaRunningModel, type OpenAICompatConfig, OpenAICompatProvider, OpenAIMediaProvider, PROVIDER_IDS, PROVIDER_LOGOS, type ProviderInfo, type ProviderLogo$1 as ProviderLogo, type SpeakOptions, type StreamEvent, type SyncResult, type TokenCountOptions, type TokenCountProvider, type TokenCountResult, type TranscriptionOptions, type TranscriptionResult, type UsageEvent, type UsageQueryOptions, type UsageSummary, type VideoOptions, WhisperLocalProvider, countTokens, countTokensAnthropic, countTokensCerebras, countTokensGoogle, countTokensGroq, countTokensMistral, countTokensOllama, countTokensOpenAI, countTokensOpenRouter, countTokensXai, detectOpenAICompatServers, getAllProviderLogos, getProviderLogo };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export { AgentContext, AgentEvent, AgentLoopConfig, AgentTool, ApiOptionsMap, AssistantMessageEvent, AssistantMessageEventStream, KnownProvider, OptionsForApi, Api as PiApi, AssistantMessage as PiAssistantMessage, Context as PiContext, ImageContent as PiImageContent, Message as PiMessage, Model as PiModel, Provider as PiProvider, StreamOptions as PiStreamOptions, ToolResultMessage as PiToolResultMessage, Usage as PiUsage, UserMessage as PiUserMessage, QueuedMessage, ReasoningEffort, SimpleStreamOptions, StopReason, StreamFunction, TextContent, ThinkingContent, ToolCall, agentLoop, calculateCost, getApiKey, getModel as getPiModel, getModels as getPiModels, getProviders as getPiProviders, complete as piComplete, completeSimple as piCompleteSimple, stream as piStream, streamSimple as piStreamSimple, setApiKey } from '@mariozechner/pi-ai';
2
+
1
3
  type Modality = 'llm' | 'image' | 'video' | 'tts' | 'stt' | 'music' | 'embedding';
2
4
  type ModelStatus = 'installed' | 'available' | 'downloading' | 'running' | 'error';
3
5
  interface LocalModelInfo {
@@ -302,6 +304,59 @@ declare class OllamaProvider implements NoosphereProvider {
302
304
  getRunningModels(): Promise<OllamaRunningModel[]>;
303
305
  }
304
306
 
307
+ type TokenCountProvider = 'openai' | 'google' | 'anthropic' | 'mistral' | 'groq' | 'xai' | 'cerebras' | 'openrouter' | 'ollama' | 'unknown';
308
+ interface TokenCountResult {
309
+ tokens: number;
310
+ model: string;
311
+ provider: TokenCountProvider;
312
+ method: 'tiktoken' | 'api' | 'estimate';
313
+ }
314
+ interface TokenCountOptions {
315
+ messages: Array<{
316
+ role: string;
317
+ content: string;
318
+ }>;
319
+ model?: string;
320
+ provider?: string;
321
+ }
322
+ declare function countTokensOpenAI(messages: Array<{
323
+ role: string;
324
+ content: string;
325
+ }>, model?: string): number;
326
+ declare function countTokensGoogle(messages: Array<{
327
+ role: string;
328
+ content: string;
329
+ }>, apiKey: string, model?: string): Promise<number>;
330
+ declare function countTokensAnthropic(messages: Array<{
331
+ role: string;
332
+ content: string;
333
+ }>, apiKey: string, model?: string): Promise<number>;
334
+ declare function countTokensGroq(messages: Array<{
335
+ role: string;
336
+ content: string;
337
+ }>, model?: string): number;
338
+ declare function countTokensMistral(messages: Array<{
339
+ role: string;
340
+ content: string;
341
+ }>, model?: string): number;
342
+ declare function countTokensXai(messages: Array<{
343
+ role: string;
344
+ content: string;
345
+ }>, model?: string): number;
346
+ declare function countTokensCerebras(messages: Array<{
347
+ role: string;
348
+ content: string;
349
+ }>, model?: string): number;
350
+ declare function countTokensOpenRouter(messages: Array<{
351
+ role: string;
352
+ content: string;
353
+ }>, model?: string): number;
354
+ declare function countTokensOllama(messages: Array<{
355
+ role: string;
356
+ content: string;
357
+ }>, model?: string): number;
358
+ declare function countTokens(options: TokenCountOptions, apiKeys?: Record<string, string>): Promise<TokenCountResult>;
359
+
305
360
  declare class Noosphere {
306
361
  private config;
307
362
  private registry;
@@ -320,6 +375,7 @@ declare class Noosphere {
320
375
  getModel(provider: string, modelId: string): Promise<ModelInfo | null>;
321
376
  syncModels(modality?: Modality): Promise<SyncResult>;
322
377
  getUsage(options?: UsageQueryOptions): UsageSummary;
378
+ countTokens(options: TokenCountOptions): Promise<TokenCountResult>;
323
379
  installModel(name: string): Promise<AsyncGenerator<OllamaPullProgress>>;
324
380
  uninstallModel(name: string): Promise<void>;
325
381
  getHardware(): Promise<{
@@ -414,7 +470,10 @@ declare class OpenAIMediaProvider implements NoosphereProvider {
414
470
  readonly modalities: Modality[];
415
471
  readonly isLocal = false;
416
472
  private modelsCache;
473
+ private voicesCache;
417
474
  constructor(apiKey: string);
475
+ /** Auto-fetch available TTS voices by sending an invalid voice and parsing the error. */
476
+ private fetchVoices;
418
477
  ping(): Promise<boolean>;
419
478
  listModels(modality?: Modality): Promise<ModelInfo[]>;
420
479
  image(options: ImageOptions): Promise<NoosphereResult>;
@@ -430,6 +489,7 @@ declare class GoogleMediaProvider implements NoosphereProvider {
430
489
  readonly modalities: Modality[];
431
490
  readonly isLocal = false;
432
491
  private modelsCache;
492
+ private voicesCache;
433
493
  constructor(apiKey: string);
434
494
  ping(): Promise<boolean>;
435
495
  listModels(modality?: Modality): Promise<ModelInfo[]>;
@@ -456,4 +516,4 @@ declare function getProviderLogo(providerId: string | undefined | null): Provide
456
516
  declare function getAllProviderLogos(): Record<string, ProviderLogo>;
457
517
  declare const PROVIDER_LOGOS: Record<string, ProviderLogo>;
458
518
 
459
- export { AudioCraftProvider, type BaseOptions, type ChatOptions, GoogleMediaProvider, HfLocalProvider, type ImageOptions, type LocalModelInfo, type LocalServiceConfig, type Modality, type ModelInfo, type ModelStatus, type MusicOptions, Noosphere, type NoosphereConfig, NoosphereError, type NoosphereErrorCode, type NoosphereProvider, type NoosphereResult, type NoosphereStream, type OllamaModelDetail, OllamaProvider, type OllamaPullProgress, type OllamaRunningModel, type OpenAICompatConfig, OpenAICompatProvider, OpenAIMediaProvider, PROVIDER_IDS, PROVIDER_LOGOS, type ProviderInfo, type ProviderLogo$1 as ProviderLogo, type SpeakOptions, type StreamEvent, type SyncResult, type TranscriptionOptions, type TranscriptionResult, type UsageEvent, type UsageQueryOptions, type UsageSummary, type VideoOptions, WhisperLocalProvider, detectOpenAICompatServers, getAllProviderLogos, getProviderLogo };
519
+ export { AudioCraftProvider, type BaseOptions, type ChatOptions, GoogleMediaProvider, HfLocalProvider, type ImageOptions, type LocalModelInfo, type LocalServiceConfig, type Modality, type ModelInfo, type ModelStatus, type MusicOptions, Noosphere, type NoosphereConfig, NoosphereError, type NoosphereErrorCode, type NoosphereProvider, type NoosphereResult, type NoosphereStream, type OllamaModelDetail, OllamaProvider, type OllamaPullProgress, type OllamaRunningModel, type OpenAICompatConfig, OpenAICompatProvider, OpenAIMediaProvider, PROVIDER_IDS, PROVIDER_LOGOS, type ProviderInfo, type ProviderLogo$1 as ProviderLogo, type SpeakOptions, type StreamEvent, type SyncResult, type TokenCountOptions, type TokenCountProvider, type TokenCountResult, type TranscriptionOptions, type TranscriptionResult, type UsageEvent, type UsageQueryOptions, type UsageSummary, type VideoOptions, WhisperLocalProvider, countTokens, countTokensAnthropic, countTokensCerebras, countTokensGoogle, countTokensGroq, countTokensMistral, countTokensOllama, countTokensOpenAI, countTokensOpenRouter, countTokensXai, detectOpenAICompatServers, getAllProviderLogos, getProviderLogo };
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 = [
@@ -2533,6 +2663,36 @@ var OpenAIMediaProvider = class {
2533
2663
  modalities = ["image", "video", "tts", "stt"];
2534
2664
  isLocal = false;
2535
2665
  modelsCache = null;
2666
+ voicesCache = null;
2667
+ /** Auto-fetch available TTS voices by sending an invalid voice and parsing the error. */
2668
+ async fetchVoices() {
2669
+ if (this.voicesCache) return this.voicesCache;
2670
+ try {
2671
+ const res = await fetch(`${OPENAI_API_BASE}/audio/speech`, {
2672
+ method: "POST",
2673
+ headers: {
2674
+ "Content-Type": "application/json",
2675
+ Authorization: `Bearer ${this.apiKey}`
2676
+ },
2677
+ body: JSON.stringify({ model: "tts-1", input: ".", voice: "__discover_voices__" })
2678
+ });
2679
+ if (!res.ok) {
2680
+ const data = await res.json();
2681
+ const msg = data?.error?.message ?? "";
2682
+ const shouldBe = msg.match(/Input should be ([^"]+)/);
2683
+ if (shouldBe) {
2684
+ const voiceList = shouldBe[1].match(/'([a-z]+)'/g);
2685
+ if (voiceList && voiceList.length > 0) {
2686
+ this.voicesCache = voiceList.map((v) => v.replace(/'/g, ""));
2687
+ return this.voicesCache;
2688
+ }
2689
+ }
2690
+ }
2691
+ } catch {
2692
+ }
2693
+ this.voicesCache = [];
2694
+ return this.voicesCache;
2695
+ }
2536
2696
  async ping() {
2537
2697
  try {
2538
2698
  const controller = new AbortController();
@@ -2568,6 +2728,7 @@ var OpenAIMediaProvider = class {
2568
2728
  } finally {
2569
2729
  clearTimeout(timer);
2570
2730
  }
2731
+ const voices = await this.fetchVoices();
2571
2732
  const entries = data?.data ?? [];
2572
2733
  const logo = getProviderLogo("openai");
2573
2734
  const models = [];
@@ -2583,7 +2744,7 @@ var OpenAIMediaProvider = class {
2583
2744
  cost: { price: 0, unit: "per_request" },
2584
2745
  logo,
2585
2746
  description: entry.description,
2586
- capabilities: this.getCapabilities(entry.id, mod)
2747
+ capabilities: this.getCapabilities(entry.id, mod, voices)
2587
2748
  };
2588
2749
  models.push(info);
2589
2750
  }
@@ -2728,7 +2889,7 @@ var OpenAIMediaProvider = class {
2728
2889
  }
2729
2890
  };
2730
2891
  }
2731
- getCapabilities(id, modality) {
2892
+ getCapabilities(id, modality, voices) {
2732
2893
  if (modality === "image") {
2733
2894
  return {
2734
2895
  maxWidth: id.startsWith("dall-e-3") ? 1792 : 1024,
@@ -2737,7 +2898,7 @@ var OpenAIMediaProvider = class {
2737
2898
  }
2738
2899
  if (modality === "tts") {
2739
2900
  return {
2740
- voices: ["alloy", "ash", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer"]
2901
+ voices: voices && voices.length > 0 ? voices : void 0
2741
2902
  };
2742
2903
  }
2743
2904
  if (modality === "video") {
@@ -2758,18 +2919,34 @@ var OpenAIMediaProvider = class {
2758
2919
  // src/providers/google-media.ts
2759
2920
  var GOOGLE_API_BASE = "https://generativelanguage.googleapis.com/v1beta";
2760
2921
  var FETCH_TIMEOUT_MS6 = 8e3;
2761
- var GOOGLE_TTS_VOICES = [
2762
- "Aoede",
2763
- "Charon",
2764
- "Fenrir",
2765
- "Kore",
2766
- "Puck",
2767
- "Leda",
2768
- "Orus",
2769
- "Perseus",
2770
- "Zephyr",
2771
- "Callirrhoe"
2772
- ];
2922
+ async function fetchGoogleVoices(apiKey) {
2923
+ try {
2924
+ const res = await fetch(
2925
+ `${GOOGLE_API_BASE}/models/gemini-2.5-flash-preview-tts:generateContent?key=${apiKey}`,
2926
+ {
2927
+ method: "POST",
2928
+ headers: { "Content-Type": "application/json" },
2929
+ body: JSON.stringify({
2930
+ contents: [{ parts: [{ text: "." }] }],
2931
+ generationConfig: {
2932
+ response_modalities: ["AUDIO"],
2933
+ speech_config: { voiceConfig: { prebuiltVoiceConfig: { voiceName: "__discover_voices__" } } }
2934
+ }
2935
+ })
2936
+ }
2937
+ );
2938
+ if (!res.ok) {
2939
+ const data = await res.json();
2940
+ const msg = data?.error?.message ?? "";
2941
+ const match = msg.match(/Allowed voice names are:\s*(.+)/i);
2942
+ if (match) {
2943
+ return match[1].split(",").map((v) => v.trim()).filter(Boolean);
2944
+ }
2945
+ }
2946
+ } catch {
2947
+ }
2948
+ return [];
2949
+ }
2773
2950
  function classifyGoogleModel(model) {
2774
2951
  const name = (model.name ?? "").replace("models/", "");
2775
2952
  const methods = model.supportedGenerationMethods ?? [];
@@ -2787,6 +2964,7 @@ var GoogleMediaProvider = class {
2787
2964
  modalities = ["image", "video", "tts"];
2788
2965
  isLocal = false;
2789
2966
  modelsCache = null;
2967
+ voicesCache = null;
2790
2968
  async ping() {
2791
2969
  try {
2792
2970
  const controller = new AbortController();
@@ -2821,6 +2999,9 @@ var GoogleMediaProvider = class {
2821
2999
  clearTimeout(timer);
2822
3000
  }
2823
3001
  const entries = data?.models ?? [];
3002
+ if (!this.voicesCache) {
3003
+ this.voicesCache = await fetchGoogleVoices(this.apiKey);
3004
+ }
2824
3005
  const logo = getProviderLogo("google");
2825
3006
  const models = [];
2826
3007
  for (const entry of entries) {
@@ -2837,7 +3018,7 @@ var GoogleMediaProvider = class {
2837
3018
  cost: { price: 0, unit: modality2 === "video" ? "per_video" : "per_image" },
2838
3019
  logo,
2839
3020
  description: entry.description,
2840
- capabilities: modality2 === "video" ? { maxDuration: 8, supportsStreaming: false } : modality2 === "tts" ? { voices: GOOGLE_TTS_VOICES } : void 0
3021
+ capabilities: modality2 === "video" ? { maxDuration: 8, supportsStreaming: false } : modality2 === "tts" ? { voices: this.voicesCache && this.voicesCache.length > 0 ? this.voicesCache : void 0 } : void 0
2841
3022
  };
2842
3023
  models.push(info);
2843
3024
  }
@@ -2940,7 +3121,7 @@ var GoogleMediaProvider = class {
2940
3121
  };
2941
3122
  }
2942
3123
  async video(options) {
2943
- const model = options.model ?? "veo-3.0-generate-001";
3124
+ const model = options.model ?? "veo-2.0-generate-001";
2944
3125
  const start = Date.now();
2945
3126
  const body = {
2946
3127
  instances: [{ prompt: options.prompt }],
@@ -2975,10 +3156,15 @@ var GoogleMediaProvider = class {
2975
3156
  if (!pollRes.ok) continue;
2976
3157
  const status = await pollRes.json();
2977
3158
  if (status.done) {
2978
- const videoBase64 = status.response?.generatedSamples?.[0]?.video?.bytesBase64Encoded;
2979
- if (videoBase64) {
3159
+ if (status.error) {
3160
+ throw new Error(`Google video generation error: ${status.error.message ?? JSON.stringify(status.error)}`);
3161
+ }
3162
+ const resp = status.response ?? {};
3163
+ const samples = resp.generateVideoResponse?.generatedSamples ?? resp.generatedSamples ?? [];
3164
+ const video = samples[0]?.video;
3165
+ if (video?.bytesBase64Encoded) {
2980
3166
  return {
2981
- buffer: Buffer.from(videoBase64, "base64"),
3167
+ buffer: Buffer.from(video.bytesBase64Encoded, "base64"),
2982
3168
  provider: "google-media",
2983
3169
  model,
2984
3170
  modality: "video",
@@ -2987,16 +3173,32 @@ var GoogleMediaProvider = class {
2987
3173
  media: { format: "mp4", duration: options.duration }
2988
3174
  };
2989
3175
  }
2990
- const videoUrl = status.response?.generatedSamples?.[0]?.video?.uri;
2991
- return {
2992
- url: videoUrl,
2993
- provider: "google-media",
2994
- model,
2995
- modality: "video",
2996
- latencyMs: Date.now() - start,
2997
- usage: { cost: 0, unit: "per_video" },
2998
- media: { format: "mp4", duration: options.duration }
2999
- };
3176
+ if (video?.uri) {
3177
+ const separator = video.uri.includes("?") ? "&" : "?";
3178
+ const videoRes = await fetch(`${video.uri}${separator}key=${this.apiKey}`, { redirect: "follow" });
3179
+ if (videoRes.ok) {
3180
+ const buffer = Buffer.from(await videoRes.arrayBuffer());
3181
+ return {
3182
+ buffer,
3183
+ provider: "google-media",
3184
+ model,
3185
+ modality: "video",
3186
+ latencyMs: Date.now() - start,
3187
+ usage: { cost: 0, unit: "per_video" },
3188
+ media: { format: "mp4", duration: options.duration }
3189
+ };
3190
+ }
3191
+ return {
3192
+ url: video.uri,
3193
+ provider: "google-media",
3194
+ model,
3195
+ modality: "video",
3196
+ latencyMs: Date.now() - start,
3197
+ usage: { cost: 0, unit: "per_video" },
3198
+ media: { format: "mp4", duration: options.duration }
3199
+ };
3200
+ }
3201
+ throw new Error("Google video generation completed but returned no video data");
3000
3202
  }
3001
3203
  }
3002
3204
  throw new Error(`Google video generation timed out after 5 minutes`);
@@ -3193,6 +3395,12 @@ var Noosphere = class {
3193
3395
  getUsage(options) {
3194
3396
  return this.tracker.getSummary(options);
3195
3397
  }
3398
+ async countTokens(options) {
3399
+ const keys = {};
3400
+ if (this.config.keys?.google) keys.google = this.config.keys.google;
3401
+ if (this.config.keys?.anthropic) keys.anthropic = this.config.keys.anthropic;
3402
+ return countTokens(options, keys);
3403
+ }
3196
3404
  // --- Local Model Management ---
3197
3405
  async installModel(name) {
3198
3406
  if (!this.initialized) await this.init();
@@ -3427,6 +3635,12 @@ var Noosphere = class {
3427
3635
  await this.tracker.record(event);
3428
3636
  }
3429
3637
  };
3638
+
3639
+ // src/index.ts
3640
+ import { agentLoop } from "@mariozechner/pi-ai";
3641
+ import { calculateCost, getModel, getModels as getModels2, getProviders as getProviders2 } from "@mariozechner/pi-ai";
3642
+ import { stream as stream2, complete as complete2, streamSimple, completeSimple } from "@mariozechner/pi-ai";
3643
+ import { setApiKey as setApiKey2, getApiKey } from "@mariozechner/pi-ai";
3430
3644
  export {
3431
3645
  AudioCraftProvider,
3432
3646
  GoogleMediaProvider,
@@ -3439,8 +3653,29 @@ export {
3439
3653
  PROVIDER_IDS,
3440
3654
  PROVIDER_LOGOS,
3441
3655
  WhisperLocalProvider,
3656
+ agentLoop,
3657
+ calculateCost,
3658
+ countTokens,
3659
+ countTokensAnthropic,
3660
+ countTokensCerebras,
3661
+ countTokensGoogle,
3662
+ countTokensGroq,
3663
+ countTokensMistral,
3664
+ countTokensOllama,
3665
+ countTokensOpenAI,
3666
+ countTokensOpenRouter,
3667
+ countTokensXai,
3442
3668
  detectOpenAICompatServers,
3443
3669
  getAllProviderLogos,
3444
- getProviderLogo
3670
+ getApiKey,
3671
+ getModel as getPiModel,
3672
+ getModels2 as getPiModels,
3673
+ getProviders2 as getPiProviders,
3674
+ getProviderLogo,
3675
+ complete2 as piComplete,
3676
+ completeSimple as piCompleteSimple,
3677
+ stream2 as piStream,
3678
+ streamSimple as piStreamSimple,
3679
+ setApiKey2 as setApiKey
3445
3680
  };
3446
3681
  //# sourceMappingURL=index.js.map