@happyvertical/ai 0.96.1 → 0.98.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -3
- package/dist/chunks/{anthropic-CD9Ud7a5.js → anthropic-6ZDF32jo.js} +181 -27
- package/dist/chunks/anthropic-6ZDF32jo.js.map +1 -0
- package/dist/chunks/{bedrock-Bv1qCquH.js → bedrock-BQi3Z9jR.js} +71 -21
- package/dist/chunks/bedrock-BQi3Z9jR.js.map +1 -0
- package/dist/chunks/{bifrost-DzT2J0Lq.js → bifrost-DsI2I4ka.js} +9 -6
- package/dist/chunks/bifrost-DsI2I4ka.js.map +1 -0
- package/dist/chunks/{byteplus-modelark-BIEXaTWw.js → byteplus-modelark-Dq8AgCQD.js} +7 -5
- package/dist/chunks/byteplus-modelark-Dq8AgCQD.js.map +1 -0
- package/dist/chunks/{claude-cli-Chfnckyj.js → claude-cli-DIFxvG3c.js} +7 -5
- package/dist/chunks/claude-cli-DIFxvG3c.js.map +1 -0
- package/dist/chunks/continuation-D0NisZA1.js +209 -0
- package/dist/chunks/continuation-D0NisZA1.js.map +1 -0
- package/dist/chunks/{gateway-admin-04mvbDHC.js → gateway-audio-models-Dn7Wrc6P.js} +53 -3
- package/dist/chunks/gateway-audio-models-Dn7Wrc6P.js.map +1 -0
- package/dist/chunks/{gemini-BGwrHeGc.js → gemini-BLFwWN90.js} +131 -13
- package/dist/chunks/gemini-BLFwWN90.js.map +1 -0
- package/dist/chunks/{huggingface-mthYVbLR.js → huggingface-BuRwlABZ.js} +8 -6
- package/dist/chunks/huggingface-BuRwlABZ.js.map +1 -0
- package/dist/chunks/{litellm-xCO8QlGI.js → litellm-CpfCT6jN.js} +9 -6
- package/dist/chunks/litellm-CpfCT6jN.js.map +1 -0
- package/dist/chunks/{ollama-Bedt8Agy.js → ollama-CnrskTUg.js} +38 -7
- package/dist/chunks/ollama-CnrskTUg.js.map +1 -0
- package/dist/chunks/{openai-compat-video-B-xVe9bW.js → openai-compat-video-CJIDdkUO.js} +7 -5
- package/dist/chunks/openai-compat-video-CJIDdkUO.js.map +1 -0
- package/dist/chunks/{qwen-tts-D-UmiMMy.js → qwen-tts-PzEkWcEb.js} +2 -2
- package/dist/chunks/{qwen-tts-D-UmiMMy.js.map → qwen-tts-PzEkWcEb.js.map} +1 -1
- package/dist/chunks/rate-limit-BzWfHA0e.js +713 -0
- package/dist/chunks/rate-limit-BzWfHA0e.js.map +1 -0
- package/dist/chunks/{seevio-BJRiguLc.js → seevio-D2y5Io7C.js} +4 -5
- package/dist/chunks/seevio-D2y5Io7C.js.map +1 -0
- package/dist/chunks/{src-BUQUZK2M.js → src-CLT6r1W0.js} +36 -23
- package/dist/chunks/src-CLT6r1W0.js.map +1 -0
- package/dist/chunks/tool-messages-0ND35mYX.js +57 -0
- package/dist/chunks/tool-messages-0ND35mYX.js.map +1 -0
- package/dist/chunks/{types-BpnNcTU1.js → types-DB5W6_F-.js} +37 -5
- package/dist/chunks/types-DB5W6_F-.js.map +1 -0
- package/dist/chunks/{typesafe-B0gPZQJg.js → typesafe-CVUqQ8N1.js} +7 -5
- package/dist/chunks/typesafe-CVUqQ8N1.js.map +1 -0
- package/dist/index.js +4 -4
- package/dist/node.js +5 -5
- package/dist/shared/continuation.d.ts +51 -0
- package/dist/shared/continuation.d.ts.map +1 -0
- package/dist/shared/factory.d.ts.map +1 -1
- package/dist/shared/providers/anthropic.d.ts +53 -0
- package/dist/shared/providers/anthropic.d.ts.map +1 -1
- package/dist/shared/providers/bedrock.d.ts +13 -0
- package/dist/shared/providers/bedrock.d.ts.map +1 -1
- package/dist/shared/providers/bifrost.d.ts.map +1 -1
- package/dist/shared/providers/claude-cli.d.ts.map +1 -1
- package/dist/shared/providers/gateway-audio-models.d.ts +24 -0
- package/dist/shared/providers/gateway-audio-models.d.ts.map +1 -0
- package/dist/shared/providers/gemini.d.ts +28 -0
- package/dist/shared/providers/gemini.d.ts.map +1 -1
- package/dist/shared/providers/huggingface.d.ts.map +1 -1
- package/dist/shared/providers/litellm.d.ts.map +1 -1
- package/dist/shared/providers/ollama.d.ts.map +1 -1
- package/dist/shared/providers/openai.d.ts.map +1 -1
- package/dist/shared/providers/seevio.d.ts.map +1 -1
- package/dist/shared/providers/typesafe.d.ts.map +1 -1
- package/dist/shared/rate-limit.d.ts +19 -1
- package/dist/shared/rate-limit.d.ts.map +1 -1
- package/dist/shared/safety.d.ts.map +1 -1
- package/dist/shared/tool-messages.d.ts +27 -0
- package/dist/shared/tool-messages.d.ts.map +1 -0
- package/dist/shared/types.d.ts +191 -22
- package/dist/shared/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/chunks/anthropic-CD9Ud7a5.js.map +0 -1
- package/dist/chunks/bedrock-Bv1qCquH.js.map +0 -1
- package/dist/chunks/bifrost-DzT2J0Lq.js.map +0 -1
- package/dist/chunks/byteplus-modelark-BIEXaTWw.js.map +0 -1
- package/dist/chunks/claude-cli-Chfnckyj.js.map +0 -1
- package/dist/chunks/gateway-admin-04mvbDHC.js.map +0 -1
- package/dist/chunks/gemini-BGwrHeGc.js.map +0 -1
- package/dist/chunks/huggingface-mthYVbLR.js.map +0 -1
- package/dist/chunks/litellm-xCO8QlGI.js.map +0 -1
- package/dist/chunks/ollama-Bedt8Agy.js.map +0 -1
- package/dist/chunks/openai-compat-video-B-xVe9bW.js.map +0 -1
- package/dist/chunks/rate-limit-BVW_1Tey.js +0 -202
- package/dist/chunks/rate-limit-BVW_1Tey.js.map +0 -1
- package/dist/chunks/safety-BKswBoqy.js +0 -279
- package/dist/chunks/safety-BKswBoqy.js.map +0 -1
- package/dist/chunks/seevio-BJRiguLc.js.map +0 -1
- package/dist/chunks/src-BUQUZK2M.js.map +0 -1
- package/dist/chunks/types-BpnNcTU1.js.map +0 -1
- package/dist/chunks/typesafe-B0gPZQJg.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"huggingface-mthYVbLR.js","names":[],"sources":["../../src/shared/providers/huggingface.ts"],"sourcesContent":["/**\n * Hugging Face provider implementation\n */\n\nimport {\n normalizeBaseAIOptions,\n normalizeChatOptions,\n type PreparedRequestControls,\n prepareRequestControls,\n} from '../safety';\nimport type {\n AICapabilities,\n AIInterface,\n AIMessage,\n AIModel,\n AIResponse,\n ChatOptions,\n CompletionOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n HuggingFaceOptions,\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 ModelNotFoundError,\n RateLimitError,\n} from '../types';\nimport { emitUsage } from './usage';\n\nexport class HuggingFaceProvider implements AIInterface {\n private options: HuggingFaceOptions;\n private baseUrl: string;\n\n constructor(options: HuggingFaceOptions) {\n this.options = normalizeBaseAIOptions({\n defaultModel: 'microsoft/DialoGPT-medium',\n useCache: true,\n waitForModel: true,\n ...options,\n });\n\n this.baseUrl =\n this.options.endpoint || 'https://api-inference.huggingface.co';\n }\n\n async chat(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): Promise<AIResponse> {\n const startTime = Date.now();\n let controls: PreparedRequestControls | undefined;\n try {\n // Convert messages to a single prompt for text generation models\n const prompt = this.messagesToPrompt(messages);\n const model =\n options.model || this.options.model || this.options.defaultModel;\n options = normalizeChatOptions(\n this.options,\n options,\n 'huggingface',\n model,\n );\n controls = prepareRequestControls(this.options, options);\n\n const response = await this.makeRequest(\n `/models/${model}`,\n {\n inputs: prompt,\n parameters: {\n max_new_tokens: options.maxTokens,\n temperature: options.temperature ?? 1.0,\n top_p: options.topP ?? 1.0,\n do_sample:\n (options.temperature && options.temperature > 0) || false,\n stop_sequences: Array.isArray(options.stop)\n ? options.stop\n : options.stop\n ? [options.stop]\n : undefined,\n },\n options: {\n use_cache: this.options.useCache,\n wait_for_model: this.options.waitForModel,\n },\n },\n controls.signal,\n );\n\n if (Array.isArray(response) && response[0]?.generated_text) {\n const generatedText = response[0].generated_text;\n // Remove the input prompt from the response\n const content = generatedText.replace(prompt, '').trim();\n\n emitUsage(\n this.options,\n 'huggingface',\n 'chat',\n model!,\n undefined,\n startTime,\n options.usageTags,\n );\n\n return {\n content,\n model,\n finishReason: 'stop',\n };\n }\n\n throw new AIError(\n 'Invalid response format from Hugging Face',\n 'INVALID_RESPONSE',\n 'huggingface',\n );\n } catch (error) {\n if (controls?.didTimeout()) {\n throw new AIError(\n `AI request timed out after ${controls.timeout}ms`,\n 'AI_TIMEOUT',\n 'huggingface',\n options.model,\n );\n }\n if (options.signal?.aborted) {\n throw new AIError(\n 'AI request aborted by caller',\n 'AI_ABORTED',\n 'huggingface',\n options.model,\n );\n }\n throw this.mapError(error);\n } finally {\n controls?.cleanup();\n }\n }\n\n async complete(\n prompt: string,\n options: CompletionOptions = {},\n ): Promise<AIResponse> {\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 signal: options.signal,\n timeout: options.timeout,\n reasoning: options.reasoning,\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 async message(text: string, options: MessageOptions = {}): Promise<string> {\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 signal: options.signal,\n timeout: options.timeout,\n reasoning: options.reasoning,\n usageTags: options.usageTags,\n });\n\n return response.content;\n }\n\n async embed(\n text: string | string[],\n options: EmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n const startTime = Date.now();\n try {\n const input = Array.isArray(text) ? text : [text];\n const model = options.model || 'sentence-transformers/all-MiniLM-L6-v2';\n\n const response = await this.makeRequest(`/models/${model}`, {\n inputs: input,\n options: {\n use_cache: this.options.useCache,\n wait_for_model: this.options.waitForModel,\n },\n });\n\n // Handle different response formats from different embedding models\n let embeddings: number[][];\n if (Array.isArray(response) && Array.isArray(response[0])) {\n // Direct array of embeddings\n embeddings = Array.isArray(text) ? response : [response[0]];\n } else if (\n response &&\n typeof response === 'object' &&\n response.embeddings\n ) {\n // Response with embeddings property\n embeddings = response.embeddings;\n } else {\n throw new AIError(\n 'Invalid embedding response format',\n 'INVALID_RESPONSE',\n 'huggingface',\n );\n }\n\n emitUsage(\n this.options,\n 'huggingface',\n 'embed',\n model,\n undefined,\n startTime,\n options.usageTags,\n );\n\n return {\n embeddings,\n model,\n };\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n async embedImage(\n _image: string | Buffer,\n _options: ImageEmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n throw new AIError(\n 'Hugging Face image embeddings not implemented. Use OpenAI or Gemini.',\n 'NOT_IMPLEMENTED',\n 'huggingface',\n );\n }\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 implemented for Hugging Face. Use OpenAI or Gemini.',\n 'NOT_IMPLEMENTED',\n 'huggingface',\n );\n }\n\n async generateImage(\n _prompt: string,\n _options: ImageGenerationOptions = {},\n ): Promise<ImageGenerationResponse> {\n // Note: Hugging Face has image generation models (Stable Diffusion), but we're keeping this as a stub\n throw new AIError(\n 'Image generation is not implemented for Hugging Face. Use OpenAI or Gemini.',\n 'NOT_IMPLEMENTED',\n 'huggingface',\n );\n }\n\n async *stream(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): AsyncIterable<string> {\n // Hugging Face Inference API doesn't support streaming for most models\n // Fall back to regular completion and yield the result\n const startTime = Date.now();\n const response = await this.chat(messages, {\n ...options,\n _skipUsage: true,\n } as any);\n\n // Simulate streaming by yielding chunks\n const content = response.content;\n const chunkSize = 10;\n\n for (let i = 0; i < content.length; i += chunkSize) {\n if (options.signal?.aborted) {\n throw new AIError(\n 'AI request aborted by caller',\n 'AI_ABORTED',\n 'huggingface',\n options.model,\n );\n }\n const chunk = content.slice(i, i + chunkSize);\n if (options.onProgress) {\n options.onProgress(chunk);\n }\n yield chunk;\n\n // Add small delay to simulate streaming\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n\n const model = options.model || this.options.defaultModel || 'gpt2';\n emitUsage(\n this.options,\n 'huggingface',\n 'stream',\n model,\n undefined,\n startTime,\n options.usageTags,\n );\n }\n\n async countTokens(text: string): Promise<number> {\n // Approximation - Hugging Face models use different tokenizers\n return Math.ceil(text.length / 4);\n }\n\n async getModels(): Promise<AIModel[]> {\n // Return some popular text generation models available on Hugging Face\n return [\n {\n id: 'microsoft/DialoGPT-medium',\n name: 'DialoGPT Medium',\n description: 'Conversational AI model by Microsoft',\n contextLength: 1024,\n capabilities: ['text', 'chat'],\n supportsFunctions: false,\n supportsVision: false,\n },\n {\n id: 'microsoft/DialoGPT-large',\n name: 'DialoGPT Large',\n description: 'Large conversational AI model by Microsoft',\n contextLength: 1024,\n capabilities: ['text', 'chat'],\n supportsFunctions: false,\n supportsVision: false,\n },\n {\n id: 'facebook/blenderbot-400M-distill',\n name: 'BlenderBot 400M',\n description: 'Conversational AI model by Meta',\n contextLength: 512,\n capabilities: ['text', 'chat'],\n supportsFunctions: false,\n supportsVision: false,\n },\n {\n id: 'gpt2',\n name: 'GPT-2',\n description: 'OpenAI GPT-2 model',\n contextLength: 1024,\n capabilities: ['text', 'completion'],\n supportsFunctions: false,\n supportsVision: false,\n },\n {\n id: 'sentence-transformers/all-MiniLM-L6-v2',\n name: 'All-MiniLM-L6-v2',\n description: 'Sentence embedding model',\n contextLength: 512,\n capabilities: ['embeddings'],\n supportsFunctions: false,\n supportsVision: false,\n },\n ];\n }\n\n async getCapabilities(): Promise<AICapabilities> {\n return {\n chat: true,\n completion: true,\n embeddings: true,\n streaming: true,\n functions: false, // Most HF models don't support function calling\n vision: false, // Limited vision model support\n fineTuning: true, // Via Hugging Face training API\n imageEmbeddings: false,\n imageGeneration: false, // Stable Diffusion exists but not implemented\n videoGeneration: false,\n tts: false,\n voiceCloning: false,\n voiceDesign: false,\n maxContextLength: 2048,\n supportedOperations: ['chat', 'completion', 'embedding', '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 HuggingFace provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'huggingface',\n );\n }\n\n async getVideoGenerationJob(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationStatusResult> {\n throw new AIError(\n 'Video generation is not supported by HuggingFace provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'huggingface',\n );\n }\n\n async fetchVideoGenerationResult(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationResult> {\n throw new AIError(\n 'Video generation is not supported by HuggingFace provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'huggingface',\n );\n }\n\n async cancelVideoGenerationJob(_handle: VideoGenerationJob): Promise<void> {\n throw new AIError(\n 'Video generation is not supported by HuggingFace provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'huggingface',\n );\n }\n\n async validateVideoGenerationAccess(): Promise<boolean> {\n throw new AIError(\n 'Video generation is not supported by HuggingFace provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'huggingface',\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 HuggingFace provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'huggingface',\n );\n }\n\n streamSpeech(_text: string, _options?: TTSOptions): AsyncIterable<Buffer> {\n const error = new AIError(\n 'TTS streaming is not supported by HuggingFace provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'huggingface',\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 HuggingFace provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'huggingface',\n );\n }\n\n async designVoice(_options: VoiceDesignOptions): Promise<Voice> {\n throw new AIError(\n 'Voice design is not supported by HuggingFace provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'huggingface',\n );\n }\n\n async getVoices(_options?: VoiceListOptions): Promise<Voice[]> {\n throw new AIError(\n 'Voice listing is not supported by HuggingFace provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'huggingface',\n );\n }\n\n private messagesToPrompt(messages: AIMessage[]): string {\n // Convert chat messages to a single prompt format\n return `${messages\n .map((message) => {\n const textContent = extractTextContent(message.content);\n switch (message.role) {\n case 'system':\n return `System: ${textContent}`;\n case 'user':\n return `Human: ${textContent}`;\n case 'assistant':\n return `Assistant: ${textContent}`;\n default:\n return textContent;\n }\n })\n .join('\\n')}\\nAssistant:`;\n }\n\n private async makeRequest(\n endpoint: string,\n data: any,\n signal?: AbortSignal,\n ): Promise<any> {\n const url = `${this.baseUrl}${endpoint}`;\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.options.apiToken}`,\n 'Content-Type': 'application/json',\n ...this.options.headers,\n },\n body: JSON.stringify(data),\n signal,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`HTTP ${response.status}: ${errorText}`);\n }\n\n return response.json();\n }\n\n private mapError(error: unknown): AIError {\n if (error instanceof AIError) {\n return error;\n }\n\n const message = error instanceof Error ? error.message : 'Unknown error';\n\n // Map common HTTP status codes\n if (message.includes('401') || message.includes('Unauthorized')) {\n return new AuthenticationError('huggingface');\n }\n\n if (message.includes('429') || message.includes('rate limit')) {\n return new RateLimitError('huggingface');\n }\n\n if (message.includes('404') || message.includes('not found')) {\n return new ModelNotFoundError(message, 'huggingface');\n }\n\n if (message.includes('413') || message.includes('too large')) {\n return new ContextLengthError('huggingface');\n }\n\n return new AIError(message, 'UNKNOWN_ERROR', 'huggingface');\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"litellm-xCO8QlGI.js","names":[],"sources":["../../src/shared/providers/litellm.ts"],"sourcesContent":["import { ValidationError } from '@happyvertical/utils';\n\nimport {\n normalizeChatOptions,\n normalizeImageGenerationOptions,\n} from '../safety';\nimport type {\n AICapabilities,\n AIMessage,\n AIModel,\n AIResponse,\n ChatOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n LiteLLMOptions,\n} from '../types';\nimport { LiteLLMAdmin, resolveGatewayAdminBaseUrl } from './gateway-admin';\nimport { type OpenAICompatibleProfile, OpenAIProvider } from './openai';\n\nconst LITELLM_CAPABILITIES: AICapabilities = {\n chat: true,\n completion: true,\n embeddings: true,\n streaming: true,\n functions: true,\n vision: true,\n fineTuning: false,\n imageEmbeddings: true,\n imageGeneration: true,\n videoGeneration: false,\n tts: false,\n voiceCloning: false,\n voiceDesign: false,\n maxContextLength: 128000,\n supportedOperations: [\n 'chat',\n 'completion',\n 'embedding',\n 'streaming',\n 'functions',\n 'vision',\n 'image_embedding',\n 'image_generation',\n ],\n};\n\nfunction isEmbeddingModel(modelId: string): boolean {\n return /embed/i.test(modelId);\n}\n\nfunction isImageEmbeddingModel(modelId: string): boolean {\n return /titan-embed-image|multimodalembedding|image[-_]?embed|clip/i.test(\n modelId,\n );\n}\n\nfunction isImageGenerationModel(modelId: string): boolean {\n return /gpt-image|dall-e|imagen|stable[-_ ]diffusion|sdxl|flux|titan-image|image-generator/i.test(\n modelId,\n );\n}\n\nfunction isFilteredLiteLLMModel(modelId: string): boolean {\n return /moderation|transcrib|whisper|speech|tts|rerank/i.test(modelId);\n}\n\nfunction inferLiteLLMContextLength(modelId: string): number {\n if (isEmbeddingModel(modelId)) return 8192;\n if (/gemini-1\\.5|gemini-2\\.0|gemini-2\\.5|gemini-3/i.test(modelId)) {\n return 1000000;\n }\n if (/claude/i.test(modelId)) return 200000;\n if (/gpt-4o|gpt-4\\.1|o1|o3|o4|llama|mistral|qwen|deepseek/i.test(modelId)) {\n return 128000;\n }\n return 32768;\n}\n\nfunction inferLiteLLMFunctions(modelId: string): boolean {\n if (isEmbeddingModel(modelId)) return false;\n return /gpt|claude|gemini|command|llama|mistral|qwen|deepseek|o1|o3|o4/i.test(\n modelId,\n );\n}\n\nfunction inferLiteLLMVision(modelId: string): boolean {\n return /gpt-4o|gpt-4\\.1|vision|claude-3|gemini|pixtral|llava|qwen.*vl|vl-/i.test(\n modelId,\n );\n}\n\nfunction inferLiteLLMCapabilities(modelId: string): string[] {\n if (isImageGenerationModel(modelId)) {\n return ['image_generation'];\n }\n\n if (isImageEmbeddingModel(modelId)) {\n return ['embeddings', 'image_embedding'];\n }\n\n if (isEmbeddingModel(modelId)) {\n return ['embeddings'];\n }\n\n const capabilities = ['text', 'chat'];\n\n if (inferLiteLLMFunctions(modelId)) {\n capabilities.push('functions');\n }\n\n if (inferLiteLLMVision(modelId)) {\n capabilities.push('vision');\n }\n\n return capabilities;\n}\n\nfunction normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n}\n\nfunction ensureLiteLLMOptions(options: LiteLLMOptions): LiteLLMOptions {\n if (!options.baseUrl?.trim()) {\n throw new ValidationError('LiteLLM baseUrl is required', {\n provider: 'litellm',\n hint: 'Pass baseUrl like https://llm.happyvertical.com/v1 or set HAVE_AI_BASE_URL / LITELLM_BASE_URL',\n });\n }\n\n return {\n ...options,\n baseUrl: normalizeBaseUrl(options.baseUrl.trim()),\n };\n}\n\nconst LITELLM_PROFILE: OpenAICompatibleProfile = {\n providerLabel: 'LiteLLM',\n providerName: 'litellm',\n defaultModel: 'gpt-4',\n capabilities: LITELLM_CAPABILITIES,\n describeModel: (modelId) => `LiteLLM model: ${modelId}`,\n getContextLength: inferLiteLLMContextLength,\n getModelCapabilities: inferLiteLLMCapabilities,\n shouldIncludeModel: (modelId) => !isFilteredLiteLLMModel(modelId),\n supportsFunctions: inferLiteLLMFunctions,\n supportsVision: inferLiteLLMVision,\n};\n\ntype LiteLLMResolvedCapability =\n | 'chat'\n | 'vision'\n | 'embeddings'\n | 'image_generation';\n\nfunction supportsCapability(\n model: AIModel,\n capability: LiteLLMResolvedCapability,\n): boolean {\n if (capability === 'vision') {\n return (\n model.capabilities.includes('vision') || model.supportsVision === true\n );\n }\n\n if (capability === 'embeddings') {\n return model.capabilities.includes('embeddings');\n }\n\n if (capability === 'image_generation') {\n return model.capabilities.includes('image_generation');\n }\n\n return model.capabilities.includes('chat');\n}\n\n/**\n * LiteLLM provider implementation.\n *\n * LiteLLM exposes an OpenAI-compatible API surface, so this provider reuses the\n * OpenAI transport while customizing provider identity, model discovery, and\n * capability heuristics for gateway-backed deployments.\n */\nexport class LiteLLMProvider extends OpenAIProvider {\n readonly admin: LiteLLMAdmin;\n private readonly configuredDefaultModel?: string;\n private readonly resolvedModelCache = new Map<\n LiteLLMResolvedCapability,\n Promise<string>\n >();\n\n constructor(options: LiteLLMOptions) {\n const normalized = ensureLiteLLMOptions(options);\n super(normalized, LITELLM_PROFILE);\n this.admin = new LiteLLMAdmin({\n provider: 'litellm',\n baseUrl: resolveGatewayAdminBaseUrl(\n normalized.baseUrl,\n normalized.adminUrl || normalized.adminBaseUrl,\n 'litellm',\n ),\n apiKey: normalized.adminApiKey || normalized.apiKey,\n headers: normalized.adminHeaders,\n timeout: normalized.timeout,\n });\n this.configuredDefaultModel = normalized.defaultModel;\n }\n\n private async resolveModel(\n capability: LiteLLMResolvedCapability,\n explicitModel?: string,\n ): Promise<string> {\n if (explicitModel) {\n return explicitModel;\n }\n\n if (\n this.configuredDefaultModel &&\n (capability === 'chat' || capability === 'vision')\n ) {\n return this.configuredDefaultModel;\n }\n\n const cached = this.resolvedModelCache.get(capability);\n if (cached) {\n return cached;\n }\n\n const pending = this.selectModel(capability).catch((error) => {\n this.resolvedModelCache.delete(capability);\n throw error;\n });\n this.resolvedModelCache.set(capability, pending);\n return pending;\n }\n\n private async selectModel(\n capability: LiteLLMResolvedCapability,\n ): Promise<string> {\n const models = await super.getModels();\n const model = models.find((candidate) =>\n supportsCapability(candidate, capability),\n );\n\n if (!model) {\n throw new ValidationError(\n `No ${capability} model is available from the LiteLLM gateway`,\n {\n provider: 'litellm',\n capability,\n hint: 'Pass defaultModel explicitly or ensure /models returns a compatible model for this key',\n },\n );\n }\n\n return model.id;\n }\n\n async chat(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): Promise<AIResponse> {\n const safe = normalizeChatOptions(\n this.options,\n options,\n 'litellm',\n options.model || this.options.defaultModel,\n );\n return super.chat(messages, {\n ...safe,\n model: await this.resolveModel('chat', safe.model),\n });\n }\n\n async *stream(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): AsyncIterable<string> {\n const safe = normalizeChatOptions(\n this.options,\n options,\n 'litellm',\n options.model || this.options.defaultModel,\n );\n yield* super.stream(messages, {\n ...safe,\n model: await this.resolveModel('chat', safe.model),\n });\n }\n\n async describeImage(\n image: string | Buffer,\n prompt?: string,\n options: ImageDescriptionOptions = {},\n ): Promise<string> {\n const safe = normalizeChatOptions(\n this.options,\n options,\n 'litellm',\n options.model || this.options.defaultModel,\n );\n return super.describeImage(image, prompt, {\n ...safe,\n model: await this.resolveModel('vision', safe.model),\n });\n }\n\n async embed(\n text: string | string[],\n options: EmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n return super.embed(text, {\n ...options,\n model: await this.resolveModel('embeddings', options.model),\n });\n }\n\n async embedImage(\n image: string | Buffer,\n options: ImageEmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n return super.embedImage(image, {\n ...options,\n model: await this.resolveModel('embeddings', options.model),\n });\n }\n\n async generateImage(\n prompt: string,\n options: ImageGenerationOptions = {},\n ): Promise<ImageGenerationResponse> {\n const safe = normalizeImageGenerationOptions(\n this.options,\n options,\n 'litellm',\n options.model,\n );\n return super.generateImage(prompt, {\n ...safe,\n model: await this.resolveModel('image_generation', safe.model),\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ollama-Bedt8Agy.js","names":[],"sources":["../../src/shared/providers/ollama.ts"],"sourcesContent":["/**\n * Ollama provider implementation.\n *\n * Uses Ollama's native `/api/*` endpoints for chat, completions, embeddings,\n * and model discovery, while bridging to the experimental OpenAI-compatible\n * image-generation endpoint when available.\n */\n\nimport { ValidationError } from '@happyvertical/utils';\n\nimport {\n normalizeBaseAIOptions,\n normalizeChatOptions,\n normalizeImageGenerationOptions,\n type PreparedRequestControls,\n prepareRequestControls,\n} from '../safety';\nimport type {\n AICapabilities,\n AIInterface,\n AIMessage,\n AIModel,\n AIResponse,\n ChatOptions,\n CompletionOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n MessageOptions,\n OllamaOptions,\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 ContentFilterError,\n ContextLengthError,\n extractTextContent,\n ModelNotFoundError,\n RateLimitError,\n} from '../types';\nimport { emitUsage } from './usage';\n\nconst DEFAULT_OLLAMA_HOST = 'http://localhost:11434';\n\nconst OLLAMA_CAPABILITIES: AICapabilities = {\n chat: true,\n completion: true,\n embeddings: true,\n streaming: true,\n functions: true,\n vision: true,\n fineTuning: false,\n imageEmbeddings: true,\n imageGeneration: true,\n videoGeneration: false,\n tts: false,\n voiceCloning: false,\n voiceDesign: false,\n maxContextLength: 131072,\n supportedOperations: [\n 'chat',\n 'completion',\n 'embedding',\n 'streaming',\n 'functions',\n 'vision',\n 'image_embedding',\n 'image_generation',\n ],\n};\n\ntype ResolvedOllamaCapability =\n | 'chat'\n | 'vision'\n | 'embedding'\n | 'image_generation';\n\ninterface OllamaModelSummary {\n name?: string;\n model?: string;\n modified_at?: string;\n size?: number;\n details?: {\n family?: string;\n families?: string[];\n format?: string;\n parameter_size?: string;\n quantization_level?: string;\n };\n}\n\ninterface OllamaTagsResponse {\n models?: OllamaModelSummary[];\n}\n\ninterface OllamaShowResponse {\n capabilities?: string[];\n parameters?: string;\n modified_at?: string;\n details?: {\n parent_model?: string;\n format?: string;\n family?: string;\n families?: string[];\n parameter_size?: string;\n quantization_level?: string;\n };\n model_info?: Record<string, unknown>;\n}\n\ninterface OllamaToolCall {\n type?: 'function';\n function?: {\n index?: number;\n name?: string;\n arguments?: Record<string, unknown>;\n };\n}\n\ninterface OllamaMessage {\n role: 'system' | 'user' | 'assistant' | 'tool';\n content?: string;\n thinking?: string;\n images?: string[];\n tool_name?: string;\n tool_calls?: OllamaToolCall[];\n}\n\ninterface OllamaChatResponse {\n model?: string;\n message?: OllamaMessage;\n done?: boolean;\n done_reason?: string;\n prompt_eval_count?: number;\n eval_count?: number;\n}\n\ninterface OllamaGenerateResponse {\n model?: string;\n response?: string;\n thinking?: string;\n done?: boolean;\n done_reason?: string;\n prompt_eval_count?: number;\n eval_count?: number;\n}\n\ninterface OllamaEmbedResponse {\n model?: string;\n embeddings?: number[][];\n embedding?: number[];\n prompt_eval_count?: number;\n}\n\ninterface OllamaImageGenerationResponse {\n created?: number;\n data?: Array<{\n b64_json?: string;\n revised_prompt?: string;\n }>;\n}\n\nfunction stripTrailingSlash(value: string): string {\n return value.replace(/\\/+$/, '');\n}\n\nfunction normalizeHost(baseUrl?: string): string {\n const rawBase = baseUrl?.trim() || DEFAULT_OLLAMA_HOST;\n const withScheme = /^[a-z][a-z0-9+.-]*:\\/\\//i.test(rawBase)\n ? rawBase\n : `http://${rawBase}`;\n const base = stripTrailingSlash(withScheme);\n if (base.endsWith('/api')) return base.slice(0, -4);\n if (base.endsWith('/v1')) return base.slice(0, -3);\n return base;\n}\n\nfunction isEmbeddingModel(modelId: string): boolean {\n return /(?:^|[-_:/])(?:embed|embedding)|nomic-embed|mxbai-embed/i.test(\n modelId,\n );\n}\n\nfunction isImageGenerationModel(modelId: string): boolean {\n return /(?:^|[-_:/])(flux|sdxl|stable(?:[-_ ]?diffusion)?|z-image|image(?:[-_ ]?(?:turbo|gen|generator)))/i.test(\n modelId,\n );\n}\n\nfunction inferVisionSupport(\n modelId: string,\n capabilities: Set<string>,\n): boolean {\n if (capabilities.has('vision')) return true;\n return /vision|llava|bakllava|moondream|pixtral|qwen.*vl|vl-|gemma3/i.test(\n modelId,\n );\n}\n\nfunction inferFunctionSupport(\n modelId: string,\n capabilities: Set<string>,\n): boolean {\n if (\n capabilities.has('tools') ||\n capabilities.has('tool_calling') ||\n capabilities.has('function_calling')\n ) {\n return true;\n }\n\n if (isEmbeddingModel(modelId) || isImageGenerationModel(modelId)) {\n return false;\n }\n\n return /qwen|llama3|gpt-oss|mistral|deepseek|command-r|phi4/i.test(modelId);\n}\n\nfunction parseCapabilities(\n modelId: string,\n show?: OllamaShowResponse | null,\n): string[] {\n const capabilities = new Set(\n (show?.capabilities || []).map((capability) => capability.toLowerCase()),\n );\n\n if (isImageGenerationModel(modelId)) {\n return ['image_generation'];\n }\n\n if (\n capabilities.has('embedding') ||\n capabilities.has('embeddings') ||\n isEmbeddingModel(modelId)\n ) {\n return ['embeddings'];\n }\n\n const supported = new Set<string>(['text', 'chat']);\n\n if (inferVisionSupport(modelId, capabilities)) {\n supported.add('vision');\n }\n\n if (inferFunctionSupport(modelId, capabilities)) {\n supported.add('functions');\n }\n\n return [...supported];\n}\n\nfunction parseContextLength(\n modelId: string,\n show?: OllamaShowResponse | null,\n): number {\n const modelInfo = show?.model_info || {};\n\n for (const [key, value] of Object.entries(modelInfo)) {\n if (key.endsWith('.context_length') && typeof value === 'number') {\n return value;\n }\n }\n\n const numCtxMatch = show?.parameters?.match(/\\bnum_ctx\\s+(\\d+)/);\n if (numCtxMatch?.[1]) {\n return Number(numCtxMatch[1]);\n }\n\n if (isEmbeddingModel(modelId)) return 8192;\n if (/gemma3|qwen3|gpt-oss|llama3/i.test(modelId)) return 131072;\n return 32768;\n}\n\nfunction mapUsage(\n promptTokens?: number,\n completionTokens?: number,\n): TokenUsage | undefined {\n if (\n typeof promptTokens !== 'number' &&\n typeof completionTokens !== 'number'\n ) {\n return undefined;\n }\n\n const prompt = promptTokens || 0;\n const completion = completionTokens || 0;\n return {\n promptTokens: prompt,\n completionTokens: completion,\n totalTokens: prompt + completion,\n };\n}\n\nfunction mapFinishReason(reason?: string): AIResponse['finishReason'] {\n switch (reason) {\n case 'stop':\n return 'stop';\n case 'length':\n return 'length';\n case 'content_filter':\n return 'content_filter';\n default:\n return 'stop';\n }\n}\n\nfunction scoreModelForCapability(\n model: AIModel,\n capability: ResolvedOllamaCapability,\n): number {\n const id = model.id.toLowerCase();\n let score = 0;\n\n if (id.includes(':latest')) score += 6;\n if (id.includes('instruct') || id.includes('-it')) score += 6;\n if (id.startsWith('hf.co/')) score -= 8;\n if (/ocr|rerank|embed|image|diffusion|flux/.test(id)) score -= 12;\n\n switch (capability) {\n case 'vision':\n if (model.supportsVision) score += 25;\n if (/gemma4|gemma3/.test(id)) score += 12;\n if (/qwen.*vl|llava|moondream|vision/.test(id)) score += 8;\n if (/ocr/.test(id)) score -= 10;\n break;\n case 'embedding':\n if (model.capabilities.includes('embeddings')) score += 20;\n if (/nomic|mxbai|embed/.test(id)) score += 6;\n break;\n case 'image_generation':\n if (model.capabilities.includes('image_generation')) score += 20;\n if (/flux|sdxl|stable/.test(id)) score += 8;\n break;\n case 'chat':\n default:\n if (model.capabilities.includes('chat')) score += 12;\n if (model.supportsFunctions) score += 4;\n if (/llama|mistral|qwen|gemma|phi|command-r|deepseek-coder/.test(id)) {\n score += 8;\n }\n if (/gpt-oss/.test(id)) score -= 3;\n break;\n }\n\n return score;\n}\n\nexport class OllamaProvider implements AIInterface {\n private readonly options: OllamaOptions;\n private readonly host: string;\n private readonly configuredDefaultModel?: string;\n private modelListPromise?: Promise<AIModel[]>;\n private readonly modelShowCache = new Map<\n string,\n Promise<OllamaShowResponse | null>\n >();\n private readonly resolvedModelCache = new Map<\n ResolvedOllamaCapability,\n Promise<string>\n >();\n\n constructor(options: OllamaOptions) {\n this.host = normalizeHost(options.baseUrl);\n this.options = normalizeBaseAIOptions({\n ...options,\n baseUrl: this.host,\n });\n this.configuredDefaultModel = options.defaultModel;\n }\n\n private get nativeBaseUrl(): string {\n return `${this.host}/api`;\n }\n\n private get compatibilityBaseUrl(): string {\n return `${this.host}/v1`;\n }\n\n private buildHeaders(stream = false): Headers {\n const headers = new Headers(this.options.headers);\n headers.set('Content-Type', 'application/json');\n headers.set(\n 'Accept',\n stream ? 'application/x-ndjson, application/json' : 'application/json',\n );\n if (this.options.apiKey) {\n headers.set('Authorization', `Bearer ${this.options.apiKey}`);\n }\n return headers;\n }\n\n private async buildHttpError(response: Response): Promise<AIError> {\n const text = await response.text();\n const message =\n text && text.trim().length > 0\n ? text.trim()\n : `Ollama request failed with status ${response.status}`;\n\n switch (response.status) {\n case 401:\n case 403:\n return new AuthenticationError('ollama');\n case 404:\n return new ModelNotFoundError(message, 'ollama');\n case 413:\n return new ContextLengthError('ollama');\n case 429:\n return new RateLimitError('ollama');\n default:\n if (/content[_ -]?filter/i.test(message)) {\n return new ContentFilterError('ollama');\n }\n if (/context|too long|maximum context/i.test(message)) {\n return new ContextLengthError('ollama');\n }\n return new AIError(message, 'API_ERROR', 'ollama');\n }\n }\n\n private mapError(error: unknown): AIError {\n if (error instanceof AIError) {\n return error;\n }\n\n if (error instanceof Error && error.name === 'AbortError') {\n return new AIError('Ollama request timed out', 'TIMEOUT', 'ollama');\n }\n\n const message =\n error instanceof Error ? error.message : 'Unknown error occurred';\n return new AIError(message, 'UNKNOWN_ERROR', 'ollama');\n }\n\n private async requestJson<T>(\n path: string,\n body?: Record<string, unknown>,\n options: { compatibility?: boolean; signal?: AbortSignal } = {},\n ): Promise<T> {\n const baseUrl = options.compatibility\n ? this.compatibilityBaseUrl\n : this.nativeBaseUrl;\n const response = await fetch(`${baseUrl}${path}`, {\n method: body ? 'POST' : 'GET',\n headers: this.buildHeaders(),\n body: body ? JSON.stringify(body) : undefined,\n signal: options.signal,\n });\n\n if (!response.ok) {\n throw await this.buildHttpError(response);\n }\n\n return (await response.json()) as T;\n }\n\n private async requestStream(\n path: string,\n body: Record<string, unknown>,\n signal: AbortSignal,\n ): Promise<Response> {\n const response = await fetch(`${this.nativeBaseUrl}${path}`, {\n method: 'POST',\n headers: this.buildHeaders(true),\n body: JSON.stringify(body),\n signal,\n });\n\n if (!response.ok) {\n throw await this.buildHttpError(response);\n }\n\n return response;\n }\n\n private async *parseNdjson<T>(response: Response): AsyncIterable<T> {\n const reader = response.body?.getReader();\n if (!reader) {\n return;\n }\n\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n let newlineIndex = buffer.indexOf('\\n');\n while (newlineIndex !== -1) {\n const line = buffer.slice(0, newlineIndex).trim();\n buffer = buffer.slice(newlineIndex + 1);\n if (line) {\n yield JSON.parse(line) as T;\n }\n newlineIndex = buffer.indexOf('\\n');\n }\n }\n\n buffer += decoder.decode();\n const line = buffer.trim();\n if (line) {\n yield JSON.parse(line) as T;\n }\n }\n\n private async showModel(model: string): Promise<OllamaShowResponse | null> {\n const cached = this.modelShowCache.get(model);\n if (cached) {\n return cached;\n }\n\n const pending = this.requestJson<OllamaShowResponse>('/show', {\n model,\n verbose: false,\n }).catch((_error) => null);\n\n this.modelShowCache.set(model, pending);\n return pending;\n }\n\n private async resolveModel(\n capability: ResolvedOllamaCapability,\n explicitModel?: string,\n ): Promise<string> {\n if (explicitModel) {\n return explicitModel;\n }\n\n if (\n this.configuredDefaultModel &&\n (capability === 'chat' || capability === 'vision')\n ) {\n return this.configuredDefaultModel;\n }\n\n const cached = this.resolvedModelCache.get(capability);\n if (cached) {\n return cached;\n }\n\n const pending = this.selectModel(capability).catch((error) => {\n this.resolvedModelCache.delete(capability);\n throw error;\n });\n this.resolvedModelCache.set(capability, pending);\n return pending;\n }\n\n private async selectModel(\n capability: ResolvedOllamaCapability,\n ): Promise<string> {\n const models = await this.getModels();\n const candidates = models.filter((candidate) => {\n switch (capability) {\n case 'vision':\n return (\n candidate.capabilities.includes('vision') ||\n candidate.supportsVision === true\n );\n case 'embedding':\n return candidate.capabilities.includes('embeddings');\n case 'image_generation':\n return candidate.capabilities.includes('image_generation');\n case 'chat':\n default:\n return candidate.capabilities.includes('chat');\n }\n });\n\n const model = candidates.sort(\n (left, right) =>\n scoreModelForCapability(right, capability) -\n scoreModelForCapability(left, capability),\n )[0];\n\n if (!model) {\n throw new ValidationError(\n `No ${capability} model is available from the Ollama host`,\n {\n provider: 'ollama',\n capability,\n hint: 'Pass model/defaultModel explicitly or make sure the Ollama host has a compatible model installed',\n },\n );\n }\n\n return model.id;\n }\n\n private isVisionRequest(messages: AIMessage[]): boolean {\n return messages.some(\n (message) =>\n Array.isArray(message.content) &&\n message.content.some((part) => part.type === 'image_url'),\n );\n }\n\n private async imageToBase64Payload(\n image: string | Buffer,\n signal?: AbortSignal,\n ): Promise<string> {\n if (Buffer.isBuffer(image)) {\n return image.toString('base64');\n }\n\n if (image.startsWith('data:')) {\n const commaIndex = image.indexOf(',');\n return commaIndex === -1 ? image : image.slice(commaIndex + 1);\n }\n\n const response = await fetch(image, { signal });\n if (!response.ok) {\n throw new AIError(\n `Failed to fetch image: ${response.status} ${response.statusText}`,\n 'IMAGE_FETCH_ERROR',\n 'ollama',\n );\n }\n\n const arrayBuffer = await response.arrayBuffer();\n return Buffer.from(arrayBuffer).toString('base64');\n }\n\n private async imageToDataUrl(\n image: string | Buffer,\n signal?: AbortSignal,\n ): Promise<string> {\n if (Buffer.isBuffer(image)) {\n return `data:image/png;base64,${image.toString('base64')}`;\n }\n\n if (image.startsWith('data:')) {\n return image;\n }\n\n const response = await fetch(image, { signal });\n if (!response.ok) {\n throw new AIError(\n `Failed to fetch image: ${response.status} ${response.statusText}`,\n 'IMAGE_FETCH_ERROR',\n 'ollama',\n );\n }\n\n const contentType = response.headers.get('content-type') || 'image/png';\n const arrayBuffer = await response.arrayBuffer();\n return `data:${contentType};base64,${Buffer.from(arrayBuffer).toString('base64')}`;\n }\n\n private parseToolArguments(argumentsText?: string): Record<string, unknown> {\n if (!argumentsText) {\n return {};\n }\n\n try {\n const parsed = JSON.parse(argumentsText);\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n }\n\n private async mapMessagesToOllama(\n messages: AIMessage[],\n signal?: AbortSignal,\n ): Promise<OllamaMessage[]> {\n const mappedMessages = await Promise.all(\n messages.map(async (message) => {\n const content = extractTextContent(message.content);\n const mapped: OllamaMessage = {\n role:\n message.role === 'function'\n ? 'tool'\n : (message.role as OllamaMessage['role']),\n content,\n };\n\n if (Array.isArray(message.content)) {\n const imageParts = message.content.filter(\n (part) => part.type === 'image_url',\n );\n if (imageParts.length > 0) {\n mapped.images = await Promise.all(\n imageParts.map((part) =>\n this.imageToBase64Payload(part.image_url.url, signal),\n ),\n );\n }\n }\n\n if (\n mapped.role === 'assistant' &&\n Array.isArray(message.tool_calls) &&\n message.tool_calls.length > 0\n ) {\n mapped.tool_calls = message.tool_calls.map((toolCall, index) => ({\n type: 'function',\n function: {\n index,\n name: toolCall.function.name,\n arguments: this.parseToolArguments(toolCall.function.arguments),\n },\n }));\n }\n\n if (mapped.role === 'tool' && message.name) {\n mapped.tool_name = message.name;\n }\n\n return mapped;\n }),\n );\n\n return mappedMessages;\n }\n\n private mapTools(\n tools: ChatOptions['tools'],\n toolChoice: ChatOptions['toolChoice'],\n ): Array<Record<string, unknown>> | undefined {\n if (!tools || tools.length === 0 || toolChoice === 'none') {\n return undefined;\n }\n\n const scopedTools =\n toolChoice && typeof toolChoice === 'object'\n ? tools.filter(\n (tool) => tool.function.name === toolChoice.function.name,\n )\n : tools;\n\n if (scopedTools.length === 0) {\n return undefined;\n }\n\n return scopedTools.map((tool) => ({\n type: 'function',\n function: {\n name: tool.function.name,\n description: tool.function.description,\n parameters: tool.function.parameters || { type: 'object' },\n },\n }));\n }\n\n private buildRuntimeOptions(options: {\n maxTokens?: number;\n temperature?: number;\n topP?: number;\n stop?: string | string[];\n seed?: number;\n }): Record<string, unknown> | undefined {\n const runtimeOptions: Record<string, unknown> = {};\n\n if (typeof options.maxTokens === 'number') {\n runtimeOptions.num_predict = options.maxTokens;\n }\n if (typeof options.temperature === 'number') {\n runtimeOptions.temperature = options.temperature;\n }\n if (typeof options.topP === 'number') {\n runtimeOptions.top_p = options.topP;\n }\n if (options.stop) {\n runtimeOptions.stop = Array.isArray(options.stop)\n ? options.stop\n : [options.stop];\n }\n if (typeof options.seed === 'number') {\n runtimeOptions.seed = options.seed;\n }\n\n return Object.keys(runtimeOptions).length > 0 ? runtimeOptions : undefined;\n }\n\n private mapThink(\n options: Pick<ChatOptions, 'thinkingLevel' | 'reasoning'>,\n ): boolean | string | undefined {\n if (options.reasoning?.maxTokens === 0) {\n return false;\n }\n if (options.reasoning?.effort) {\n return options.reasoning.effort === 'none'\n ? false\n : options.reasoning.effort;\n }\n if (!Object.hasOwn(options, 'thinkingLevel')) {\n return undefined;\n }\n\n return options.thinkingLevel;\n }\n\n async chat(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): Promise<AIResponse> {\n const startTime = Date.now();\n let controls: PreparedRequestControls | undefined;\n try {\n options = normalizeChatOptions(\n this.options,\n options,\n 'ollama',\n options.model || this.options.defaultModel,\n );\n controls = prepareRequestControls(this.options, options);\n const model = await this.resolveModel(\n this.isVisionRequest(messages) ? 'vision' : 'chat',\n options.model,\n );\n\n const response = await this.requestJson<OllamaChatResponse>(\n '/chat',\n {\n model,\n messages: await this.mapMessagesToOllama(messages, controls.signal),\n stream: false,\n format:\n options.responseFormat?.type === 'json_object' ? 'json' : undefined,\n tools: this.mapTools(options.tools, options.toolChoice),\n think: this.mapThink(options),\n keep_alive: this.options.keepAlive,\n options: this.buildRuntimeOptions(options),\n },\n { signal: controls.signal },\n );\n\n const usage = mapUsage(response.prompt_eval_count, response.eval_count);\n\n emitUsage(\n this.options,\n 'ollama',\n 'chat',\n response.model || model,\n usage,\n startTime,\n options.usageTags,\n );\n\n const toolCalls =\n response.message?.tool_calls\n ?.filter((call) => call.function?.name)\n .map((call, index) => ({\n id: `${response.model || model}-tool-${index + 1}`,\n type: 'function' as const,\n function: {\n name: call.function?.name || '',\n arguments: JSON.stringify(call.function?.arguments || {}),\n },\n })) || undefined;\n\n return {\n content: response.message?.content || '',\n model: response.model || model,\n usage,\n finishReason:\n toolCalls && toolCalls.length > 0\n ? 'tool_calls'\n : mapFinishReason(response.done_reason),\n toolCalls: toolCalls && toolCalls.length > 0 ? toolCalls : undefined,\n };\n } catch (error) {\n if (controls?.didTimeout()) {\n throw new AIError(\n `AI request timed out after ${controls.timeout}ms`,\n 'AI_TIMEOUT',\n 'ollama',\n options.model,\n );\n }\n if (options.signal?.aborted) {\n throw new AIError(\n 'AI request aborted by caller',\n 'AI_ABORTED',\n 'ollama',\n options.model,\n );\n }\n throw this.mapError(error);\n } finally {\n controls?.cleanup();\n }\n }\n\n async complete(\n prompt: string,\n options: CompletionOptions = {},\n ): Promise<AIResponse> {\n const startTime = Date.now();\n let controls: PreparedRequestControls | undefined;\n try {\n options = normalizeChatOptions(\n this.options,\n options,\n 'ollama',\n options.model || this.options.defaultModel,\n );\n controls = prepareRequestControls(this.options, options);\n const model = await this.resolveModel('chat', options.model);\n const response = await this.requestJson<OllamaGenerateResponse>(\n '/generate',\n {\n model,\n prompt,\n stream: false,\n keep_alive: this.options.keepAlive,\n options: this.buildRuntimeOptions(options),\n },\n { signal: controls.signal },\n );\n\n const usage = mapUsage(response.prompt_eval_count, response.eval_count);\n\n emitUsage(\n this.options,\n 'ollama',\n 'complete',\n response.model || model,\n usage,\n startTime,\n options.usageTags,\n );\n\n return {\n content: response.response || '',\n model: response.model || model,\n usage,\n finishReason: mapFinishReason(response.done_reason),\n };\n } catch (error) {\n if (controls?.didTimeout()) {\n throw new AIError(\n `AI request timed out after ${controls.timeout}ms`,\n 'AI_TIMEOUT',\n 'ollama',\n options.model,\n );\n }\n if (options.signal?.aborted) {\n throw new AIError(\n 'AI request aborted by caller',\n 'AI_ABORTED',\n 'ollama',\n options.model,\n );\n }\n throw this.mapError(error);\n } finally {\n controls?.cleanup();\n }\n }\n\n async message(text: string, options: MessageOptions = {}): Promise<string> {\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 signal: options.signal,\n timeout: options.timeout,\n reasoning: options.reasoning,\n usageTags: options.usageTags,\n });\n\n return response.content;\n }\n\n async embed(\n text: string | string[],\n options: EmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n const startTime = Date.now();\n try {\n const model = await this.resolveModel('embedding', options.model);\n const input = Array.isArray(text) ? text : [text];\n const response = await this.requestJson<OllamaEmbedResponse>('/embed', {\n model,\n input,\n keep_alive: this.options.keepAlive,\n });\n\n const embeddings =\n response.embeddings ||\n (response.embedding ? [response.embedding] : undefined);\n\n if (!embeddings || embeddings.length === 0) {\n throw new AIError(\n 'Invalid embedding response from Ollama',\n 'INVALID_RESPONSE',\n 'ollama',\n );\n }\n\n const usage = mapUsage(response.prompt_eval_count, 0);\n emitUsage(\n this.options,\n 'ollama',\n 'embed',\n response.model || model,\n usage,\n startTime,\n options.usageTags,\n );\n\n return {\n embeddings,\n usage,\n model: response.model || model,\n };\n } catch (error) {\n throw this.mapError(error);\n }\n }\n\n async embedImage(\n image: string | Buffer,\n options: ImageEmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n const description = await this.describeImage(\n image,\n 'Describe this image in detail for semantic embedding. Include objects, people, text, setting, colors, composition, and notable visual relationships.',\n );\n\n return this.embed(description, {\n model: options.model,\n dimensions: options.dimensions,\n user: options.user,\n });\n }\n\n async describeImage(\n image: string | Buffer,\n prompt?: string,\n options: ImageDescriptionOptions = {},\n ): Promise<string> {\n const defaultPrompt =\n 'Describe this image for a search index. Include objects, mood, lighting, and any visible text.';\n\n const imageUrl = await this.imageToDataUrl(image, options.signal);\n const response = await this.chat(\n [\n {\n role: 'user',\n content: [\n {\n type: 'text',\n text: prompt || defaultPrompt,\n },\n {\n type: 'image_url',\n image_url: {\n url: imageUrl,\n detail: options.detail || 'auto',\n },\n },\n ],\n },\n ],\n {\n model: options.model,\n maxTokens: options.maxTokens ?? 500,\n reasoning: options.reasoning || { effort: 'none', maxTokens: 0 },\n signal: options.signal,\n timeout: options.timeout,\n usageTags: options.usageTags,\n },\n );\n\n return response.content;\n }\n\n async generateImage(\n prompt: string,\n options: ImageGenerationOptions = {},\n ): Promise<ImageGenerationResponse> {\n let controls: PreparedRequestControls | undefined;\n try {\n options = normalizeImageGenerationOptions(\n this.options,\n options,\n 'ollama',\n options.model,\n );\n controls = prepareRequestControls(this.options, options);\n if (options.imageInput) {\n throw new AIError(\n 'Ollama image generation does not support imageInput in this adapter yet.',\n 'NOT_IMPLEMENTED',\n 'ollama',\n );\n }\n\n if (options.outputFormat === 'url') {\n throw new AIError(\n 'Ollama image generation only supports buffer or base64 outputs in this adapter.',\n 'NOT_SUPPORTED',\n 'ollama',\n );\n }\n\n const model = await this.resolveModel('image_generation', options.model);\n const response = await this.requestJson<OllamaImageGenerationResponse>(\n '/images/generations',\n {\n model,\n prompt,\n n: options.n,\n size: options.size || '1024x1024',\n response_format: 'b64_json',\n quality: options.quality,\n style: options.style,\n },\n { compatibility: true, signal: controls.signal },\n );\n\n const images = (response.data || []).map((item) => {\n const b64 = item.b64_json || '';\n return {\n data:\n options.outputFormat === 'base64'\n ? b64\n : Buffer.from(b64, 'base64'),\n mimeType: 'image/png',\n revisedPrompt: item.revised_prompt,\n };\n });\n\n return {\n images,\n model,\n };\n } catch (error) {\n if (controls?.didTimeout()) {\n throw new AIError(\n `AI request timed out after ${controls.timeout}ms`,\n 'AI_TIMEOUT',\n 'ollama',\n options.model,\n );\n }\n if (options.signal?.aborted) {\n throw new AIError(\n 'AI request aborted by caller',\n 'AI_ABORTED',\n 'ollama',\n options.model,\n );\n }\n throw this.mapError(error);\n } finally {\n controls?.cleanup();\n }\n }\n\n async *stream(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): AsyncIterable<string> {\n const startTime = Date.now();\n let controls: PreparedRequestControls | undefined;\n try {\n options = normalizeChatOptions(\n this.options,\n options,\n 'ollama',\n options.model || this.options.defaultModel,\n );\n controls = prepareRequestControls(this.options, options);\n const model = await this.resolveModel(\n this.isVisionRequest(messages) ? 'vision' : 'chat',\n options.model,\n );\n\n const response = await this.requestStream(\n '/chat',\n {\n model,\n messages: await this.mapMessagesToOllama(messages, controls.signal),\n stream: true,\n format:\n options.responseFormat?.type === 'json_object' ? 'json' : undefined,\n tools: this.mapTools(options.tools, options.toolChoice),\n think: this.mapThink(options),\n keep_alive: this.options.keepAlive,\n options: this.buildRuntimeOptions(options),\n },\n controls.signal,\n );\n\n let finalUsage: TokenUsage | undefined;\n let finalModel = model;\n\n for await (const chunk of this.parseNdjson<OllamaChatResponse>(\n response,\n )) {\n if (chunk.model) {\n finalModel = chunk.model;\n }\n\n if (\n typeof chunk.message?.content === 'string' &&\n chunk.message.content\n ) {\n if (options.onProgress) {\n options.onProgress(chunk.message.content);\n }\n yield chunk.message.content;\n }\n\n if (chunk.done) {\n finalUsage = mapUsage(chunk.prompt_eval_count, chunk.eval_count);\n }\n }\n\n emitUsage(\n this.options,\n 'ollama',\n 'stream',\n finalModel,\n finalUsage,\n startTime,\n options.usageTags,\n );\n } catch (error) {\n if (controls?.didTimeout()) {\n throw new AIError(\n `AI request timed out after ${controls.timeout}ms`,\n 'AI_TIMEOUT',\n 'ollama',\n options.model,\n );\n }\n if (options.signal?.aborted) {\n throw new AIError(\n 'AI request aborted by caller',\n 'AI_ABORTED',\n 'ollama',\n options.model,\n );\n }\n throw this.mapError(error);\n } finally {\n controls?.cleanup();\n }\n }\n\n async countTokens(text: string): Promise<number> {\n return Math.ceil(text.length / 4);\n }\n\n async getModels(): Promise<AIModel[]> {\n if (this.modelListPromise) {\n return this.modelListPromise;\n }\n\n this.modelListPromise = (async () => {\n const response = await this.requestJson<OllamaTagsResponse>('/tags');\n const summaries = response.models || [];\n\n return Promise.all(\n summaries.map(async (summary) => {\n const modelId = summary.name || summary.model;\n if (!modelId) {\n throw new AIError(\n 'Ollama returned a model without an identifier',\n 'INVALID_RESPONSE',\n 'ollama',\n );\n }\n\n const show = await this.showModel(modelId);\n const capabilities = parseCapabilities(modelId, show);\n const vision = capabilities.includes('vision');\n const functions = capabilities.includes('functions');\n const parameterSize =\n show?.details?.parameter_size || summary.details?.parameter_size;\n const family = show?.details?.family || summary.details?.family;\n\n return {\n id: modelId,\n name: modelId,\n description:\n parameterSize || family\n ? `${family || 'Ollama'} ${parameterSize || ''}`.trim()\n : `Ollama model: ${modelId}`,\n contextLength: parseContextLength(modelId, show),\n capabilities,\n supportsFunctions: functions,\n supportsVision: vision,\n } satisfies AIModel;\n }),\n );\n })().catch((error) => {\n this.modelListPromise = undefined;\n throw this.mapError(error);\n });\n\n return this.modelListPromise;\n }\n\n async getCapabilities(): Promise<AICapabilities> {\n return { ...OLLAMA_CAPABILITIES };\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 the Ollama provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'ollama',\n );\n }\n\n async getVideoGenerationJob(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationStatusResult> {\n throw new AIError(\n 'Video generation is not supported by the Ollama provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'ollama',\n );\n }\n\n async fetchVideoGenerationResult(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationResult> {\n throw new AIError(\n 'Video generation is not supported by the Ollama provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'ollama',\n );\n }\n\n async cancelVideoGenerationJob(_handle: VideoGenerationJob): Promise<void> {\n throw new AIError(\n 'Video generation is not supported by the Ollama provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'ollama',\n );\n }\n\n async validateVideoGenerationAccess(): Promise<boolean> {\n throw new AIError(\n 'Video generation is not supported by the Ollama provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'ollama',\n );\n }\n\n async synthesizeSpeech(\n _text: string,\n _options?: TTSOptions,\n ): Promise<TTSResponse> {\n throw new AIError(\n 'TTS is not supported by the Ollama provider.',\n 'NOT_IMPLEMENTED',\n 'ollama',\n );\n }\n\n streamSpeech(_text: string, _options?: TTSOptions): AsyncIterable<Buffer> {\n const error = new AIError(\n 'TTS streaming is not supported by the Ollama provider.',\n 'NOT_IMPLEMENTED',\n 'ollama',\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 the Ollama provider.',\n 'NOT_IMPLEMENTED',\n 'ollama',\n );\n }\n\n async designVoice(_options: VoiceDesignOptions): Promise<Voice> {\n throw new AIError(\n 'Voice design is not supported by the Ollama provider.',\n 'NOT_IMPLEMENTED',\n 'ollama',\n );\n }\n\n async getVoices(_options?: VoiceListOptions): Promise<Voice[]> {\n throw new AIError(\n 'Voice listing is not supported by the Ollama provider.',\n 'NOT_IMPLEMENTED',\n 'ollama',\n );\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"openai-compat-video-B-xVe9bW.js","names":[],"sources":["../../src/shared/providers/openai-compat-video.ts"],"sourcesContent":["/**\n * OpenAI-compatible video-generation provider implementation\n *\n * Thin, raw-HTTP adapter over a `/v1/videos`-shaped REST surface (create /\n * retrieve / cancel / download content) configured by base URL + key. This\n * is the shape LiteLLM normalizes Veo behind, and the shape used by\n * Sora-style gateways. The surface is young and still churning across\n * vendors, so this adapter is deliberately thin: it forwards the fields the\n * OpenAI `videos` API documents (model, prompt, seconds, size, a single\n * image reference) and leaves everything else to the gateway's defaults.\n *\n * Only video-generation methods are implemented; use the `openai` or\n * `litellm` provider types against the same gateway for chat/embeddings.\n *\n * Request bodies are sent as JSON, not the `multipart/form-data` the\n * official `openai` npm package uses for `input_reference` uploads. This is\n * a deliberate choice for this thin adapter: LiteLLM (the primary target\n * gateway) accepts `input_reference.image_url` as a URL or base64 data URL\n * in a JSON body, and staying JSON-only keeps this file a plain HTTP client\n * with no multipart-encoding dependency. A raw OpenAI Sora endpoint that\n * requires actual multipart file uploads is not supported by this adapter.\n */\n\nimport { ValidationError } from '@happyvertical/utils';\nimport { extractRetryAfterSeconds } from '../rate-limit';\nimport { normalizeBaseAIOptions } from '../safety';\nimport type {\n AICapabilities,\n AIInterface,\n AIMessage,\n AIModel,\n AIResponse,\n ChatOptions,\n CompletionOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n MessageOptions,\n OpenAICompatVideoOptions,\n TTSOptions,\n TTSResponse,\n VideoGenerationJob,\n VideoGenerationOptions,\n VideoGenerationResult,\n VideoGenerationStatusResult,\n Voice,\n VoiceCloneOptions,\n VoiceDesignOptions,\n VoiceListOptions,\n} from '../types';\nimport { AIError, AuthenticationError, RateLimitError } from '../types';\nimport { emitUsage } from './usage';\n\n/** Raw video-job object shape, following the OpenAI `videos` API. */\ninterface CompatVideoObject {\n id: string;\n status: string;\n model: string;\n progress?: number;\n seconds?: string;\n size?: string;\n error?: { code?: string; message?: string } | null;\n /** Some gateways (LiteLLM's Veo passthrough) include a direct download URL. */\n url?: string;\n}\n\nfunction ensureOpenAICompatVideoOptions(\n options: OpenAICompatVideoOptions,\n): OpenAICompatVideoOptions {\n if (!options.baseUrl?.trim()) {\n throw new ValidationError('openai-compat-video baseUrl is required', {\n provider: 'openai-compat-video',\n hint: 'Pass baseUrl like https://llm.happyvertical.com/v1 or set OPENAI_COMPAT_VIDEO_BASE_URL',\n });\n }\n\n return {\n ...options,\n baseUrl: options.baseUrl.trim().replace(/\\/+$/, ''),\n };\n}\n\nfunction parseSize(size: string | undefined): {\n width?: number;\n height?: number;\n} {\n const match = size?.match(/^(\\d+)x(\\d+)$/);\n if (!match) return {};\n return { width: Number(match[1]), height: Number(match[2]) };\n}\n\n/**\n * Map a gateway's raw status string to our status enum.\n *\n * LiteLLM (the primary target gateway) documents `'processing'` as its\n * mid-render status rather than OpenAI's own `'in_progress'`; both, plus\n * `'pending'`, map to `'running'`. Only an explicit `'failed'` maps to a\n * terminal failure — an unrecognized status is never assumed to be\n * terminal, since misreading a mid-render job as failed would cause a\n * caller to resubmit (and double-bill) a job that was actually still\n * rendering. Unrecognized values map to `'running'` with the raw string\n * preserved via `rawStatus` so callers can still see and log it.\n */\nfunction mapStatus(status: string): {\n status: VideoGenerationStatusResult['status'];\n rawStatus?: string;\n} {\n switch (status) {\n case 'queued':\n return { status: 'queued' };\n case 'in_progress':\n case 'processing':\n case 'pending':\n return { status: 'running' };\n case 'completed':\n return { status: 'succeeded' };\n case 'failed':\n return { status: 'failed' };\n case 'cancelled':\n case 'canceled':\n return { status: 'cancelled' };\n default:\n return { status: 'running', rawStatus: status };\n }\n}\n\n/**\n * OpenAI-compatible video-generation provider for `/v1/videos`-shaped\n * gateways (LiteLLM, Sora-shaped providers).\n *\n * @example\n * ```typescript\n * import { getAI } from '@happyvertical/ai';\n *\n * const video = await getAI({\n * type: 'openai-compat-video',\n * baseUrl: 'https://llm.happyvertical.com/v1',\n * apiKey: process.env.OPENAI_COMPAT_VIDEO_API_KEY!,\n * });\n *\n * const job = await video.submitVideoGenerationJob({\n * prompt: 'A time-lapse of clouds over a mountain range',\n * durationSeconds: 8,\n * });\n * ```\n */\nexport class OpenAICompatVideoProvider implements AIInterface {\n private options: OpenAICompatVideoOptions;\n\n constructor(options: OpenAICompatVideoOptions) {\n this.options = normalizeBaseAIOptions(\n ensureOpenAICompatVideoOptions(options),\n );\n }\n\n private get baseUrl(): string {\n return this.options.baseUrl as string;\n }\n\n private async request<T>(\n path: string,\n options: {\n method?: 'GET' | 'POST';\n body?: unknown;\n query?: Record<string, string | number>;\n signal?: AbortSignal;\n timeout?: number;\n } = {},\n ): Promise<T> {\n const response = await this.rawRequest(path, options);\n const text = await response.text();\n return text ? (JSON.parse(text) as T) : ({} as T);\n }\n\n private async rawRequest(\n path: string,\n options: {\n method?: 'GET' | 'POST';\n body?: unknown;\n query?: Record<string, string | number>;\n signal?: AbortSignal;\n timeout?: number;\n /** When true, a 404 response is returned as-is instead of throwing. */\n allowNotFound?: boolean;\n } = {},\n ): Promise<Response> {\n if (options.signal?.aborted) {\n throw new AIError(\n 'openai-compat-video request aborted by caller',\n 'AI_ABORTED',\n 'openai-compat-video',\n );\n }\n\n const url = new URL(`${this.baseUrl}${path}`);\n for (const [key, value] of Object.entries(options.query ?? {})) {\n url.searchParams.set(key, String(value));\n }\n\n const controller = new AbortController();\n const timeoutMs = options.timeout ?? this.options.timeout;\n let timedOut = false;\n const timer = timeoutMs\n ? setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, timeoutMs)\n : undefined;\n // Mirrors `prepareRequestControls`'s pattern (packages/ai/src/shared/safety.ts):\n // addEventListener on an already-aborted signal never fires, so an\n // already-aborted `signal` must invoke the abort immediately instead of\n // only listening for a future 'abort' event.\n const onExternalAbort = () => controller.abort(options.signal?.reason);\n if (options.signal?.aborted) {\n onExternalAbort();\n } else {\n options.signal?.addEventListener('abort', onExternalAbort, {\n once: true,\n });\n }\n\n try {\n const headers: Record<string, string> = {\n ...(this.options.headers || {}),\n };\n if (this.options.apiKey) {\n headers.Authorization = `Bearer ${this.options.apiKey}`;\n }\n if (options.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: options.method || 'GET',\n headers,\n body:\n options.body !== undefined\n ? JSON.stringify(options.body)\n : undefined,\n signal: controller.signal,\n });\n } catch (error) {\n // The fetch() call itself failed (network error, DNS failure,\n // timeout/abort) — wrap it into the AIError taxonomy so callers get\n // a consistent, retryable-vs-terminal-distinguishable error rather\n // than a raw TypeError/DOMException.\n if (timedOut) {\n throw new AIError(\n `openai-compat-video request to ${path} timed out after ${timeoutMs}ms`,\n 'AI_TIMEOUT',\n 'openai-compat-video',\n undefined,\n true,\n );\n }\n if (options.signal?.aborted) {\n throw new AIError(\n 'openai-compat-video request aborted by caller',\n 'AI_ABORTED',\n 'openai-compat-video',\n );\n }\n throw new AIError(\n `Network error calling openai-compat-video ${path}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n 'NETWORK_ERROR',\n 'openai-compat-video',\n undefined,\n true,\n );\n }\n\n if (!response.ok) {\n if (response.status === 404 && options.allowNotFound) {\n return response;\n }\n\n const errorText = await response.text();\n\n if (response.status === 401 || response.status === 403) {\n throw new AuthenticationError('openai-compat-video');\n }\n if (response.status === 429) {\n throw new RateLimitError(\n 'openai-compat-video',\n extractRetryAfterSeconds({ headers: response.headers }),\n );\n }\n\n throw new AIError(\n `openai-compat-video request to ${path} failed: ${response.status} ${errorText}`,\n 'OPENAI_COMPAT_VIDEO_REQUEST_FAILED',\n 'openai-compat-video',\n );\n }\n\n return response;\n } finally {\n if (timer) clearTimeout(timer);\n options.signal?.removeEventListener('abort', onExternalAbort);\n }\n }\n\n // ============================================================================\n // Video Generation Methods (Implemented)\n // ============================================================================\n\n /**\n * Submit an asynchronous video-generation job.\n *\n * `resolution` is forwarded as the gateway's `size` field only when it is\n * already in `WIDTHxHEIGHT` form (e.g. `'1280x720'`); other resolution\n * labels (`'720p'`) are not translated and the gateway's default size\n * applies instead — keeping this adapter thin rather than guessing a\n * pixel size per vendor. `negativePrompt`, `seed`, and `fps` are not part\n * of the documented OpenAI videos schema and are not forwarded.\n */\n async submitVideoGenerationJob(\n options: VideoGenerationOptions,\n ): Promise<VideoGenerationJob> {\n const startTime = Date.now();\n const model = options.model || this.options.defaultModel || 'sora-2';\n\n const body: Record<string, unknown> = {\n model,\n prompt: options.prompt,\n };\n if (options.durationSeconds !== undefined) {\n body.seconds = String(options.durationSeconds);\n }\n if (options.resolution?.includes('x')) {\n body.size = options.resolution;\n }\n const firstReference = options.referenceImages?.[0];\n if (firstReference) {\n body.input_reference = {\n image_url:\n typeof firstReference.image === 'string'\n ? firstReference.image\n : `data:${firstReference.mimeType || 'image/png'};base64,${firstReference.image.toString('base64')}`,\n };\n }\n\n const video = await this.request<CompatVideoObject>('/videos', {\n method: 'POST',\n body,\n signal: options.signal,\n timeout: options.timeout,\n });\n\n emitUsage(\n this.options,\n 'openai-compat-video',\n 'submitVideoGenerationJob',\n video.model || model,\n undefined,\n startTime,\n {\n ...(options.durationSeconds !== undefined\n ? { durationSeconds: String(options.durationSeconds) }\n : {}),\n ...(options.resolution ? { resolution: options.resolution } : {}),\n ...(options.aspectRatio ? { aspectRatio: options.aspectRatio } : {}),\n ...options.usageTags,\n },\n );\n\n return {\n jobId: video.id,\n provider: 'openai-compat-video',\n model: video.model || model,\n createdAt: new Date().toISOString(),\n };\n }\n\n /**\n * Poll the status of a video-generation job.\n *\n * The gateway's status response does not include the video bytes, so\n * `result` on a succeeded job carries only metadata (mimeType, duration,\n * dimensions) plus a `url` when the gateway happens to include one\n * directly. Call {@link fetchVideoGenerationResult} to download the bytes.\n */\n async getVideoGenerationJob(\n handle: VideoGenerationJob,\n ): Promise<VideoGenerationStatusResult> {\n const video = await this.request<CompatVideoObject>(\n `/videos/${handle.jobId}`,\n );\n return this.mapVideo(video);\n }\n\n /**\n * Fetch the result of a completed video-generation job, downloading the\n * rendered bytes from the gateway's content endpoint.\n *\n * @throws {AIError} When the job has not succeeded yet\n */\n async fetchVideoGenerationResult(\n handle: VideoGenerationJob,\n ): Promise<VideoGenerationResult> {\n const video = await this.request<CompatVideoObject>(\n `/videos/${handle.jobId}`,\n );\n const status = this.mapVideo(video);\n if (status.status !== 'succeeded') {\n throw new AIError(\n `openai-compat-video job ${handle.jobId} has not succeeded (status: ${status.status})`,\n 'VIDEO_JOB_NOT_READY',\n 'openai-compat-video',\n handle.model,\n );\n }\n\n if (video.url) {\n return status.result as VideoGenerationResult;\n }\n\n const response = await this.rawRequest(`/videos/${handle.jobId}/content`);\n const arrayBuffer = await response.arrayBuffer();\n const { width, height } = parseSize(video.size);\n\n return {\n data: Buffer.from(arrayBuffer),\n mimeType: response.headers.get('content-type') || 'video/mp4',\n durationSeconds: video.seconds ? Number(video.seconds) : undefined,\n width,\n height,\n };\n }\n\n /**\n * Cancel a video-generation job. Cancellation of an in-flight Sora-shaped\n * job is not part of the documented OpenAI `videos` API and is\n * gateway-dependent (e.g. LiteLLM's Veo passthrough may support it); this\n * attempts the conventional `/videos/{id}/cancel` REST call and surfaces\n * an {@link AIError} if the gateway rejects or does not implement it.\n */\n async cancelVideoGenerationJob(handle: VideoGenerationJob): Promise<void> {\n await this.rawRequest(`/videos/${handle.jobId}/cancel`, {\n method: 'POST',\n });\n }\n\n /**\n * Cheap auth-shaped check for video-generation access.\n *\n * This probes `GET /videos/{sentinel-id}` rather than listing videos:\n * LiteLLM (the primary target gateway) requires a `custom_llm_provider`\n * parameter on its `GET /v1/videos` list route that this generic adapter\n * has no context to supply, so a list-based check would fail with valid\n * credentials on the flagship gateway. A single-resource GET works\n * uniformly instead, classified as:\n * - `401`/`403` → invalid credentials, throws {@link AuthenticationError}\n * - `404` → authenticated successfully, the sentinel id just doesn't\n * exist — this is a **valid** result, not a failure\n * - `2xx` → valid (the sentinel id coincidentally exists)\n *\n * Callers on a hot path must cache the result themselves.\n */\n async validateVideoGenerationAccess(): Promise<boolean> {\n const sentinelId = 'have-ai-validate-video-access';\n const response = await this.rawRequest(`/videos/${sentinelId}`, {\n allowNotFound: true,\n });\n return response.ok || response.status === 404;\n }\n\n private mapVideo(video: CompatVideoObject): VideoGenerationStatusResult {\n const { status, rawStatus } = mapStatus(video.status);\n\n if (status === 'failed') {\n return {\n status: 'failed',\n error:\n video.error?.message ||\n `Video generation failed (status: ${video.status})`,\n };\n }\n\n if (status !== 'succeeded') {\n return { status, progress: video.progress, rawStatus };\n }\n\n const { width, height } = parseSize(video.size);\n const result: VideoGenerationResult = {\n url: video.url,\n mimeType: 'video/mp4',\n durationSeconds: video.seconds ? Number(video.seconds) : undefined,\n width,\n height,\n };\n\n return { status: 'succeeded', result };\n }\n\n async getModels(): Promise<AIModel[]> {\n return [\n {\n id: 'sora-2',\n name: 'Sora 2',\n description: 'OpenAI-compatible Sora 2 video-generation model',\n contextLength: 0,\n capabilities: ['video_generation'],\n supportsFunctions: false,\n supportsVision: true,\n },\n {\n id: 'sora-2-pro',\n name: 'Sora 2 Pro',\n description: 'OpenAI-compatible Sora 2 Pro video-generation model',\n contextLength: 0,\n capabilities: ['video_generation'],\n supportsFunctions: false,\n supportsVision: true,\n },\n ];\n }\n\n async getCapabilities(): Promise<AICapabilities> {\n return {\n chat: false,\n completion: false,\n embeddings: false,\n streaming: false,\n functions: false,\n vision: false,\n fineTuning: false,\n imageEmbeddings: false,\n imageGeneration: false,\n videoGeneration: true,\n tts: false,\n voiceCloning: false,\n voiceDesign: false,\n maxContextLength: 0,\n supportedOperations: [\n 'submitVideoGenerationJob',\n 'getVideoGenerationJob',\n 'fetchVideoGenerationResult',\n 'cancelVideoGenerationJob',\n ],\n };\n }\n\n // ============================================================================\n // Text Generation / TTS Methods (Not Implemented - Use another provider)\n // ============================================================================\n\n async chat(\n _messages: AIMessage[],\n _options?: ChatOptions,\n ): Promise<AIResponse> {\n throw new AIError(\n 'Chat is not supported by the openai-compat-video provider. Use the openai or litellm provider for text generation.',\n 'NOT_IMPLEMENTED',\n 'openai-compat-video',\n );\n }\n\n async complete(\n _prompt: string,\n _options?: CompletionOptions,\n ): Promise<AIResponse> {\n throw new AIError(\n 'Complete is not supported by the openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'openai-compat-video',\n );\n }\n\n async message(_text: string, _options?: MessageOptions): Promise<string> {\n throw new AIError(\n 'Message is not supported by the openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'openai-compat-video',\n );\n }\n\n async embed(\n _text: string | string[],\n _options?: EmbeddingOptions,\n ): Promise<EmbeddingResponse> {\n throw new AIError(\n 'Embeddings are not supported by the openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'openai-compat-video',\n );\n }\n\n async embedImage(\n _image: string | Buffer,\n _options?: ImageEmbeddingOptions,\n ): Promise<EmbeddingResponse> {\n throw new AIError(\n 'Image embeddings are not supported by the openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'openai-compat-video',\n );\n }\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 supported by the openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'openai-compat-video',\n );\n }\n\n async generateImage(\n _prompt: string,\n _options?: ImageGenerationOptions,\n ): Promise<ImageGenerationResponse> {\n throw new AIError(\n 'Image generation is not supported by the openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'openai-compat-video',\n );\n }\n\n stream(\n _messages: AIMessage[],\n _options?: ChatOptions,\n ): AsyncIterable<string> {\n const error = new AIError(\n 'Chat streaming is not supported by the openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'openai-compat-video',\n );\n return {\n [Symbol.asyncIterator]: () => ({\n next: () => Promise.reject(error),\n }),\n };\n }\n\n async countTokens(_text: string): Promise<number> {\n throw new AIError(\n 'Token counting is not supported by the openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'openai-compat-video',\n );\n }\n\n async synthesizeSpeech(\n _text: string,\n _options?: TTSOptions,\n ): Promise<TTSResponse> {\n throw new AIError(\n 'TTS is not supported by the openai-compat-video provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'openai-compat-video',\n );\n }\n\n streamSpeech(_text: string, _options?: TTSOptions): AsyncIterable<Buffer> {\n const error = new AIError(\n 'TTS streaming is not supported by the openai-compat-video provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'openai-compat-video',\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 the openai-compat-video provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'openai-compat-video',\n );\n }\n\n async designVoice(_options: VoiceDesignOptions): Promise<Voice> {\n throw new AIError(\n 'Voice design is not supported by the openai-compat-video provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'openai-compat-video',\n );\n }\n\n async getVoices(_options?: VoiceListOptions): Promise<Voice[]> {\n throw new AIError(\n 'Voice listing is not supported by the openai-compat-video provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'openai-compat-video',\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqEA,SAAS,+BACP,SAC0B;CAC1B,IAAI,CAAC,QAAQ,SAAS,KAAK,GACzB,MAAM,IAAI,gBAAgB,2CAA2C;EACnE,UAAU;EACV,MAAM;CACR,CAAC;CAGH,OAAO;EACL,GAAG;EACH,SAAS,QAAQ,QAAQ,KAAK,CAAC,CAAC,QAAQ,QAAQ,EAAE;CACpD;AACF;AAEA,SAAS,UAAU,MAGjB;CACA,MAAM,QAAQ,MAAM,MAAM,eAAe;CACzC,IAAI,CAAC,OAAO,OAAO,CAAC;CACpB,OAAO;EAAE,OAAO,OAAO,MAAM,EAAE;EAAG,QAAQ,OAAO,MAAM,EAAE;CAAE;AAC7D;;;;;;;;;;;;;AAcA,SAAS,UAAU,QAGjB;CACA,QAAQ,QAAR;EACE,KAAK,UACH,OAAO,EAAE,QAAQ,SAAS;EAC5B,KAAK;EACL,KAAK;EACL,KAAK,WACH,OAAO,EAAE,QAAQ,UAAU;EAC7B,KAAK,aACH,OAAO,EAAE,QAAQ,YAAY;EAC/B,KAAK,UACH,OAAO,EAAE,QAAQ,SAAS;EAC5B,KAAK;EACL,KAAK,YACH,OAAO,EAAE,QAAQ,YAAY;EAC/B,SACE,OAAO;GAAE,QAAQ;GAAW,WAAW;EAAO;CAClD;AACF;;;;;;;;;;;;;;;;;;;;;AAsBA,IAAa,4BAAb,MAA8D;CAC5D;CAEA,YAAY,SAAmC;EAC7C,KAAK,UAAU,uBACb,+BAA+B,OAAO,CACxC;CACF;CAEA,IAAY,UAAkB;EAC5B,OAAO,KAAK,QAAQ;CACtB;CAEA,MAAc,QACZ,MACA,UAMI,CAAC,GACO;EAEZ,MAAM,OAAO,OAAM,MADI,KAAK,WAAW,MAAM,OAAO,EAAA,CACxB,KAAK;EACjC,OAAO,OAAQ,KAAK,MAAM,IAAI,IAAW,CAAC;CAC5C;CAEA,MAAc,WACZ,MACA,UAQI,CAAC,GACc;EACnB,IAAI,QAAQ,QAAQ,SAClB,MAAM,IAAI,QACR,iDACA,cACA,qBACF;EAGF,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,UAAU,MAAM;EAC5C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,SAAS,CAAC,CAAC,GAC3D,IAAI,aAAa,IAAI,KAAK,OAAO,KAAK,CAAC;EAGzC,MAAM,aAAa,IAAI,gBAAgB;EACvC,MAAM,YAAY,QAAQ,WAAW,KAAK,QAAQ;EAClD,IAAI,WAAW;EACf,MAAM,QAAQ,YACV,iBAAiB;GACf,WAAW;GACX,WAAW,MAAM;EACnB,GAAG,SAAS,IACZ,KAAA;EAKJ,MAAM,wBAAwB,WAAW,MAAM,QAAQ,QAAQ,MAAM;EACrE,IAAI,QAAQ,QAAQ,SAClB,gBAAgB;OAEhB,QAAQ,QAAQ,iBAAiB,SAAS,iBAAiB,EACzD,MAAM,KACR,CAAC;EAGH,IAAI;GACF,MAAM,UAAkC,EACtC,GAAI,KAAK,QAAQ,WAAW,CAAC,EAC/B;GACA,IAAI,KAAK,QAAQ,QACf,QAAQ,gBAAgB,UAAU,KAAK,QAAQ;GAEjD,IAAI,QAAQ,SAAS,KAAA,GACnB,QAAQ,kBAAkB;GAG5B,IAAI;GACJ,IAAI;IACF,WAAW,MAAM,MAAM,KAAK;KAC1B,QAAQ,QAAQ,UAAU;KAC1B;KACA,MACE,QAAQ,SAAS,KAAA,IACb,KAAK,UAAU,QAAQ,IAAI,IAC3B,KAAA;KACN,QAAQ,WAAW;IACrB,CAAC;GACH,SAAS,OAAO;IAKd,IAAI,UACF,MAAM,IAAI,QACR,kCAAkC,KAAK,mBAAmB,UAAU,KACpE,cACA,uBACA,KAAA,GACA,IACF;IAEF,IAAI,QAAQ,QAAQ,SAClB,MAAM,IAAI,QACR,iDACA,cACA,qBACF;IAEF,MAAM,IAAI,QACR,6CAA6C,KAAK,IAChD,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAEvD,iBACA,uBACA,KAAA,GACA,IACF;GACF;GAEA,IAAI,CAAC,SAAS,IAAI;IAChB,IAAI,SAAS,WAAW,OAAO,QAAQ,eACrC,OAAO;IAGT,MAAM,YAAY,MAAM,SAAS,KAAK;IAEtC,IAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KACjD,MAAM,IAAI,oBAAoB,qBAAqB;IAErD,IAAI,SAAS,WAAW,KACtB,MAAM,IAAI,eACR,uBACA,yBAAyB,EAAE,SAAS,SAAS,QAAQ,CAAC,CACxD;IAGF,MAAM,IAAI,QACR,kCAAkC,KAAK,WAAW,SAAS,OAAO,GAAG,aACrE,sCACA,qBACF;GACF;GAEA,OAAO;EACT,UAAU;GACR,IAAI,OAAO,aAAa,KAAK;GAC7B,QAAQ,QAAQ,oBAAoB,SAAS,eAAe;EAC9D;CACF;;;;;;;;;;;CAgBA,MAAM,yBACJ,SAC6B;EAC7B,MAAM,YAAY,KAAK,IAAI;EAC3B,MAAM,QAAQ,QAAQ,SAAS,KAAK,QAAQ,gBAAgB;EAE5D,MAAM,OAAgC;GACpC;GACA,QAAQ,QAAQ;EAClB;EACA,IAAI,QAAQ,oBAAoB,KAAA,GAC9B,KAAK,UAAU,OAAO,QAAQ,eAAe;EAE/C,IAAI,QAAQ,YAAY,SAAS,GAAG,GAClC,KAAK,OAAO,QAAQ;EAEtB,MAAM,iBAAiB,QAAQ,kBAAkB;EACjD,IAAI,gBACF,KAAK,kBAAkB,EACrB,WACE,OAAO,eAAe,UAAU,WAC5B,eAAe,QACf,QAAQ,eAAe,YAAY,YAAY,UAAU,eAAe,MAAM,SAAS,QAAQ,IACvG;EAGF,MAAM,QAAQ,MAAM,KAAK,QAA2B,WAAW;GAC7D,QAAQ;GACR;GACA,QAAQ,QAAQ;GAChB,SAAS,QAAQ;EACnB,CAAC;EAED,UACE,KAAK,SACL,uBACA,4BACA,MAAM,SAAS,OACf,KAAA,GACA,WACA;GACE,GAAI,QAAQ,oBAAoB,KAAA,IAC5B,EAAE,iBAAiB,OAAO,QAAQ,eAAe,EAAE,IACnD,CAAC;GACL,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;GAC/D,GAAI,QAAQ,cAAc,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;GAClE,GAAG,QAAQ;EACb,CACF;EAEA,OAAO;GACL,OAAO,MAAM;GACb,UAAU;GACV,OAAO,MAAM,SAAS;GACtB,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;EACpC;CACF;;;;;;;;;CAUA,MAAM,sBACJ,QACsC;EACtC,MAAM,QAAQ,MAAM,KAAK,QACvB,WAAW,OAAO,OACpB;EACA,OAAO,KAAK,SAAS,KAAK;CAC5B;;;;;;;CAQA,MAAM,2BACJ,QACgC;EAChC,MAAM,QAAQ,MAAM,KAAK,QACvB,WAAW,OAAO,OACpB;EACA,MAAM,SAAS,KAAK,SAAS,KAAK;EAClC,IAAI,OAAO,WAAW,aACpB,MAAM,IAAI,QACR,2BAA2B,OAAO,MAAM,8BAA8B,OAAO,OAAO,IACpF,uBACA,uBACA,OAAO,KACT;EAGF,IAAI,MAAM,KACR,OAAO,OAAO;EAGhB,MAAM,WAAW,MAAM,KAAK,WAAW,WAAW,OAAO,MAAM,SAAS;EACxE,MAAM,cAAc,MAAM,SAAS,YAAY;EAC/C,MAAM,EAAE,OAAO,WAAW,UAAU,MAAM,IAAI;EAE9C,OAAO;GACL,MAAM,OAAO,KAAK,WAAW;GAC7B,UAAU,SAAS,QAAQ,IAAI,cAAc,KAAK;GAClD,iBAAiB,MAAM,UAAU,OAAO,MAAM,OAAO,IAAI,KAAA;GACzD;GACA;EACF;CACF;;;;;;;;CASA,MAAM,yBAAyB,QAA2C;EACxE,MAAM,KAAK,WAAW,WAAW,OAAO,MAAM,UAAU,EACtD,QAAQ,OACV,CAAC;CACH;;;;;;;;;;;;;;;;;CAkBA,MAAM,gCAAkD;EAEtD,MAAM,WAAW,MAAM,KAAK,WAAW,yCAAyB,EAC9D,eAAe,KACjB,CAAC;EACD,OAAO,SAAS,MAAM,SAAS,WAAW;CAC5C;CAEA,SAAiB,OAAuD;EACtE,MAAM,EAAE,QAAQ,cAAc,UAAU,MAAM,MAAM;EAEpD,IAAI,WAAW,UACb,OAAO;GACL,QAAQ;GACR,OACE,MAAM,OAAO,WACb,oCAAoC,MAAM,OAAO;EACrD;EAGF,IAAI,WAAW,aACb,OAAO;GAAE;GAAQ,UAAU,MAAM;GAAU;EAAU;EAGvD,MAAM,EAAE,OAAO,WAAW,UAAU,MAAM,IAAI;EAS9C,OAAO;GAAE,QAAQ;GAAa,QAAA;IAP5B,KAAK,MAAM;IACX,UAAU;IACV,iBAAiB,MAAM,UAAU,OAAO,MAAM,OAAO,IAAI,KAAA;IACzD;IACA;GAG4B;EAAO;CACvC;CAEA,MAAM,YAAgC;EACpC,OAAO,CACL;GACE,IAAI;GACJ,MAAM;GACN,aAAa;GACb,eAAe;GACf,cAAc,CAAC,kBAAkB;GACjC,mBAAmB;GACnB,gBAAgB;EAClB,GACA;GACE,IAAI;GACJ,MAAM;GACN,aAAa;GACb,eAAe;GACf,cAAc,CAAC,kBAAkB;GACjC,mBAAmB;GACnB,gBAAgB;EAClB,CACF;CACF;CAEA,MAAM,kBAA2C;EAC/C,OAAO;GACL,MAAM;GACN,YAAY;GACZ,YAAY;GACZ,WAAW;GACX,WAAW;GACX,QAAQ;GACR,YAAY;GACZ,iBAAiB;GACjB,iBAAiB;GACjB,iBAAiB;GACjB,KAAK;GACL,cAAc;GACd,aAAa;GACb,kBAAkB;GAClB,qBAAqB;IACnB;IACA;IACA;IACA;GACF;EACF;CACF;CAMA,MAAM,KACJ,WACA,UACqB;EACrB,MAAM,IAAI,QACR,sHACA,mBACA,qBACF;CACF;CAEA,MAAM,SACJ,SACA,UACqB;EACrB,MAAM,IAAI,QACR,kEACA,mBACA,qBACF;CACF;CAEA,MAAM,QAAQ,OAAe,UAA4C;EACvE,MAAM,IAAI,QACR,iEACA,mBACA,qBACF;CACF;CAEA,MAAM,MACJ,OACA,UAC4B;EAC5B,MAAM,IAAI,QACR,qEACA,mBACA,qBACF;CACF;CAEA,MAAM,WACJ,QACA,UAC4B;EAC5B,MAAM,IAAI,QACR,2EACA,mBACA,qBACF;CACF;CAEA,MAAM,cACJ,QACA,SACA,UACiB;EACjB,MAAM,IAAI,QACR,2EACA,mBACA,qBACF;CACF;CAEA,MAAM,cACJ,SACA,UACkC;EAClC,MAAM,IAAI,QACR,0EACA,mBACA,qBACF;CACF;CAEA,OACE,WACA,UACuB;EACvB,MAAM,QAAQ,IAAI,QAChB,wEACA,mBACA,qBACF;EACA,OAAO,GACJ,OAAO,uBAAuB,EAC7B,YAAY,QAAQ,OAAO,KAAK,EAClC,GACF;CACF;CAEA,MAAM,YAAY,OAAgC;EAChD,MAAM,IAAI,QACR,wEACA,mBACA,qBACF;CACF;CAEA,MAAM,iBACJ,OACA,UACsB;EACtB,MAAM,IAAI,QACR,qFACA,mBACA,qBACF;CACF;CAEA,aAAa,OAAe,UAA8C;EACxE,MAAM,QAAQ,IAAI,QAChB,+FACA,mBACA,qBACF;EACA,OAAO,GACJ,OAAO,uBAAuB,EAC7B,YAAY,QAAQ,OAAO,KAAK,EAClC,GACF;CACF;CAEA,MAAM,WAAW,UAA6C;EAC5D,MAAM,IAAI,QACR,+FACA,mBACA,qBACF;CACF;CAEA,MAAM,YAAY,UAA8C;EAC9D,MAAM,IAAI,QACR,8FACA,mBACA,qBACF;CACF;CAEA,MAAM,UAAU,UAA+C;EAC7D,MAAM,IAAI,QACR,+FACA,mBACA,qBACF;CACF;AACF"}
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import { s as RateLimitError } from "./types-BpnNcTU1.js";
|
|
2
|
-
//#region src/shared/rate-limit.ts
|
|
3
|
-
var RATE_LIMITED_METHODS = /* @__PURE__ */ new Set([
|
|
4
|
-
"decide",
|
|
5
|
-
"chat",
|
|
6
|
-
"complete",
|
|
7
|
-
"message",
|
|
8
|
-
"embed",
|
|
9
|
-
"embedImage",
|
|
10
|
-
"describeImage",
|
|
11
|
-
"generateImage",
|
|
12
|
-
"getModels",
|
|
13
|
-
"synthesizeSpeech",
|
|
14
|
-
"cloneVoice",
|
|
15
|
-
"designVoice",
|
|
16
|
-
"getVoices",
|
|
17
|
-
"submitVideoGenerationJob"
|
|
18
|
-
]);
|
|
19
|
-
var MAX_BUDGET_COORDINATORS = 128;
|
|
20
|
-
var BUDGET_COORDINATOR_TTL_MS = 900 * 1e3;
|
|
21
|
-
var BudgetCoordinator = class {
|
|
22
|
-
nextAvailableAt = 0;
|
|
23
|
-
pendingSchedules = 0;
|
|
24
|
-
tail = Promise.resolve();
|
|
25
|
-
lastUsedAt = Date.now();
|
|
26
|
-
touch() {
|
|
27
|
-
this.lastUsedAt = Date.now();
|
|
28
|
-
}
|
|
29
|
-
schedule(work) {
|
|
30
|
-
this.pendingSchedules += 1;
|
|
31
|
-
this.touch();
|
|
32
|
-
const run = this.tail.then(work, work);
|
|
33
|
-
this.tail = run.then(() => void 0, () => void 0);
|
|
34
|
-
return run.finally(() => {
|
|
35
|
-
this.pendingSchedules = Math.max(0, this.pendingSchedules - 1);
|
|
36
|
-
this.touch();
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
async waitUntilReady() {
|
|
40
|
-
this.touch();
|
|
41
|
-
const delayMs = this.nextAvailableAt - Date.now();
|
|
42
|
-
if (delayMs > 0) await sleep(delayMs);
|
|
43
|
-
}
|
|
44
|
-
delayFor(delayMs) {
|
|
45
|
-
this.touch();
|
|
46
|
-
if (delayMs <= 0) return;
|
|
47
|
-
this.nextAvailableAt = Math.max(this.nextAvailableAt, Date.now() + delayMs);
|
|
48
|
-
}
|
|
49
|
-
isEvictable(now) {
|
|
50
|
-
return this.pendingSchedules === 0 && now >= this.nextAvailableAt;
|
|
51
|
-
}
|
|
52
|
-
isExpired(now) {
|
|
53
|
-
return this.isEvictable(now) && now - this.lastUsedAt >= BUDGET_COORDINATOR_TTL_MS;
|
|
54
|
-
}
|
|
55
|
-
getLastUsedAt() {
|
|
56
|
-
return this.lastUsedAt;
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
var budgetCoordinators = /* @__PURE__ */ new Map();
|
|
60
|
-
function sleep(delayMs) {
|
|
61
|
-
return new Promise((resolve) => {
|
|
62
|
-
setTimeout(resolve, delayMs);
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
function pruneBudgetCoordinators() {
|
|
66
|
-
const now = Date.now();
|
|
67
|
-
for (const [key, coordinator] of budgetCoordinators.entries()) if (coordinator.isExpired(now)) budgetCoordinators.delete(key);
|
|
68
|
-
while (budgetCoordinators.size > MAX_BUDGET_COORDINATORS) {
|
|
69
|
-
const evictableEntries = [...budgetCoordinators.entries()].filter(([, coordinator]) => coordinator.isEvictable(now)).sort(([, leftCoordinator], [, rightCoordinator]) => leftCoordinator.getLastUsedAt() - rightCoordinator.getLastUsedAt());
|
|
70
|
-
if (evictableEntries.length === 0) break;
|
|
71
|
-
budgetCoordinators.delete(evictableEntries[0][0]);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
function getBudgetCoordinator(key) {
|
|
75
|
-
pruneBudgetCoordinators();
|
|
76
|
-
let coordinator = budgetCoordinators.get(key);
|
|
77
|
-
if (!coordinator) {
|
|
78
|
-
coordinator = new BudgetCoordinator();
|
|
79
|
-
budgetCoordinators.set(key, coordinator);
|
|
80
|
-
pruneBudgetCoordinators();
|
|
81
|
-
}
|
|
82
|
-
coordinator.touch();
|
|
83
|
-
return coordinator;
|
|
84
|
-
}
|
|
85
|
-
function hasPacingConfig(rateLimit) {
|
|
86
|
-
if (!rateLimit || rateLimit.enabled === false) return false;
|
|
87
|
-
return rateLimit.enabled === true || rateLimit.key !== void 0 || rateLimit.cooldownMs !== void 0 || rateLimit.initialDelayMs !== void 0 || rateLimit.maxAttempts !== void 0;
|
|
88
|
-
}
|
|
89
|
-
function normalizeNonNegativeInteger(value, fallback) {
|
|
90
|
-
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
91
|
-
return Math.max(0, Math.trunc(value));
|
|
92
|
-
}
|
|
93
|
-
function hashKey(value) {
|
|
94
|
-
let hash = 2166136261;
|
|
95
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
96
|
-
hash ^= value.charCodeAt(index);
|
|
97
|
-
hash = Math.imul(hash, 16777619);
|
|
98
|
-
}
|
|
99
|
-
return (hash >>> 0).toString(36);
|
|
100
|
-
}
|
|
101
|
-
function deriveBudgetKey(options) {
|
|
102
|
-
const type = options.type;
|
|
103
|
-
const provider = typeof type === "string" && type ? type : "openai";
|
|
104
|
-
const seed = [
|
|
105
|
-
"apiKey" in options ? options.apiKey : void 0,
|
|
106
|
-
"apiToken" in options ? options.apiToken : void 0,
|
|
107
|
-
"credentials" in options ? options.credentials?.accessKeyId : void 0,
|
|
108
|
-
"endpoint" in options ? options.endpoint : void 0,
|
|
109
|
-
"baseUrl" in options ? options.baseUrl : void 0,
|
|
110
|
-
"cliPath" in options ? options.cliPath : void 0
|
|
111
|
-
].find((value) => typeof value === "string" && value.length > 0);
|
|
112
|
-
return seed ? `${provider}:${hashKey(seed)}` : `${provider}:default`;
|
|
113
|
-
}
|
|
114
|
-
function normalizeRateLimitConfig(options) {
|
|
115
|
-
const rateLimit = "rateLimit" in options ? options.rateLimit : void 0;
|
|
116
|
-
if (!hasPacingConfig(rateLimit)) return null;
|
|
117
|
-
return {
|
|
118
|
-
cooldownMs: normalizeNonNegativeInteger(rateLimit?.cooldownMs, 0),
|
|
119
|
-
initialDelayMs: normalizeNonNegativeInteger(rateLimit?.initialDelayMs, 5e3),
|
|
120
|
-
key: rateLimit?.key?.trim() || deriveBudgetKey(options),
|
|
121
|
-
maxAttempts: Math.max(1, normalizeNonNegativeInteger(rateLimit?.maxAttempts, 1))
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
function getRetryDelayMs(error, config) {
|
|
125
|
-
const hintedDelayMs = typeof error.retryAfter === "number" && Number.isFinite(error.retryAfter) ? Math.max(0, Math.ceil(error.retryAfter * 1e3)) : void 0;
|
|
126
|
-
if (hintedDelayMs !== void 0) return Math.max(config.cooldownMs, hintedDelayMs);
|
|
127
|
-
return Math.max(config.cooldownMs, config.initialDelayMs);
|
|
128
|
-
}
|
|
129
|
-
async function invokeWithPacing(execute, coordinator, config, allowRetry = true) {
|
|
130
|
-
let attempt = 1;
|
|
131
|
-
while (true) {
|
|
132
|
-
await coordinator.waitUntilReady();
|
|
133
|
-
try {
|
|
134
|
-
const result = await execute();
|
|
135
|
-
coordinator.delayFor(config.cooldownMs);
|
|
136
|
-
return result;
|
|
137
|
-
} catch (error) {
|
|
138
|
-
if (error instanceof RateLimitError) {
|
|
139
|
-
coordinator.delayFor(getRetryDelayMs(error, config));
|
|
140
|
-
if (allowRetry && error.retryable && attempt < config.maxAttempts) {
|
|
141
|
-
attempt += 1;
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
throw error;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
function parseRetryAfterSeconds(retryAfter) {
|
|
150
|
-
if (typeof retryAfter === "number" && Number.isFinite(retryAfter)) return Math.max(0, retryAfter);
|
|
151
|
-
if (typeof retryAfter !== "string") return;
|
|
152
|
-
const trimmed = retryAfter.trim();
|
|
153
|
-
if (!trimmed) return;
|
|
154
|
-
const seconds = Number.parseFloat(trimmed);
|
|
155
|
-
if (!Number.isNaN(seconds)) return Math.max(0, seconds);
|
|
156
|
-
const retryAt = Date.parse(trimmed);
|
|
157
|
-
if (Number.isNaN(retryAt)) return;
|
|
158
|
-
return Math.max(0, Math.ceil((retryAt - Date.now()) / 1e3));
|
|
159
|
-
}
|
|
160
|
-
function getHeaderValue(headers, headerName) {
|
|
161
|
-
if (!headers || typeof headers !== "object") return;
|
|
162
|
-
if ("get" in headers && typeof headers.get === "function") return headers.get(headerName) ?? headers.get(headerName.toLowerCase());
|
|
163
|
-
const objectHeaders = headers;
|
|
164
|
-
return objectHeaders[headerName] ?? objectHeaders[headerName.toLowerCase()];
|
|
165
|
-
}
|
|
166
|
-
function extractRetryAfterSeconds(error) {
|
|
167
|
-
if (!error || typeof error !== "object") return;
|
|
168
|
-
if ("retryAfter" in error) {
|
|
169
|
-
const retryAfter = parseRetryAfterSeconds(error.retryAfter);
|
|
170
|
-
if (retryAfter !== void 0) return retryAfter;
|
|
171
|
-
}
|
|
172
|
-
if ("headers" in error) {
|
|
173
|
-
const retryAfter = parseRetryAfterSeconds(getHeaderValue(error.headers, "retry-after"));
|
|
174
|
-
if (retryAfter !== void 0) return retryAfter;
|
|
175
|
-
}
|
|
176
|
-
const message = "message" in error && typeof error.message === "string" ? error.message : "";
|
|
177
|
-
const retryAfterMatch = message.match(/retry after\s+(\d+(?:\.\d+)?)\s*s?/i) ?? message.match(/retryDelay[^\d]*(\d+(?:\.\d+)?)s/i);
|
|
178
|
-
return retryAfterMatch ? parseRetryAfterSeconds(retryAfterMatch[1]) : void 0;
|
|
179
|
-
}
|
|
180
|
-
function createRateLimitedAI(client, options) {
|
|
181
|
-
const config = normalizeRateLimitConfig(options);
|
|
182
|
-
if (!config) return client;
|
|
183
|
-
const wrappedMethods = /* @__PURE__ */ new Map();
|
|
184
|
-
const allowSeevioSubmitRetry = options.type !== "seevio";
|
|
185
|
-
return new Proxy(client, { get(target, property, receiver) {
|
|
186
|
-
const value = Reflect.get(target, property, receiver);
|
|
187
|
-
if (typeof value !== "function") return value;
|
|
188
|
-
if (!RATE_LIMITED_METHODS.has(property)) {
|
|
189
|
-
if (!wrappedMethods.has(property)) wrappedMethods.set(property, value.bind(target));
|
|
190
|
-
return wrappedMethods.get(property);
|
|
191
|
-
}
|
|
192
|
-
if (!wrappedMethods.has(property)) wrappedMethods.set(property, (...args) => {
|
|
193
|
-
const coordinator = getBudgetCoordinator(config.key);
|
|
194
|
-
return coordinator.schedule(() => invokeWithPacing(() => Reflect.apply(value, target, args), coordinator, config, property !== "submitVideoGenerationJob" || allowSeevioSubmitRetry));
|
|
195
|
-
});
|
|
196
|
-
return wrappedMethods.get(property);
|
|
197
|
-
} });
|
|
198
|
-
}
|
|
199
|
-
//#endregion
|
|
200
|
-
export { extractRetryAfterSeconds as n, createRateLimitedAI as t };
|
|
201
|
-
|
|
202
|
-
//# sourceMappingURL=rate-limit-BVW_1Tey.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit-BVW_1Tey.js","names":[],"sources":["../../src/shared/rate-limit.ts"],"sourcesContent":["import type { AIClientOptions } from './client';\nimport type { AIInterface, AIRateLimitOptions, GetAIOptions } from './types';\nimport { AIError, RateLimitError } from './types';\n\nconst RATE_LIMITED_METHODS = new Set<keyof AIInterface>([\n 'decide',\n 'chat',\n 'complete',\n 'message',\n 'embed',\n 'embedImage',\n 'describeImage',\n 'generateImage',\n 'getModels',\n 'synthesizeSpeech',\n 'cloneVoice',\n 'designVoice',\n 'getVoices',\n 'submitVideoGenerationJob',\n]);\n\nconst MAX_BUDGET_COORDINATORS = 128;\nconst BUDGET_COORDINATOR_TTL_MS = 15 * 60 * 1000;\n\ninterface NormalizedRateLimitConfig {\n cooldownMs: number;\n initialDelayMs: number;\n key: string;\n maxAttempts: number;\n}\n\nclass BudgetCoordinator {\n private nextAvailableAt = 0;\n private pendingSchedules = 0;\n private tail: Promise<void> = Promise.resolve();\n private lastUsedAt = Date.now();\n\n touch(): void {\n this.lastUsedAt = Date.now();\n }\n\n schedule<T>(work: () => Promise<T>): Promise<T> {\n this.pendingSchedules += 1;\n this.touch();\n\n const run = this.tail.then(work, work);\n this.tail = run.then(\n () => undefined,\n () => undefined,\n );\n\n return run.finally(() => {\n this.pendingSchedules = Math.max(0, this.pendingSchedules - 1);\n this.touch();\n });\n }\n\n async waitUntilReady(): Promise<void> {\n this.touch();\n const delayMs = this.nextAvailableAt - Date.now();\n if (delayMs > 0) {\n await sleep(delayMs);\n }\n }\n\n delayFor(delayMs: number): void {\n this.touch();\n if (delayMs <= 0) {\n return;\n }\n\n this.nextAvailableAt = Math.max(this.nextAvailableAt, Date.now() + delayMs);\n }\n\n isEvictable(now: number): boolean {\n return this.pendingSchedules === 0 && now >= this.nextAvailableAt;\n }\n\n isExpired(now: number): boolean {\n return (\n this.isEvictable(now) &&\n now - this.lastUsedAt >= BUDGET_COORDINATOR_TTL_MS\n );\n }\n\n getLastUsedAt(): number {\n return this.lastUsedAt;\n }\n}\n\nconst budgetCoordinators = new Map<string, BudgetCoordinator>();\n\nfunction sleep(delayMs: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, delayMs);\n });\n}\n\nfunction pruneBudgetCoordinators(): void {\n const now = Date.now();\n\n for (const [key, coordinator] of budgetCoordinators.entries()) {\n if (coordinator.isExpired(now)) {\n budgetCoordinators.delete(key);\n }\n }\n\n while (budgetCoordinators.size > MAX_BUDGET_COORDINATORS) {\n const evictableEntries = [...budgetCoordinators.entries()]\n .filter(([, coordinator]) => coordinator.isEvictable(now))\n .sort(\n ([, leftCoordinator], [, rightCoordinator]) =>\n leftCoordinator.getLastUsedAt() - rightCoordinator.getLastUsedAt(),\n );\n\n if (evictableEntries.length === 0) {\n break;\n }\n\n budgetCoordinators.delete(evictableEntries[0][0]);\n }\n}\n\nfunction getBudgetCoordinator(key: string): BudgetCoordinator {\n pruneBudgetCoordinators();\n\n let coordinator = budgetCoordinators.get(key);\n if (!coordinator) {\n coordinator = new BudgetCoordinator();\n budgetCoordinators.set(key, coordinator);\n pruneBudgetCoordinators();\n }\n\n coordinator.touch();\n return coordinator;\n}\n\nfunction hasPacingConfig(rateLimit?: AIRateLimitOptions): boolean {\n if (!rateLimit || rateLimit.enabled === false) {\n return false;\n }\n\n return (\n rateLimit.enabled === true ||\n rateLimit.key !== undefined ||\n rateLimit.cooldownMs !== undefined ||\n rateLimit.initialDelayMs !== undefined ||\n rateLimit.maxAttempts !== undefined\n );\n}\n\nfunction normalizeNonNegativeInteger(\n value: number | undefined,\n fallback: number,\n): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n return fallback;\n }\n\n return Math.max(0, Math.trunc(value));\n}\n\nfunction hashKey(value: string): string {\n let hash = 2166136261;\n\n for (let index = 0; index < value.length; index += 1) {\n hash ^= value.charCodeAt(index);\n hash = Math.imul(hash, 16777619);\n }\n\n return (hash >>> 0).toString(36);\n}\n\nfunction deriveBudgetKey(options: GetAIOptions | AIClientOptions): string {\n const type = (options as { type?: string }).type;\n const provider = typeof type === 'string' && type ? type : 'openai';\n\n const credentialLikeValues = [\n 'apiKey' in options ? options.apiKey : undefined,\n 'apiToken' in options ? options.apiToken : undefined,\n 'credentials' in options ? options.credentials?.accessKeyId : undefined,\n 'endpoint' in options ? options.endpoint : undefined,\n 'baseUrl' in options ? options.baseUrl : undefined,\n 'cliPath' in options ? options.cliPath : undefined,\n ];\n\n const seed = credentialLikeValues.find(\n (value): value is string => typeof value === 'string' && value.length > 0,\n );\n\n return seed ? `${provider}:${hashKey(seed)}` : `${provider}:default`;\n}\n\nfunction normalizeRateLimitConfig(\n options: GetAIOptions | AIClientOptions,\n): NormalizedRateLimitConfig | null {\n const rateLimit = 'rateLimit' in options ? options.rateLimit : undefined;\n\n if (!hasPacingConfig(rateLimit)) {\n return null;\n }\n\n return {\n cooldownMs: normalizeNonNegativeInteger(rateLimit?.cooldownMs, 0),\n initialDelayMs: normalizeNonNegativeInteger(\n rateLimit?.initialDelayMs,\n 5000,\n ),\n key: rateLimit?.key?.trim() || deriveBudgetKey(options),\n maxAttempts: Math.max(\n 1,\n normalizeNonNegativeInteger(rateLimit?.maxAttempts, 1),\n ),\n };\n}\n\nfunction getRetryDelayMs(\n error: RateLimitError,\n config: NormalizedRateLimitConfig,\n): number {\n const hintedDelayMs =\n typeof error.retryAfter === 'number' && Number.isFinite(error.retryAfter)\n ? Math.max(0, Math.ceil(error.retryAfter * 1000))\n : undefined;\n\n if (hintedDelayMs !== undefined) {\n return Math.max(config.cooldownMs, hintedDelayMs);\n }\n\n return Math.max(config.cooldownMs, config.initialDelayMs);\n}\n\nasync function invokeWithPacing<T>(\n execute: () => Promise<T>,\n coordinator: BudgetCoordinator,\n config: NormalizedRateLimitConfig,\n allowRetry = true,\n): Promise<T> {\n let attempt = 1;\n\n while (true) {\n await coordinator.waitUntilReady();\n\n try {\n const result = await execute();\n coordinator.delayFor(config.cooldownMs);\n return result;\n } catch (error) {\n if (error instanceof RateLimitError) {\n coordinator.delayFor(getRetryDelayMs(error, config));\n\n if (allowRetry && error.retryable && attempt < config.maxAttempts) {\n attempt += 1;\n continue;\n }\n }\n\n throw error;\n }\n }\n}\n\nexport function parseRetryAfterSeconds(\n retryAfter: number | string | null | undefined,\n): number | undefined {\n if (typeof retryAfter === 'number' && Number.isFinite(retryAfter)) {\n return Math.max(0, retryAfter);\n }\n\n if (typeof retryAfter !== 'string') {\n return undefined;\n }\n\n const trimmed = retryAfter.trim();\n if (!trimmed) {\n return undefined;\n }\n\n const seconds = Number.parseFloat(trimmed);\n if (!Number.isNaN(seconds)) {\n return Math.max(0, seconds);\n }\n\n const retryAt = Date.parse(trimmed);\n if (Number.isNaN(retryAt)) {\n return undefined;\n }\n\n return Math.max(0, Math.ceil((retryAt - Date.now()) / 1000));\n}\n\nfunction getHeaderValue(\n headers: unknown,\n headerName: string,\n): number | string | undefined {\n if (!headers || typeof headers !== 'object') {\n return undefined;\n }\n\n if ('get' in headers && typeof headers.get === 'function') {\n return headers.get(headerName) ?? headers.get(headerName.toLowerCase());\n }\n\n const objectHeaders = headers as Record<string, number | string | undefined>;\n return objectHeaders[headerName] ?? objectHeaders[headerName.toLowerCase()];\n}\n\nexport function extractRetryAfterSeconds(error: unknown): number | undefined {\n if (!error || typeof error !== 'object') {\n return undefined;\n }\n\n if ('retryAfter' in error) {\n const retryAfter = parseRetryAfterSeconds(\n (error as { retryAfter?: number | string | null }).retryAfter,\n );\n if (retryAfter !== undefined) {\n return retryAfter;\n }\n }\n\n if ('headers' in error) {\n const retryAfter = parseRetryAfterSeconds(\n getHeaderValue((error as { headers?: unknown }).headers, 'retry-after'),\n );\n if (retryAfter !== undefined) {\n return retryAfter;\n }\n }\n\n const message =\n 'message' in error && typeof error.message === 'string'\n ? error.message\n : '';\n\n const retryAfterMatch =\n message.match(/retry after\\s+(\\d+(?:\\.\\d+)?)\\s*s?/i) ??\n message.match(/retryDelay[^\\d]*(\\d+(?:\\.\\d+)?)s/i);\n\n return retryAfterMatch\n ? parseRetryAfterSeconds(retryAfterMatch[1])\n : undefined;\n}\n\nexport function createRateLimitedAI<T extends AIInterface>(\n client: T,\n options: GetAIOptions | AIClientOptions,\n): T {\n const config = normalizeRateLimitConfig(options);\n if (!config) {\n return client;\n }\n\n const wrappedMethods = new Map<PropertyKey, unknown>();\n // Seevio does not document idempotency for billed video-task submission.\n // Existing providers retain their established pacing-retry behavior.\n const allowSeevioSubmitRetry = options.type !== 'seevio';\n\n return new Proxy(client, {\n get(target, property, receiver) {\n const value = Reflect.get(target, property, receiver);\n\n if (typeof value !== 'function') {\n return value;\n }\n\n if (!RATE_LIMITED_METHODS.has(property as keyof AIInterface)) {\n if (!wrappedMethods.has(property)) {\n wrappedMethods.set(property, value.bind(target));\n }\n\n return wrappedMethods.get(property);\n }\n\n if (!wrappedMethods.has(property)) {\n wrappedMethods.set(property, (...args: unknown[]) => {\n const coordinator = getBudgetCoordinator(config.key);\n return coordinator.schedule(() =>\n invokeWithPacing(\n () => Reflect.apply(value, target, args) as Promise<unknown>,\n coordinator,\n config,\n property !== 'submitVideoGenerationJob' || allowSeevioSubmitRetry,\n ),\n );\n });\n }\n\n return wrappedMethods.get(property);\n },\n }) as T;\n}\n\nexport function __resetAIRateLimitStateForTests(): void {\n budgetCoordinators.clear();\n}\n\nexport function __getAIRateLimitStateForTests(): {\n count: number;\n maxBudgetCoordinators: number;\n ttlMs: number;\n} {\n return {\n count: budgetCoordinators.size,\n maxBudgetCoordinators: MAX_BUDGET_COORDINATORS,\n ttlMs: BUDGET_COORDINATOR_TTL_MS,\n };\n}\n\nexport function isRetryableAIError(error: unknown): error is AIError {\n return error instanceof AIError && error.retryable;\n}\n"],"mappings":"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"}
|