@happyvertical/ai 0.85.4 → 0.85.5

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.
Files changed (84) hide show
  1. package/README.md +87 -1
  2. package/dist/chunks/{anthropic-DA8sdt12.js → anthropic-DuYUnw8j.js} +20 -4
  3. package/dist/chunks/anthropic-DuYUnw8j.js.map +1 -0
  4. package/dist/chunks/{bedrock-DDiYFUw0.js → bedrock-ikQJAwWB.js} +19 -3
  5. package/dist/chunks/bedrock-ikQJAwWB.js.map +1 -0
  6. package/dist/chunks/{bifrost-fM9FpW_w.js → bifrost-CaYhelVL.js} +5 -4
  7. package/dist/chunks/bifrost-CaYhelVL.js.map +1 -0
  8. package/dist/chunks/byteplus-modelark-B8l8sPXu.js +473 -0
  9. package/dist/chunks/byteplus-modelark-B8l8sPXu.js.map +1 -0
  10. package/dist/chunks/{claude-cli-CnJh5KQT.js → claude-cli-DeTKk9nu.js} +19 -3
  11. package/dist/chunks/claude-cli-DeTKk9nu.js.map +1 -0
  12. package/dist/chunks/{gateway-admin-CiKvHst7.js → gateway-admin-DJXEDpUP.js} +2 -2
  13. package/dist/chunks/{gateway-admin-CiKvHst7.js.map → gateway-admin-DJXEDpUP.js.map} +1 -1
  14. package/dist/chunks/{gemini-Bu7Fke7c.js → gemini-BT8fjXWz.js} +294 -11
  15. package/dist/chunks/gemini-BT8fjXWz.js.map +1 -0
  16. package/dist/chunks/{huggingface-B07_6wHW.js → huggingface-CqzyxeLB.js} +19 -3
  17. package/dist/chunks/huggingface-CqzyxeLB.js.map +1 -0
  18. package/dist/chunks/{litellm-D_Oo9OQ_.js → litellm-viqTuSY8.js} +5 -4
  19. package/dist/chunks/litellm-viqTuSY8.js.map +1 -0
  20. package/dist/chunks/{ollama-D4ksOTO8.js → ollama-CdWqwZ05.js} +19 -3
  21. package/dist/chunks/ollama-CdWqwZ05.js.map +1 -0
  22. package/dist/chunks/{openai--F38QHiJ.js → openai-BgrK3OfW.js} +20 -4
  23. package/dist/chunks/openai-BgrK3OfW.js.map +1 -0
  24. package/dist/chunks/openai-compat-video-D_U7jo0d.js +369 -0
  25. package/dist/chunks/openai-compat-video-D_U7jo0d.js.map +1 -0
  26. package/dist/chunks/{qwen-tts-DRRbOrhA.js → qwen-tts-BXjzY7DX.js} +21 -4
  27. package/dist/chunks/qwen-tts-BXjzY7DX.js.map +1 -0
  28. package/dist/chunks/{rate-limit-BwyPGXFW.js → rate-limit-DgEhanDL.js} +4 -3
  29. package/dist/chunks/rate-limit-DgEhanDL.js.map +1 -0
  30. package/dist/chunks/{safety-CEnoJS6X.js → safety-C415clBm.js} +25 -7
  31. package/dist/chunks/safety-C415clBm.js.map +1 -0
  32. package/dist/chunks/src-CU4qWJ2c.js +740 -0
  33. package/dist/chunks/src-CU4qWJ2c.js.map +1 -0
  34. package/dist/chunks/{types-FHs-KbGL.js → types-CINwUPDo.js} +4 -2
  35. package/dist/chunks/types-CINwUPDo.js.map +1 -0
  36. package/dist/index.js +3 -717
  37. package/dist/node/factory.d.ts +9 -0
  38. package/dist/node/factory.d.ts.map +1 -1
  39. package/dist/node.d.ts +29 -0
  40. package/dist/node.d.ts.map +1 -0
  41. package/dist/node.js +198 -0
  42. package/dist/node.js.map +1 -0
  43. package/dist/shared/factory.d.ts.map +1 -1
  44. package/dist/shared/providers/anthropic.d.ts +6 -1
  45. package/dist/shared/providers/anthropic.d.ts.map +1 -1
  46. package/dist/shared/providers/bedrock.d.ts +6 -1
  47. package/dist/shared/providers/bedrock.d.ts.map +1 -1
  48. package/dist/shared/providers/bifrost.d.ts.map +1 -1
  49. package/dist/shared/providers/byteplus-modelark.d.ts +91 -0
  50. package/dist/shared/providers/byteplus-modelark.d.ts.map +1 -0
  51. package/dist/shared/providers/claude-cli.d.ts +6 -1
  52. package/dist/shared/providers/claude-cli.d.ts.map +1 -1
  53. package/dist/shared/providers/gemini.d.ts +115 -2
  54. package/dist/shared/providers/gemini.d.ts.map +1 -1
  55. package/dist/shared/providers/huggingface.d.ts +6 -1
  56. package/dist/shared/providers/huggingface.d.ts.map +1 -1
  57. package/dist/shared/providers/litellm.d.ts.map +1 -1
  58. package/dist/shared/providers/ollama.d.ts +6 -1
  59. package/dist/shared/providers/ollama.d.ts.map +1 -1
  60. package/dist/shared/providers/openai-compat-video.d.ts +98 -0
  61. package/dist/shared/providers/openai-compat-video.d.ts.map +1 -0
  62. package/dist/shared/providers/openai.d.ts +6 -1
  63. package/dist/shared/providers/openai.d.ts.map +1 -1
  64. package/dist/shared/providers/qwen-tts.d.ts +6 -1
  65. package/dist/shared/providers/qwen-tts.d.ts.map +1 -1
  66. package/dist/shared/rate-limit.d.ts.map +1 -1
  67. package/dist/shared/safety.d.ts.map +1 -1
  68. package/dist/shared/types.d.ts +343 -5
  69. package/dist/shared/types.d.ts.map +1 -1
  70. package/package.json +7 -3
  71. package/dist/chunks/anthropic-DA8sdt12.js.map +0 -1
  72. package/dist/chunks/bedrock-DDiYFUw0.js.map +0 -1
  73. package/dist/chunks/bifrost-fM9FpW_w.js.map +0 -1
  74. package/dist/chunks/claude-cli-CnJh5KQT.js.map +0 -1
  75. package/dist/chunks/gemini-Bu7Fke7c.js.map +0 -1
  76. package/dist/chunks/huggingface-B07_6wHW.js.map +0 -1
  77. package/dist/chunks/litellm-D_Oo9OQ_.js.map +0 -1
  78. package/dist/chunks/ollama-D4ksOTO8.js.map +0 -1
  79. package/dist/chunks/openai--F38QHiJ.js.map +0 -1
  80. package/dist/chunks/qwen-tts-DRRbOrhA.js.map +0 -1
  81. package/dist/chunks/rate-limit-BwyPGXFW.js.map +0 -1
  82. package/dist/chunks/safety-CEnoJS6X.js.map +0 -1
  83. package/dist/chunks/types-FHs-KbGL.js.map +0 -1
  84. package/dist/index.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-cli-DeTKk9nu.js","names":[],"sources":["../../src/shared/providers/claude-cli.ts"],"sourcesContent":["/**\n * Claude CLI provider implementation\n *\n * Provides a standardized interface for interacting with Claude via the Claude Code CLI,\n * enabling users with Claude Max subscriptions to use their subscription instead of\n * paying for API usage. Supports chat completions and streaming responses.\n * Authentication is handled via existing Claude session or setup-token.\n *\n * **Authentication Priority:**\n * 1. ANTHROPIC_API_KEY environment variable (uses Anthropic SDK, no keychain prompts)\n * 2. Claude CLI with setup-token (for CI/CD, no keychain prompts)\n * 3. Claude CLI with keychain (may prompt for password on macOS)\n */\n\nimport { exec, spawn } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nimport type {\n AICapabilities,\n AIInterface,\n AIMessage,\n AIModel,\n AIResponse,\n ChatOptions,\n ClaudeCliOptions,\n CompletionOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n MessageOptions,\n TokenUsage,\n TTSOptions,\n TTSResponse,\n VideoGenerationJob,\n VideoGenerationOptions,\n VideoGenerationResult,\n VideoGenerationStatusResult,\n Voice,\n VoiceCloneOptions,\n VoiceDesignOptions,\n VoiceListOptions,\n} from '../types';\nimport {\n AIError,\n AuthenticationError,\n ContextLengthError,\n extractTextContent,\n RateLimitError,\n} from '../types';\nimport { emitUsage } from './usage';\n\nconst execAsync = promisify(exec);\n\n/**\n * Claude CLI provider implementation that shells out to the Claude Code CLI.\n * Supports chat completions, streaming, and leverages Claude Max subscription.\n * Does not support embeddings (use OpenAI or another provider for embeddings).\n *\n * If ANTHROPIC_API_KEY environment variable is set, automatically falls back to\n * using the Anthropic provider to avoid macOS keychain password prompts.\n */\nexport class ClaudeCliProvider implements AIInterface {\n private options: ClaudeCliOptions;\n private cliPath: string | null = null;\n private anthropicFallback: AIInterface | null = null;\n\n /**\n * Creates a new Claude CLI provider instance\n * @param options - Configuration options for the Claude CLI provider\n */\n constructor(options: ClaudeCliOptions) {\n this.options = {\n defaultModel: 'sonnet',\n ...options,\n };\n }\n\n /**\n * Checks if ANTHROPIC_API_KEY is available and initializes fallback provider if needed\n * @private\n */\n private async initializeFallback(): Promise<void> {\n if (this.anthropicFallback !== null) {\n return; // Already initialized\n }\n\n const apiKey = process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n return; // No API key, will use CLI\n }\n\n // Lazy load Anthropic provider to avoid circular dependency\n const { AnthropicProvider } = await import('./anthropic.js');\n\n // Map claude-cli model names to Anthropic model IDs\n const modelMap: Record<string, string> = {\n sonnet: 'claude-3-5-sonnet-20241022',\n opus: 'claude-3-opus-20240229',\n haiku: 'claude-3-haiku-20240307',\n };\n\n const defaultModel =\n modelMap[this.options.defaultModel || 'sonnet'] ||\n 'claude-3-5-sonnet-20241022';\n\n this.anthropicFallback = new AnthropicProvider({\n type: 'anthropic',\n apiKey,\n defaultModel,\n onUsage: this.options.onUsage,\n usageTags: this.options.usageTags,\n });\n }\n\n /**\n * Finds the Claude CLI binary in PATH or uses custom cliPath\n * Does NOT execute the CLI during detection to avoid keychain prompts\n * @throws {AIError} When CLI cannot be found\n * @private\n */\n private async findCli(): Promise<string> {\n if (this.cliPath) {\n return this.cliPath;\n }\n\n // If custom path provided, check if it exists (don't execute it)\n if (this.options.cliPath) {\n const fs = await import('node:fs/promises');\n try {\n await fs.access(this.options.cliPath);\n this.cliPath = this.options.cliPath;\n return this.cliPath;\n } catch (_error) {\n throw new AIError(\n `Claude CLI not found at specified path: ${this.options.cliPath}. Set ANTHROPIC_API_KEY environment variable to avoid CLI dependency.`,\n 'CLI_NOT_FOUND',\n 'claude-cli',\n );\n }\n }\n\n // Try common installation locations first\n const commonPaths = [\n `${process.env.HOME}/.claude/local/claude`, // Default installation path\n '/usr/local/bin/claude',\n '/opt/homebrew/bin/claude',\n ];\n\n const fs = await import('node:fs/promises');\n for (const path of commonPaths) {\n try {\n await fs.access(path);\n this.cliPath = path;\n return this.cliPath;\n } catch (_error) {\n // Try next path\n }\n }\n\n // Try to find in PATH with which (doesn't execute the binary)\n try {\n const { stdout } = await execAsync('which claude');\n const path = stdout.trim();\n if (path) {\n this.cliPath = path;\n return this.cliPath;\n }\n } catch (_error) {\n // Not found\n }\n\n throw new AIError(\n 'Claude CLI not found. Set ANTHROPIC_API_KEY environment variable to use Anthropic SDK instead, or install Claude Code CLI. Visit https://docs.claude.com/en/docs/claude-code/ for installation instructions.',\n 'CLI_NOT_FOUND',\n 'claude-cli',\n );\n }\n\n /**\n * Normalizes model name to full model ID or short name\n * @private\n */\n private normalizeModel(model?: string): string {\n const m = model || this.options.defaultModel || 'sonnet';\n\n // Map short names to CLI-compatible values\n const modelMap: Record<string, string> = {\n sonnet: 'sonnet',\n opus: 'opus',\n haiku: 'haiku',\n };\n\n return modelMap[m] || m;\n }\n\n /**\n * Execute Claude CLI command and return parsed JSON output\n * @private\n */\n private async executeCommand(\n prompt: string,\n options: {\n model?: string;\n systemPrompt?: string;\n temperature?: number;\n maxTokens?: number;\n } = {},\n ): Promise<any> {\n const cliPath = await this.findCli();\n const model = this.normalizeModel(options.model);\n\n const args = ['--print', '--output-format', 'json', '--model', model];\n\n if (options.systemPrompt) {\n args.push('--system-prompt', options.systemPrompt);\n }\n\n args.push(prompt);\n\n return new Promise((resolve, reject) => {\n let stdout = '';\n let stderr = '';\n\n const child = spawn(cliPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n child.stdout.on('data', (data) => {\n stdout += data.toString();\n });\n\n child.stderr.on('data', (data) => {\n stderr += data.toString();\n });\n\n child.on('error', (error) => {\n reject(\n new AIError(\n `Failed to execute Claude CLI: ${error.message}`,\n 'EXECUTION_ERROR',\n 'claude-cli',\n ),\n );\n });\n\n child.on('close', (code) => {\n if (code !== 0) {\n reject(this.mapCliError(stderr, code || undefined));\n return;\n }\n\n try {\n // Parse JSON output\n const result = JSON.parse(stdout);\n resolve(result);\n } catch (_error) {\n reject(\n new AIError(\n 'Failed to parse CLI output as JSON',\n 'PARSE_ERROR',\n 'claude-cli',\n ),\n );\n }\n });\n });\n }\n\n /**\n * Execute Claude CLI command with streaming output\n * @private\n */\n private async *executeStreamingCommand(\n prompt: string,\n options: {\n model?: string;\n systemPrompt?: string;\n onProgress?: (chunk: string) => void;\n } = {},\n ): AsyncIterable<string> {\n const cliPath = await this.findCli();\n const model = this.normalizeModel(options.model);\n\n const args = [\n '--print',\n '--output-format',\n 'stream-json',\n '--verbose', // Required for stream-json format\n '--model',\n model,\n ];\n\n if (options.systemPrompt) {\n args.push('--system-prompt', options.systemPrompt);\n }\n\n args.push(prompt);\n\n const child = spawn(cliPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n let stderr = '';\n\n child.stderr.on('data', (data) => {\n stderr += data.toString();\n });\n\n // Process streaming JSON chunks\n let buffer = '';\n\n for await (const chunk of child.stdout) {\n buffer += chunk.toString();\n\n // Process complete JSON objects (one per line)\n const lines = buffer.split('\\n');\n buffer = lines.pop() || ''; // Keep incomplete line in buffer\n\n for (const line of lines) {\n if (line.trim()) {\n try {\n const parsed = JSON.parse(line);\n // Claude CLI stream-json format outputs complete messages\n // Look for assistant message with content\n if (parsed.type === 'assistant' && parsed.message?.content) {\n for (const contentBlock of parsed.message.content) {\n if (contentBlock.type === 'text' && contentBlock.text) {\n const text = contentBlock.text;\n if (options.onProgress) {\n options.onProgress(text);\n }\n yield text;\n }\n }\n }\n // Also handle result type for final output\n else if (parsed.type === 'result' && parsed.result) {\n const text = parsed.result;\n if (options.onProgress) {\n options.onProgress(text);\n }\n yield text;\n }\n } catch (_error) {\n // Skip malformed JSON lines\n }\n }\n }\n }\n\n // Wait for process to complete\n const exitCode = await new Promise<number>((resolve) => {\n child.on('close', (code) => resolve(code || 0));\n });\n\n if (exitCode !== 0) {\n throw this.mapCliError(stderr, exitCode);\n }\n }\n\n /**\n * Maps messages to a single prompt for CLI\n * @private\n */\n private mapMessagesToPrompt(messages: AIMessage[]): {\n prompt: string;\n systemPrompt?: string;\n } {\n let systemPrompt: string | undefined;\n const conversationParts: string[] = [];\n\n for (const message of messages) {\n const textContent = extractTextContent(message.content);\n if (message.role === 'system') {\n systemPrompt = systemPrompt\n ? `${systemPrompt}\\n\\n${textContent}`\n : textContent;\n } else if (message.role === 'user') {\n conversationParts.push(`User: ${textContent}`);\n } else if (message.role === 'assistant') {\n conversationParts.push(`Assistant: ${textContent}`);\n }\n }\n\n // If only one user message and no conversation, return it directly\n if (\n conversationParts.length === 1 &&\n conversationParts[0].startsWith('User: ')\n ) {\n return {\n prompt: conversationParts[0].substring(6), // Remove \"User: \" prefix\n systemPrompt,\n };\n }\n\n return {\n prompt: conversationParts.join('\\n\\n'),\n systemPrompt,\n };\n }\n\n /**\n * Maps CLI errors to standardized error types\n * @private\n */\n\n private mapCliError(stderr: string, exitCode?: number): AIError {\n const errorText = stderr.toLowerCase();\n\n // Authentication errors\n if (\n errorText.includes('not authenticated') ||\n errorText.includes('login required')\n ) {\n return new AuthenticationError('claude-cli');\n }\n\n // Rate limiting\n if (\n errorText.includes('rate limit') ||\n errorText.includes('too many requests')\n ) {\n return new RateLimitError('claude-cli');\n }\n\n // Context length\n if (\n errorText.includes('context length') ||\n errorText.includes('too long')\n ) {\n return new ContextLengthError('claude-cli');\n }\n\n return new AIError(\n `Claude CLI error (exit code ${exitCode}): ${stderr}`,\n 'CLI_ERROR',\n 'claude-cli',\n );\n }\n\n /**\n * Generate a chat completion using Claude CLI\n * @param messages - Array of conversation messages\n * @param options - Optional configuration for the chat completion\n * @returns Promise resolving to the AI response with content and metadata\n * @throws {AIError} When the CLI execution fails\n *\n * @example\n * ```typescript\n * const response = await provider.chat([\n * { role: 'system', content: 'You are a helpful assistant.' },\n * { role: 'user', content: 'Explain quantum computing' }\n * ], {\n * model: 'sonnet'\n * });\n * ```\n */\n async chat(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): Promise<AIResponse> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.chat(messages, options);\n }\n\n const startTime = Date.now();\n let { prompt, systemPrompt } = this.mapMessagesToPrompt(messages);\n\n // Add JSON format instruction if requested\n if (options.responseFormat?.type === 'json_object') {\n const jsonInstruction =\n '\\n\\nIMPORTANT: You must respond with valid JSON only. Do not include any explanatory text outside the JSON object.';\n systemPrompt = systemPrompt\n ? systemPrompt + jsonInstruction\n : jsonInstruction.trim();\n }\n\n const result = await this.executeCommand(prompt, {\n model: options.model,\n systemPrompt,\n temperature: options.temperature,\n maxTokens: options.maxTokens,\n });\n\n const model = options.model || this.options.defaultModel;\n const usage: TokenUsage | undefined = result.usage\n ? {\n promptTokens: result.usage.input_tokens || 0,\n completionTokens: result.usage.output_tokens || 0,\n totalTokens:\n (result.usage.input_tokens || 0) +\n (result.usage.output_tokens || 0),\n }\n : undefined;\n\n emitUsage(\n this.options,\n 'claude-cli',\n 'chat',\n model!,\n usage,\n startTime,\n options.usageTags,\n );\n\n // Parse Claude CLI JSON output\n return {\n content:\n result.result ||\n result.content ||\n result.text ||\n JSON.stringify(result),\n model,\n finishReason: result.is_error ? 'stop' : 'stop',\n usage,\n };\n }\n\n /**\n * Generate a text completion (delegates to chat)\n */\n async complete(\n prompt: string,\n options: CompletionOptions = {},\n ): Promise<AIResponse> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.complete(prompt, options);\n }\n\n return this.chat([{ role: 'user', content: prompt }], {\n model: options.model,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n topP: options.topP,\n n: options.n,\n stop: options.stop,\n stream: options.stream,\n onProgress: options.onProgress,\n usageTags: options.usageTags,\n });\n }\n\n /**\n * Simple message interface for single-turn interactions with optional history\n *\n * @param text - The message text to send\n * @param options - Configuration options including history, model, etc.\n * @returns Promise resolving to the response content string\n *\n * @example\n * ```typescript\n * // Simple usage\n * const response = await provider.message('Hello!');\n *\n * // With history\n * const response = await provider.message('What was my question?', {\n * history: [\n * { role: 'user', content: 'What is 2+2?' },\n * { role: 'assistant', content: '4' }\n * ]\n * });\n * ```\n */\n async message(text: string, options: MessageOptions = {}): Promise<string> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.message(text, options);\n }\n\n // Build messages array from history + current message\n const messages: AIMessage[] = [\n ...(options.history || []),\n { role: options.role || 'user', content: text },\n ];\n\n const response = await this.chat(messages, {\n model: options.model,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n topP: options.topP,\n stop: options.stop,\n stream: options.stream,\n frequencyPenalty: options.frequencyPenalty,\n presencePenalty: options.presencePenalty,\n responseFormat: options.responseFormat,\n seed: options.seed,\n tools: options.tools,\n toolChoice: options.toolChoice,\n onProgress: options.onProgress,\n usageTags: options.usageTags,\n });\n\n return response.content;\n }\n\n /**\n * Embeddings are not supported by Claude CLI or Anthropic\n */\n async embed(\n _text: string | string[],\n _options: EmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n // Note: Even with fallback, Anthropic doesn't support embeddings\n throw new AIError(\n 'Claude CLI does not support embeddings. Use OpenAI or another provider for embeddings.',\n 'NOT_SUPPORTED',\n 'claude-cli',\n );\n }\n\n /**\n * Image embeddings are not supported by Claude CLI\n */\n async embedImage(\n _image: string | Buffer,\n _options: ImageEmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n throw new AIError(\n 'Claude CLI does not support image embeddings. Use OpenAI or Gemini.',\n 'NOT_SUPPORTED',\n 'claude-cli',\n );\n }\n\n /**\n * Image description is not yet implemented for Claude CLI\n * Note: Claude supports vision, but this would require significant implementation\n */\n async describeImage(\n _image: string | Buffer,\n _prompt?: string,\n _options: ImageDescriptionOptions = {},\n ): Promise<string> {\n throw new AIError(\n 'Image description is not yet implemented for Claude CLI. Use OpenAI or Gemini.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n /**\n * Image generation is not supported by Claude\n */\n async generateImage(\n _prompt: string,\n _options: ImageGenerationOptions = {},\n ): Promise<ImageGenerationResponse> {\n throw new AIError(\n 'Claude does not support image generation. Use OpenAI or Gemini.',\n 'NOT_SUPPORTED',\n 'claude-cli',\n );\n }\n\n /**\n * Stream chat completion using Claude CLI\n */\n async *stream(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): AsyncIterable<string> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n yield* this.anthropicFallback.stream(messages, options);\n return;\n }\n\n const startTime = Date.now();\n let { prompt, systemPrompt } = this.mapMessagesToPrompt(messages);\n\n // Add JSON format instruction if requested\n if (options.responseFormat?.type === 'json_object') {\n const jsonInstruction =\n '\\n\\nIMPORTANT: You must respond with valid JSON only. Do not include any explanatory text outside the JSON object.';\n systemPrompt = systemPrompt\n ? systemPrompt + jsonInstruction\n : jsonInstruction.trim();\n }\n\n yield* this.executeStreamingCommand(prompt, {\n model: options.model,\n systemPrompt,\n onProgress: options.onProgress,\n });\n\n const model = options.model || this.options.defaultModel;\n emitUsage(\n this.options,\n 'claude-cli',\n 'stream',\n model!,\n undefined,\n startTime,\n options.usageTags,\n );\n }\n\n /**\n * Count tokens in text (approximation)\n */\n async countTokens(text: string): Promise<number> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.countTokens(text);\n }\n\n // Claude uses a different tokenizer, approximate similar to Anthropic\n return Math.ceil(text.length / 3.5);\n }\n\n /**\n * Get available models (static list of Claude models)\n */\n async getModels(): Promise<AIModel[]> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.getModels();\n }\n\n return [\n {\n id: 'sonnet',\n name: 'Claude Sonnet',\n description: 'Balanced Claude model via CLI',\n contextLength: 200000,\n capabilities: ['text', 'chat', 'vision'],\n supportsFunctions: false,\n supportsVision: false,\n },\n {\n id: 'opus',\n name: 'Claude Opus',\n description: 'Most powerful Claude model via CLI',\n contextLength: 200000,\n capabilities: ['text', 'chat'],\n supportsFunctions: false,\n supportsVision: false,\n },\n {\n id: 'haiku',\n name: 'Claude Haiku',\n description: 'Fast Claude model via CLI',\n contextLength: 200000,\n capabilities: ['text', 'chat'],\n supportsFunctions: false,\n supportsVision: false,\n },\n ];\n }\n\n /**\n * Get provider capabilities\n */\n async getCapabilities(): Promise<AICapabilities> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.getCapabilities();\n }\n\n return {\n chat: true,\n completion: true,\n embeddings: false, // Claude CLI doesn't support embeddings\n streaming: true,\n functions: false, // Not supported via CLI\n vision: false, // Not supported in current CLI version\n fineTuning: false,\n imageEmbeddings: false,\n imageGeneration: false,\n videoGeneration: false,\n tts: false,\n voiceCloning: false,\n voiceDesign: false,\n maxContextLength: 200000,\n supportedOperations: ['chat', 'completion', 'streaming'],\n };\n }\n\n // ============================================================================\n // Video Generation Methods (Not supported - use gemini, byteplus-modelark,\n // or openai-compat-video provider)\n // ============================================================================\n\n async submitVideoGenerationJob(\n _options: VideoGenerationOptions,\n ): Promise<VideoGenerationJob> {\n throw new AIError(\n 'Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async getVideoGenerationJob(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationStatusResult> {\n throw new AIError(\n 'Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async fetchVideoGenerationResult(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationResult> {\n throw new AIError(\n 'Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async cancelVideoGenerationJob(_handle: VideoGenerationJob): Promise<void> {\n throw new AIError(\n 'Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async validateVideoGenerationAccess(): Promise<boolean> {\n throw new AIError(\n 'Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n // ============================================================================\n // TTS Methods (Not supported - use Qwen3-TTS provider)\n // ============================================================================\n\n async synthesizeSpeech(\n _text: string,\n _options?: TTSOptions,\n ): Promise<TTSResponse> {\n throw new AIError(\n 'TTS is not supported by Claude CLI provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n streamSpeech(_text: string, _options?: TTSOptions): AsyncIterable<Buffer> {\n const error = new AIError(\n 'TTS streaming is not supported by Claude CLI provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n return {\n [Symbol.asyncIterator]: () => ({\n next: () => Promise.reject(error),\n }),\n };\n }\n\n async cloneVoice(_options: VoiceCloneOptions): Promise<Voice> {\n throw new AIError(\n 'Voice cloning is not supported by Claude CLI provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async designVoice(_options: VoiceDesignOptions): Promise<Voice> {\n throw new AIError(\n 'Voice design is not supported by Claude CLI provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async getVoices(_options?: VoiceListOptions): Promise<Voice[]> {\n throw new AIError(\n 'Voice listing is not supported by Claude CLI provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { t as AIError } from "./types-FHs-KbGL.js";
1
+ import { t as AIError } from "./types-CINwUPDo.js";
2
2
  //#region src/shared/providers/gateway-admin.ts
3
3
  function stripTrailingSlash(value) {
4
4
  return value.endsWith("/") ? value.slice(0, -1) : value;
@@ -280,4 +280,4 @@ var LiteLLMAdmin = class {
280
280
  //#endregion
281
281
  export { resolveGatewayAdminBaseUrl as i, LiteLLMAdmin as n, normalizeGatewayBaseUrl as r, BifrostAdmin as t };
282
282
 
283
- //# sourceMappingURL=gateway-admin-CiKvHst7.js.map
283
+ //# sourceMappingURL=gateway-admin-DJXEDpUP.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-admin-CiKvHst7.js","names":[],"sources":["../../src/shared/providers/gateway-admin.ts"],"sourcesContent":["import type {\n AIAdminBudget,\n AIAdminInterface,\n AIAdminProject,\n AIAdminProviderConfig,\n AIAdminRateLimit,\n AIVirtualKey,\n CreateAIProjectOptions,\n CreateAIVirtualKeyOptions,\n} from '../types';\nimport { AIError } from '../types';\n\ninterface GatewayAdminTransportOptions {\n provider: string;\n baseUrl: string;\n apiKey?: string;\n username?: string;\n password?: string;\n headers?: Record<string, string>;\n timeout?: number;\n}\n\ntype JsonRecord = Record<string, unknown>;\n\nfunction stripTrailingSlash(value: string): string {\n return value.endsWith('/') ? value.slice(0, -1) : value;\n}\n\nexport function normalizeGatewayBaseUrl(baseUrl: string): string {\n return stripTrailingSlash(baseUrl.trim());\n}\n\n/**\n * Derive the gateway root URL for admin APIs from an OpenAI-compatible base URL.\n *\n * Strips known inference path suffixes iteratively so that a baseUrl such as\n * `http://host/openai/v1` collapses to the gateway root rather than to the\n * partial `http://host/openai`.\n */\nexport function deriveGatewayAdminBaseUrl(baseUrl: string): string {\n let normalized = normalizeGatewayBaseUrl(baseUrl);\n const suffixes = ['/pydanticai/v1', '/openai', '/v1'];\n let stripped = true;\n\n while (stripped) {\n stripped = false;\n for (const suffix of suffixes) {\n if (normalized.endsWith(suffix)) {\n normalized = normalized.slice(0, -suffix.length);\n stripped = true;\n break;\n }\n }\n }\n\n return normalized || normalizeGatewayBaseUrl(baseUrl);\n}\n\nexport function resolveGatewayAdminBaseUrl(\n baseUrl: string | undefined,\n adminBaseUrl: string | undefined,\n provider: string,\n): string {\n const configuredBaseUrl = adminBaseUrl || baseUrl;\n if (!configuredBaseUrl?.trim()) {\n throw new AIError(\n `${provider} baseUrl is required for admin operations`,\n 'ADMIN_BASE_URL_REQUIRED',\n provider,\n );\n }\n\n return adminBaseUrl\n ? normalizeGatewayBaseUrl(adminBaseUrl)\n : deriveGatewayAdminBaseUrl(configuredBaseUrl);\n}\n\nexport function slugifyProjectId(name: string, tenantId?: string): string {\n const value = [tenantId, name].filter(Boolean).join('-');\n const slug = value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n\n return slug || 'ai-project';\n}\n\nfunction toJsonRecord(value: unknown): JsonRecord {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? (value as JsonRecord)\n : {};\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction encodeBasicCredentials(username: string, password: string): string {\n const value = `${username}:${password}`;\n\n if (typeof btoa === 'function') {\n return btoa(value);\n }\n\n return Buffer.from(value, 'utf8').toString('base64');\n}\n\nfunction removeUndefinedValues<T extends JsonRecord>(value: T): T {\n for (const key of Object.keys(value)) {\n if (value[key] === undefined) {\n delete value[key];\n }\n }\n\n return value;\n}\n\nfunction createMetadata(\n options: {\n tenantId?: string;\n description?: string;\n metadata?: Record<string, unknown>;\n },\n tenantField = 'tenant_id',\n): Record<string, unknown> | undefined {\n const metadata = {\n ...options.metadata,\n };\n\n if (options.tenantId) {\n metadata[tenantField] = options.tenantId;\n }\n\n if (options.description) {\n metadata.description = options.description;\n }\n\n return Object.keys(metadata).length > 0 ? metadata : undefined;\n}\n\nfunction responseMessage(body: unknown, fallback: string): string {\n const record = toJsonRecord(body);\n const error = toJsonRecord(record.error);\n\n return (\n stringValue(error.message) ||\n stringValue(record.message) ||\n stringValue(record.detail) ||\n fallback\n );\n}\n\nclass GatewayAdminTransport {\n private readonly baseUrl: string;\n private readonly provider: string;\n private readonly apiKey?: string;\n private readonly username?: string;\n private readonly password?: string;\n private readonly headers?: Record<string, string>;\n private readonly timeout?: number;\n\n constructor(options: GatewayAdminTransportOptions) {\n this.provider = options.provider;\n this.baseUrl = normalizeGatewayBaseUrl(options.baseUrl);\n this.apiKey = options.apiKey;\n this.username = options.username;\n this.password = options.password;\n this.headers = options.headers;\n this.timeout = options.timeout;\n }\n\n async request<T>(\n method: string,\n path: string,\n body?: unknown,\n query?: Record<string, string | undefined>,\n ): Promise<T> {\n const url = new URL(\n `${this.baseUrl}${path.startsWith('/') ? path : `/${path}`}`,\n );\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) {\n url.searchParams.set(key, value);\n }\n }\n }\n\n const controller =\n typeof AbortController === 'undefined'\n ? undefined\n : new AbortController();\n const timeoutHandle =\n controller && this.timeout\n ? setTimeout(() => controller.abort(), this.timeout)\n : undefined;\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n Accept: 'application/json',\n ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...this.headers,\n ...this.createAuthHeaders(),\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: controller?.signal,\n });\n\n const text = await response.text();\n const parsed = text ? this.parseJson(text) : undefined;\n\n if (!response.ok) {\n throw this.mapResponseError(response, parsed);\n }\n\n return parsed as T;\n } catch (error) {\n if (error instanceof AIError) {\n throw error;\n }\n\n throw new AIError(\n `${this.provider} admin request failed: ${\n error instanceof Error ? error.message : String(error)\n }`,\n 'ADMIN_REQUEST_FAILED',\n this.provider,\n );\n } finally {\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n }\n }\n\n private createAuthHeaders(): Record<string, string> {\n if (this.username && this.password) {\n return {\n Authorization: `Basic ${encodeBasicCredentials(\n this.username,\n this.password,\n )}`,\n };\n }\n\n return this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {};\n }\n\n private parseJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch (_error) {\n return { message: text };\n }\n }\n\n private mapResponseError(response: Response, body: unknown): AIError {\n const message = responseMessage(\n body,\n `${this.provider} admin request failed with ${response.status}`,\n );\n\n if (response.status === 401 || response.status === 403) {\n return new AIError(message, 'AUTH_ERROR', this.provider);\n }\n\n if (response.status === 429) {\n return new AIError(message, 'RATE_LIMIT', this.provider, undefined, true);\n }\n\n return new AIError(\n message,\n `ADMIN_HTTP_${response.status}`,\n this.provider,\n undefined,\n response.status >= 500,\n );\n }\n}\n\nfunction mapBifrostBudget(budget?: AIAdminBudget): JsonRecord | undefined {\n if (!budget) return undefined;\n\n return removeUndefinedValues({\n max_limit: budget.maxLimit,\n reset_duration: budget.resetDuration,\n calendar_aligned: budget.calendarAligned,\n });\n}\n\nfunction mapBifrostRateLimit(\n rateLimit?: AIAdminRateLimit,\n): JsonRecord | undefined {\n if (!rateLimit) return undefined;\n\n return removeUndefinedValues({\n token_max_limit: rateLimit.tokenMaxLimit ?? rateLimit.tpmLimit,\n token_reset_duration: rateLimit.tokenResetDuration,\n request_max_limit: rateLimit.requestMaxLimit ?? rateLimit.rpmLimit,\n request_reset_duration: rateLimit.requestResetDuration,\n });\n}\n\nfunction mapBifrostProviderConfigs(\n configs?: AIAdminProviderConfig[],\n): JsonRecord[] | undefined {\n if (!configs) return undefined;\n\n return configs.map((config) =>\n removeUndefinedValues({\n provider: config.provider,\n weight: config.weight,\n allowed_models: config.allowedModels,\n key_ids: config.keyIds,\n }),\n );\n}\n\nfunction mapLiteLLMBudget(\n budget?: AIAdminBudget,\n): Pick<JsonRecord, 'max_budget' | 'budget_duration'> {\n return removeUndefinedValues({\n max_budget: budget?.maxLimit,\n budget_duration: budget?.resetDuration,\n });\n}\n\nfunction mapLiteLLMRateLimit(\n rateLimit?: AIAdminRateLimit,\n): Pick<JsonRecord, 'tpm_limit' | 'rpm_limit'> {\n return removeUndefinedValues({\n tpm_limit: rateLimit?.tpmLimit ?? rateLimit?.tokenMaxLimit,\n rpm_limit: rateLimit?.rpmLimit ?? rateLimit?.requestMaxLimit,\n });\n}\n\nexport class BifrostAdmin implements AIAdminInterface {\n private readonly transport: GatewayAdminTransport;\n\n constructor(options: GatewayAdminTransportOptions) {\n this.transport = new GatewayAdminTransport(options);\n }\n\n async createProject(\n options: CreateAIProjectOptions,\n ): Promise<AIAdminProject> {\n const body = removeUndefinedValues({\n name: options.name,\n customer_id: options.tenantId,\n budget: mapBifrostBudget(options.budget),\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/api/governance/teams',\n body,\n );\n const team = toJsonRecord(response.team ?? response);\n const id = stringValue(team.id) || stringValue(team.team_id);\n\n if (!id) {\n throw new AIError(\n 'Bifrost did not return a project id',\n 'ADMIN_INVALID_RESPONSE',\n 'bifrost',\n );\n }\n\n return {\n id,\n name:\n stringValue(team.name) || stringValue(team.team_alias) || options.name,\n tenantId: stringValue(team.customer_id) || options.tenantId,\n budgetId: stringValue(team.budget_id),\n provider: 'bifrost',\n raw: response,\n };\n }\n\n async createVirtualKey(\n options: CreateAIVirtualKeyOptions,\n ): Promise<AIVirtualKey> {\n const body = removeUndefinedValues({\n name: options.name,\n description: options.description,\n provider_configs: mapBifrostProviderConfigs(options.providerConfigs),\n team_id: options.projectId,\n customer_id: options.projectId ? undefined : options.tenantId,\n budget: mapBifrostBudget(options.budget),\n rate_limit: mapBifrostRateLimit(options.rateLimit),\n key_ids: options.keyIds,\n is_active: options.isActive ?? true,\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/api/governance/virtual-keys',\n body,\n );\n const virtualKey = toJsonRecord(\n response.virtual_key ?? response.virtualKey ?? response,\n );\n\n return {\n id: stringValue(virtualKey.id) || stringValue(virtualKey.key_id),\n name: stringValue(virtualKey.name) || options.name,\n key: stringValue(virtualKey.value) || stringValue(virtualKey.key),\n maskedKey:\n stringValue(virtualKey.key_name) || stringValue(virtualKey.masked_key),\n projectId: stringValue(virtualKey.team_id) || options.projectId,\n tenantId: stringValue(virtualKey.customer_id) || options.tenantId,\n expiresAt:\n stringValue(virtualKey.expires) || stringValue(virtualKey.expires_at),\n provider: 'bifrost',\n raw: response,\n };\n }\n}\n\nexport class LiteLLMAdmin implements AIAdminInterface {\n private readonly transport: GatewayAdminTransport;\n\n constructor(options: GatewayAdminTransportOptions) {\n this.transport = new GatewayAdminTransport(options);\n }\n\n async createProject(\n options: CreateAIProjectOptions,\n ): Promise<AIAdminProject> {\n const projectId =\n options.id || slugifyProjectId(options.name, options.tenantId);\n const body = removeUndefinedValues({\n team_id: projectId,\n team_alias: options.name,\n models: options.models,\n ...mapLiteLLMBudget(options.budget),\n ...mapLiteLLMRateLimit(options.rateLimit),\n metadata: createMetadata(options),\n blocked: options.isBlocked,\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/team/new',\n body,\n );\n const team = toJsonRecord(response.team ?? response);\n\n return {\n id: stringValue(team.team_id) || projectId,\n name:\n stringValue(team.team_alias) || stringValue(team.name) || options.name,\n tenantId: options.tenantId,\n provider: 'litellm',\n raw: response,\n };\n }\n\n async createVirtualKey(\n options: CreateAIVirtualKeyOptions,\n ): Promise<AIVirtualKey> {\n const body = removeUndefinedValues({\n key_alias: options.name,\n team_id: options.projectId,\n user_id: options.userId,\n models: options.models,\n duration: options.duration,\n ...mapLiteLLMBudget(options.budget),\n ...mapLiteLLMRateLimit(options.rateLimit),\n metadata: createMetadata(options),\n aliases: options.aliases,\n config: options.config,\n permissions: options.permissions,\n blocked: options.isActive === undefined ? undefined : !options.isActive,\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/key/generate',\n body,\n );\n\n return {\n id: stringValue(response.token_id) || stringValue(response.key_id),\n name:\n stringValue(response.key_alias) ||\n stringValue(response.key_name) ||\n options.name,\n key: stringValue(response.key),\n maskedKey: stringValue(response.key_name),\n projectId: stringValue(response.team_id) || options.projectId,\n tenantId: options.tenantId,\n expiresAt: stringValue(response.expires),\n provider: 'litellm',\n raw: response,\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gateway-admin-DJXEDpUP.js","names":[],"sources":["../../src/shared/providers/gateway-admin.ts"],"sourcesContent":["import type {\n AIAdminBudget,\n AIAdminInterface,\n AIAdminProject,\n AIAdminProviderConfig,\n AIAdminRateLimit,\n AIVirtualKey,\n CreateAIProjectOptions,\n CreateAIVirtualKeyOptions,\n} from '../types';\nimport { AIError } from '../types';\n\ninterface GatewayAdminTransportOptions {\n provider: string;\n baseUrl: string;\n apiKey?: string;\n username?: string;\n password?: string;\n headers?: Record<string, string>;\n timeout?: number;\n}\n\ntype JsonRecord = Record<string, unknown>;\n\nfunction stripTrailingSlash(value: string): string {\n return value.endsWith('/') ? value.slice(0, -1) : value;\n}\n\nexport function normalizeGatewayBaseUrl(baseUrl: string): string {\n return stripTrailingSlash(baseUrl.trim());\n}\n\n/**\n * Derive the gateway root URL for admin APIs from an OpenAI-compatible base URL.\n *\n * Strips known inference path suffixes iteratively so that a baseUrl such as\n * `http://host/openai/v1` collapses to the gateway root rather than to the\n * partial `http://host/openai`.\n */\nexport function deriveGatewayAdminBaseUrl(baseUrl: string): string {\n let normalized = normalizeGatewayBaseUrl(baseUrl);\n const suffixes = ['/pydanticai/v1', '/openai', '/v1'];\n let stripped = true;\n\n while (stripped) {\n stripped = false;\n for (const suffix of suffixes) {\n if (normalized.endsWith(suffix)) {\n normalized = normalized.slice(0, -suffix.length);\n stripped = true;\n break;\n }\n }\n }\n\n return normalized || normalizeGatewayBaseUrl(baseUrl);\n}\n\nexport function resolveGatewayAdminBaseUrl(\n baseUrl: string | undefined,\n adminBaseUrl: string | undefined,\n provider: string,\n): string {\n const configuredBaseUrl = adminBaseUrl || baseUrl;\n if (!configuredBaseUrl?.trim()) {\n throw new AIError(\n `${provider} baseUrl is required for admin operations`,\n 'ADMIN_BASE_URL_REQUIRED',\n provider,\n );\n }\n\n return adminBaseUrl\n ? normalizeGatewayBaseUrl(adminBaseUrl)\n : deriveGatewayAdminBaseUrl(configuredBaseUrl);\n}\n\nexport function slugifyProjectId(name: string, tenantId?: string): string {\n const value = [tenantId, name].filter(Boolean).join('-');\n const slug = value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n\n return slug || 'ai-project';\n}\n\nfunction toJsonRecord(value: unknown): JsonRecord {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? (value as JsonRecord)\n : {};\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction encodeBasicCredentials(username: string, password: string): string {\n const value = `${username}:${password}`;\n\n if (typeof btoa === 'function') {\n return btoa(value);\n }\n\n return Buffer.from(value, 'utf8').toString('base64');\n}\n\nfunction removeUndefinedValues<T extends JsonRecord>(value: T): T {\n for (const key of Object.keys(value)) {\n if (value[key] === undefined) {\n delete value[key];\n }\n }\n\n return value;\n}\n\nfunction createMetadata(\n options: {\n tenantId?: string;\n description?: string;\n metadata?: Record<string, unknown>;\n },\n tenantField = 'tenant_id',\n): Record<string, unknown> | undefined {\n const metadata = {\n ...options.metadata,\n };\n\n if (options.tenantId) {\n metadata[tenantField] = options.tenantId;\n }\n\n if (options.description) {\n metadata.description = options.description;\n }\n\n return Object.keys(metadata).length > 0 ? metadata : undefined;\n}\n\nfunction responseMessage(body: unknown, fallback: string): string {\n const record = toJsonRecord(body);\n const error = toJsonRecord(record.error);\n\n return (\n stringValue(error.message) ||\n stringValue(record.message) ||\n stringValue(record.detail) ||\n fallback\n );\n}\n\nclass GatewayAdminTransport {\n private readonly baseUrl: string;\n private readonly provider: string;\n private readonly apiKey?: string;\n private readonly username?: string;\n private readonly password?: string;\n private readonly headers?: Record<string, string>;\n private readonly timeout?: number;\n\n constructor(options: GatewayAdminTransportOptions) {\n this.provider = options.provider;\n this.baseUrl = normalizeGatewayBaseUrl(options.baseUrl);\n this.apiKey = options.apiKey;\n this.username = options.username;\n this.password = options.password;\n this.headers = options.headers;\n this.timeout = options.timeout;\n }\n\n async request<T>(\n method: string,\n path: string,\n body?: unknown,\n query?: Record<string, string | undefined>,\n ): Promise<T> {\n const url = new URL(\n `${this.baseUrl}${path.startsWith('/') ? path : `/${path}`}`,\n );\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) {\n url.searchParams.set(key, value);\n }\n }\n }\n\n const controller =\n typeof AbortController === 'undefined'\n ? undefined\n : new AbortController();\n const timeoutHandle =\n controller && this.timeout\n ? setTimeout(() => controller.abort(), this.timeout)\n : undefined;\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n Accept: 'application/json',\n ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...this.headers,\n ...this.createAuthHeaders(),\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: controller?.signal,\n });\n\n const text = await response.text();\n const parsed = text ? this.parseJson(text) : undefined;\n\n if (!response.ok) {\n throw this.mapResponseError(response, parsed);\n }\n\n return parsed as T;\n } catch (error) {\n if (error instanceof AIError) {\n throw error;\n }\n\n throw new AIError(\n `${this.provider} admin request failed: ${\n error instanceof Error ? error.message : String(error)\n }`,\n 'ADMIN_REQUEST_FAILED',\n this.provider,\n );\n } finally {\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n }\n }\n\n private createAuthHeaders(): Record<string, string> {\n if (this.username && this.password) {\n return {\n Authorization: `Basic ${encodeBasicCredentials(\n this.username,\n this.password,\n )}`,\n };\n }\n\n return this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {};\n }\n\n private parseJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch (_error) {\n return { message: text };\n }\n }\n\n private mapResponseError(response: Response, body: unknown): AIError {\n const message = responseMessage(\n body,\n `${this.provider} admin request failed with ${response.status}`,\n );\n\n if (response.status === 401 || response.status === 403) {\n return new AIError(message, 'AUTH_ERROR', this.provider);\n }\n\n if (response.status === 429) {\n return new AIError(message, 'RATE_LIMIT', this.provider, undefined, true);\n }\n\n return new AIError(\n message,\n `ADMIN_HTTP_${response.status}`,\n this.provider,\n undefined,\n response.status >= 500,\n );\n }\n}\n\nfunction mapBifrostBudget(budget?: AIAdminBudget): JsonRecord | undefined {\n if (!budget) return undefined;\n\n return removeUndefinedValues({\n max_limit: budget.maxLimit,\n reset_duration: budget.resetDuration,\n calendar_aligned: budget.calendarAligned,\n });\n}\n\nfunction mapBifrostRateLimit(\n rateLimit?: AIAdminRateLimit,\n): JsonRecord | undefined {\n if (!rateLimit) return undefined;\n\n return removeUndefinedValues({\n token_max_limit: rateLimit.tokenMaxLimit ?? rateLimit.tpmLimit,\n token_reset_duration: rateLimit.tokenResetDuration,\n request_max_limit: rateLimit.requestMaxLimit ?? rateLimit.rpmLimit,\n request_reset_duration: rateLimit.requestResetDuration,\n });\n}\n\nfunction mapBifrostProviderConfigs(\n configs?: AIAdminProviderConfig[],\n): JsonRecord[] | undefined {\n if (!configs) return undefined;\n\n return configs.map((config) =>\n removeUndefinedValues({\n provider: config.provider,\n weight: config.weight,\n allowed_models: config.allowedModels,\n key_ids: config.keyIds,\n }),\n );\n}\n\nfunction mapLiteLLMBudget(\n budget?: AIAdminBudget,\n): Pick<JsonRecord, 'max_budget' | 'budget_duration'> {\n return removeUndefinedValues({\n max_budget: budget?.maxLimit,\n budget_duration: budget?.resetDuration,\n });\n}\n\nfunction mapLiteLLMRateLimit(\n rateLimit?: AIAdminRateLimit,\n): Pick<JsonRecord, 'tpm_limit' | 'rpm_limit'> {\n return removeUndefinedValues({\n tpm_limit: rateLimit?.tpmLimit ?? rateLimit?.tokenMaxLimit,\n rpm_limit: rateLimit?.rpmLimit ?? rateLimit?.requestMaxLimit,\n });\n}\n\nexport class BifrostAdmin implements AIAdminInterface {\n private readonly transport: GatewayAdminTransport;\n\n constructor(options: GatewayAdminTransportOptions) {\n this.transport = new GatewayAdminTransport(options);\n }\n\n async createProject(\n options: CreateAIProjectOptions,\n ): Promise<AIAdminProject> {\n const body = removeUndefinedValues({\n name: options.name,\n customer_id: options.tenantId,\n budget: mapBifrostBudget(options.budget),\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/api/governance/teams',\n body,\n );\n const team = toJsonRecord(response.team ?? response);\n const id = stringValue(team.id) || stringValue(team.team_id);\n\n if (!id) {\n throw new AIError(\n 'Bifrost did not return a project id',\n 'ADMIN_INVALID_RESPONSE',\n 'bifrost',\n );\n }\n\n return {\n id,\n name:\n stringValue(team.name) || stringValue(team.team_alias) || options.name,\n tenantId: stringValue(team.customer_id) || options.tenantId,\n budgetId: stringValue(team.budget_id),\n provider: 'bifrost',\n raw: response,\n };\n }\n\n async createVirtualKey(\n options: CreateAIVirtualKeyOptions,\n ): Promise<AIVirtualKey> {\n const body = removeUndefinedValues({\n name: options.name,\n description: options.description,\n provider_configs: mapBifrostProviderConfigs(options.providerConfigs),\n team_id: options.projectId,\n customer_id: options.projectId ? undefined : options.tenantId,\n budget: mapBifrostBudget(options.budget),\n rate_limit: mapBifrostRateLimit(options.rateLimit),\n key_ids: options.keyIds,\n is_active: options.isActive ?? true,\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/api/governance/virtual-keys',\n body,\n );\n const virtualKey = toJsonRecord(\n response.virtual_key ?? response.virtualKey ?? response,\n );\n\n return {\n id: stringValue(virtualKey.id) || stringValue(virtualKey.key_id),\n name: stringValue(virtualKey.name) || options.name,\n key: stringValue(virtualKey.value) || stringValue(virtualKey.key),\n maskedKey:\n stringValue(virtualKey.key_name) || stringValue(virtualKey.masked_key),\n projectId: stringValue(virtualKey.team_id) || options.projectId,\n tenantId: stringValue(virtualKey.customer_id) || options.tenantId,\n expiresAt:\n stringValue(virtualKey.expires) || stringValue(virtualKey.expires_at),\n provider: 'bifrost',\n raw: response,\n };\n }\n}\n\nexport class LiteLLMAdmin implements AIAdminInterface {\n private readonly transport: GatewayAdminTransport;\n\n constructor(options: GatewayAdminTransportOptions) {\n this.transport = new GatewayAdminTransport(options);\n }\n\n async createProject(\n options: CreateAIProjectOptions,\n ): Promise<AIAdminProject> {\n const projectId =\n options.id || slugifyProjectId(options.name, options.tenantId);\n const body = removeUndefinedValues({\n team_id: projectId,\n team_alias: options.name,\n models: options.models,\n ...mapLiteLLMBudget(options.budget),\n ...mapLiteLLMRateLimit(options.rateLimit),\n metadata: createMetadata(options),\n blocked: options.isBlocked,\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/team/new',\n body,\n );\n const team = toJsonRecord(response.team ?? response);\n\n return {\n id: stringValue(team.team_id) || projectId,\n name:\n stringValue(team.team_alias) || stringValue(team.name) || options.name,\n tenantId: options.tenantId,\n provider: 'litellm',\n raw: response,\n };\n }\n\n async createVirtualKey(\n options: CreateAIVirtualKeyOptions,\n ): Promise<AIVirtualKey> {\n const body = removeUndefinedValues({\n key_alias: options.name,\n team_id: options.projectId,\n user_id: options.userId,\n models: options.models,\n duration: options.duration,\n ...mapLiteLLMBudget(options.budget),\n ...mapLiteLLMRateLimit(options.rateLimit),\n metadata: createMetadata(options),\n aliases: options.aliases,\n config: options.config,\n permissions: options.permissions,\n blocked: options.isActive === undefined ? undefined : !options.isActive,\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/key/generate',\n body,\n );\n\n return {\n id: stringValue(response.token_id) || stringValue(response.key_id),\n name:\n stringValue(response.key_alias) ||\n stringValue(response.key_name) ||\n options.name,\n key: stringValue(response.key),\n maskedKey: stringValue(response.key_name),\n projectId: stringValue(response.team_id) || options.projectId,\n tenantId: options.tenantId,\n expiresAt: stringValue(response.expires),\n provider: 'litellm',\n raw: response,\n };\n }\n}\n"],"mappings":"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"}
@@ -1,15 +1,27 @@
1
- import { c as extractTextContent, o as ModelNotFoundError, r as AuthenticationError, s as RateLimitError, t as AIError } from "./types-FHs-KbGL.js";
2
- import { n as extractRetryAfterSeconds } from "./rate-limit-BwyPGXFW.js";
3
- import { a as normalizeBaseAIOptions, c as prepareRequestControls, o as normalizeChatOptions, s as normalizeImageGenerationOptions } from "./safety-CEnoJS6X.js";
1
+ import { c as extractTextContent, o as ModelNotFoundError, r as AuthenticationError, s as RateLimitError, t as AIError } from "./types-CINwUPDo.js";
2
+ import { n as extractRetryAfterSeconds } from "./rate-limit-DgEhanDL.js";
3
+ import { a as normalizeBaseAIOptions, c as prepareRequestControls, o as normalizeChatOptions, s as normalizeImageGenerationOptions } from "./safety-C415clBm.js";
4
4
  import { t as emitUsage } from "./usage-C1Y1Nlg4.js";
5
+ import { join } from "node:path";
5
6
  import crypto from "node:crypto";
7
+ import { mkdtemp, readFile, rm } from "node:fs/promises";
8
+ import { tmpdir } from "node:os";
6
9
  //#region src/shared/providers/gemini.ts
7
10
  /**
8
11
  * Google Gemini provider implementation
9
12
  */
13
+ /**
14
+ * Default Veo model for video generation. Veo 2 / Veo 3.0 are deprecated in
15
+ * favor of Veo 3.1 (still "preview" in its model id, but the current
16
+ * non-deprecated generation per Google's docs as of this writing). Veo 3.1
17
+ * supports the `resolution` config field this provider forwards; Veo 2 does
18
+ * not, so callers pinning back to Veo 2 should omit `resolution`.
19
+ */
20
+ var DEFAULT_VEO_MODEL = "veo-3.1-generate-preview";
10
21
  var GeminiProvider = class {
11
22
  options;
12
23
  client;
24
+ operationCtor;
13
25
  constructor(options) {
14
26
  this.options = normalizeBaseAIOptions({
15
27
  defaultModel: "gemini-2.5-flash",
@@ -19,15 +31,17 @@ var GeminiProvider = class {
19
31
  }
20
32
  initializeClientSync() {
21
33
  try {
22
- import("@google/genai").then(({ GoogleGenAI }) => {
34
+ import("@google/genai").then(({ GoogleGenAI, GenerateVideosOperation }) => {
23
35
  this.client = new GoogleGenAI(this.buildClientConfig());
36
+ this.operationCtor = GenerateVideosOperation;
24
37
  }).catch(() => {});
25
38
  } catch (_error) {}
26
39
  }
27
40
  async ensureClient() {
28
41
  if (!this.client) try {
29
- const { GoogleGenAI } = await import("@google/genai");
42
+ const { GoogleGenAI, GenerateVideosOperation } = await import("@google/genai");
30
43
  this.client = new GoogleGenAI(this.buildClientConfig());
44
+ this.operationCtor = GenerateVideosOperation;
31
45
  } catch (_error) {
32
46
  throw new AIError("Failed to initialize Gemini client. Make sure @google/genai is installed.", "INITIALIZATION_ERROR", "gemini");
33
47
  }
@@ -217,12 +231,13 @@ var GeminiProvider = class {
217
231
  }
218
232
  }
219
233
  /**
220
- * Convert an image to Gemini inline format
221
- * @param image - Image as URL, base64 data URL, or Buffer
222
- * @returns Gemini inline data format
234
+ * Decode an image input (URL, base64 data URL, or Buffer) to raw bytes.
235
+ * Shared by the chat/vision inline-data format and the video-generation
236
+ * `imageBytes` format, which use different wrapper shapes around the same
237
+ * base64 payload.
223
238
  * @private
224
239
  */
225
- async imageToGeminiFormat(image, signal) {
240
+ async decodeImageInput(image, signal) {
226
241
  let mimeType = "image/png";
227
242
  let base64Data;
228
243
  if (Buffer.isBuffer(image)) base64Data = image.toString("base64");
@@ -239,12 +254,44 @@ var GeminiProvider = class {
239
254
  base64Data = Buffer.from(arrayBuffer).toString("base64");
240
255
  mimeType = response.headers.get("content-type") || "image/png";
241
256
  }
257
+ return {
258
+ mimeType,
259
+ base64Data
260
+ };
261
+ }
262
+ /**
263
+ * Convert an image to Gemini's inline-data format, used by chat/vision
264
+ * `contents` parts (`generateContent`, `embedContent`).
265
+ * @param image - Image as URL, base64 data URL, or Buffer
266
+ * @returns Gemini inline data format
267
+ * @private
268
+ */
269
+ async imageToGeminiFormat(image, signal) {
270
+ const { mimeType, base64Data } = await this.decodeImageInput(image, signal);
242
271
  return { inlineData: {
243
272
  mimeType,
244
273
  data: base64Data
245
274
  } };
246
275
  }
247
276
  /**
277
+ * Convert a reference image to the shape `generateVideos`'s `image`
278
+ * parameter expects: `{ imageBytes, mimeType }` (or `{ gcsUri }` for a
279
+ * `gs://` URI in Vertex AI mode). This is a different shape than
280
+ * {@link imageToGeminiFormat}'s `{ inlineData: {...} }` — the SDK's video
281
+ * converters read `imageBytes`/`mimeType`/`gcsUri` directly and silently
282
+ * drop anything else, which previously caused image-to-video requests to
283
+ * convert to `{}` and fall back to text-to-video.
284
+ * @private
285
+ */
286
+ async imageToGeminiVideoFormat(image, signal) {
287
+ if (typeof image === "string" && image.startsWith("gs://")) return { gcsUri: image };
288
+ const { mimeType, base64Data } = await this.decodeImageInput(image, signal);
289
+ return {
290
+ imageBytes: base64Data,
291
+ mimeType
292
+ };
293
+ }
294
+ /**
248
295
  * Generate a text description of an image
249
296
  * @param image - Image as URL, base64 data URL, or Buffer
250
297
  * @param prompt - Custom prompt for description (optional)
@@ -367,6 +414,240 @@ var GeminiProvider = class {
367
414
  controls?.cleanup();
368
415
  }
369
416
  }
417
+ /**
418
+ * Submit an asynchronous video-generation job using Veo.
419
+ *
420
+ * Uses the same `@google/genai` client and credentials as {@link generateImage}.
421
+ * Returns a serializable handle immediately; the render itself runs as a
422
+ * long-running operation that callers poll via {@link getVideoGenerationJob}.
423
+ *
424
+ * @param options - Prompt, reference image(s), and generation parameters
425
+ * @returns Promise resolving to a serializable job handle
426
+ *
427
+ * @example
428
+ * ```typescript
429
+ * const job = await provider.submitVideoGenerationJob({
430
+ * prompt: 'A neon hologram of a cat driving at top speed',
431
+ * durationSeconds: 8,
432
+ * });
433
+ * ```
434
+ */
435
+ async submitVideoGenerationJob(options) {
436
+ const startTime = Date.now();
437
+ let controls;
438
+ try {
439
+ await this.ensureClient();
440
+ const model = options.model || DEFAULT_VEO_MODEL;
441
+ controls = prepareRequestControls(this.options, options);
442
+ let image;
443
+ const firstReference = options.referenceImages?.[0];
444
+ if (firstReference) image = await this.imageToGeminiVideoFormat(firstReference.image, controls.signal);
445
+ const config = Object.fromEntries(Object.entries({
446
+ abortSignal: controls.signal,
447
+ httpOptions: {
448
+ timeout: controls.timeout,
449
+ retryOptions: { attempts: (this.options.maxRetries || 0) + 1 }
450
+ },
451
+ numberOfVideos: 1,
452
+ durationSeconds: options.durationSeconds,
453
+ fps: options.fps,
454
+ seed: options.seed,
455
+ aspectRatio: options.aspectRatio,
456
+ resolution: options.resolution,
457
+ negativePrompt: options.negativePrompt
458
+ }).filter(([, value]) => value !== void 0));
459
+ const operation = await this.client.models.generateVideos({
460
+ model,
461
+ prompt: options.prompt,
462
+ image,
463
+ config
464
+ });
465
+ if (!operation.name) throw new AIError("Gemini did not return an operation name for the video-generation job", "VIDEO_JOB_SUBMIT_FAILED", "gemini", model);
466
+ emitUsage(this.options, "gemini", "submitVideoGenerationJob", model, void 0, startTime, {
467
+ ...options.durationSeconds !== void 0 ? { durationSeconds: String(options.durationSeconds) } : {},
468
+ ...options.resolution ? { resolution: options.resolution } : {},
469
+ ...options.aspectRatio ? { aspectRatio: options.aspectRatio } : {},
470
+ ...options.usageTags
471
+ });
472
+ return {
473
+ jobId: operation.name,
474
+ provider: "gemini",
475
+ model,
476
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
477
+ raw: { vertexai: Boolean(this.options.projectId && this.options.location) }
478
+ };
479
+ } catch (error) {
480
+ if (controls?.didTimeout()) throw new AIError(`AI request timed out after ${controls.timeout}ms`, "AI_TIMEOUT", "gemini", options.model);
481
+ if (options.signal?.aborted) throw new AIError("AI request aborted by caller", "AI_ABORTED", "gemini", options.model);
482
+ throw this.mapError(error);
483
+ } finally {
484
+ controls?.cleanup();
485
+ }
486
+ }
487
+ /**
488
+ * Build a real `GenerateVideosOperation` instance for polling, and assert
489
+ * the handle is being resumed against a provider configured in the same
490
+ * mode (Google AI Studio vs. Vertex AI) it was submitted in.
491
+ * @private
492
+ */
493
+ buildVideoOperation(handle) {
494
+ if (!this.operationCtor) throw new AIError("Gemini client failed to initialize the video-operation constructor. Make sure @google/genai is installed.", "INITIALIZATION_ERROR", "gemini", handle.model);
495
+ this.assertResumeModeMatches(handle);
496
+ const operation = new this.operationCtor();
497
+ operation.name = handle.jobId;
498
+ return operation;
499
+ }
500
+ /**
501
+ * A handle submitted in Vertex AI mode cannot be resumed against a
502
+ * provider configured for Google AI Studio (or vice versa): the SDK
503
+ * routes `operations.getVideosOperation` based on how *this* client was
504
+ * constructed, not how the job was submitted, so a mode mismatch would
505
+ * otherwise surface as a confusing 404 deep in the SDK.
506
+ * @private
507
+ */
508
+ assertResumeModeMatches(handle) {
509
+ const submittedVertexAI = handle.raw?.vertexai;
510
+ if (typeof submittedVertexAI !== "boolean") return;
511
+ const currentVertexAI = Boolean(this.options.projectId && this.options.location);
512
+ if (submittedVertexAI !== currentVertexAI) throw new AIError(`Video-generation job ${handle.jobId} was submitted in ${submittedVertexAI ? "Vertex AI" : "Google AI Studio"} mode, but this provider instance is configured for ${currentVertexAI ? "Vertex AI" : "Google AI Studio"} mode. Resume with a provider configured the same way it was submitted.`, "VIDEO_JOB_MODE_MISMATCH", "gemini", handle.model);
513
+ }
514
+ /**
515
+ * Poll the status of a Veo video-generation job.
516
+ *
517
+ * On success, `result` carries the provider's `url` (a `files/*:download`
518
+ * resource the caller cannot fetch directly without this provider's API
519
+ * key) plus `mimeType`, but not `data` — call
520
+ * {@link fetchVideoGenerationResult} to download the bytes.
521
+ *
522
+ * @param handle - The job handle returned by {@link submitVideoGenerationJob}
523
+ * @returns Promise resolving to the current status, and result once succeeded
524
+ */
525
+ async getVideoGenerationJob(handle) {
526
+ try {
527
+ await this.ensureClient();
528
+ const operation = this.buildVideoOperation(handle);
529
+ const updated = await this.client.operations.getVideosOperation({ operation });
530
+ return this.mapVideoOperation(updated);
531
+ } catch (error) {
532
+ throw this.mapError(error);
533
+ }
534
+ }
535
+ /**
536
+ * Fetch the result of a completed Veo video-generation job, downloading
537
+ * the rendered bytes with this provider's API key (the `uri` Gemini
538
+ * returns is a `files/*:download` resource that requires the same
539
+ * credentials this provider already holds; a bare consumer cannot fetch
540
+ * it directly).
541
+ *
542
+ * @param handle - The job handle returned by {@link submitVideoGenerationJob}
543
+ * @returns Promise resolving to the generated video's bytes and metadata
544
+ * @throws {AIError} When the job has not succeeded yet
545
+ */
546
+ async fetchVideoGenerationResult(handle) {
547
+ try {
548
+ await this.ensureClient();
549
+ const operation = this.buildVideoOperation(handle);
550
+ const updated = await this.client.operations.getVideosOperation({ operation });
551
+ const status = this.mapVideoOperation(updated);
552
+ if (status.status !== "succeeded" || !status.result) throw new AIError(`Gemini video-generation job ${handle.jobId} has not succeeded (status: ${status.status})`, "VIDEO_JOB_NOT_READY", "gemini", handle.model);
553
+ const video = updated.response?.generatedVideos?.[0]?.video;
554
+ const mimeType = status.result.mimeType;
555
+ if (video?.videoBytes) return {
556
+ url: status.result.url,
557
+ data: Buffer.from(video.videoBytes, "base64"),
558
+ mimeType
559
+ };
560
+ return {
561
+ url: status.result.url,
562
+ data: await this.downloadGeneratedVideo(video),
563
+ mimeType
564
+ };
565
+ } catch (error) {
566
+ throw this.mapError(error);
567
+ }
568
+ }
569
+ /**
570
+ * Download a generated video's bytes via `ai.files.download`, which
571
+ * resolves the `files/*:download` resource with this provider's API key
572
+ * (matching Google's own documented usage pattern). The SDK's `download`
573
+ * method only writes to a filesystem path, so this round-trips through a
574
+ * temporary file and reads it back into memory.
575
+ * @private
576
+ */
577
+ async downloadGeneratedVideo(video) {
578
+ if (!video?.uri) throw new AIError("Gemini video-generation job succeeded with no downloadable output", "VIDEO_JOB_NOT_READY", "gemini");
579
+ const dir = await mkdtemp(join(tmpdir(), "have-ai-veo-"));
580
+ const filePath = join(dir, "video.mp4");
581
+ try {
582
+ await this.client.files.download({
583
+ file: video,
584
+ downloadPath: filePath
585
+ });
586
+ return await readFile(filePath);
587
+ } finally {
588
+ await rm(dir, {
589
+ recursive: true,
590
+ force: true
591
+ }).catch(() => {});
592
+ }
593
+ }
594
+ /**
595
+ * Cancel an in-flight Veo video-generation job.
596
+ *
597
+ * **The Gemini API has no cancel endpoint for video-generation
598
+ * operations.** The generativelanguage v1beta discovery document only
599
+ * defines `batches.cancel` (unrelated to video jobs); there is no
600
+ * `models.operations.cancel`. This always throws — cancellation of a
601
+ * Veo render is unsupported by the provider itself, not merely by this
602
+ * client. Callers must treat cancellation as best-effort across all
603
+ * video-generation providers and tolerate this failure (e.g. by simply
604
+ * discarding the handle and not billing for further polling).
605
+ *
606
+ * @param handle - The job handle returned by {@link submitVideoGenerationJob}
607
+ * @throws {AIError} Always — cancellation is unsupported by the Gemini API
608
+ */
609
+ async cancelVideoGenerationJob(handle) {
610
+ if (this.options.projectId && this.options.location) throw new AIError("Cancelling Gemini video-generation jobs is not implemented for Vertex AI mode by this provider (it requires OAuth credentials this provider does not manage).", "NOT_IMPLEMENTED", "gemini", handle.model);
611
+ throw new AIError("The Gemini API has no cancel endpoint for video-generation operations (only batches.cancel exists, which does not apply to Veo jobs). cancelVideoGenerationJob cannot stop an in-flight Veo render on this provider; treat cancellation as best-effort and stop polling / discard the handle instead.", "VIDEO_CANCEL_UNSUPPORTED", "gemini", handle.model);
612
+ }
613
+ /**
614
+ * Cheap auth-shaped check for Veo access: lists models with a page size
615
+ * of 1. Callers on a hot path must cache the result themselves.
616
+ *
617
+ * @returns Promise resolving to true when access looks valid
618
+ */
619
+ async validateVideoGenerationAccess() {
620
+ try {
621
+ await this.ensureClient();
622
+ await this.client.models.list({ config: { pageSize: 1 } });
623
+ return true;
624
+ } catch (error) {
625
+ throw this.mapError(error);
626
+ }
627
+ }
628
+ mapVideoOperation(operation) {
629
+ if (!operation.done) return { status: "running" };
630
+ if (operation.error) {
631
+ const errorInfo = operation.error;
632
+ const message = typeof errorInfo.message === "string" ? errorInfo.message : JSON.stringify(operation.error);
633
+ return errorInfo.code === 1 || /cancel/i.test(message) ? { status: "cancelled" } : {
634
+ status: "failed",
635
+ error: message
636
+ };
637
+ }
638
+ const video = operation.response?.generatedVideos?.[0]?.video;
639
+ if (!video) return {
640
+ status: "failed",
641
+ error: "Gemini reported the video-generation job as done with no output"
642
+ };
643
+ return {
644
+ status: "succeeded",
645
+ result: {
646
+ url: video.uri,
647
+ mimeType: video.mimeType || "video/mp4"
648
+ }
649
+ };
650
+ }
370
651
  async *stream(messages, options = {}) {
371
652
  const startTime = Date.now();
372
653
  let controls;
@@ -484,6 +765,7 @@ var GeminiProvider = class {
484
765
  fineTuning: false,
485
766
  imageEmbeddings: true,
486
767
  imageGeneration: true,
768
+ videoGeneration: true,
487
769
  tts: false,
488
770
  voiceCloning: false,
489
771
  voiceDesign: false,
@@ -496,7 +778,8 @@ var GeminiProvider = class {
496
778
  "functions",
497
779
  "vision",
498
780
  "image_embedding",
499
- "image_generation"
781
+ "image_generation",
782
+ "video_generation"
500
783
  ]
501
784
  };
502
785
  }
@@ -590,4 +873,4 @@ var GeminiProvider = class {
590
873
  //#endregion
591
874
  export { GeminiProvider };
592
875
 
593
- //# sourceMappingURL=gemini-Bu7Fke7c.js.map
876
+ //# sourceMappingURL=gemini-BT8fjXWz.js.map