@happyvertical/ai 0.86.3 → 0.87.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.
Files changed (52) hide show
  1. package/README.md +60 -2
  2. package/dist/chunks/{anthropic-DuYUnw8j.js → anthropic-ByuqC5mC.js} +4 -4
  3. package/dist/chunks/{anthropic-DuYUnw8j.js.map → anthropic-ByuqC5mC.js.map} +1 -1
  4. package/dist/chunks/{bedrock-ikQJAwWB.js → bedrock-BeVCmkrS.js} +3 -3
  5. package/dist/chunks/{bedrock-ikQJAwWB.js.map → bedrock-BeVCmkrS.js.map} +1 -1
  6. package/dist/chunks/{bifrost-CaYhelVL.js → bifrost-DvYZ3LNf.js} +4 -4
  7. package/dist/chunks/{bifrost-CaYhelVL.js.map → bifrost-DvYZ3LNf.js.map} +1 -1
  8. package/dist/chunks/{byteplus-modelark-B8l8sPXu.js → byteplus-modelark-DaF0U4n1.js} +4 -4
  9. package/dist/chunks/{byteplus-modelark-B8l8sPXu.js.map → byteplus-modelark-DaF0U4n1.js.map} +1 -1
  10. package/dist/chunks/{claude-cli-DeTKk9nu.js → claude-cli-BH_C62dV.js} +3 -3
  11. package/dist/chunks/{claude-cli-DeTKk9nu.js.map → claude-cli-BH_C62dV.js.map} +1 -1
  12. package/dist/chunks/{gateway-admin-DJXEDpUP.js → gateway-admin-B4RSBDmo.js} +2 -2
  13. package/dist/chunks/{gateway-admin-DJXEDpUP.js.map → gateway-admin-B4RSBDmo.js.map} +1 -1
  14. package/dist/chunks/{gemini-BT8fjXWz.js → gemini-GQdugon1.js} +4 -4
  15. package/dist/chunks/{gemini-BT8fjXWz.js.map → gemini-GQdugon1.js.map} +1 -1
  16. package/dist/chunks/{huggingface-CqzyxeLB.js → huggingface-DxPspM6Q.js} +3 -3
  17. package/dist/chunks/{huggingface-CqzyxeLB.js.map → huggingface-DxPspM6Q.js.map} +1 -1
  18. package/dist/chunks/{litellm-viqTuSY8.js → litellm-nOCNI7Vd.js} +4 -4
  19. package/dist/chunks/{litellm-viqTuSY8.js.map → litellm-nOCNI7Vd.js.map} +1 -1
  20. package/dist/chunks/{ollama-CdWqwZ05.js → ollama-BrB6bjnz.js} +3 -3
  21. package/dist/chunks/{ollama-CdWqwZ05.js.map → ollama-BrB6bjnz.js.map} +1 -1
  22. package/dist/chunks/{openai-BgrK3OfW.js → openai-BuwvBruA.js} +4 -4
  23. package/dist/chunks/{openai-BgrK3OfW.js.map → openai-BuwvBruA.js.map} +1 -1
  24. package/dist/chunks/{openai-compat-video-D_U7jo0d.js → openai-compat-video-DNE1Yvp6.js} +4 -4
  25. package/dist/chunks/{openai-compat-video-D_U7jo0d.js.map → openai-compat-video-DNE1Yvp6.js.map} +1 -1
  26. package/dist/chunks/{qwen-tts-BXjzY7DX.js → qwen-tts-CAh1upii.js} +2 -2
  27. package/dist/chunks/{qwen-tts-BXjzY7DX.js.map → qwen-tts-CAh1upii.js.map} +1 -1
  28. package/dist/chunks/{rate-limit-DgEhanDL.js → rate-limit-C2oPwLGI.js} +6 -5
  29. package/dist/chunks/rate-limit-C2oPwLGI.js.map +1 -0
  30. package/dist/chunks/{safety-C415clBm.js → safety-ClfpyKEN.js} +2 -2
  31. package/dist/chunks/{safety-C415clBm.js.map → safety-ClfpyKEN.js.map} +1 -1
  32. package/dist/chunks/seevio-Bbu_hTUF.js +451 -0
  33. package/dist/chunks/seevio-Bbu_hTUF.js.map +1 -0
  34. package/dist/chunks/{src-CU4qWJ2c.js → src-B091S0I4.js} +23 -16
  35. package/dist/chunks/src-B091S0I4.js.map +1 -0
  36. package/dist/chunks/{types-CINwUPDo.js → types-sSecpd_N.js} +3 -2
  37. package/dist/chunks/types-sSecpd_N.js.map +1 -0
  38. package/dist/index.js +3 -3
  39. package/dist/node/factory.d.ts +2 -1
  40. package/dist/node/factory.d.ts.map +1 -1
  41. package/dist/node.js +21 -6
  42. package/dist/node.js.map +1 -1
  43. package/dist/shared/factory.d.ts.map +1 -1
  44. package/dist/shared/providers/seevio.d.ts +48 -0
  45. package/dist/shared/providers/seevio.d.ts.map +1 -0
  46. package/dist/shared/rate-limit.d.ts.map +1 -1
  47. package/dist/shared/types.d.ts +61 -2
  48. package/dist/shared/types.d.ts.map +1 -1
  49. package/package.json +2 -2
  50. package/dist/chunks/rate-limit-DgEhanDL.js.map +0 -1
  51. package/dist/chunks/src-CU4qWJ2c.js.map +0 -1
  52. package/dist/chunks/types-CINwUPDo.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"bifrost-CaYhelVL.js","names":[],"sources":["../../src/shared/providers/bifrost.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 BifrostOptions,\n ChatOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n} from '../types';\nimport {\n BifrostAdmin,\n normalizeGatewayBaseUrl,\n resolveGatewayAdminBaseUrl,\n} from './gateway-admin';\nimport { type OpenAICompatibleProfile, OpenAIProvider } from './openai';\n\nconst BIFROST_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 isFilteredBifrostModel(modelId: string): boolean {\n return /moderation|transcrib|whisper|speech|tts|rerank/i.test(modelId);\n}\n\nfunction inferBifrostContextLength(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 inferBifrostFunctions(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 inferBifrostVision(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 inferBifrostCapabilities(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 (inferBifrostFunctions(modelId)) {\n capabilities.push('functions');\n }\n\n if (inferBifrostVision(modelId)) {\n capabilities.push('vision');\n }\n\n return capabilities;\n}\n\nfunction ensureBifrostOptions(options: BifrostOptions): BifrostOptions {\n if (!options.baseUrl?.trim()) {\n throw new ValidationError('Bifrost baseUrl is required', {\n provider: 'bifrost',\n hint: 'Pass baseUrl like http://localhost:8080, http://localhost:8080/openai, or http://localhost:8080/v1 and optionally adminBaseUrl for governance endpoints',\n });\n }\n\n const baseUrl = normalizeGatewayBaseUrl(options.baseUrl);\n const inferenceBaseUrl = /\\/(?:openai|v1|pydanticai\\/v1)$/.test(baseUrl)\n ? baseUrl\n : `${baseUrl}/openai`;\n const headers = options.apiKey\n ? {\n ...options.headers,\n 'x-bf-vk': options.apiKey,\n 'x-api-key': options.apiKey,\n }\n : options.headers;\n\n return {\n ...options,\n baseUrl: inferenceBaseUrl,\n headers,\n };\n}\n\nconst BIFROST_PROFILE: OpenAICompatibleProfile = {\n providerLabel: 'Bifrost',\n providerName: 'bifrost',\n defaultModel: 'openai/gpt-4o-mini',\n capabilities: BIFROST_CAPABILITIES,\n describeModel: (modelId) => `Bifrost model: ${modelId}`,\n getContextLength: inferBifrostContextLength,\n getModelCapabilities: inferBifrostCapabilities,\n shouldIncludeModel: (modelId) => !isFilteredBifrostModel(modelId),\n supportsFunctions: inferBifrostFunctions,\n supportsVision: inferBifrostVision,\n};\n\ntype BifrostResolvedCapability =\n | 'chat'\n | 'vision'\n | 'embeddings'\n | 'image_generation';\n\nfunction supportsCapability(\n model: AIModel,\n capability: BifrostResolvedCapability,\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 * Bifrost provider implementation.\n *\n * Bifrost exposes OpenAI-compatible inference and governance admin APIs. This\n * provider reuses the OpenAI-compatible transport for inference and exposes\n * `admin` for tenant project and virtual-key provisioning.\n */\nexport class BifrostProvider extends OpenAIProvider {\n readonly admin: BifrostAdmin;\n private readonly configuredDefaultModel?: string;\n private readonly resolvedModelCache = new Map<\n BifrostResolvedCapability,\n Promise<string>\n >();\n\n constructor(options: BifrostOptions) {\n const normalized = ensureBifrostOptions(options);\n super(normalized, BIFROST_PROFILE);\n const adminKey = normalized.adminApiKey || normalized.apiKey;\n const adminUsername = normalized.adminUser || normalized.adminUsername;\n this.admin = new BifrostAdmin({\n provider: 'bifrost',\n baseUrl: resolveGatewayAdminBaseUrl(\n normalized.baseUrl,\n normalized.adminUrl || normalized.adminBaseUrl,\n 'bifrost',\n ),\n apiKey: adminKey,\n username: adminUsername,\n password: normalized.adminPassword,\n headers:\n adminKey && !(adminUsername && normalized.adminPassword)\n ? {\n ...normalized.adminHeaders,\n 'x-bf-vk': adminKey,\n 'x-api-key': adminKey,\n }\n : normalized.adminHeaders,\n timeout: normalized.timeout,\n });\n this.configuredDefaultModel = normalized.defaultModel;\n }\n\n private async resolveModel(\n capability: BifrostResolvedCapability,\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: BifrostResolvedCapability,\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 Bifrost gateway`,\n {\n provider: 'bifrost',\n capability,\n hint: 'Pass defaultModel explicitly or ensure the gateway 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 'bifrost',\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 'bifrost',\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 'bifrost',\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 'bifrost',\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
+ {"version":3,"file":"bifrost-DvYZ3LNf.js","names":[],"sources":["../../src/shared/providers/bifrost.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 BifrostOptions,\n ChatOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n} from '../types';\nimport {\n BifrostAdmin,\n normalizeGatewayBaseUrl,\n resolveGatewayAdminBaseUrl,\n} from './gateway-admin';\nimport { type OpenAICompatibleProfile, OpenAIProvider } from './openai';\n\nconst BIFROST_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 isFilteredBifrostModel(modelId: string): boolean {\n return /moderation|transcrib|whisper|speech|tts|rerank/i.test(modelId);\n}\n\nfunction inferBifrostContextLength(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 inferBifrostFunctions(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 inferBifrostVision(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 inferBifrostCapabilities(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 (inferBifrostFunctions(modelId)) {\n capabilities.push('functions');\n }\n\n if (inferBifrostVision(modelId)) {\n capabilities.push('vision');\n }\n\n return capabilities;\n}\n\nfunction ensureBifrostOptions(options: BifrostOptions): BifrostOptions {\n if (!options.baseUrl?.trim()) {\n throw new ValidationError('Bifrost baseUrl is required', {\n provider: 'bifrost',\n hint: 'Pass baseUrl like http://localhost:8080, http://localhost:8080/openai, or http://localhost:8080/v1 and optionally adminBaseUrl for governance endpoints',\n });\n }\n\n const baseUrl = normalizeGatewayBaseUrl(options.baseUrl);\n const inferenceBaseUrl = /\\/(?:openai|v1|pydanticai\\/v1)$/.test(baseUrl)\n ? baseUrl\n : `${baseUrl}/openai`;\n const headers = options.apiKey\n ? {\n ...options.headers,\n 'x-bf-vk': options.apiKey,\n 'x-api-key': options.apiKey,\n }\n : options.headers;\n\n return {\n ...options,\n baseUrl: inferenceBaseUrl,\n headers,\n };\n}\n\nconst BIFROST_PROFILE: OpenAICompatibleProfile = {\n providerLabel: 'Bifrost',\n providerName: 'bifrost',\n defaultModel: 'openai/gpt-4o-mini',\n capabilities: BIFROST_CAPABILITIES,\n describeModel: (modelId) => `Bifrost model: ${modelId}`,\n getContextLength: inferBifrostContextLength,\n getModelCapabilities: inferBifrostCapabilities,\n shouldIncludeModel: (modelId) => !isFilteredBifrostModel(modelId),\n supportsFunctions: inferBifrostFunctions,\n supportsVision: inferBifrostVision,\n};\n\ntype BifrostResolvedCapability =\n | 'chat'\n | 'vision'\n | 'embeddings'\n | 'image_generation';\n\nfunction supportsCapability(\n model: AIModel,\n capability: BifrostResolvedCapability,\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 * Bifrost provider implementation.\n *\n * Bifrost exposes OpenAI-compatible inference and governance admin APIs. This\n * provider reuses the OpenAI-compatible transport for inference and exposes\n * `admin` for tenant project and virtual-key provisioning.\n */\nexport class BifrostProvider extends OpenAIProvider {\n readonly admin: BifrostAdmin;\n private readonly configuredDefaultModel?: string;\n private readonly resolvedModelCache = new Map<\n BifrostResolvedCapability,\n Promise<string>\n >();\n\n constructor(options: BifrostOptions) {\n const normalized = ensureBifrostOptions(options);\n super(normalized, BIFROST_PROFILE);\n const adminKey = normalized.adminApiKey || normalized.apiKey;\n const adminUsername = normalized.adminUser || normalized.adminUsername;\n this.admin = new BifrostAdmin({\n provider: 'bifrost',\n baseUrl: resolveGatewayAdminBaseUrl(\n normalized.baseUrl,\n normalized.adminUrl || normalized.adminBaseUrl,\n 'bifrost',\n ),\n apiKey: adminKey,\n username: adminUsername,\n password: normalized.adminPassword,\n headers:\n adminKey && !(adminUsername && normalized.adminPassword)\n ? {\n ...normalized.adminHeaders,\n 'x-bf-vk': adminKey,\n 'x-api-key': adminKey,\n }\n : normalized.adminHeaders,\n timeout: normalized.timeout,\n });\n this.configuredDefaultModel = normalized.defaultModel;\n }\n\n private async resolveModel(\n capability: BifrostResolvedCapability,\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: BifrostResolvedCapability,\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 Bifrost gateway`,\n {\n provider: 'bifrost',\n capability,\n hint: 'Pass defaultModel explicitly or ensure the gateway 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 'bifrost',\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 'bifrost',\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 'bifrost',\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 'bifrost',\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,6 +1,6 @@
1
- import { r as AuthenticationError, s as RateLimitError, t as AIError } from "./types-CINwUPDo.js";
2
- import { n as extractRetryAfterSeconds } from "./rate-limit-DgEhanDL.js";
3
- import { a as normalizeBaseAIOptions } from "./safety-C415clBm.js";
1
+ import { r as AuthenticationError, s as RateLimitError, t as AIError } from "./types-sSecpd_N.js";
2
+ import { n as extractRetryAfterSeconds } from "./rate-limit-C2oPwLGI.js";
3
+ import { a as normalizeBaseAIOptions } from "./safety-ClfpyKEN.js";
4
4
  import { t as emitUsage } from "./usage-C1Y1Nlg4.js";
5
5
  //#region src/shared/providers/byteplus-modelark.ts
6
6
  /**
@@ -470,4 +470,4 @@ var ByteplusModelArkProvider = class {
470
470
  //#endregion
471
471
  export { ByteplusModelArkProvider };
472
472
 
473
- //# sourceMappingURL=byteplus-modelark-B8l8sPXu.js.map
473
+ //# sourceMappingURL=byteplus-modelark-DaF0U4n1.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"byteplus-modelark-B8l8sPXu.js","names":[],"sources":["../../src/shared/providers/byteplus-modelark.ts"],"sourcesContent":["/**\n * BytePlus ModelArk (Seedance) video-generation provider implementation\n *\n * Raw-HTTP provider following the `qwen3-tts` precedent: no vendor SDK, just\n * the ModelArk video-generation task API (create task / poll / fetch /\n * cancel). This provider is video-generation only; it throws\n * `NOT_IMPLEMENTED` for chat, embeddings, and other text/TTS operations.\n *\n * The base URL, endpoint paths (`/contents/generations/tasks`), request/\n * response field names, and the `queued|running|cancelled|succeeded|failed|\n * expired` status enum were confirmed directly against BytePlus's own\n * published docs and blog examples at the time this was written (see the\n * link below). The `expired` status, the `page_num`/`page_size`/`filter.*`\n * list query params, and the DELETE-as-cancel-or-delete semantics\n * (`queued` → cancelled, `running` → rejected, terminal → deleted) are\n * corroborated from that same documentation but have not been exercised\n * against a live ModelArk account by this package's test suite — see\n * `packages/ai/src/byteplus-modelark.integration.test.ts` for an opt-in\n * check (skipped unless `MODELARK_API_KEY` is set) that exercises those\n * specifics against the real API.\n *\n * @see https://docs.byteplus.com/en/docs/ModelArk/Video_Generation_API\n */\n\nimport { extractRetryAfterSeconds } from '../rate-limit';\nimport { normalizeBaseAIOptions } from '../safety';\nimport type {\n AICapabilities,\n AIInterface,\n AIMessage,\n AIModel,\n AIResponse,\n ByteplusModelArkOptions,\n ChatOptions,\n CompletionOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n MessageOptions,\n TTSOptions,\n TTSResponse,\n VideoGenerationJob,\n VideoGenerationOptions,\n VideoGenerationReferenceImage,\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\nconst DEFAULT_MODELARK_BASE_URL =\n 'https://ark.ap-southeast.bytepluses.com/api/v3';\nconst DEFAULT_MODELARK_MODEL = 'seedance-1-0-lite-t2v-250428';\nconst DEFAULT_MODELARK_I2V_MODEL = 'seedance-1-0-lite-i2v-250428';\n/** Burst capacity for the submit token bucket — see {@link SubmitRateLimiter}. */\nconst SUBMIT_BURST_CAPACITY = 2;\n\ntype ModelArkTaskStatus =\n | 'queued'\n | 'running'\n | 'cancelled'\n | 'succeeded'\n | 'failed'\n | 'expired';\n\ninterface ModelArkContentItem {\n type: 'text' | 'image_url';\n text?: string;\n image_url?: { url: string };\n role?: string;\n}\n\ninterface ModelArkCreateTaskRequest {\n model: string;\n content: ModelArkContentItem[];\n resolution?: string;\n ratio?: string;\n duration?: number;\n seed?: number;\n}\n\ninterface ModelArkCreateTaskResponse {\n id: string;\n}\n\ninterface ModelArkTaskResponse {\n id: string;\n model: string;\n status: ModelArkTaskStatus;\n content?: { video_url?: string; last_frame_url?: string };\n error?: { code: string; message: string } | null;\n created_at: number;\n updated_at: number;\n seed?: number;\n resolution?: string;\n ratio?: string;\n duration?: number;\n}\n\n/**\n * Resolve after `ms` milliseconds, or as soon as `signal` aborts —\n * whichever comes first. Never rejects; callers re-check `signal.aborted`\n * themselves immediately after, so a single check covers both \"timed out\n * normally\" and \"woke up early because of an abort.\"\n */\nfunction delayOrAbort(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n return;\n }\n const timer = setTimeout(resolve, ms);\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n resolve();\n },\n { once: true },\n );\n });\n}\n\n/**\n * Local token-bucket + concurrency limiter for submit-time throttling.\n *\n * ModelArk documents Seedance account limits of QPS 2 / 3 *concurrent\n * submissions*; this only gates `submitVideoGenerationJob` (task creation\n * HTTP calls), matching the \"submit-time throttling\" requirement — polling\n * and cancellation are not subject to the same account-level task-creation\n * limits. `maxConcurrent` bounds concurrent in-flight *submit HTTP\n * requests*, not the lifetime of the provider-side render tasks those\n * submissions create — this limiter has no way to observe when a task\n * actually finishes rendering on ModelArk's infrastructure, so\n * task-lifetime concurrency is not, and cannot be, tracked here.\n *\n * The bucket's burst capacity is capped at {@link SUBMIT_BURST_CAPACITY}\n * (independent of `requestsPerMinute`) so the bucket doesn't start full —\n * without this cap, a bucket sized to `requestsPerMinute` (e.g. 120) would\n * start with 120 tokens and let the first 120 submits through instantly,\n * defeating the QPS limit entirely.\n */\nclass SubmitRateLimiter {\n private tokens: number;\n private lastRefill: number;\n private readonly maxTokens: number;\n private readonly refillRate: number; // tokens per second\n private activeRequests = 0;\n private readonly maxConcurrent: number;\n\n constructor(options: { requestsPerMinute?: number; maxConcurrent?: number }) {\n const requestsPerMinute = options.requestsPerMinute ?? 120; // ~QPS 2\n this.refillRate = requestsPerMinute / 60;\n this.maxTokens = SUBMIT_BURST_CAPACITY;\n this.tokens = this.maxTokens;\n this.lastRefill = Date.now();\n this.maxConcurrent = options.maxConcurrent ?? 3;\n }\n\n /**\n * Acquire a submit slot + token, honoring `signal` throughout the wait.\n *\n * The concurrency slot and the token are checked and committed together\n * in one synchronous section (no `await` between the check and the\n * `tokens -=`/`activeRequests +=` mutations below), so a waiter can never\n * observe a stale \"slot available\" snapshot from before another waiter's\n * own commit — closing the TOCTOU window where two callers could both\n * pass the checks and push `activeRequests` past `maxConcurrent`.\n *\n * An aborted `signal` — whether already aborted at entry or aborted\n * while waiting for a slot/token — throws before any token is consumed\n * or `activeRequests` is incremented, so an aborted submission never\n * silently proceeds to create a billable job.\n */\n async acquire(signal?: AbortSignal): Promise<void> {\n const throwIfAborted = () => {\n if (signal?.aborted) {\n throw new AIError(\n 'ModelArk submission aborted by caller while waiting for the rate limiter',\n 'AI_ABORTED',\n 'byteplus-modelark',\n );\n }\n };\n\n throwIfAborted();\n\n const now = Date.now();\n const elapsed = (now - this.lastRefill) / 1000;\n this.tokens = Math.min(\n this.maxTokens,\n this.tokens + elapsed * this.refillRate,\n );\n this.lastRefill = now;\n\n for (;;) {\n throwIfAborted();\n\n if (this.activeRequests < this.maxConcurrent && this.tokens >= 1) {\n this.tokens -= 1;\n this.activeRequests += 1;\n return;\n }\n\n if (this.activeRequests >= this.maxConcurrent) {\n await delayOrAbort(100, signal);\n } else {\n const waitTime = Math.max(\n 1,\n ((1 - this.tokens) / this.refillRate) * 1000,\n );\n await delayOrAbort(waitTime, signal);\n const elapsedNow = (Date.now() - this.lastRefill) / 1000;\n this.tokens = Math.min(\n this.maxTokens,\n this.tokens + elapsedNow * this.refillRate,\n );\n this.lastRefill = Date.now();\n }\n }\n }\n\n release(): void {\n this.activeRequests = Math.max(0, this.activeRequests - 1);\n }\n}\n\n/**\n * FNV-1a hash, used only to avoid retaining raw API keys as in-memory Map\n * keys in {@link sharedSubmitLimiters}. Not cryptographic — collisions just\n * mean two distinct keys share a rate-limit bucket, which is a pacing\n * inefficiency, not a security issue.\n */\nfunction hashKey(value: string): string {\n let hash = 2166136261;\n for (let index = 0; index < value.length; index += 1) {\n hash ^= value.charCodeAt(index);\n hash = Math.imul(hash, 16777619);\n }\n return (hash >>> 0).toString(36);\n}\n\n/**\n * Submit-time limiters keyed by credential/config, shared module-wide.\n *\n * Provider instances are frequently constructed per pipeline step (a fresh\n * `getAI({ type: 'byteplus-modelark', ... })` per job), so a limiter owned\n * by a single provider instance would reset on every construction and never\n * actually enforce the account-wide QPS/concurrency limits. Keying by the\n * resolved credentials (or an explicit `rateLimit.key`) lets independently\n * constructed instances that target the same ModelArk account share one\n * bucket, mirroring how {@link createRateLimitedAI}'s budget coordinators\n * are shared by `rateLimit.key`.\n */\nconst sharedSubmitLimiters = new Map<string, SubmitRateLimiter>();\n\nfunction getSharedSubmitLimiter(\n options: ByteplusModelArkOptions,\n): SubmitRateLimiter {\n const key =\n options.rateLimit?.key?.trim() ||\n `byteplus-modelark:${hashKey(options.apiKey || 'default')}`;\n\n let limiter = sharedSubmitLimiters.get(key);\n if (!limiter) {\n limiter = new SubmitRateLimiter({\n requestsPerMinute: options.rateLimit?.requestsPerMinute,\n maxConcurrent: options.rateLimit?.maxConcurrent,\n });\n sharedSubmitLimiters.set(key, limiter);\n }\n return limiter;\n}\n\n/** @internal Test-only: clear shared submit limiters between test cases. */\nexport function __resetByteplusModelArkRateLimitersForTests(): void {\n sharedSubmitLimiters.clear();\n}\n\n/**\n * BytePlus ModelArk provider implementation for asynchronous video\n * generation (Seedance). Focuses on video-generation capabilities and\n * throws `NOT_IMPLEMENTED` for text generation methods.\n *\n * @example\n * ```typescript\n * import { getAI } from '@happyvertical/ai';\n *\n * const modelark = await getAI({\n * type: 'byteplus-modelark',\n * apiKey: process.env.MODELARK_API_KEY!,\n * });\n *\n * const job = await modelark.submitVideoGenerationJob({\n * prompt: 'A drone shot flying over a coastal cliff at sunrise',\n * durationSeconds: 5,\n * resolution: '720p',\n * aspectRatio: '16:9',\n * });\n *\n * // Persist `job`, poll later (even after a restart):\n * const status = await modelark.getVideoGenerationJob(job);\n * ```\n */\nexport class ByteplusModelArkProvider implements AIInterface {\n private options: ByteplusModelArkOptions;\n private readonly submitLimiter: SubmitRateLimiter;\n\n constructor(options: ByteplusModelArkOptions) {\n // Note: deliberately no `defaultModel` fallback baked in here — the\n // model-selection logic in submitVideoGenerationJob picks between the\n // t2v and i2v defaults based on whether referenceImages are present,\n // and an unconditional default here would always shadow that.\n this.options = normalizeBaseAIOptions({ ...options });\n\n this.submitLimiter = getSharedSubmitLimiter(this.options);\n }\n\n private get baseUrl(): string {\n return (this.options.baseUrl || DEFAULT_MODELARK_BASE_URL).replace(\n /\\/+$/,\n '',\n );\n }\n\n private async request<T>(\n path: string,\n options: {\n method?: 'GET' | 'POST' | 'DELETE';\n body?: unknown;\n query?: Record<string, string | number>;\n signal?: AbortSignal;\n timeout?: number;\n } = {},\n ): Promise<T> {\n if (options.signal?.aborted) {\n throw new AIError(\n 'ModelArk request aborted by caller',\n 'AI_ABORTED',\n 'byteplus-modelark',\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 let response: Response;\n try {\n response = await fetch(url, {\n method: options.method || 'GET',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.options.apiKey ?? ''}`,\n ...(this.options.headers || {}),\n },\n body: options.body ? JSON.stringify(options.body) : 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 `ModelArk request to ${path} timed out after ${timeoutMs}ms`,\n 'AI_TIMEOUT',\n 'byteplus-modelark',\n undefined,\n true,\n );\n }\n if (options.signal?.aborted) {\n throw new AIError(\n 'ModelArk request aborted by caller',\n 'AI_ABORTED',\n 'byteplus-modelark',\n );\n }\n throw new AIError(\n `Network error calling ModelArk ${path}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n 'NETWORK_ERROR',\n 'byteplus-modelark',\n undefined,\n true,\n );\n }\n\n if (!response.ok) {\n const errorText = await response.text();\n\n if (response.status === 401 || response.status === 403) {\n throw new AuthenticationError('byteplus-modelark');\n }\n if (response.status === 429) {\n throw new RateLimitError(\n 'byteplus-modelark',\n extractRetryAfterSeconds({ headers: response.headers }),\n );\n }\n\n throw new AIError(\n `ModelArk request to ${path} failed: ${response.status} ${errorText}`,\n 'MODELARK_REQUEST_FAILED',\n 'byteplus-modelark',\n );\n }\n\n if (response.status === 204) {\n return {} as T;\n }\n\n const text = await response.text();\n return text ? (JSON.parse(text) as T) : ({} as T);\n } finally {\n if (timer) clearTimeout(timer);\n options.signal?.removeEventListener('abort', onExternalAbort);\n }\n }\n\n private toImageUrl(reference: VideoGenerationReferenceImage): string {\n if (typeof reference.image === 'string') {\n return reference.image;\n }\n const mimeType = reference.mimeType || 'image/png';\n return `data:${mimeType};base64,${reference.image.toString('base64')}`;\n }\n\n // ============================================================================\n // Video Generation Methods (Implemented)\n // ============================================================================\n\n /**\n * Submit an asynchronous Seedance video-generation task.\n *\n * Submission is locally throttled to approximate ModelArk's documented\n * Seedance account limits (QPS 2, capped at {@link SUBMIT_BURST_CAPACITY}\n * burst / 3 concurrent *submissions* — task-lifetime concurrency on\n * ModelArk's infrastructure is not tracked; see {@link SubmitRateLimiter}).\n *\n * When `referenceImages` are supplied and no explicit `model` is chosen,\n * this selects the image-to-video default model rather than silently\n * generating text-to-video with the image ignored.\n */\n async submitVideoGenerationJob(\n options: VideoGenerationOptions,\n ): Promise<VideoGenerationJob> {\n const startTime = Date.now();\n await this.submitLimiter.acquire(options.signal);\n\n try {\n const hasReferenceImages = (options.referenceImages?.length ?? 0) > 0;\n const model =\n options.model ||\n this.options.defaultModel ||\n (hasReferenceImages\n ? DEFAULT_MODELARK_I2V_MODEL\n : DEFAULT_MODELARK_MODEL);\n\n const content: ModelArkContentItem[] = [];\n if (options.prompt) {\n content.push({ type: 'text', text: options.prompt });\n }\n for (const reference of options.referenceImages ?? []) {\n content.push({\n type: 'image_url',\n image_url: { url: this.toImageUrl(reference) },\n role: reference.role || 'reference_image',\n });\n }\n\n if (content.length === 0) {\n throw new AIError(\n 'submitVideoGenerationJob requires a prompt or at least one reference image',\n 'INVALID_INPUT',\n 'byteplus-modelark',\n );\n }\n\n const body: ModelArkCreateTaskRequest = {\n model,\n content,\n resolution: options.resolution,\n ratio: options.aspectRatio,\n duration: options.durationSeconds,\n seed: options.seed,\n };\n // Note: ModelArk's documented create-task schema has no negative_prompt\n // or fps fields, so those VideoGenerationOptions are intentionally not\n // forwarded for this provider.\n\n const response = await this.request<ModelArkCreateTaskResponse>(\n '/contents/generations/tasks',\n {\n method: 'POST',\n body,\n signal: options.signal,\n timeout: options.timeout,\n },\n );\n\n emitUsage(\n this.options,\n 'byteplus-modelark',\n 'submitVideoGenerationJob',\n 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: response.id,\n provider: 'byteplus-modelark',\n model,\n createdAt: new Date().toISOString(),\n };\n } finally {\n this.submitLimiter.release();\n }\n }\n\n /**\n * Poll the status of a ModelArk video-generation task.\n */\n async getVideoGenerationJob(\n handle: VideoGenerationJob,\n ): Promise<VideoGenerationStatusResult> {\n const task = await this.request<ModelArkTaskResponse>(\n `/contents/generations/tasks/${handle.jobId}`,\n );\n return this.mapTask(task);\n }\n\n /**\n * Fetch the result of a completed ModelArk video-generation task.\n *\n * @throws {AIError} When the job has not succeeded yet\n */\n async fetchVideoGenerationResult(\n handle: VideoGenerationJob,\n ): Promise<VideoGenerationResult> {\n const status = await this.getVideoGenerationJob(handle);\n if (status.status !== 'succeeded' || !status.result) {\n throw new AIError(\n `ModelArk video-generation job ${handle.jobId} has not succeeded (status: ${status.status})`,\n 'VIDEO_JOB_NOT_READY',\n 'byteplus-modelark',\n handle.model,\n );\n }\n return status.result;\n }\n\n /**\n * Cancel a ModelArk video-generation task.\n *\n * ModelArk's DELETE endpoint only transitions a `queued` task to\n * `cancelled`; a task that is already `running` cannot be cancelled and\n * the request fails with an {@link AIError} describing why. Terminal\n * tasks (`succeeded`/`failed`/`expired`) are deleted instead.\n */\n async cancelVideoGenerationJob(handle: VideoGenerationJob): Promise<void> {\n await this.request(`/contents/generations/tasks/${handle.jobId}`, {\n method: 'DELETE',\n });\n }\n\n /**\n * Cheap auth-shaped check for ModelArk access: lists tasks with a page\n * size of 1. Callers on a hot path must cache the result themselves.\n */\n async validateVideoGenerationAccess(): Promise<boolean> {\n await this.request('/contents/generations/tasks', {\n query: { page_size: 1 },\n });\n return true;\n }\n\n private mapTask(task: ModelArkTaskResponse): VideoGenerationStatusResult {\n switch (task.status) {\n case 'queued':\n return { status: 'queued' };\n case 'running':\n return { status: 'running' };\n case 'cancelled':\n return { status: 'cancelled' };\n case 'expired':\n return {\n status: 'failed',\n error: 'ModelArk task expired before the result was retrieved',\n };\n case 'failed':\n return {\n status: 'failed',\n error: task.error?.message || 'ModelArk video generation failed',\n };\n case 'succeeded': {\n if (!task.content?.video_url) {\n return {\n status: 'failed',\n error: 'ModelArk reported success with no video_url',\n };\n }\n return {\n status: 'succeeded',\n result: {\n url: task.content.video_url,\n mimeType: 'video/mp4',\n durationSeconds: task.duration,\n },\n };\n }\n default:\n // Never invent a terminal state for a status this package doesn't\n // recognize (e.g. a future ModelArk status addition) — treat it as\n // still in flight so a caller doesn't misread a mid-render job as\n // failed, resubmit, and double-bill. The raw value is preserved for\n // diagnostics.\n return { status: 'running', rawStatus: task.status };\n }\n }\n\n async getModels(): Promise<AIModel[]> {\n return [\n {\n id: 'seedance-1-0-lite-t2v-250428',\n name: 'Seedance 1.0 Lite (text-to-video)',\n description: 'ModelArk Seedance 1.0 Lite text-to-video model',\n contextLength: 0,\n capabilities: ['video_generation'],\n supportsFunctions: false,\n supportsVision: false,\n },\n {\n id: 'seedance-1-0-lite-i2v-250428',\n name: 'Seedance 1.0 Lite (image-to-video)',\n description: 'ModelArk Seedance 1.0 Lite image-to-video 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 BytePlus ModelArk provider. Use OpenAI, Anthropic, or another provider for text generation.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async message(_text: string, _options?: MessageOptions): Promise<string> {\n throw new AIError(\n 'Message is not supported by the BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async generateImage(\n _prompt: string,\n _options?: ImageGenerationOptions,\n ): Promise<ImageGenerationResponse> {\n throw new AIError(\n 'Synchronous image generation is not yet implemented by the BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n streamSpeech(_text: string, _options?: TTSOptions): AsyncIterable<Buffer> {\n const error = new AIError(\n 'TTS streaming is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async designVoice(_options: VoiceDesignOptions): Promise<Voice> {\n throw new AIError(\n 'Voice design is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async getVoices(_options?: VoiceListOptions): Promise<Voice[]> {\n throw new AIError(\n 'Voice listing is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"byteplus-modelark-DaF0U4n1.js","names":[],"sources":["../../src/shared/providers/byteplus-modelark.ts"],"sourcesContent":["/**\n * BytePlus ModelArk (Seedance) video-generation provider implementation\n *\n * Raw-HTTP provider following the `qwen3-tts` precedent: no vendor SDK, just\n * the ModelArk video-generation task API (create task / poll / fetch /\n * cancel). This provider is video-generation only; it throws\n * `NOT_IMPLEMENTED` for chat, embeddings, and other text/TTS operations.\n *\n * The base URL, endpoint paths (`/contents/generations/tasks`), request/\n * response field names, and the `queued|running|cancelled|succeeded|failed|\n * expired` status enum were confirmed directly against BytePlus's own\n * published docs and blog examples at the time this was written (see the\n * link below). The `expired` status, the `page_num`/`page_size`/`filter.*`\n * list query params, and the DELETE-as-cancel-or-delete semantics\n * (`queued` → cancelled, `running` → rejected, terminal → deleted) are\n * corroborated from that same documentation but have not been exercised\n * against a live ModelArk account by this package's test suite — see\n * `packages/ai/src/byteplus-modelark.integration.test.ts` for an opt-in\n * check (skipped unless `MODELARK_API_KEY` is set) that exercises those\n * specifics against the real API.\n *\n * @see https://docs.byteplus.com/en/docs/ModelArk/Video_Generation_API\n */\n\nimport { extractRetryAfterSeconds } from '../rate-limit';\nimport { normalizeBaseAIOptions } from '../safety';\nimport type {\n AICapabilities,\n AIInterface,\n AIMessage,\n AIModel,\n AIResponse,\n ByteplusModelArkOptions,\n ChatOptions,\n CompletionOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n MessageOptions,\n TTSOptions,\n TTSResponse,\n VideoGenerationJob,\n VideoGenerationOptions,\n VideoGenerationReferenceImage,\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\nconst DEFAULT_MODELARK_BASE_URL =\n 'https://ark.ap-southeast.bytepluses.com/api/v3';\nconst DEFAULT_MODELARK_MODEL = 'seedance-1-0-lite-t2v-250428';\nconst DEFAULT_MODELARK_I2V_MODEL = 'seedance-1-0-lite-i2v-250428';\n/** Burst capacity for the submit token bucket — see {@link SubmitRateLimiter}. */\nconst SUBMIT_BURST_CAPACITY = 2;\n\ntype ModelArkTaskStatus =\n | 'queued'\n | 'running'\n | 'cancelled'\n | 'succeeded'\n | 'failed'\n | 'expired';\n\ninterface ModelArkContentItem {\n type: 'text' | 'image_url';\n text?: string;\n image_url?: { url: string };\n role?: string;\n}\n\ninterface ModelArkCreateTaskRequest {\n model: string;\n content: ModelArkContentItem[];\n resolution?: string;\n ratio?: string;\n duration?: number;\n seed?: number;\n}\n\ninterface ModelArkCreateTaskResponse {\n id: string;\n}\n\ninterface ModelArkTaskResponse {\n id: string;\n model: string;\n status: ModelArkTaskStatus;\n content?: { video_url?: string; last_frame_url?: string };\n error?: { code: string; message: string } | null;\n created_at: number;\n updated_at: number;\n seed?: number;\n resolution?: string;\n ratio?: string;\n duration?: number;\n}\n\n/**\n * Resolve after `ms` milliseconds, or as soon as `signal` aborts —\n * whichever comes first. Never rejects; callers re-check `signal.aborted`\n * themselves immediately after, so a single check covers both \"timed out\n * normally\" and \"woke up early because of an abort.\"\n */\nfunction delayOrAbort(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n return;\n }\n const timer = setTimeout(resolve, ms);\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n resolve();\n },\n { once: true },\n );\n });\n}\n\n/**\n * Local token-bucket + concurrency limiter for submit-time throttling.\n *\n * ModelArk documents Seedance account limits of QPS 2 / 3 *concurrent\n * submissions*; this only gates `submitVideoGenerationJob` (task creation\n * HTTP calls), matching the \"submit-time throttling\" requirement — polling\n * and cancellation are not subject to the same account-level task-creation\n * limits. `maxConcurrent` bounds concurrent in-flight *submit HTTP\n * requests*, not the lifetime of the provider-side render tasks those\n * submissions create — this limiter has no way to observe when a task\n * actually finishes rendering on ModelArk's infrastructure, so\n * task-lifetime concurrency is not, and cannot be, tracked here.\n *\n * The bucket's burst capacity is capped at {@link SUBMIT_BURST_CAPACITY}\n * (independent of `requestsPerMinute`) so the bucket doesn't start full —\n * without this cap, a bucket sized to `requestsPerMinute` (e.g. 120) would\n * start with 120 tokens and let the first 120 submits through instantly,\n * defeating the QPS limit entirely.\n */\nclass SubmitRateLimiter {\n private tokens: number;\n private lastRefill: number;\n private readonly maxTokens: number;\n private readonly refillRate: number; // tokens per second\n private activeRequests = 0;\n private readonly maxConcurrent: number;\n\n constructor(options: { requestsPerMinute?: number; maxConcurrent?: number }) {\n const requestsPerMinute = options.requestsPerMinute ?? 120; // ~QPS 2\n this.refillRate = requestsPerMinute / 60;\n this.maxTokens = SUBMIT_BURST_CAPACITY;\n this.tokens = this.maxTokens;\n this.lastRefill = Date.now();\n this.maxConcurrent = options.maxConcurrent ?? 3;\n }\n\n /**\n * Acquire a submit slot + token, honoring `signal` throughout the wait.\n *\n * The concurrency slot and the token are checked and committed together\n * in one synchronous section (no `await` between the check and the\n * `tokens -=`/`activeRequests +=` mutations below), so a waiter can never\n * observe a stale \"slot available\" snapshot from before another waiter's\n * own commit — closing the TOCTOU window where two callers could both\n * pass the checks and push `activeRequests` past `maxConcurrent`.\n *\n * An aborted `signal` — whether already aborted at entry or aborted\n * while waiting for a slot/token — throws before any token is consumed\n * or `activeRequests` is incremented, so an aborted submission never\n * silently proceeds to create a billable job.\n */\n async acquire(signal?: AbortSignal): Promise<void> {\n const throwIfAborted = () => {\n if (signal?.aborted) {\n throw new AIError(\n 'ModelArk submission aborted by caller while waiting for the rate limiter',\n 'AI_ABORTED',\n 'byteplus-modelark',\n );\n }\n };\n\n throwIfAborted();\n\n const now = Date.now();\n const elapsed = (now - this.lastRefill) / 1000;\n this.tokens = Math.min(\n this.maxTokens,\n this.tokens + elapsed * this.refillRate,\n );\n this.lastRefill = now;\n\n for (;;) {\n throwIfAborted();\n\n if (this.activeRequests < this.maxConcurrent && this.tokens >= 1) {\n this.tokens -= 1;\n this.activeRequests += 1;\n return;\n }\n\n if (this.activeRequests >= this.maxConcurrent) {\n await delayOrAbort(100, signal);\n } else {\n const waitTime = Math.max(\n 1,\n ((1 - this.tokens) / this.refillRate) * 1000,\n );\n await delayOrAbort(waitTime, signal);\n const elapsedNow = (Date.now() - this.lastRefill) / 1000;\n this.tokens = Math.min(\n this.maxTokens,\n this.tokens + elapsedNow * this.refillRate,\n );\n this.lastRefill = Date.now();\n }\n }\n }\n\n release(): void {\n this.activeRequests = Math.max(0, this.activeRequests - 1);\n }\n}\n\n/**\n * FNV-1a hash, used only to avoid retaining raw API keys as in-memory Map\n * keys in {@link sharedSubmitLimiters}. Not cryptographic — collisions just\n * mean two distinct keys share a rate-limit bucket, which is a pacing\n * inefficiency, not a security issue.\n */\nfunction hashKey(value: string): string {\n let hash = 2166136261;\n for (let index = 0; index < value.length; index += 1) {\n hash ^= value.charCodeAt(index);\n hash = Math.imul(hash, 16777619);\n }\n return (hash >>> 0).toString(36);\n}\n\n/**\n * Submit-time limiters keyed by credential/config, shared module-wide.\n *\n * Provider instances are frequently constructed per pipeline step (a fresh\n * `getAI({ type: 'byteplus-modelark', ... })` per job), so a limiter owned\n * by a single provider instance would reset on every construction and never\n * actually enforce the account-wide QPS/concurrency limits. Keying by the\n * resolved credentials (or an explicit `rateLimit.key`) lets independently\n * constructed instances that target the same ModelArk account share one\n * bucket, mirroring how {@link createRateLimitedAI}'s budget coordinators\n * are shared by `rateLimit.key`.\n */\nconst sharedSubmitLimiters = new Map<string, SubmitRateLimiter>();\n\nfunction getSharedSubmitLimiter(\n options: ByteplusModelArkOptions,\n): SubmitRateLimiter {\n const key =\n options.rateLimit?.key?.trim() ||\n `byteplus-modelark:${hashKey(options.apiKey || 'default')}`;\n\n let limiter = sharedSubmitLimiters.get(key);\n if (!limiter) {\n limiter = new SubmitRateLimiter({\n requestsPerMinute: options.rateLimit?.requestsPerMinute,\n maxConcurrent: options.rateLimit?.maxConcurrent,\n });\n sharedSubmitLimiters.set(key, limiter);\n }\n return limiter;\n}\n\n/** @internal Test-only: clear shared submit limiters between test cases. */\nexport function __resetByteplusModelArkRateLimitersForTests(): void {\n sharedSubmitLimiters.clear();\n}\n\n/**\n * BytePlus ModelArk provider implementation for asynchronous video\n * generation (Seedance). Focuses on video-generation capabilities and\n * throws `NOT_IMPLEMENTED` for text generation methods.\n *\n * @example\n * ```typescript\n * import { getAI } from '@happyvertical/ai';\n *\n * const modelark = await getAI({\n * type: 'byteplus-modelark',\n * apiKey: process.env.MODELARK_API_KEY!,\n * });\n *\n * const job = await modelark.submitVideoGenerationJob({\n * prompt: 'A drone shot flying over a coastal cliff at sunrise',\n * durationSeconds: 5,\n * resolution: '720p',\n * aspectRatio: '16:9',\n * });\n *\n * // Persist `job`, poll later (even after a restart):\n * const status = await modelark.getVideoGenerationJob(job);\n * ```\n */\nexport class ByteplusModelArkProvider implements AIInterface {\n private options: ByteplusModelArkOptions;\n private readonly submitLimiter: SubmitRateLimiter;\n\n constructor(options: ByteplusModelArkOptions) {\n // Note: deliberately no `defaultModel` fallback baked in here — the\n // model-selection logic in submitVideoGenerationJob picks between the\n // t2v and i2v defaults based on whether referenceImages are present,\n // and an unconditional default here would always shadow that.\n this.options = normalizeBaseAIOptions({ ...options });\n\n this.submitLimiter = getSharedSubmitLimiter(this.options);\n }\n\n private get baseUrl(): string {\n return (this.options.baseUrl || DEFAULT_MODELARK_BASE_URL).replace(\n /\\/+$/,\n '',\n );\n }\n\n private async request<T>(\n path: string,\n options: {\n method?: 'GET' | 'POST' | 'DELETE';\n body?: unknown;\n query?: Record<string, string | number>;\n signal?: AbortSignal;\n timeout?: number;\n } = {},\n ): Promise<T> {\n if (options.signal?.aborted) {\n throw new AIError(\n 'ModelArk request aborted by caller',\n 'AI_ABORTED',\n 'byteplus-modelark',\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 let response: Response;\n try {\n response = await fetch(url, {\n method: options.method || 'GET',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.options.apiKey ?? ''}`,\n ...(this.options.headers || {}),\n },\n body: options.body ? JSON.stringify(options.body) : 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 `ModelArk request to ${path} timed out after ${timeoutMs}ms`,\n 'AI_TIMEOUT',\n 'byteplus-modelark',\n undefined,\n true,\n );\n }\n if (options.signal?.aborted) {\n throw new AIError(\n 'ModelArk request aborted by caller',\n 'AI_ABORTED',\n 'byteplus-modelark',\n );\n }\n throw new AIError(\n `Network error calling ModelArk ${path}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n 'NETWORK_ERROR',\n 'byteplus-modelark',\n undefined,\n true,\n );\n }\n\n if (!response.ok) {\n const errorText = await response.text();\n\n if (response.status === 401 || response.status === 403) {\n throw new AuthenticationError('byteplus-modelark');\n }\n if (response.status === 429) {\n throw new RateLimitError(\n 'byteplus-modelark',\n extractRetryAfterSeconds({ headers: response.headers }),\n );\n }\n\n throw new AIError(\n `ModelArk request to ${path} failed: ${response.status} ${errorText}`,\n 'MODELARK_REQUEST_FAILED',\n 'byteplus-modelark',\n );\n }\n\n if (response.status === 204) {\n return {} as T;\n }\n\n const text = await response.text();\n return text ? (JSON.parse(text) as T) : ({} as T);\n } finally {\n if (timer) clearTimeout(timer);\n options.signal?.removeEventListener('abort', onExternalAbort);\n }\n }\n\n private toImageUrl(reference: VideoGenerationReferenceImage): string {\n if (typeof reference.image === 'string') {\n return reference.image;\n }\n const mimeType = reference.mimeType || 'image/png';\n return `data:${mimeType};base64,${reference.image.toString('base64')}`;\n }\n\n // ============================================================================\n // Video Generation Methods (Implemented)\n // ============================================================================\n\n /**\n * Submit an asynchronous Seedance video-generation task.\n *\n * Submission is locally throttled to approximate ModelArk's documented\n * Seedance account limits (QPS 2, capped at {@link SUBMIT_BURST_CAPACITY}\n * burst / 3 concurrent *submissions* — task-lifetime concurrency on\n * ModelArk's infrastructure is not tracked; see {@link SubmitRateLimiter}).\n *\n * When `referenceImages` are supplied and no explicit `model` is chosen,\n * this selects the image-to-video default model rather than silently\n * generating text-to-video with the image ignored.\n */\n async submitVideoGenerationJob(\n options: VideoGenerationOptions,\n ): Promise<VideoGenerationJob> {\n const startTime = Date.now();\n await this.submitLimiter.acquire(options.signal);\n\n try {\n const hasReferenceImages = (options.referenceImages?.length ?? 0) > 0;\n const model =\n options.model ||\n this.options.defaultModel ||\n (hasReferenceImages\n ? DEFAULT_MODELARK_I2V_MODEL\n : DEFAULT_MODELARK_MODEL);\n\n const content: ModelArkContentItem[] = [];\n if (options.prompt) {\n content.push({ type: 'text', text: options.prompt });\n }\n for (const reference of options.referenceImages ?? []) {\n content.push({\n type: 'image_url',\n image_url: { url: this.toImageUrl(reference) },\n role: reference.role || 'reference_image',\n });\n }\n\n if (content.length === 0) {\n throw new AIError(\n 'submitVideoGenerationJob requires a prompt or at least one reference image',\n 'INVALID_INPUT',\n 'byteplus-modelark',\n );\n }\n\n const body: ModelArkCreateTaskRequest = {\n model,\n content,\n resolution: options.resolution,\n ratio: options.aspectRatio,\n duration: options.durationSeconds,\n seed: options.seed,\n };\n // Note: ModelArk's documented create-task schema has no negative_prompt\n // or fps fields, so those VideoGenerationOptions are intentionally not\n // forwarded for this provider.\n\n const response = await this.request<ModelArkCreateTaskResponse>(\n '/contents/generations/tasks',\n {\n method: 'POST',\n body,\n signal: options.signal,\n timeout: options.timeout,\n },\n );\n\n emitUsage(\n this.options,\n 'byteplus-modelark',\n 'submitVideoGenerationJob',\n 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: response.id,\n provider: 'byteplus-modelark',\n model,\n createdAt: new Date().toISOString(),\n };\n } finally {\n this.submitLimiter.release();\n }\n }\n\n /**\n * Poll the status of a ModelArk video-generation task.\n */\n async getVideoGenerationJob(\n handle: VideoGenerationJob,\n ): Promise<VideoGenerationStatusResult> {\n const task = await this.request<ModelArkTaskResponse>(\n `/contents/generations/tasks/${handle.jobId}`,\n );\n return this.mapTask(task);\n }\n\n /**\n * Fetch the result of a completed ModelArk video-generation task.\n *\n * @throws {AIError} When the job has not succeeded yet\n */\n async fetchVideoGenerationResult(\n handle: VideoGenerationJob,\n ): Promise<VideoGenerationResult> {\n const status = await this.getVideoGenerationJob(handle);\n if (status.status !== 'succeeded' || !status.result) {\n throw new AIError(\n `ModelArk video-generation job ${handle.jobId} has not succeeded (status: ${status.status})`,\n 'VIDEO_JOB_NOT_READY',\n 'byteplus-modelark',\n handle.model,\n );\n }\n return status.result;\n }\n\n /**\n * Cancel a ModelArk video-generation task.\n *\n * ModelArk's DELETE endpoint only transitions a `queued` task to\n * `cancelled`; a task that is already `running` cannot be cancelled and\n * the request fails with an {@link AIError} describing why. Terminal\n * tasks (`succeeded`/`failed`/`expired`) are deleted instead.\n */\n async cancelVideoGenerationJob(handle: VideoGenerationJob): Promise<void> {\n await this.request(`/contents/generations/tasks/${handle.jobId}`, {\n method: 'DELETE',\n });\n }\n\n /**\n * Cheap auth-shaped check for ModelArk access: lists tasks with a page\n * size of 1. Callers on a hot path must cache the result themselves.\n */\n async validateVideoGenerationAccess(): Promise<boolean> {\n await this.request('/contents/generations/tasks', {\n query: { page_size: 1 },\n });\n return true;\n }\n\n private mapTask(task: ModelArkTaskResponse): VideoGenerationStatusResult {\n switch (task.status) {\n case 'queued':\n return { status: 'queued' };\n case 'running':\n return { status: 'running' };\n case 'cancelled':\n return { status: 'cancelled' };\n case 'expired':\n return {\n status: 'failed',\n error: 'ModelArk task expired before the result was retrieved',\n };\n case 'failed':\n return {\n status: 'failed',\n error: task.error?.message || 'ModelArk video generation failed',\n };\n case 'succeeded': {\n if (!task.content?.video_url) {\n return {\n status: 'failed',\n error: 'ModelArk reported success with no video_url',\n };\n }\n return {\n status: 'succeeded',\n result: {\n url: task.content.video_url,\n mimeType: 'video/mp4',\n durationSeconds: task.duration,\n },\n };\n }\n default:\n // Never invent a terminal state for a status this package doesn't\n // recognize (e.g. a future ModelArk status addition) — treat it as\n // still in flight so a caller doesn't misread a mid-render job as\n // failed, resubmit, and double-bill. The raw value is preserved for\n // diagnostics.\n return { status: 'running', rawStatus: task.status };\n }\n }\n\n async getModels(): Promise<AIModel[]> {\n return [\n {\n id: 'seedance-1-0-lite-t2v-250428',\n name: 'Seedance 1.0 Lite (text-to-video)',\n description: 'ModelArk Seedance 1.0 Lite text-to-video model',\n contextLength: 0,\n capabilities: ['video_generation'],\n supportsFunctions: false,\n supportsVision: false,\n },\n {\n id: 'seedance-1-0-lite-i2v-250428',\n name: 'Seedance 1.0 Lite (image-to-video)',\n description: 'ModelArk Seedance 1.0 Lite image-to-video 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 BytePlus ModelArk provider. Use OpenAI, Anthropic, or another provider for text generation.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async message(_text: string, _options?: MessageOptions): Promise<string> {\n throw new AIError(\n 'Message is not supported by the BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async generateImage(\n _prompt: string,\n _options?: ImageGenerationOptions,\n ): Promise<ImageGenerationResponse> {\n throw new AIError(\n 'Synchronous image generation is not yet implemented by the BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n streamSpeech(_text: string, _options?: TTSOptions): AsyncIterable<Buffer> {\n const error = new AIError(\n 'TTS streaming is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\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 BytePlus ModelArk provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async designVoice(_options: VoiceDesignOptions): Promise<Voice> {\n throw new AIError(\n 'Voice design is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n\n async getVoices(_options?: VoiceListOptions): Promise<Voice[]> {\n throw new AIError(\n 'Voice listing is not supported by the BytePlus ModelArk provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'byteplus-modelark',\n );\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { a as ContextLengthError, c as extractTextContent, r as AuthenticationError, s as RateLimitError, t as AIError } from "./types-CINwUPDo.js";
1
+ import { a as ContextLengthError, c as extractTextContent, r as AuthenticationError, s as RateLimitError, t as AIError } from "./types-sSecpd_N.js";
2
2
  import { t as emitUsage } from "./usage-C1Y1Nlg4.js";
3
3
  import { exec, spawn } from "node:child_process";
4
4
  import { promisify } from "node:util";
@@ -47,7 +47,7 @@ var ClaudeCliProvider = class {
47
47
  if (this.anthropicFallback !== null) return;
48
48
  const apiKey = process.env.ANTHROPIC_API_KEY;
49
49
  if (!apiKey) return;
50
- const { AnthropicProvider } = await import("./anthropic-DuYUnw8j.js");
50
+ const { AnthropicProvider } = await import("./anthropic-ByuqC5mC.js");
51
51
  const defaultModel = {
52
52
  sonnet: "claude-3-5-sonnet-20241022",
53
53
  opus: "claude-3-opus-20240229",
@@ -511,4 +511,4 @@ var ClaudeCliProvider = class {
511
511
  //#endregion
512
512
  export { ClaudeCliProvider };
513
513
 
514
- //# sourceMappingURL=claude-cli-DeTKk9nu.js.map
514
+ //# sourceMappingURL=claude-cli-BH_C62dV.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"claude-cli-DeTKk9nu.js","names":[],"sources":["../../src/shared/providers/claude-cli.ts"],"sourcesContent":["/**\n * Claude CLI provider implementation\n *\n * Provides a standardized interface for interacting with Claude via the Claude Code CLI,\n * enabling users with Claude Max subscriptions to use their subscription instead of\n * paying for API usage. Supports chat completions and streaming responses.\n * Authentication is handled via existing Claude session or setup-token.\n *\n * **Authentication Priority:**\n * 1. ANTHROPIC_API_KEY environment variable (uses Anthropic SDK, no keychain prompts)\n * 2. Claude CLI with setup-token (for CI/CD, no keychain prompts)\n * 3. Claude CLI with keychain (may prompt for password on macOS)\n */\n\nimport { exec, spawn } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nimport type {\n AICapabilities,\n AIInterface,\n AIMessage,\n AIModel,\n AIResponse,\n ChatOptions,\n ClaudeCliOptions,\n CompletionOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n MessageOptions,\n TokenUsage,\n TTSOptions,\n TTSResponse,\n VideoGenerationJob,\n VideoGenerationOptions,\n VideoGenerationResult,\n VideoGenerationStatusResult,\n Voice,\n VoiceCloneOptions,\n VoiceDesignOptions,\n VoiceListOptions,\n} from '../types';\nimport {\n AIError,\n AuthenticationError,\n ContextLengthError,\n extractTextContent,\n RateLimitError,\n} from '../types';\nimport { emitUsage } from './usage';\n\nconst execAsync = promisify(exec);\n\n/**\n * Claude CLI provider implementation that shells out to the Claude Code CLI.\n * Supports chat completions, streaming, and leverages Claude Max subscription.\n * Does not support embeddings (use OpenAI or another provider for embeddings).\n *\n * If ANTHROPIC_API_KEY environment variable is set, automatically falls back to\n * using the Anthropic provider to avoid macOS keychain password prompts.\n */\nexport class ClaudeCliProvider implements AIInterface {\n private options: ClaudeCliOptions;\n private cliPath: string | null = null;\n private anthropicFallback: AIInterface | null = null;\n\n /**\n * Creates a new Claude CLI provider instance\n * @param options - Configuration options for the Claude CLI provider\n */\n constructor(options: ClaudeCliOptions) {\n this.options = {\n defaultModel: 'sonnet',\n ...options,\n };\n }\n\n /**\n * Checks if ANTHROPIC_API_KEY is available and initializes fallback provider if needed\n * @private\n */\n private async initializeFallback(): Promise<void> {\n if (this.anthropicFallback !== null) {\n return; // Already initialized\n }\n\n const apiKey = process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n return; // No API key, will use CLI\n }\n\n // Lazy load Anthropic provider to avoid circular dependency\n const { AnthropicProvider } = await import('./anthropic.js');\n\n // Map claude-cli model names to Anthropic model IDs\n const modelMap: Record<string, string> = {\n sonnet: 'claude-3-5-sonnet-20241022',\n opus: 'claude-3-opus-20240229',\n haiku: 'claude-3-haiku-20240307',\n };\n\n const defaultModel =\n modelMap[this.options.defaultModel || 'sonnet'] ||\n 'claude-3-5-sonnet-20241022';\n\n this.anthropicFallback = new AnthropicProvider({\n type: 'anthropic',\n apiKey,\n defaultModel,\n onUsage: this.options.onUsage,\n usageTags: this.options.usageTags,\n });\n }\n\n /**\n * Finds the Claude CLI binary in PATH or uses custom cliPath\n * Does NOT execute the CLI during detection to avoid keychain prompts\n * @throws {AIError} When CLI cannot be found\n * @private\n */\n private async findCli(): Promise<string> {\n if (this.cliPath) {\n return this.cliPath;\n }\n\n // If custom path provided, check if it exists (don't execute it)\n if (this.options.cliPath) {\n const fs = await import('node:fs/promises');\n try {\n await fs.access(this.options.cliPath);\n this.cliPath = this.options.cliPath;\n return this.cliPath;\n } catch (_error) {\n throw new AIError(\n `Claude CLI not found at specified path: ${this.options.cliPath}. Set ANTHROPIC_API_KEY environment variable to avoid CLI dependency.`,\n 'CLI_NOT_FOUND',\n 'claude-cli',\n );\n }\n }\n\n // Try common installation locations first\n const commonPaths = [\n `${process.env.HOME}/.claude/local/claude`, // Default installation path\n '/usr/local/bin/claude',\n '/opt/homebrew/bin/claude',\n ];\n\n const fs = await import('node:fs/promises');\n for (const path of commonPaths) {\n try {\n await fs.access(path);\n this.cliPath = path;\n return this.cliPath;\n } catch (_error) {\n // Try next path\n }\n }\n\n // Try to find in PATH with which (doesn't execute the binary)\n try {\n const { stdout } = await execAsync('which claude');\n const path = stdout.trim();\n if (path) {\n this.cliPath = path;\n return this.cliPath;\n }\n } catch (_error) {\n // Not found\n }\n\n throw new AIError(\n 'Claude CLI not found. Set ANTHROPIC_API_KEY environment variable to use Anthropic SDK instead, or install Claude Code CLI. Visit https://docs.claude.com/en/docs/claude-code/ for installation instructions.',\n 'CLI_NOT_FOUND',\n 'claude-cli',\n );\n }\n\n /**\n * Normalizes model name to full model ID or short name\n * @private\n */\n private normalizeModel(model?: string): string {\n const m = model || this.options.defaultModel || 'sonnet';\n\n // Map short names to CLI-compatible values\n const modelMap: Record<string, string> = {\n sonnet: 'sonnet',\n opus: 'opus',\n haiku: 'haiku',\n };\n\n return modelMap[m] || m;\n }\n\n /**\n * Execute Claude CLI command and return parsed JSON output\n * @private\n */\n private async executeCommand(\n prompt: string,\n options: {\n model?: string;\n systemPrompt?: string;\n temperature?: number;\n maxTokens?: number;\n } = {},\n ): Promise<any> {\n const cliPath = await this.findCli();\n const model = this.normalizeModel(options.model);\n\n const args = ['--print', '--output-format', 'json', '--model', model];\n\n if (options.systemPrompt) {\n args.push('--system-prompt', options.systemPrompt);\n }\n\n args.push(prompt);\n\n return new Promise((resolve, reject) => {\n let stdout = '';\n let stderr = '';\n\n const child = spawn(cliPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n child.stdout.on('data', (data) => {\n stdout += data.toString();\n });\n\n child.stderr.on('data', (data) => {\n stderr += data.toString();\n });\n\n child.on('error', (error) => {\n reject(\n new AIError(\n `Failed to execute Claude CLI: ${error.message}`,\n 'EXECUTION_ERROR',\n 'claude-cli',\n ),\n );\n });\n\n child.on('close', (code) => {\n if (code !== 0) {\n reject(this.mapCliError(stderr, code || undefined));\n return;\n }\n\n try {\n // Parse JSON output\n const result = JSON.parse(stdout);\n resolve(result);\n } catch (_error) {\n reject(\n new AIError(\n 'Failed to parse CLI output as JSON',\n 'PARSE_ERROR',\n 'claude-cli',\n ),\n );\n }\n });\n });\n }\n\n /**\n * Execute Claude CLI command with streaming output\n * @private\n */\n private async *executeStreamingCommand(\n prompt: string,\n options: {\n model?: string;\n systemPrompt?: string;\n onProgress?: (chunk: string) => void;\n } = {},\n ): AsyncIterable<string> {\n const cliPath = await this.findCli();\n const model = this.normalizeModel(options.model);\n\n const args = [\n '--print',\n '--output-format',\n 'stream-json',\n '--verbose', // Required for stream-json format\n '--model',\n model,\n ];\n\n if (options.systemPrompt) {\n args.push('--system-prompt', options.systemPrompt);\n }\n\n args.push(prompt);\n\n const child = spawn(cliPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n let stderr = '';\n\n child.stderr.on('data', (data) => {\n stderr += data.toString();\n });\n\n // Process streaming JSON chunks\n let buffer = '';\n\n for await (const chunk of child.stdout) {\n buffer += chunk.toString();\n\n // Process complete JSON objects (one per line)\n const lines = buffer.split('\\n');\n buffer = lines.pop() || ''; // Keep incomplete line in buffer\n\n for (const line of lines) {\n if (line.trim()) {\n try {\n const parsed = JSON.parse(line);\n // Claude CLI stream-json format outputs complete messages\n // Look for assistant message with content\n if (parsed.type === 'assistant' && parsed.message?.content) {\n for (const contentBlock of parsed.message.content) {\n if (contentBlock.type === 'text' && contentBlock.text) {\n const text = contentBlock.text;\n if (options.onProgress) {\n options.onProgress(text);\n }\n yield text;\n }\n }\n }\n // Also handle result type for final output\n else if (parsed.type === 'result' && parsed.result) {\n const text = parsed.result;\n if (options.onProgress) {\n options.onProgress(text);\n }\n yield text;\n }\n } catch (_error) {\n // Skip malformed JSON lines\n }\n }\n }\n }\n\n // Wait for process to complete\n const exitCode = await new Promise<number>((resolve) => {\n child.on('close', (code) => resolve(code || 0));\n });\n\n if (exitCode !== 0) {\n throw this.mapCliError(stderr, exitCode);\n }\n }\n\n /**\n * Maps messages to a single prompt for CLI\n * @private\n */\n private mapMessagesToPrompt(messages: AIMessage[]): {\n prompt: string;\n systemPrompt?: string;\n } {\n let systemPrompt: string | undefined;\n const conversationParts: string[] = [];\n\n for (const message of messages) {\n const textContent = extractTextContent(message.content);\n if (message.role === 'system') {\n systemPrompt = systemPrompt\n ? `${systemPrompt}\\n\\n${textContent}`\n : textContent;\n } else if (message.role === 'user') {\n conversationParts.push(`User: ${textContent}`);\n } else if (message.role === 'assistant') {\n conversationParts.push(`Assistant: ${textContent}`);\n }\n }\n\n // If only one user message and no conversation, return it directly\n if (\n conversationParts.length === 1 &&\n conversationParts[0].startsWith('User: ')\n ) {\n return {\n prompt: conversationParts[0].substring(6), // Remove \"User: \" prefix\n systemPrompt,\n };\n }\n\n return {\n prompt: conversationParts.join('\\n\\n'),\n systemPrompt,\n };\n }\n\n /**\n * Maps CLI errors to standardized error types\n * @private\n */\n\n private mapCliError(stderr: string, exitCode?: number): AIError {\n const errorText = stderr.toLowerCase();\n\n // Authentication errors\n if (\n errorText.includes('not authenticated') ||\n errorText.includes('login required')\n ) {\n return new AuthenticationError('claude-cli');\n }\n\n // Rate limiting\n if (\n errorText.includes('rate limit') ||\n errorText.includes('too many requests')\n ) {\n return new RateLimitError('claude-cli');\n }\n\n // Context length\n if (\n errorText.includes('context length') ||\n errorText.includes('too long')\n ) {\n return new ContextLengthError('claude-cli');\n }\n\n return new AIError(\n `Claude CLI error (exit code ${exitCode}): ${stderr}`,\n 'CLI_ERROR',\n 'claude-cli',\n );\n }\n\n /**\n * Generate a chat completion using Claude CLI\n * @param messages - Array of conversation messages\n * @param options - Optional configuration for the chat completion\n * @returns Promise resolving to the AI response with content and metadata\n * @throws {AIError} When the CLI execution fails\n *\n * @example\n * ```typescript\n * const response = await provider.chat([\n * { role: 'system', content: 'You are a helpful assistant.' },\n * { role: 'user', content: 'Explain quantum computing' }\n * ], {\n * model: 'sonnet'\n * });\n * ```\n */\n async chat(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): Promise<AIResponse> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.chat(messages, options);\n }\n\n const startTime = Date.now();\n let { prompt, systemPrompt } = this.mapMessagesToPrompt(messages);\n\n // Add JSON format instruction if requested\n if (options.responseFormat?.type === 'json_object') {\n const jsonInstruction =\n '\\n\\nIMPORTANT: You must respond with valid JSON only. Do not include any explanatory text outside the JSON object.';\n systemPrompt = systemPrompt\n ? systemPrompt + jsonInstruction\n : jsonInstruction.trim();\n }\n\n const result = await this.executeCommand(prompt, {\n model: options.model,\n systemPrompt,\n temperature: options.temperature,\n maxTokens: options.maxTokens,\n });\n\n const model = options.model || this.options.defaultModel;\n const usage: TokenUsage | undefined = result.usage\n ? {\n promptTokens: result.usage.input_tokens || 0,\n completionTokens: result.usage.output_tokens || 0,\n totalTokens:\n (result.usage.input_tokens || 0) +\n (result.usage.output_tokens || 0),\n }\n : undefined;\n\n emitUsage(\n this.options,\n 'claude-cli',\n 'chat',\n model!,\n usage,\n startTime,\n options.usageTags,\n );\n\n // Parse Claude CLI JSON output\n return {\n content:\n result.result ||\n result.content ||\n result.text ||\n JSON.stringify(result),\n model,\n finishReason: result.is_error ? 'stop' : 'stop',\n usage,\n };\n }\n\n /**\n * Generate a text completion (delegates to chat)\n */\n async complete(\n prompt: string,\n options: CompletionOptions = {},\n ): Promise<AIResponse> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.complete(prompt, options);\n }\n\n return this.chat([{ role: 'user', content: prompt }], {\n model: options.model,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n topP: options.topP,\n n: options.n,\n stop: options.stop,\n stream: options.stream,\n onProgress: options.onProgress,\n usageTags: options.usageTags,\n });\n }\n\n /**\n * Simple message interface for single-turn interactions with optional history\n *\n * @param text - The message text to send\n * @param options - Configuration options including history, model, etc.\n * @returns Promise resolving to the response content string\n *\n * @example\n * ```typescript\n * // Simple usage\n * const response = await provider.message('Hello!');\n *\n * // With history\n * const response = await provider.message('What was my question?', {\n * history: [\n * { role: 'user', content: 'What is 2+2?' },\n * { role: 'assistant', content: '4' }\n * ]\n * });\n * ```\n */\n async message(text: string, options: MessageOptions = {}): Promise<string> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.message(text, options);\n }\n\n // Build messages array from history + current message\n const messages: AIMessage[] = [\n ...(options.history || []),\n { role: options.role || 'user', content: text },\n ];\n\n const response = await this.chat(messages, {\n model: options.model,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n topP: options.topP,\n stop: options.stop,\n stream: options.stream,\n frequencyPenalty: options.frequencyPenalty,\n presencePenalty: options.presencePenalty,\n responseFormat: options.responseFormat,\n seed: options.seed,\n tools: options.tools,\n toolChoice: options.toolChoice,\n onProgress: options.onProgress,\n usageTags: options.usageTags,\n });\n\n return response.content;\n }\n\n /**\n * Embeddings are not supported by Claude CLI or Anthropic\n */\n async embed(\n _text: string | string[],\n _options: EmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n // Note: Even with fallback, Anthropic doesn't support embeddings\n throw new AIError(\n 'Claude CLI does not support embeddings. Use OpenAI or another provider for embeddings.',\n 'NOT_SUPPORTED',\n 'claude-cli',\n );\n }\n\n /**\n * Image embeddings are not supported by Claude CLI\n */\n async embedImage(\n _image: string | Buffer,\n _options: ImageEmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n throw new AIError(\n 'Claude CLI does not support image embeddings. Use OpenAI or Gemini.',\n 'NOT_SUPPORTED',\n 'claude-cli',\n );\n }\n\n /**\n * Image description is not yet implemented for Claude CLI\n * Note: Claude supports vision, but this would require significant implementation\n */\n async describeImage(\n _image: string | Buffer,\n _prompt?: string,\n _options: ImageDescriptionOptions = {},\n ): Promise<string> {\n throw new AIError(\n 'Image description is not yet implemented for Claude CLI. Use OpenAI or Gemini.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n /**\n * Image generation is not supported by Claude\n */\n async generateImage(\n _prompt: string,\n _options: ImageGenerationOptions = {},\n ): Promise<ImageGenerationResponse> {\n throw new AIError(\n 'Claude does not support image generation. Use OpenAI or Gemini.',\n 'NOT_SUPPORTED',\n 'claude-cli',\n );\n }\n\n /**\n * Stream chat completion using Claude CLI\n */\n async *stream(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): AsyncIterable<string> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n yield* this.anthropicFallback.stream(messages, options);\n return;\n }\n\n const startTime = Date.now();\n let { prompt, systemPrompt } = this.mapMessagesToPrompt(messages);\n\n // Add JSON format instruction if requested\n if (options.responseFormat?.type === 'json_object') {\n const jsonInstruction =\n '\\n\\nIMPORTANT: You must respond with valid JSON only. Do not include any explanatory text outside the JSON object.';\n systemPrompt = systemPrompt\n ? systemPrompt + jsonInstruction\n : jsonInstruction.trim();\n }\n\n yield* this.executeStreamingCommand(prompt, {\n model: options.model,\n systemPrompt,\n onProgress: options.onProgress,\n });\n\n const model = options.model || this.options.defaultModel;\n emitUsage(\n this.options,\n 'claude-cli',\n 'stream',\n model!,\n undefined,\n startTime,\n options.usageTags,\n );\n }\n\n /**\n * Count tokens in text (approximation)\n */\n async countTokens(text: string): Promise<number> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.countTokens(text);\n }\n\n // Claude uses a different tokenizer, approximate similar to Anthropic\n return Math.ceil(text.length / 3.5);\n }\n\n /**\n * Get available models (static list of Claude models)\n */\n async getModels(): Promise<AIModel[]> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.getModels();\n }\n\n return [\n {\n id: 'sonnet',\n name: 'Claude Sonnet',\n description: 'Balanced Claude model via CLI',\n contextLength: 200000,\n capabilities: ['text', 'chat', 'vision'],\n supportsFunctions: false,\n supportsVision: false,\n },\n {\n id: 'opus',\n name: 'Claude Opus',\n description: 'Most powerful Claude model via CLI',\n contextLength: 200000,\n capabilities: ['text', 'chat'],\n supportsFunctions: false,\n supportsVision: false,\n },\n {\n id: 'haiku',\n name: 'Claude Haiku',\n description: 'Fast Claude model via CLI',\n contextLength: 200000,\n capabilities: ['text', 'chat'],\n supportsFunctions: false,\n supportsVision: false,\n },\n ];\n }\n\n /**\n * Get provider capabilities\n */\n async getCapabilities(): Promise<AICapabilities> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.getCapabilities();\n }\n\n return {\n chat: true,\n completion: true,\n embeddings: false, // Claude CLI doesn't support embeddings\n streaming: true,\n functions: false, // Not supported via CLI\n vision: false, // Not supported in current CLI version\n fineTuning: false,\n imageEmbeddings: false,\n imageGeneration: false,\n videoGeneration: false,\n tts: false,\n voiceCloning: false,\n voiceDesign: false,\n maxContextLength: 200000,\n supportedOperations: ['chat', 'completion', 'streaming'],\n };\n }\n\n // ============================================================================\n // Video Generation Methods (Not supported - use gemini, byteplus-modelark,\n // or openai-compat-video provider)\n // ============================================================================\n\n async submitVideoGenerationJob(\n _options: VideoGenerationOptions,\n ): Promise<VideoGenerationJob> {\n throw new AIError(\n 'Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async getVideoGenerationJob(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationStatusResult> {\n throw new AIError(\n 'Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async fetchVideoGenerationResult(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationResult> {\n throw new AIError(\n 'Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async cancelVideoGenerationJob(_handle: VideoGenerationJob): Promise<void> {\n throw new AIError(\n 'Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async validateVideoGenerationAccess(): Promise<boolean> {\n throw new AIError(\n 'Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n // ============================================================================\n // TTS Methods (Not supported - use Qwen3-TTS provider)\n // ============================================================================\n\n async synthesizeSpeech(\n _text: string,\n _options?: TTSOptions,\n ): Promise<TTSResponse> {\n throw new AIError(\n 'TTS is not supported by Claude CLI provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n streamSpeech(_text: string, _options?: TTSOptions): AsyncIterable<Buffer> {\n const error = new AIError(\n 'TTS streaming is not supported by Claude CLI provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n return {\n [Symbol.asyncIterator]: () => ({\n next: () => Promise.reject(error),\n }),\n };\n }\n\n async cloneVoice(_options: VoiceCloneOptions): Promise<Voice> {\n throw new AIError(\n 'Voice cloning is not supported by Claude CLI provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async designVoice(_options: VoiceDesignOptions): Promise<Voice> {\n throw new AIError(\n 'Voice design is not supported by Claude CLI provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async getVoices(_options?: VoiceListOptions): Promise<Voice[]> {\n throw new AIError(\n 'Voice listing is not supported by Claude CLI provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"claude-cli-BH_C62dV.js","names":[],"sources":["../../src/shared/providers/claude-cli.ts"],"sourcesContent":["/**\n * Claude CLI provider implementation\n *\n * Provides a standardized interface for interacting with Claude via the Claude Code CLI,\n * enabling users with Claude Max subscriptions to use their subscription instead of\n * paying for API usage. Supports chat completions and streaming responses.\n * Authentication is handled via existing Claude session or setup-token.\n *\n * **Authentication Priority:**\n * 1. ANTHROPIC_API_KEY environment variable (uses Anthropic SDK, no keychain prompts)\n * 2. Claude CLI with setup-token (for CI/CD, no keychain prompts)\n * 3. Claude CLI with keychain (may prompt for password on macOS)\n */\n\nimport { exec, spawn } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nimport type {\n AICapabilities,\n AIInterface,\n AIMessage,\n AIModel,\n AIResponse,\n ChatOptions,\n ClaudeCliOptions,\n CompletionOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n MessageOptions,\n TokenUsage,\n TTSOptions,\n TTSResponse,\n VideoGenerationJob,\n VideoGenerationOptions,\n VideoGenerationResult,\n VideoGenerationStatusResult,\n Voice,\n VoiceCloneOptions,\n VoiceDesignOptions,\n VoiceListOptions,\n} from '../types';\nimport {\n AIError,\n AuthenticationError,\n ContextLengthError,\n extractTextContent,\n RateLimitError,\n} from '../types';\nimport { emitUsage } from './usage';\n\nconst execAsync = promisify(exec);\n\n/**\n * Claude CLI provider implementation that shells out to the Claude Code CLI.\n * Supports chat completions, streaming, and leverages Claude Max subscription.\n * Does not support embeddings (use OpenAI or another provider for embeddings).\n *\n * If ANTHROPIC_API_KEY environment variable is set, automatically falls back to\n * using the Anthropic provider to avoid macOS keychain password prompts.\n */\nexport class ClaudeCliProvider implements AIInterface {\n private options: ClaudeCliOptions;\n private cliPath: string | null = null;\n private anthropicFallback: AIInterface | null = null;\n\n /**\n * Creates a new Claude CLI provider instance\n * @param options - Configuration options for the Claude CLI provider\n */\n constructor(options: ClaudeCliOptions) {\n this.options = {\n defaultModel: 'sonnet',\n ...options,\n };\n }\n\n /**\n * Checks if ANTHROPIC_API_KEY is available and initializes fallback provider if needed\n * @private\n */\n private async initializeFallback(): Promise<void> {\n if (this.anthropicFallback !== null) {\n return; // Already initialized\n }\n\n const apiKey = process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n return; // No API key, will use CLI\n }\n\n // Lazy load Anthropic provider to avoid circular dependency\n const { AnthropicProvider } = await import('./anthropic.js');\n\n // Map claude-cli model names to Anthropic model IDs\n const modelMap: Record<string, string> = {\n sonnet: 'claude-3-5-sonnet-20241022',\n opus: 'claude-3-opus-20240229',\n haiku: 'claude-3-haiku-20240307',\n };\n\n const defaultModel =\n modelMap[this.options.defaultModel || 'sonnet'] ||\n 'claude-3-5-sonnet-20241022';\n\n this.anthropicFallback = new AnthropicProvider({\n type: 'anthropic',\n apiKey,\n defaultModel,\n onUsage: this.options.onUsage,\n usageTags: this.options.usageTags,\n });\n }\n\n /**\n * Finds the Claude CLI binary in PATH or uses custom cliPath\n * Does NOT execute the CLI during detection to avoid keychain prompts\n * @throws {AIError} When CLI cannot be found\n * @private\n */\n private async findCli(): Promise<string> {\n if (this.cliPath) {\n return this.cliPath;\n }\n\n // If custom path provided, check if it exists (don't execute it)\n if (this.options.cliPath) {\n const fs = await import('node:fs/promises');\n try {\n await fs.access(this.options.cliPath);\n this.cliPath = this.options.cliPath;\n return this.cliPath;\n } catch (_error) {\n throw new AIError(\n `Claude CLI not found at specified path: ${this.options.cliPath}. Set ANTHROPIC_API_KEY environment variable to avoid CLI dependency.`,\n 'CLI_NOT_FOUND',\n 'claude-cli',\n );\n }\n }\n\n // Try common installation locations first\n const commonPaths = [\n `${process.env.HOME}/.claude/local/claude`, // Default installation path\n '/usr/local/bin/claude',\n '/opt/homebrew/bin/claude',\n ];\n\n const fs = await import('node:fs/promises');\n for (const path of commonPaths) {\n try {\n await fs.access(path);\n this.cliPath = path;\n return this.cliPath;\n } catch (_error) {\n // Try next path\n }\n }\n\n // Try to find in PATH with which (doesn't execute the binary)\n try {\n const { stdout } = await execAsync('which claude');\n const path = stdout.trim();\n if (path) {\n this.cliPath = path;\n return this.cliPath;\n }\n } catch (_error) {\n // Not found\n }\n\n throw new AIError(\n 'Claude CLI not found. Set ANTHROPIC_API_KEY environment variable to use Anthropic SDK instead, or install Claude Code CLI. Visit https://docs.claude.com/en/docs/claude-code/ for installation instructions.',\n 'CLI_NOT_FOUND',\n 'claude-cli',\n );\n }\n\n /**\n * Normalizes model name to full model ID or short name\n * @private\n */\n private normalizeModel(model?: string): string {\n const m = model || this.options.defaultModel || 'sonnet';\n\n // Map short names to CLI-compatible values\n const modelMap: Record<string, string> = {\n sonnet: 'sonnet',\n opus: 'opus',\n haiku: 'haiku',\n };\n\n return modelMap[m] || m;\n }\n\n /**\n * Execute Claude CLI command and return parsed JSON output\n * @private\n */\n private async executeCommand(\n prompt: string,\n options: {\n model?: string;\n systemPrompt?: string;\n temperature?: number;\n maxTokens?: number;\n } = {},\n ): Promise<any> {\n const cliPath = await this.findCli();\n const model = this.normalizeModel(options.model);\n\n const args = ['--print', '--output-format', 'json', '--model', model];\n\n if (options.systemPrompt) {\n args.push('--system-prompt', options.systemPrompt);\n }\n\n args.push(prompt);\n\n return new Promise((resolve, reject) => {\n let stdout = '';\n let stderr = '';\n\n const child = spawn(cliPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n child.stdout.on('data', (data) => {\n stdout += data.toString();\n });\n\n child.stderr.on('data', (data) => {\n stderr += data.toString();\n });\n\n child.on('error', (error) => {\n reject(\n new AIError(\n `Failed to execute Claude CLI: ${error.message}`,\n 'EXECUTION_ERROR',\n 'claude-cli',\n ),\n );\n });\n\n child.on('close', (code) => {\n if (code !== 0) {\n reject(this.mapCliError(stderr, code || undefined));\n return;\n }\n\n try {\n // Parse JSON output\n const result = JSON.parse(stdout);\n resolve(result);\n } catch (_error) {\n reject(\n new AIError(\n 'Failed to parse CLI output as JSON',\n 'PARSE_ERROR',\n 'claude-cli',\n ),\n );\n }\n });\n });\n }\n\n /**\n * Execute Claude CLI command with streaming output\n * @private\n */\n private async *executeStreamingCommand(\n prompt: string,\n options: {\n model?: string;\n systemPrompt?: string;\n onProgress?: (chunk: string) => void;\n } = {},\n ): AsyncIterable<string> {\n const cliPath = await this.findCli();\n const model = this.normalizeModel(options.model);\n\n const args = [\n '--print',\n '--output-format',\n 'stream-json',\n '--verbose', // Required for stream-json format\n '--model',\n model,\n ];\n\n if (options.systemPrompt) {\n args.push('--system-prompt', options.systemPrompt);\n }\n\n args.push(prompt);\n\n const child = spawn(cliPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n let stderr = '';\n\n child.stderr.on('data', (data) => {\n stderr += data.toString();\n });\n\n // Process streaming JSON chunks\n let buffer = '';\n\n for await (const chunk of child.stdout) {\n buffer += chunk.toString();\n\n // Process complete JSON objects (one per line)\n const lines = buffer.split('\\n');\n buffer = lines.pop() || ''; // Keep incomplete line in buffer\n\n for (const line of lines) {\n if (line.trim()) {\n try {\n const parsed = JSON.parse(line);\n // Claude CLI stream-json format outputs complete messages\n // Look for assistant message with content\n if (parsed.type === 'assistant' && parsed.message?.content) {\n for (const contentBlock of parsed.message.content) {\n if (contentBlock.type === 'text' && contentBlock.text) {\n const text = contentBlock.text;\n if (options.onProgress) {\n options.onProgress(text);\n }\n yield text;\n }\n }\n }\n // Also handle result type for final output\n else if (parsed.type === 'result' && parsed.result) {\n const text = parsed.result;\n if (options.onProgress) {\n options.onProgress(text);\n }\n yield text;\n }\n } catch (_error) {\n // Skip malformed JSON lines\n }\n }\n }\n }\n\n // Wait for process to complete\n const exitCode = await new Promise<number>((resolve) => {\n child.on('close', (code) => resolve(code || 0));\n });\n\n if (exitCode !== 0) {\n throw this.mapCliError(stderr, exitCode);\n }\n }\n\n /**\n * Maps messages to a single prompt for CLI\n * @private\n */\n private mapMessagesToPrompt(messages: AIMessage[]): {\n prompt: string;\n systemPrompt?: string;\n } {\n let systemPrompt: string | undefined;\n const conversationParts: string[] = [];\n\n for (const message of messages) {\n const textContent = extractTextContent(message.content);\n if (message.role === 'system') {\n systemPrompt = systemPrompt\n ? `${systemPrompt}\\n\\n${textContent}`\n : textContent;\n } else if (message.role === 'user') {\n conversationParts.push(`User: ${textContent}`);\n } else if (message.role === 'assistant') {\n conversationParts.push(`Assistant: ${textContent}`);\n }\n }\n\n // If only one user message and no conversation, return it directly\n if (\n conversationParts.length === 1 &&\n conversationParts[0].startsWith('User: ')\n ) {\n return {\n prompt: conversationParts[0].substring(6), // Remove \"User: \" prefix\n systemPrompt,\n };\n }\n\n return {\n prompt: conversationParts.join('\\n\\n'),\n systemPrompt,\n };\n }\n\n /**\n * Maps CLI errors to standardized error types\n * @private\n */\n\n private mapCliError(stderr: string, exitCode?: number): AIError {\n const errorText = stderr.toLowerCase();\n\n // Authentication errors\n if (\n errorText.includes('not authenticated') ||\n errorText.includes('login required')\n ) {\n return new AuthenticationError('claude-cli');\n }\n\n // Rate limiting\n if (\n errorText.includes('rate limit') ||\n errorText.includes('too many requests')\n ) {\n return new RateLimitError('claude-cli');\n }\n\n // Context length\n if (\n errorText.includes('context length') ||\n errorText.includes('too long')\n ) {\n return new ContextLengthError('claude-cli');\n }\n\n return new AIError(\n `Claude CLI error (exit code ${exitCode}): ${stderr}`,\n 'CLI_ERROR',\n 'claude-cli',\n );\n }\n\n /**\n * Generate a chat completion using Claude CLI\n * @param messages - Array of conversation messages\n * @param options - Optional configuration for the chat completion\n * @returns Promise resolving to the AI response with content and metadata\n * @throws {AIError} When the CLI execution fails\n *\n * @example\n * ```typescript\n * const response = await provider.chat([\n * { role: 'system', content: 'You are a helpful assistant.' },\n * { role: 'user', content: 'Explain quantum computing' }\n * ], {\n * model: 'sonnet'\n * });\n * ```\n */\n async chat(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): Promise<AIResponse> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.chat(messages, options);\n }\n\n const startTime = Date.now();\n let { prompt, systemPrompt } = this.mapMessagesToPrompt(messages);\n\n // Add JSON format instruction if requested\n if (options.responseFormat?.type === 'json_object') {\n const jsonInstruction =\n '\\n\\nIMPORTANT: You must respond with valid JSON only. Do not include any explanatory text outside the JSON object.';\n systemPrompt = systemPrompt\n ? systemPrompt + jsonInstruction\n : jsonInstruction.trim();\n }\n\n const result = await this.executeCommand(prompt, {\n model: options.model,\n systemPrompt,\n temperature: options.temperature,\n maxTokens: options.maxTokens,\n });\n\n const model = options.model || this.options.defaultModel;\n const usage: TokenUsage | undefined = result.usage\n ? {\n promptTokens: result.usage.input_tokens || 0,\n completionTokens: result.usage.output_tokens || 0,\n totalTokens:\n (result.usage.input_tokens || 0) +\n (result.usage.output_tokens || 0),\n }\n : undefined;\n\n emitUsage(\n this.options,\n 'claude-cli',\n 'chat',\n model!,\n usage,\n startTime,\n options.usageTags,\n );\n\n // Parse Claude CLI JSON output\n return {\n content:\n result.result ||\n result.content ||\n result.text ||\n JSON.stringify(result),\n model,\n finishReason: result.is_error ? 'stop' : 'stop',\n usage,\n };\n }\n\n /**\n * Generate a text completion (delegates to chat)\n */\n async complete(\n prompt: string,\n options: CompletionOptions = {},\n ): Promise<AIResponse> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.complete(prompt, options);\n }\n\n return this.chat([{ role: 'user', content: prompt }], {\n model: options.model,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n topP: options.topP,\n n: options.n,\n stop: options.stop,\n stream: options.stream,\n onProgress: options.onProgress,\n usageTags: options.usageTags,\n });\n }\n\n /**\n * Simple message interface for single-turn interactions with optional history\n *\n * @param text - The message text to send\n * @param options - Configuration options including history, model, etc.\n * @returns Promise resolving to the response content string\n *\n * @example\n * ```typescript\n * // Simple usage\n * const response = await provider.message('Hello!');\n *\n * // With history\n * const response = await provider.message('What was my question?', {\n * history: [\n * { role: 'user', content: 'What is 2+2?' },\n * { role: 'assistant', content: '4' }\n * ]\n * });\n * ```\n */\n async message(text: string, options: MessageOptions = {}): Promise<string> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.message(text, options);\n }\n\n // Build messages array from history + current message\n const messages: AIMessage[] = [\n ...(options.history || []),\n { role: options.role || 'user', content: text },\n ];\n\n const response = await this.chat(messages, {\n model: options.model,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n topP: options.topP,\n stop: options.stop,\n stream: options.stream,\n frequencyPenalty: options.frequencyPenalty,\n presencePenalty: options.presencePenalty,\n responseFormat: options.responseFormat,\n seed: options.seed,\n tools: options.tools,\n toolChoice: options.toolChoice,\n onProgress: options.onProgress,\n usageTags: options.usageTags,\n });\n\n return response.content;\n }\n\n /**\n * Embeddings are not supported by Claude CLI or Anthropic\n */\n async embed(\n _text: string | string[],\n _options: EmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n // Note: Even with fallback, Anthropic doesn't support embeddings\n throw new AIError(\n 'Claude CLI does not support embeddings. Use OpenAI or another provider for embeddings.',\n 'NOT_SUPPORTED',\n 'claude-cli',\n );\n }\n\n /**\n * Image embeddings are not supported by Claude CLI\n */\n async embedImage(\n _image: string | Buffer,\n _options: ImageEmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n throw new AIError(\n 'Claude CLI does not support image embeddings. Use OpenAI or Gemini.',\n 'NOT_SUPPORTED',\n 'claude-cli',\n );\n }\n\n /**\n * Image description is not yet implemented for Claude CLI\n * Note: Claude supports vision, but this would require significant implementation\n */\n async describeImage(\n _image: string | Buffer,\n _prompt?: string,\n _options: ImageDescriptionOptions = {},\n ): Promise<string> {\n throw new AIError(\n 'Image description is not yet implemented for Claude CLI. Use OpenAI or Gemini.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n /**\n * Image generation is not supported by Claude\n */\n async generateImage(\n _prompt: string,\n _options: ImageGenerationOptions = {},\n ): Promise<ImageGenerationResponse> {\n throw new AIError(\n 'Claude does not support image generation. Use OpenAI or Gemini.',\n 'NOT_SUPPORTED',\n 'claude-cli',\n );\n }\n\n /**\n * Stream chat completion using Claude CLI\n */\n async *stream(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): AsyncIterable<string> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n yield* this.anthropicFallback.stream(messages, options);\n return;\n }\n\n const startTime = Date.now();\n let { prompt, systemPrompt } = this.mapMessagesToPrompt(messages);\n\n // Add JSON format instruction if requested\n if (options.responseFormat?.type === 'json_object') {\n const jsonInstruction =\n '\\n\\nIMPORTANT: You must respond with valid JSON only. Do not include any explanatory text outside the JSON object.';\n systemPrompt = systemPrompt\n ? systemPrompt + jsonInstruction\n : jsonInstruction.trim();\n }\n\n yield* this.executeStreamingCommand(prompt, {\n model: options.model,\n systemPrompt,\n onProgress: options.onProgress,\n });\n\n const model = options.model || this.options.defaultModel;\n emitUsage(\n this.options,\n 'claude-cli',\n 'stream',\n model!,\n undefined,\n startTime,\n options.usageTags,\n );\n }\n\n /**\n * Count tokens in text (approximation)\n */\n async countTokens(text: string): Promise<number> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.countTokens(text);\n }\n\n // Claude uses a different tokenizer, approximate similar to Anthropic\n return Math.ceil(text.length / 3.5);\n }\n\n /**\n * Get available models (static list of Claude models)\n */\n async getModels(): Promise<AIModel[]> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.getModels();\n }\n\n return [\n {\n id: 'sonnet',\n name: 'Claude Sonnet',\n description: 'Balanced Claude model via CLI',\n contextLength: 200000,\n capabilities: ['text', 'chat', 'vision'],\n supportsFunctions: false,\n supportsVision: false,\n },\n {\n id: 'opus',\n name: 'Claude Opus',\n description: 'Most powerful Claude model via CLI',\n contextLength: 200000,\n capabilities: ['text', 'chat'],\n supportsFunctions: false,\n supportsVision: false,\n },\n {\n id: 'haiku',\n name: 'Claude Haiku',\n description: 'Fast Claude model via CLI',\n contextLength: 200000,\n capabilities: ['text', 'chat'],\n supportsFunctions: false,\n supportsVision: false,\n },\n ];\n }\n\n /**\n * Get provider capabilities\n */\n async getCapabilities(): Promise<AICapabilities> {\n // Check if ANTHROPIC_API_KEY is available and use fallback if so\n await this.initializeFallback();\n if (this.anthropicFallback) {\n return this.anthropicFallback.getCapabilities();\n }\n\n return {\n chat: true,\n completion: true,\n embeddings: false, // Claude CLI doesn't support embeddings\n streaming: true,\n functions: false, // Not supported via CLI\n vision: false, // Not supported in current CLI version\n fineTuning: false,\n imageEmbeddings: false,\n imageGeneration: false,\n videoGeneration: false,\n tts: false,\n voiceCloning: false,\n voiceDesign: false,\n maxContextLength: 200000,\n supportedOperations: ['chat', 'completion', 'streaming'],\n };\n }\n\n // ============================================================================\n // Video Generation Methods (Not supported - use gemini, byteplus-modelark,\n // or openai-compat-video provider)\n // ============================================================================\n\n async submitVideoGenerationJob(\n _options: VideoGenerationOptions,\n ): Promise<VideoGenerationJob> {\n throw new AIError(\n 'Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async getVideoGenerationJob(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationStatusResult> {\n throw new AIError(\n 'Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async fetchVideoGenerationResult(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationResult> {\n throw new AIError(\n 'Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async cancelVideoGenerationJob(_handle: VideoGenerationJob): Promise<void> {\n throw new AIError(\n 'Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async validateVideoGenerationAccess(): Promise<boolean> {\n throw new AIError(\n 'Video generation is not supported by Claude CLI provider. Use gemini, byteplus-modelark, or openai-compat-video provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n // ============================================================================\n // TTS Methods (Not supported - use Qwen3-TTS provider)\n // ============================================================================\n\n async synthesizeSpeech(\n _text: string,\n _options?: TTSOptions,\n ): Promise<TTSResponse> {\n throw new AIError(\n 'TTS is not supported by Claude CLI provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n streamSpeech(_text: string, _options?: TTSOptions): AsyncIterable<Buffer> {\n const error = new AIError(\n 'TTS streaming is not supported by Claude CLI provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n return {\n [Symbol.asyncIterator]: () => ({\n next: () => Promise.reject(error),\n }),\n };\n }\n\n async cloneVoice(_options: VoiceCloneOptions): Promise<Voice> {\n throw new AIError(\n 'Voice cloning is not supported by Claude CLI provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async designVoice(_options: VoiceDesignOptions): Promise<Voice> {\n throw new AIError(\n 'Voice design is not supported by Claude CLI provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n\n async getVoices(_options?: VoiceListOptions): Promise<Voice[]> {\n throw new AIError(\n 'Voice listing is not supported by Claude CLI provider. Use Qwen3-TTS provider.',\n 'NOT_IMPLEMENTED',\n 'claude-cli',\n );\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { t as AIError } from "./types-CINwUPDo.js";
1
+ import { t as AIError } from "./types-sSecpd_N.js";
2
2
  //#region src/shared/providers/gateway-admin.ts
3
3
  function stripTrailingSlash(value) {
4
4
  return value.endsWith("/") ? value.slice(0, -1) : value;
@@ -280,4 +280,4 @@ var LiteLLMAdmin = class {
280
280
  //#endregion
281
281
  export { resolveGatewayAdminBaseUrl as i, LiteLLMAdmin as n, normalizeGatewayBaseUrl as r, BifrostAdmin as t };
282
282
 
283
- //# sourceMappingURL=gateway-admin-DJXEDpUP.js.map
283
+ //# sourceMappingURL=gateway-admin-B4RSBDmo.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-admin-DJXEDpUP.js","names":[],"sources":["../../src/shared/providers/gateway-admin.ts"],"sourcesContent":["import type {\n AIAdminBudget,\n AIAdminInterface,\n AIAdminProject,\n AIAdminProviderConfig,\n AIAdminRateLimit,\n AIVirtualKey,\n CreateAIProjectOptions,\n CreateAIVirtualKeyOptions,\n} from '../types';\nimport { AIError } from '../types';\n\ninterface GatewayAdminTransportOptions {\n provider: string;\n baseUrl: string;\n apiKey?: string;\n username?: string;\n password?: string;\n headers?: Record<string, string>;\n timeout?: number;\n}\n\ntype JsonRecord = Record<string, unknown>;\n\nfunction stripTrailingSlash(value: string): string {\n return value.endsWith('/') ? value.slice(0, -1) : value;\n}\n\nexport function normalizeGatewayBaseUrl(baseUrl: string): string {\n return stripTrailingSlash(baseUrl.trim());\n}\n\n/**\n * Derive the gateway root URL for admin APIs from an OpenAI-compatible base URL.\n *\n * Strips known inference path suffixes iteratively so that a baseUrl such as\n * `http://host/openai/v1` collapses to the gateway root rather than to the\n * partial `http://host/openai`.\n */\nexport function deriveGatewayAdminBaseUrl(baseUrl: string): string {\n let normalized = normalizeGatewayBaseUrl(baseUrl);\n const suffixes = ['/pydanticai/v1', '/openai', '/v1'];\n let stripped = true;\n\n while (stripped) {\n stripped = false;\n for (const suffix of suffixes) {\n if (normalized.endsWith(suffix)) {\n normalized = normalized.slice(0, -suffix.length);\n stripped = true;\n break;\n }\n }\n }\n\n return normalized || normalizeGatewayBaseUrl(baseUrl);\n}\n\nexport function resolveGatewayAdminBaseUrl(\n baseUrl: string | undefined,\n adminBaseUrl: string | undefined,\n provider: string,\n): string {\n const configuredBaseUrl = adminBaseUrl || baseUrl;\n if (!configuredBaseUrl?.trim()) {\n throw new AIError(\n `${provider} baseUrl is required for admin operations`,\n 'ADMIN_BASE_URL_REQUIRED',\n provider,\n );\n }\n\n return adminBaseUrl\n ? normalizeGatewayBaseUrl(adminBaseUrl)\n : deriveGatewayAdminBaseUrl(configuredBaseUrl);\n}\n\nexport function slugifyProjectId(name: string, tenantId?: string): string {\n const value = [tenantId, name].filter(Boolean).join('-');\n const slug = value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n\n return slug || 'ai-project';\n}\n\nfunction toJsonRecord(value: unknown): JsonRecord {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? (value as JsonRecord)\n : {};\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction encodeBasicCredentials(username: string, password: string): string {\n const value = `${username}:${password}`;\n\n if (typeof btoa === 'function') {\n return btoa(value);\n }\n\n return Buffer.from(value, 'utf8').toString('base64');\n}\n\nfunction removeUndefinedValues<T extends JsonRecord>(value: T): T {\n for (const key of Object.keys(value)) {\n if (value[key] === undefined) {\n delete value[key];\n }\n }\n\n return value;\n}\n\nfunction createMetadata(\n options: {\n tenantId?: string;\n description?: string;\n metadata?: Record<string, unknown>;\n },\n tenantField = 'tenant_id',\n): Record<string, unknown> | undefined {\n const metadata = {\n ...options.metadata,\n };\n\n if (options.tenantId) {\n metadata[tenantField] = options.tenantId;\n }\n\n if (options.description) {\n metadata.description = options.description;\n }\n\n return Object.keys(metadata).length > 0 ? metadata : undefined;\n}\n\nfunction responseMessage(body: unknown, fallback: string): string {\n const record = toJsonRecord(body);\n const error = toJsonRecord(record.error);\n\n return (\n stringValue(error.message) ||\n stringValue(record.message) ||\n stringValue(record.detail) ||\n fallback\n );\n}\n\nclass GatewayAdminTransport {\n private readonly baseUrl: string;\n private readonly provider: string;\n private readonly apiKey?: string;\n private readonly username?: string;\n private readonly password?: string;\n private readonly headers?: Record<string, string>;\n private readonly timeout?: number;\n\n constructor(options: GatewayAdminTransportOptions) {\n this.provider = options.provider;\n this.baseUrl = normalizeGatewayBaseUrl(options.baseUrl);\n this.apiKey = options.apiKey;\n this.username = options.username;\n this.password = options.password;\n this.headers = options.headers;\n this.timeout = options.timeout;\n }\n\n async request<T>(\n method: string,\n path: string,\n body?: unknown,\n query?: Record<string, string | undefined>,\n ): Promise<T> {\n const url = new URL(\n `${this.baseUrl}${path.startsWith('/') ? path : `/${path}`}`,\n );\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) {\n url.searchParams.set(key, value);\n }\n }\n }\n\n const controller =\n typeof AbortController === 'undefined'\n ? undefined\n : new AbortController();\n const timeoutHandle =\n controller && this.timeout\n ? setTimeout(() => controller.abort(), this.timeout)\n : undefined;\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n Accept: 'application/json',\n ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...this.headers,\n ...this.createAuthHeaders(),\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: controller?.signal,\n });\n\n const text = await response.text();\n const parsed = text ? this.parseJson(text) : undefined;\n\n if (!response.ok) {\n throw this.mapResponseError(response, parsed);\n }\n\n return parsed as T;\n } catch (error) {\n if (error instanceof AIError) {\n throw error;\n }\n\n throw new AIError(\n `${this.provider} admin request failed: ${\n error instanceof Error ? error.message : String(error)\n }`,\n 'ADMIN_REQUEST_FAILED',\n this.provider,\n );\n } finally {\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n }\n }\n\n private createAuthHeaders(): Record<string, string> {\n if (this.username && this.password) {\n return {\n Authorization: `Basic ${encodeBasicCredentials(\n this.username,\n this.password,\n )}`,\n };\n }\n\n return this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {};\n }\n\n private parseJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch (_error) {\n return { message: text };\n }\n }\n\n private mapResponseError(response: Response, body: unknown): AIError {\n const message = responseMessage(\n body,\n `${this.provider} admin request failed with ${response.status}`,\n );\n\n if (response.status === 401 || response.status === 403) {\n return new AIError(message, 'AUTH_ERROR', this.provider);\n }\n\n if (response.status === 429) {\n return new AIError(message, 'RATE_LIMIT', this.provider, undefined, true);\n }\n\n return new AIError(\n message,\n `ADMIN_HTTP_${response.status}`,\n this.provider,\n undefined,\n response.status >= 500,\n );\n }\n}\n\nfunction mapBifrostBudget(budget?: AIAdminBudget): JsonRecord | undefined {\n if (!budget) return undefined;\n\n return removeUndefinedValues({\n max_limit: budget.maxLimit,\n reset_duration: budget.resetDuration,\n calendar_aligned: budget.calendarAligned,\n });\n}\n\nfunction mapBifrostRateLimit(\n rateLimit?: AIAdminRateLimit,\n): JsonRecord | undefined {\n if (!rateLimit) return undefined;\n\n return removeUndefinedValues({\n token_max_limit: rateLimit.tokenMaxLimit ?? rateLimit.tpmLimit,\n token_reset_duration: rateLimit.tokenResetDuration,\n request_max_limit: rateLimit.requestMaxLimit ?? rateLimit.rpmLimit,\n request_reset_duration: rateLimit.requestResetDuration,\n });\n}\n\nfunction mapBifrostProviderConfigs(\n configs?: AIAdminProviderConfig[],\n): JsonRecord[] | undefined {\n if (!configs) return undefined;\n\n return configs.map((config) =>\n removeUndefinedValues({\n provider: config.provider,\n weight: config.weight,\n allowed_models: config.allowedModels,\n key_ids: config.keyIds,\n }),\n );\n}\n\nfunction mapLiteLLMBudget(\n budget?: AIAdminBudget,\n): Pick<JsonRecord, 'max_budget' | 'budget_duration'> {\n return removeUndefinedValues({\n max_budget: budget?.maxLimit,\n budget_duration: budget?.resetDuration,\n });\n}\n\nfunction mapLiteLLMRateLimit(\n rateLimit?: AIAdminRateLimit,\n): Pick<JsonRecord, 'tpm_limit' | 'rpm_limit'> {\n return removeUndefinedValues({\n tpm_limit: rateLimit?.tpmLimit ?? rateLimit?.tokenMaxLimit,\n rpm_limit: rateLimit?.rpmLimit ?? rateLimit?.requestMaxLimit,\n });\n}\n\nexport class BifrostAdmin implements AIAdminInterface {\n private readonly transport: GatewayAdminTransport;\n\n constructor(options: GatewayAdminTransportOptions) {\n this.transport = new GatewayAdminTransport(options);\n }\n\n async createProject(\n options: CreateAIProjectOptions,\n ): Promise<AIAdminProject> {\n const body = removeUndefinedValues({\n name: options.name,\n customer_id: options.tenantId,\n budget: mapBifrostBudget(options.budget),\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/api/governance/teams',\n body,\n );\n const team = toJsonRecord(response.team ?? response);\n const id = stringValue(team.id) || stringValue(team.team_id);\n\n if (!id) {\n throw new AIError(\n 'Bifrost did not return a project id',\n 'ADMIN_INVALID_RESPONSE',\n 'bifrost',\n );\n }\n\n return {\n id,\n name:\n stringValue(team.name) || stringValue(team.team_alias) || options.name,\n tenantId: stringValue(team.customer_id) || options.tenantId,\n budgetId: stringValue(team.budget_id),\n provider: 'bifrost',\n raw: response,\n };\n }\n\n async createVirtualKey(\n options: CreateAIVirtualKeyOptions,\n ): Promise<AIVirtualKey> {\n const body = removeUndefinedValues({\n name: options.name,\n description: options.description,\n provider_configs: mapBifrostProviderConfigs(options.providerConfigs),\n team_id: options.projectId,\n customer_id: options.projectId ? undefined : options.tenantId,\n budget: mapBifrostBudget(options.budget),\n rate_limit: mapBifrostRateLimit(options.rateLimit),\n key_ids: options.keyIds,\n is_active: options.isActive ?? true,\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/api/governance/virtual-keys',\n body,\n );\n const virtualKey = toJsonRecord(\n response.virtual_key ?? response.virtualKey ?? response,\n );\n\n return {\n id: stringValue(virtualKey.id) || stringValue(virtualKey.key_id),\n name: stringValue(virtualKey.name) || options.name,\n key: stringValue(virtualKey.value) || stringValue(virtualKey.key),\n maskedKey:\n stringValue(virtualKey.key_name) || stringValue(virtualKey.masked_key),\n projectId: stringValue(virtualKey.team_id) || options.projectId,\n tenantId: stringValue(virtualKey.customer_id) || options.tenantId,\n expiresAt:\n stringValue(virtualKey.expires) || stringValue(virtualKey.expires_at),\n provider: 'bifrost',\n raw: response,\n };\n }\n}\n\nexport class LiteLLMAdmin implements AIAdminInterface {\n private readonly transport: GatewayAdminTransport;\n\n constructor(options: GatewayAdminTransportOptions) {\n this.transport = new GatewayAdminTransport(options);\n }\n\n async createProject(\n options: CreateAIProjectOptions,\n ): Promise<AIAdminProject> {\n const projectId =\n options.id || slugifyProjectId(options.name, options.tenantId);\n const body = removeUndefinedValues({\n team_id: projectId,\n team_alias: options.name,\n models: options.models,\n ...mapLiteLLMBudget(options.budget),\n ...mapLiteLLMRateLimit(options.rateLimit),\n metadata: createMetadata(options),\n blocked: options.isBlocked,\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/team/new',\n body,\n );\n const team = toJsonRecord(response.team ?? response);\n\n return {\n id: stringValue(team.team_id) || projectId,\n name:\n stringValue(team.team_alias) || stringValue(team.name) || options.name,\n tenantId: options.tenantId,\n provider: 'litellm',\n raw: response,\n };\n }\n\n async createVirtualKey(\n options: CreateAIVirtualKeyOptions,\n ): Promise<AIVirtualKey> {\n const body = removeUndefinedValues({\n key_alias: options.name,\n team_id: options.projectId,\n user_id: options.userId,\n models: options.models,\n duration: options.duration,\n ...mapLiteLLMBudget(options.budget),\n ...mapLiteLLMRateLimit(options.rateLimit),\n metadata: createMetadata(options),\n aliases: options.aliases,\n config: options.config,\n permissions: options.permissions,\n blocked: options.isActive === undefined ? undefined : !options.isActive,\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/key/generate',\n body,\n );\n\n return {\n id: stringValue(response.token_id) || stringValue(response.key_id),\n name:\n stringValue(response.key_alias) ||\n stringValue(response.key_name) ||\n options.name,\n key: stringValue(response.key),\n maskedKey: stringValue(response.key_name),\n projectId: stringValue(response.team_id) || options.projectId,\n tenantId: options.tenantId,\n expiresAt: stringValue(response.expires),\n provider: 'litellm',\n raw: response,\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gateway-admin-B4RSBDmo.js","names":[],"sources":["../../src/shared/providers/gateway-admin.ts"],"sourcesContent":["import type {\n AIAdminBudget,\n AIAdminInterface,\n AIAdminProject,\n AIAdminProviderConfig,\n AIAdminRateLimit,\n AIVirtualKey,\n CreateAIProjectOptions,\n CreateAIVirtualKeyOptions,\n} from '../types';\nimport { AIError } from '../types';\n\ninterface GatewayAdminTransportOptions {\n provider: string;\n baseUrl: string;\n apiKey?: string;\n username?: string;\n password?: string;\n headers?: Record<string, string>;\n timeout?: number;\n}\n\ntype JsonRecord = Record<string, unknown>;\n\nfunction stripTrailingSlash(value: string): string {\n return value.endsWith('/') ? value.slice(0, -1) : value;\n}\n\nexport function normalizeGatewayBaseUrl(baseUrl: string): string {\n return stripTrailingSlash(baseUrl.trim());\n}\n\n/**\n * Derive the gateway root URL for admin APIs from an OpenAI-compatible base URL.\n *\n * Strips known inference path suffixes iteratively so that a baseUrl such as\n * `http://host/openai/v1` collapses to the gateway root rather than to the\n * partial `http://host/openai`.\n */\nexport function deriveGatewayAdminBaseUrl(baseUrl: string): string {\n let normalized = normalizeGatewayBaseUrl(baseUrl);\n const suffixes = ['/pydanticai/v1', '/openai', '/v1'];\n let stripped = true;\n\n while (stripped) {\n stripped = false;\n for (const suffix of suffixes) {\n if (normalized.endsWith(suffix)) {\n normalized = normalized.slice(0, -suffix.length);\n stripped = true;\n break;\n }\n }\n }\n\n return normalized || normalizeGatewayBaseUrl(baseUrl);\n}\n\nexport function resolveGatewayAdminBaseUrl(\n baseUrl: string | undefined,\n adminBaseUrl: string | undefined,\n provider: string,\n): string {\n const configuredBaseUrl = adminBaseUrl || baseUrl;\n if (!configuredBaseUrl?.trim()) {\n throw new AIError(\n `${provider} baseUrl is required for admin operations`,\n 'ADMIN_BASE_URL_REQUIRED',\n provider,\n );\n }\n\n return adminBaseUrl\n ? normalizeGatewayBaseUrl(adminBaseUrl)\n : deriveGatewayAdminBaseUrl(configuredBaseUrl);\n}\n\nexport function slugifyProjectId(name: string, tenantId?: string): string {\n const value = [tenantId, name].filter(Boolean).join('-');\n const slug = value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n\n return slug || 'ai-project';\n}\n\nfunction toJsonRecord(value: unknown): JsonRecord {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? (value as JsonRecord)\n : {};\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction encodeBasicCredentials(username: string, password: string): string {\n const value = `${username}:${password}`;\n\n if (typeof btoa === 'function') {\n return btoa(value);\n }\n\n return Buffer.from(value, 'utf8').toString('base64');\n}\n\nfunction removeUndefinedValues<T extends JsonRecord>(value: T): T {\n for (const key of Object.keys(value)) {\n if (value[key] === undefined) {\n delete value[key];\n }\n }\n\n return value;\n}\n\nfunction createMetadata(\n options: {\n tenantId?: string;\n description?: string;\n metadata?: Record<string, unknown>;\n },\n tenantField = 'tenant_id',\n): Record<string, unknown> | undefined {\n const metadata = {\n ...options.metadata,\n };\n\n if (options.tenantId) {\n metadata[tenantField] = options.tenantId;\n }\n\n if (options.description) {\n metadata.description = options.description;\n }\n\n return Object.keys(metadata).length > 0 ? metadata : undefined;\n}\n\nfunction responseMessage(body: unknown, fallback: string): string {\n const record = toJsonRecord(body);\n const error = toJsonRecord(record.error);\n\n return (\n stringValue(error.message) ||\n stringValue(record.message) ||\n stringValue(record.detail) ||\n fallback\n );\n}\n\nclass GatewayAdminTransport {\n private readonly baseUrl: string;\n private readonly provider: string;\n private readonly apiKey?: string;\n private readonly username?: string;\n private readonly password?: string;\n private readonly headers?: Record<string, string>;\n private readonly timeout?: number;\n\n constructor(options: GatewayAdminTransportOptions) {\n this.provider = options.provider;\n this.baseUrl = normalizeGatewayBaseUrl(options.baseUrl);\n this.apiKey = options.apiKey;\n this.username = options.username;\n this.password = options.password;\n this.headers = options.headers;\n this.timeout = options.timeout;\n }\n\n async request<T>(\n method: string,\n path: string,\n body?: unknown,\n query?: Record<string, string | undefined>,\n ): Promise<T> {\n const url = new URL(\n `${this.baseUrl}${path.startsWith('/') ? path : `/${path}`}`,\n );\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) {\n url.searchParams.set(key, value);\n }\n }\n }\n\n const controller =\n typeof AbortController === 'undefined'\n ? undefined\n : new AbortController();\n const timeoutHandle =\n controller && this.timeout\n ? setTimeout(() => controller.abort(), this.timeout)\n : undefined;\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n Accept: 'application/json',\n ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...this.headers,\n ...this.createAuthHeaders(),\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: controller?.signal,\n });\n\n const text = await response.text();\n const parsed = text ? this.parseJson(text) : undefined;\n\n if (!response.ok) {\n throw this.mapResponseError(response, parsed);\n }\n\n return parsed as T;\n } catch (error) {\n if (error instanceof AIError) {\n throw error;\n }\n\n throw new AIError(\n `${this.provider} admin request failed: ${\n error instanceof Error ? error.message : String(error)\n }`,\n 'ADMIN_REQUEST_FAILED',\n this.provider,\n );\n } finally {\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n }\n }\n\n private createAuthHeaders(): Record<string, string> {\n if (this.username && this.password) {\n return {\n Authorization: `Basic ${encodeBasicCredentials(\n this.username,\n this.password,\n )}`,\n };\n }\n\n return this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {};\n }\n\n private parseJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch (_error) {\n return { message: text };\n }\n }\n\n private mapResponseError(response: Response, body: unknown): AIError {\n const message = responseMessage(\n body,\n `${this.provider} admin request failed with ${response.status}`,\n );\n\n if (response.status === 401 || response.status === 403) {\n return new AIError(message, 'AUTH_ERROR', this.provider);\n }\n\n if (response.status === 429) {\n return new AIError(message, 'RATE_LIMIT', this.provider, undefined, true);\n }\n\n return new AIError(\n message,\n `ADMIN_HTTP_${response.status}`,\n this.provider,\n undefined,\n response.status >= 500,\n );\n }\n}\n\nfunction mapBifrostBudget(budget?: AIAdminBudget): JsonRecord | undefined {\n if (!budget) return undefined;\n\n return removeUndefinedValues({\n max_limit: budget.maxLimit,\n reset_duration: budget.resetDuration,\n calendar_aligned: budget.calendarAligned,\n });\n}\n\nfunction mapBifrostRateLimit(\n rateLimit?: AIAdminRateLimit,\n): JsonRecord | undefined {\n if (!rateLimit) return undefined;\n\n return removeUndefinedValues({\n token_max_limit: rateLimit.tokenMaxLimit ?? rateLimit.tpmLimit,\n token_reset_duration: rateLimit.tokenResetDuration,\n request_max_limit: rateLimit.requestMaxLimit ?? rateLimit.rpmLimit,\n request_reset_duration: rateLimit.requestResetDuration,\n });\n}\n\nfunction mapBifrostProviderConfigs(\n configs?: AIAdminProviderConfig[],\n): JsonRecord[] | undefined {\n if (!configs) return undefined;\n\n return configs.map((config) =>\n removeUndefinedValues({\n provider: config.provider,\n weight: config.weight,\n allowed_models: config.allowedModels,\n key_ids: config.keyIds,\n }),\n );\n}\n\nfunction mapLiteLLMBudget(\n budget?: AIAdminBudget,\n): Pick<JsonRecord, 'max_budget' | 'budget_duration'> {\n return removeUndefinedValues({\n max_budget: budget?.maxLimit,\n budget_duration: budget?.resetDuration,\n });\n}\n\nfunction mapLiteLLMRateLimit(\n rateLimit?: AIAdminRateLimit,\n): Pick<JsonRecord, 'tpm_limit' | 'rpm_limit'> {\n return removeUndefinedValues({\n tpm_limit: rateLimit?.tpmLimit ?? rateLimit?.tokenMaxLimit,\n rpm_limit: rateLimit?.rpmLimit ?? rateLimit?.requestMaxLimit,\n });\n}\n\nexport class BifrostAdmin implements AIAdminInterface {\n private readonly transport: GatewayAdminTransport;\n\n constructor(options: GatewayAdminTransportOptions) {\n this.transport = new GatewayAdminTransport(options);\n }\n\n async createProject(\n options: CreateAIProjectOptions,\n ): Promise<AIAdminProject> {\n const body = removeUndefinedValues({\n name: options.name,\n customer_id: options.tenantId,\n budget: mapBifrostBudget(options.budget),\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/api/governance/teams',\n body,\n );\n const team = toJsonRecord(response.team ?? response);\n const id = stringValue(team.id) || stringValue(team.team_id);\n\n if (!id) {\n throw new AIError(\n 'Bifrost did not return a project id',\n 'ADMIN_INVALID_RESPONSE',\n 'bifrost',\n );\n }\n\n return {\n id,\n name:\n stringValue(team.name) || stringValue(team.team_alias) || options.name,\n tenantId: stringValue(team.customer_id) || options.tenantId,\n budgetId: stringValue(team.budget_id),\n provider: 'bifrost',\n raw: response,\n };\n }\n\n async createVirtualKey(\n options: CreateAIVirtualKeyOptions,\n ): Promise<AIVirtualKey> {\n const body = removeUndefinedValues({\n name: options.name,\n description: options.description,\n provider_configs: mapBifrostProviderConfigs(options.providerConfigs),\n team_id: options.projectId,\n customer_id: options.projectId ? undefined : options.tenantId,\n budget: mapBifrostBudget(options.budget),\n rate_limit: mapBifrostRateLimit(options.rateLimit),\n key_ids: options.keyIds,\n is_active: options.isActive ?? true,\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/api/governance/virtual-keys',\n body,\n );\n const virtualKey = toJsonRecord(\n response.virtual_key ?? response.virtualKey ?? response,\n );\n\n return {\n id: stringValue(virtualKey.id) || stringValue(virtualKey.key_id),\n name: stringValue(virtualKey.name) || options.name,\n key: stringValue(virtualKey.value) || stringValue(virtualKey.key),\n maskedKey:\n stringValue(virtualKey.key_name) || stringValue(virtualKey.masked_key),\n projectId: stringValue(virtualKey.team_id) || options.projectId,\n tenantId: stringValue(virtualKey.customer_id) || options.tenantId,\n expiresAt:\n stringValue(virtualKey.expires) || stringValue(virtualKey.expires_at),\n provider: 'bifrost',\n raw: response,\n };\n }\n}\n\nexport class LiteLLMAdmin implements AIAdminInterface {\n private readonly transport: GatewayAdminTransport;\n\n constructor(options: GatewayAdminTransportOptions) {\n this.transport = new GatewayAdminTransport(options);\n }\n\n async createProject(\n options: CreateAIProjectOptions,\n ): Promise<AIAdminProject> {\n const projectId =\n options.id || slugifyProjectId(options.name, options.tenantId);\n const body = removeUndefinedValues({\n team_id: projectId,\n team_alias: options.name,\n models: options.models,\n ...mapLiteLLMBudget(options.budget),\n ...mapLiteLLMRateLimit(options.rateLimit),\n metadata: createMetadata(options),\n blocked: options.isBlocked,\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/team/new',\n body,\n );\n const team = toJsonRecord(response.team ?? response);\n\n return {\n id: stringValue(team.team_id) || projectId,\n name:\n stringValue(team.team_alias) || stringValue(team.name) || options.name,\n tenantId: options.tenantId,\n provider: 'litellm',\n raw: response,\n };\n }\n\n async createVirtualKey(\n options: CreateAIVirtualKeyOptions,\n ): Promise<AIVirtualKey> {\n const body = removeUndefinedValues({\n key_alias: options.name,\n team_id: options.projectId,\n user_id: options.userId,\n models: options.models,\n duration: options.duration,\n ...mapLiteLLMBudget(options.budget),\n ...mapLiteLLMRateLimit(options.rateLimit),\n metadata: createMetadata(options),\n aliases: options.aliases,\n config: options.config,\n permissions: options.permissions,\n blocked: options.isActive === undefined ? undefined : !options.isActive,\n ...options.raw,\n });\n\n const response = await this.transport.request<JsonRecord>(\n 'POST',\n '/key/generate',\n body,\n );\n\n return {\n id: stringValue(response.token_id) || stringValue(response.key_id),\n name:\n stringValue(response.key_alias) ||\n stringValue(response.key_name) ||\n options.name,\n key: stringValue(response.key),\n maskedKey: stringValue(response.key_name),\n projectId: stringValue(response.team_id) || options.projectId,\n tenantId: options.tenantId,\n expiresAt: stringValue(response.expires),\n provider: 'litellm',\n raw: response,\n };\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { c as extractTextContent, o as ModelNotFoundError, r as AuthenticationError, s as RateLimitError, t as AIError } from "./types-CINwUPDo.js";
2
- import { n as extractRetryAfterSeconds } from "./rate-limit-DgEhanDL.js";
3
- import { a as normalizeBaseAIOptions, c as prepareRequestControls, o as normalizeChatOptions, s as normalizeImageGenerationOptions } from "./safety-C415clBm.js";
1
+ import { c as extractTextContent, o as ModelNotFoundError, r as AuthenticationError, s as RateLimitError, t as AIError } from "./types-sSecpd_N.js";
2
+ import { n as extractRetryAfterSeconds } from "./rate-limit-C2oPwLGI.js";
3
+ import { a as normalizeBaseAIOptions, c as prepareRequestControls, o as normalizeChatOptions, s as normalizeImageGenerationOptions } from "./safety-ClfpyKEN.js";
4
4
  import { t as emitUsage } from "./usage-C1Y1Nlg4.js";
5
5
  import { join } from "node:path";
6
6
  import crypto from "node:crypto";
@@ -873,4 +873,4 @@ var GeminiProvider = class {
873
873
  //#endregion
874
874
  export { GeminiProvider };
875
875
 
876
- //# sourceMappingURL=gemini-BT8fjXWz.js.map
876
+ //# sourceMappingURL=gemini-GQdugon1.js.map