@happyvertical/ai 0.110.0 → 0.111.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -0
- package/dist/chunks/{anthropic-DhNGsQpl.js → anthropic-DQjBd3wu.js} +3 -3
- package/dist/chunks/{anthropic-DhNGsQpl.js.map → anthropic-DQjBd3wu.js.map} +1 -1
- package/dist/chunks/{bedrock-CrtUO8yx.js → bedrock-gtE6OU5Z.js} +3 -3
- package/dist/chunks/{bedrock-CrtUO8yx.js.map → bedrock-gtE6OU5Z.js.map} +1 -1
- package/dist/chunks/{bifrost-BU6OvIP9.js → bifrost-DSeAoUz-.js} +4 -4
- package/dist/chunks/{bifrost-BU6OvIP9.js.map → bifrost-DSeAoUz-.js.map} +1 -1
- package/dist/chunks/{byteplus-modelark-gG941oea.js → byteplus-modelark-C0rRxYwh.js} +3 -3
- package/dist/chunks/{byteplus-modelark-gG941oea.js.map → byteplus-modelark-C0rRxYwh.js.map} +1 -1
- package/dist/chunks/{claude-cli-CW_gIocm.js → claude-cli-DysSNpKY.js} +3 -3
- package/dist/chunks/{claude-cli-CW_gIocm.js.map → claude-cli-DysSNpKY.js.map} +1 -1
- package/dist/chunks/{continuation-BMGvMlzq.js → continuation-B-b0Iqgj.js} +52 -39
- package/dist/chunks/continuation-B-b0Iqgj.js.map +1 -0
- package/dist/chunks/{gateway-audio-models-DuXbi_2q.js → gateway-audio-models-57sR3hHE.js} +2 -2
- package/dist/chunks/{gateway-audio-models-DuXbi_2q.js.map → gateway-audio-models-57sR3hHE.js.map} +1 -1
- package/dist/chunks/{gemini-DCo6d-n1.js → gemini-BHdv492T.js} +3 -3
- package/dist/chunks/{gemini-DCo6d-n1.js.map → gemini-BHdv492T.js.map} +1 -1
- package/dist/chunks/{huggingface-jqjeFPdr.js → huggingface-vooWYknE.js} +3 -3
- package/dist/chunks/{huggingface-jqjeFPdr.js.map → huggingface-vooWYknE.js.map} +1 -1
- package/dist/chunks/{laya-D8o5TEO7.js → laya-B7aCsYqu.js} +3 -3
- package/dist/chunks/{laya-D8o5TEO7.js.map → laya-B7aCsYqu.js.map} +1 -1
- package/dist/chunks/{litellm-C4TKClkF.js → litellm-CV2uEz8Q.js} +4 -4
- package/dist/chunks/{litellm-C4TKClkF.js.map → litellm-CV2uEz8Q.js.map} +1 -1
- package/dist/chunks/{ollama-CuDjAyTy.js → ollama-pL2WCpkS.js} +11 -4
- package/dist/chunks/ollama-pL2WCpkS.js.map +1 -0
- package/dist/chunks/{openai-compat-video-ByTC2nzb.js → openai-compat-video-BoLwWXJM.js} +3 -3
- package/dist/chunks/{openai-compat-video-ByTC2nzb.js.map → openai-compat-video-BoLwWXJM.js.map} +1 -1
- package/dist/chunks/{qwen-tts-CWdO07D_.js → qwen-tts-BbGkn1oW.js} +2 -2
- package/dist/chunks/{qwen-tts-CWdO07D_.js.map → qwen-tts-BbGkn1oW.js.map} +1 -1
- package/dist/chunks/{rate-limit-Dwyde1BY.js → rate-limit-B3Kz6v4j.js} +2 -2
- package/dist/chunks/{rate-limit-Dwyde1BY.js.map → rate-limit-B3Kz6v4j.js.map} +1 -1
- package/dist/chunks/reasoning-DCCN0I3I.js +33 -0
- package/dist/chunks/reasoning-DCCN0I3I.js.map +1 -0
- package/dist/chunks/{seevio-B9V2nZtr.js → seevio-Dt8kWgpg.js} +3 -3
- package/dist/chunks/{seevio-B9V2nZtr.js.map → seevio-Dt8kWgpg.js.map} +1 -1
- package/dist/chunks/{src-ChaEcI7l.js → src-mOOK32QB.js} +275 -44
- package/dist/chunks/src-mOOK32QB.js.map +1 -0
- package/dist/chunks/{typesafe-B1_jGusy.js → typesafe-DDVSVUR_.js} +3 -3
- package/dist/chunks/{typesafe-B1_jGusy.js.map → typesafe-DDVSVUR_.js.map} +1 -1
- package/dist/chunks/{usage-DS7uDhi1.js → usage-yIT5lpQm.js} +37 -4
- package/dist/chunks/usage-yIT5lpQm.js.map +1 -0
- package/dist/chunks/{webllm-DVDsgVT7.js → webllm-DrwR5V-y.js} +2 -2
- package/dist/chunks/{webllm-DVDsgVT7.js.map → webllm-DrwR5V-y.js.map} +1 -1
- package/dist/index.js +2 -2
- package/dist/local.js +2 -2
- package/dist/node.js +3 -3
- package/dist/shared/chat-template-kwargs.d.ts +17 -0
- package/dist/shared/chat-template-kwargs.d.ts.map +1 -0
- package/dist/shared/continuation.d.ts.map +1 -1
- package/dist/shared/providers/connection-retry.d.ts +34 -0
- package/dist/shared/providers/connection-retry.d.ts.map +1 -0
- package/dist/shared/providers/ollama.d.ts.map +1 -1
- package/dist/shared/providers/openai.d.ts +21 -1
- package/dist/shared/providers/openai.d.ts.map +1 -1
- package/dist/shared/reasoning.d.ts +19 -0
- package/dist/shared/reasoning.d.ts.map +1 -0
- package/dist/shared/safety.d.ts +12 -0
- package/dist/shared/safety.d.ts.map +1 -1
- package/dist/shared/types.d.ts +81 -3
- package/dist/shared/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/chunks/continuation-BMGvMlzq.js.map +0 -1
- package/dist/chunks/ollama-CuDjAyTy.js.map +0 -1
- package/dist/chunks/src-ChaEcI7l.js.map +0 -1
- package/dist/chunks/usage-DS7uDhi1.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seevio-B9V2nZtr.js","names":[],"sources":["../../src/shared/providers/seevio.ts"],"sourcesContent":["/**\n * Seevio's native asynchronous Seedance 2.5 video-generation provider.\n *\n * Seevio is deliberately implemented as its own raw-HTTP adapter rather than\n * being shaped to ModelArk. Both services can run Seedance, but their task,\n * billing, and media contracts differ.\n *\n * @see https://seevio.ai/api-docs\n */\n\nimport { ValidationError } from '@happyvertical/utils';\nimport { rateLimitErrorFrom } from '../rate-limit';\nimport { normalizeBaseAIOptions } from '../safety';\nimport type {\n AICapabilities,\n AIInterface,\n AIMessage,\n AIModel,\n AIResponse,\n ChatOptions,\n CompletionOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n MessageOptions,\n SeevioOptions,\n TTSOptions,\n TTSResponse,\n VideoGenerationBillingMetadata,\n VideoGenerationJob,\n VideoGenerationOptions,\n VideoGenerationReferenceMedia,\n VideoGenerationResult,\n VideoGenerationStatusResult,\n Voice,\n VoiceCloneOptions,\n VoiceDesignOptions,\n VoiceListOptions,\n} from '../types';\nimport { AIError, AuthenticationError } from '../types';\nimport { emitUsage } from './usage';\n\nconst DEFAULT_BASE_URL = 'https://api.seevio.ai';\nconst DEFAULT_RESULT_ORIGIN = 'https://cdn.seevio.ai';\nconst MODEL = 'seedance-2-5';\nconst MIN_POLL_INTERVAL_MS = 10_000;\nconst MAX_REDIRECTS = 5;\nconst DEFAULT_MAX_RESULT_BYTES = 200 * 1024 * 1024;\nconst COMPLETED_SNAPSHOT_TTL_MS = MIN_POLL_INTERVAL_MS;\nconst MAX_COMPLETED_SNAPSHOTS = 128;\n\ninterface SeevioTask {\n id: string;\n status: string;\n model?: string;\n billing_status?: string;\n credits?: number;\n failed_reason?: string | null;\n data?: {\n results?: string[];\n video_expires_at?: string;\n last_frame_url?: string | null;\n processing_time?: number;\n };\n}\n\ninterface SeevioErrorResponse {\n error?: {\n code?: string;\n message?: string;\n required?: number;\n available?: number;\n };\n}\n\ninterface SeevioCreateResponse {\n taskId: string;\n credits?: number;\n}\n\ninterface SeevioInput {\n prompt: string;\n generation_type: 'text-to-video' | 'image-to-video' | 'reference-to-video';\n image_urls?: string[];\n video_urls?: string[];\n audio_urls?: string[];\n duration: number;\n aspect_ratio: 'adaptive' | '16:9' | '9:16' | '1:1' | '4:3' | '3:4' | '21:9';\n resolution: '480p' | '720p';\n generate_audio: boolean;\n return_last_frame: boolean;\n}\n\nfunction ensureOptions(options: SeevioOptions): SeevioOptions {\n const baseUrl = (options.baseUrl || DEFAULT_BASE_URL)\n .trim()\n .replace(/\\/+$/, '');\n const parsedBaseUrl = new URL(baseUrl);\n if (parsedBaseUrl.protocol !== 'https:') {\n throw new ValidationError('seevio baseUrl must use HTTPS', {\n provider: 'seevio',\n });\n }\n const origins = options.resultUrlOrigins?.length\n ? options.resultUrlOrigins\n : [DEFAULT_RESULT_ORIGIN];\n for (const origin of origins) {\n const parsed = new URL(origin);\n if (\n parsed.protocol !== 'https:' ||\n parsed.origin !== origin.replace(/\\/+$/, '')\n ) {\n throw new ValidationError(\n 'seevio resultUrlOrigins must contain HTTPS origins only',\n {\n provider: 'seevio',\n },\n );\n }\n }\n if (\n options.maxResultBytes !== undefined &&\n (!Number.isSafeInteger(options.maxResultBytes) ||\n options.maxResultBytes <= 0)\n ) {\n throw new ValidationError(\n 'seevio maxResultBytes must be a positive safe integer',\n { provider: 'seevio' },\n );\n }\n return {\n ...options,\n baseUrl,\n resultUrlOrigins: origins.map((origin) => origin.replace(/\\/+$/, '')),\n };\n}\n\nfunction validatedHttpsUrl(value: string, label: string): string {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new ValidationError(`${label} must be a valid public HTTPS URL`, {\n provider: 'seevio',\n });\n }\n if (url.protocol !== 'https:') {\n throw new ValidationError(`${label} must use HTTPS`, {\n provider: 'seevio',\n });\n }\n const host = url.hostname.toLowerCase();\n if (\n url.username ||\n url.password ||\n host === 'localhost' ||\n host.endsWith('.localhost') ||\n host.includes(':') ||\n /^\\d{1,3}(?:\\.\\d{1,3}){3}$/.test(host)\n ) {\n throw new ValidationError(\n `${label} must not use credentials, localhost, or an IP-literal host`,\n { provider: 'seevio' },\n );\n }\n return url.toString();\n}\n\nfunction normalizeBilling(\n task: SeevioTask,\n): VideoGenerationBillingMetadata | undefined {\n if (task.credits === undefined && !task.billing_status) return undefined;\n const normalized = [\n 'reserved',\n 'charged',\n 'refunded',\n 'refund_failed',\n ].includes(task.billing_status || '')\n ? (task.billing_status as VideoGenerationBillingMetadata['status'])\n : undefined;\n return {\n ...(task.credits !== undefined ? { credits: task.credits } : {}),\n ...(normalized ? { status: normalized } : {}),\n ...(!normalized && task.billing_status\n ? { rawStatus: task.billing_status }\n : {}),\n };\n}\n\n/** Native Seevio adapter for the pinned `seedance-2-5` model. */\nexport class SeevioProvider implements AIInterface {\n private readonly options: SeevioOptions;\n private readonly resultOrigins: Set<string>;\n private readonly nextPollAt = new Map<string, number>();\n private readonly completedSnapshots = new Map<\n string,\n { task: SeevioTask; recordedAt: number }\n >();\n\n /** Creates a Seevio task client using the documented HTTPS API root. */\n constructor(options: SeevioOptions) {\n this.options = normalizeBaseAIOptions(ensureOptions(options));\n this.resultOrigins = new Set(this.options.resultUrlOrigins);\n }\n\n private async request<T>(\n path: string,\n options: {\n method?: 'GET' | 'POST';\n body?: unknown;\n signal?: AbortSignal;\n timeout?: number;\n allowNotFound?: boolean;\n } = {},\n ): Promise<T> {\n const response = await this.rawRequest(path, options);\n const text = await response.text();\n return text ? (JSON.parse(text) as T) : ({} as T);\n }\n\n private async rawRequest(\n path: string,\n options: {\n method?: 'GET' | 'POST';\n body?: unknown;\n signal?: AbortSignal;\n timeout?: number;\n allowNotFound?: boolean;\n } = {},\n ): Promise<Response> {\n if (options.signal?.aborted)\n throw new AIError(\n 'Seevio request aborted by caller',\n 'AI_ABORTED',\n 'seevio',\n );\n const controller = new AbortController();\n const timeoutMs = options.timeout ?? this.options.timeout;\n let timedOut = false;\n const timer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, timeoutMs);\n const abort = () => controller.abort(options.signal?.reason);\n options.signal?.addEventListener('abort', abort, { once: true });\n // An abort may occur between the preflight check and listener\n // registration. Re-check after registration so that race still reaches\n // the fetch signal rather than allowing a paid request to continue.\n if (options.signal?.aborted) abort();\n try {\n let response: Response;\n try {\n response = await fetch(`${this.options.baseUrl}${path}`, {\n method: options.method || 'GET',\n headers: {\n Authorization: `Bearer ${this.options.apiKey ?? ''}`,\n ...(options.body !== undefined\n ? { 'Content-Type': 'application/json' }\n : {}),\n ...(this.options.headers || {}),\n },\n body:\n options.body === undefined\n ? undefined\n : JSON.stringify(options.body),\n signal: controller.signal,\n });\n } catch (error) {\n if (timedOut)\n throw new AIError(\n `Seevio request to ${path} timed out after ${timeoutMs}ms`,\n 'AI_TIMEOUT',\n 'seevio',\n undefined,\n options.method !== 'POST',\n );\n if (options.signal?.aborted)\n throw new AIError(\n 'Seevio request aborted by caller',\n 'AI_ABORTED',\n 'seevio',\n );\n throw new AIError(\n `Network error calling Seevio ${path}: ${error instanceof Error ? error.message : String(error)}`,\n 'NETWORK_ERROR',\n 'seevio',\n undefined,\n options.method !== 'POST',\n );\n }\n if (response.status === 404 && options.allowNotFound) return response;\n if (response.ok) return response;\n const errorText = await response.text();\n let payload: SeevioErrorResponse = {};\n try {\n payload = JSON.parse(errorText) as SeevioErrorResponse;\n } catch {\n /* preserve status fallback below */\n }\n const message =\n payload.error?.message ||\n `Seevio request to ${path} failed with HTTP ${response.status}`;\n if (response.status === 401) throw new AuthenticationError('seevio');\n if (response.status === 403)\n throw new AIError(message, 'SEEVIO_FORBIDDEN', 'seevio');\n if (response.status === 402)\n throw new AIError(message, 'INSUFFICIENT_CREDITS', 'seevio');\n if (response.status === 429)\n throw rateLimitErrorFrom(\n 'seevio',\n { headers: response.headers },\n { reason: payload.error?.message },\n );\n throw new AIError(\n message,\n 'SEEVIO_REQUEST_FAILED',\n 'seevio',\n undefined,\n response.status >= 500 && options.method !== 'POST',\n );\n } finally {\n clearTimeout(timer);\n options.signal?.removeEventListener('abort', abort);\n }\n }\n\n private assertReviewedResultUrl(value: string): string {\n const url = validatedHttpsUrl(value, 'Seevio result URL');\n if (!this.resultOrigins.has(new URL(url).origin)) {\n throw new AIError(\n 'Seevio returned a result URL outside configured reviewed origins',\n 'UNTRUSTED_RESULT_URL',\n 'seevio',\n );\n }\n return url;\n }\n\n private async downloadReviewedResult(\n initialUrl: string,\n ): Promise<{ data: Buffer; mimeType: string; url: string }> {\n let current = this.assertReviewedResultUrl(initialUrl);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.options.timeout);\n const maxBytes = this.options.maxResultBytes ?? DEFAULT_MAX_RESULT_BYTES;\n try {\n for (let redirects = 0; redirects <= MAX_REDIRECTS; redirects += 1) {\n const response = await fetch(current, {\n redirect: 'manual',\n signal: controller.signal,\n });\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get('location');\n if (!location)\n throw new AIError(\n 'Seevio result redirect omitted Location',\n 'SEEVIO_RESULT_DOWNLOAD_FAILED',\n 'seevio',\n );\n current = this.assertReviewedResultUrl(\n new URL(location, current).toString(),\n );\n continue;\n }\n if (!response.ok)\n throw new AIError(\n `Seevio result download failed with HTTP ${response.status}`,\n 'SEEVIO_RESULT_DOWNLOAD_FAILED',\n 'seevio',\n );\n const contentType = response.headers.get('content-type');\n if (!contentType?.toLowerCase().startsWith('video/'))\n throw new AIError(\n `Seevio result download returned non-video content type ${contentType}`,\n 'SEEVIO_RESULT_MIME_INVALID',\n 'seevio',\n );\n const contentLength = response.headers.get('content-length');\n if (contentLength && Number(contentLength) > maxBytes)\n throw new AIError(\n `Seevio result exceeds configured ${maxBytes}-byte download limit`,\n 'SEEVIO_RESULT_TOO_LARGE',\n 'seevio',\n );\n if (!response.body)\n throw new AIError(\n 'Seevio result download had no response body',\n 'SEEVIO_RESULT_DOWNLOAD_FAILED',\n 'seevio',\n );\n const chunks: Uint8Array[] = [];\n let size = 0;\n const reader = response.body.getReader();\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n size += value.byteLength;\n if (size > maxBytes) {\n await reader.cancel();\n throw new AIError(\n `Seevio result exceeds configured ${maxBytes}-byte download limit`,\n 'SEEVIO_RESULT_TOO_LARGE',\n 'seevio',\n );\n }\n chunks.push(value);\n }\n return {\n data: Buffer.concat(chunks.map((chunk) => Buffer.from(chunk))),\n mimeType: contentType,\n url: current,\n };\n }\n throw new AIError(\n 'Seevio result exceeded the maximum redirect count',\n 'SEEVIO_RESULT_DOWNLOAD_FAILED',\n 'seevio',\n );\n } catch (error) {\n if (controller.signal.aborted)\n throw new AIError(\n `Seevio result download timed out after ${this.options.timeout}ms`,\n 'AI_TIMEOUT',\n 'seevio',\n undefined,\n true,\n );\n throw error;\n } finally {\n clearTimeout(timer);\n }\n }\n\n private mediaUrls(options: VideoGenerationOptions): {\n images: string[];\n videos: string[];\n audio: string[];\n } {\n const images = (options.referenceImages ?? []).map((reference) => {\n if (\n typeof reference.image !== 'string' ||\n reference.image.startsWith('data:')\n ) {\n throw new ValidationError(\n 'Seevio referenceImages must be public HTTPS URLs; it cannot retrieve bytes or data URLs',\n { provider: 'seevio' },\n );\n }\n return validatedHttpsUrl(reference.image, 'Seevio reference image');\n });\n const references = options.referenceMedia ?? [];\n if (references.length + images.length > 50)\n throw new ValidationError(\n 'Seevio accepts at most 50 total reference assets',\n { provider: 'seevio' },\n );\n const collect = (type: VideoGenerationReferenceMedia['type']) =>\n references\n .filter((reference) => reference.type === type)\n .map((reference) =>\n validatedHttpsUrl(reference.url, `Seevio ${type} reference`),\n );\n const mediaImages = collect('image');\n const videos = collect('video');\n const audio = collect('audio');\n if (\n images.length + mediaImages.length > 30 ||\n videos.length > 10 ||\n audio.length > 10\n ) {\n throw new ValidationError(\n 'Seevio reference limits are 30 images, 10 videos, and 10 audio files',\n { provider: 'seevio' },\n );\n }\n for (const reference of references) {\n if (\n (reference.type === 'video' || reference.type === 'audio') &&\n reference.durationSeconds !== undefined &&\n (!Number.isFinite(reference.durationSeconds) ||\n reference.durationSeconds < 2 ||\n reference.durationSeconds > 30)\n ) {\n throw new ValidationError(\n 'Seevio video/audio reference durations must be 2-30 seconds when supplied',\n { provider: 'seevio' },\n );\n }\n }\n for (const [label, type] of [\n ['video', 'video'],\n ['audio', 'audio'],\n ] as const) {\n const durations = references\n .filter((reference) => reference.type === type)\n .flatMap((reference) =>\n reference.durationSeconds === undefined\n ? []\n : [reference.durationSeconds],\n );\n if (durations.reduce((total, duration) => total + duration, 0) > 30) {\n throw new ValidationError(\n `Seevio ${label} reference durations must total no more than 30 seconds`,\n { provider: 'seevio' },\n );\n }\n }\n return { images: [...images, ...mediaImages], videos, audio };\n }\n\n private async pollTask(jobId: string): Promise<SeevioTask> {\n const now = Date.now();\n for (const [knownJobId, expiry] of this.nextPollAt) {\n if (expiry <= now) this.nextPollAt.delete(knownJobId);\n }\n const next = this.nextPollAt.get(jobId);\n if (next && now < next)\n throw new AIError(\n `Seevio tasks may be polled no more than once every 10 seconds; retry after ${Math.ceil((next - now) / 1000)} seconds`,\n 'VIDEO_POLL_TOO_FREQUENT',\n 'seevio',\n );\n const expiry = now + MIN_POLL_INTERVAL_MS;\n this.nextPollAt.set(jobId, expiry);\n const cleanup = setTimeout(() => {\n if (this.nextPollAt.get(jobId) === expiry) this.nextPollAt.delete(jobId);\n }, MIN_POLL_INTERVAL_MS);\n // Let idle Node workers exit rather than keeping the process alive for a\n // cadence cleanup timer. Browser timer handles do not expose `unref`.\n if (typeof cleanup === 'object' && 'unref' in cleanup) cleanup.unref();\n const task = await this.request<SeevioTask>(\n `/v1/tasks/${encodeURIComponent(jobId)}`,\n );\n if (task.status === 'completed') {\n // This is a bounded, short-lived handoff cache, not task history. A\n // completed task can be fetched without another status request only\n // within the provider's ten-second cadence window.\n if (!this.completedSnapshots.has(jobId)) {\n const oldestJobId = this.completedSnapshots.keys().next().value;\n if (\n this.completedSnapshots.size >= MAX_COMPLETED_SNAPSHOTS &&\n oldestJobId !== undefined\n ) {\n this.completedSnapshots.delete(oldestJobId);\n }\n }\n this.completedSnapshots.set(jobId, { task, recordedAt: Date.now() });\n }\n return task;\n }\n\n private completedSnapshot(jobId: string): SeevioTask | undefined {\n const snapshot = this.completedSnapshots.get(jobId);\n if (!snapshot) return undefined;\n if (Date.now() - snapshot.recordedAt >= COMPLETED_SNAPSHOT_TTL_MS) {\n this.completedSnapshots.delete(jobId);\n return undefined;\n }\n return snapshot.task;\n }\n\n /** Submits one unambiguous, non-retried Seedance 2.5 task. */\n async submitVideoGenerationJob(\n options: VideoGenerationOptions,\n ): Promise<VideoGenerationJob> {\n const startTime = Date.now();\n const model = options.model || this.options.defaultModel || MODEL;\n if (model !== MODEL)\n throw new ValidationError(\n `Seevio provider supports the pinned model ${MODEL}`,\n { provider: 'seevio' },\n );\n if (!options.prompt?.trim())\n throw new ValidationError('Seevio requires a non-empty prompt', {\n provider: 'seevio',\n });\n if (options.seed !== undefined)\n throw new ValidationError(\n 'seed is not supported by Seevio Seedance 2.5',\n { provider: 'seevio' },\n );\n const duration = options.durationSeconds ?? 5;\n if (!Number.isInteger(duration) || duration < 4 || duration > 30)\n throw new ValidationError(\n 'Seevio durationSeconds must be an integer from 4 through 30',\n { provider: 'seevio' },\n );\n const resolution = options.resolution ?? '720p';\n if (resolution !== '480p' && resolution !== '720p')\n throw new ValidationError('Seevio resolution must be 480p or 720p', {\n provider: 'seevio',\n });\n const { images, videos, audio } = this.mediaUrls(options);\n const isImageToVideo =\n images.length >= 1 &&\n images.length <= 2 &&\n videos.length === 0 &&\n audio.length === 0;\n const generationType = isImageToVideo\n ? 'image-to-video'\n : images.length || videos.length || audio.length\n ? 'reference-to-video'\n : 'text-to-video';\n const aspectRatio = options.aspectRatio ?? 'adaptive';\n const supportedRatios: SeevioInput['aspect_ratio'][] = [\n 'adaptive',\n '16:9',\n '9:16',\n '1:1',\n '4:3',\n '3:4',\n '21:9',\n ];\n if (!supportedRatios.includes(aspectRatio as SeevioInput['aspect_ratio']))\n throw new ValidationError('Unsupported Seevio aspectRatio', {\n provider: 'seevio',\n });\n if (isImageToVideo && aspectRatio !== 'adaptive')\n throw new ValidationError(\n 'Seevio image-to-video requires aspectRatio: adaptive',\n { provider: 'seevio' },\n );\n const input: SeevioInput = {\n prompt: options.prompt.trim(),\n generation_type: generationType,\n duration,\n aspect_ratio: aspectRatio as SeevioInput['aspect_ratio'],\n resolution: resolution as '480p' | '720p',\n generate_audio: options.generateAudio ?? true,\n return_last_frame: options.returnLastFrame ?? false,\n ...(images.length ? { image_urls: images } : {}),\n ...(videos.length ? { video_urls: videos } : {}),\n ...(audio.length ? { audio_urls: audio } : {}),\n };\n const created = await this.request<SeevioCreateResponse>(\n '/v1/videos/generations',\n {\n method: 'POST',\n body: { model: MODEL, input },\n signal: options.signal,\n timeout: options.timeout,\n },\n );\n if (!created.taskId)\n throw new AIError(\n 'Seevio accepted submission without a taskId',\n 'SEEVIO_INVALID_RESPONSE',\n 'seevio',\n );\n emitUsage(\n this.options,\n 'seevio',\n 'submitVideoGenerationJob',\n MODEL,\n undefined,\n startTime,\n {\n durationSeconds: String(duration),\n resolution,\n aspectRatio,\n ...options.usageTags,\n },\n );\n return {\n jobId: created.taskId,\n provider: 'seevio',\n model: MODEL,\n createdAt: new Date().toISOString(),\n raw:\n created.credits === undefined\n ? undefined\n : { credits: created.credits, billingStatus: 'reserved' },\n };\n }\n\n /** Returns a normalized task snapshot, enforcing Seevio's 10-second poll cadence. */\n async getVideoGenerationJob(\n handle: VideoGenerationJob,\n ): Promise<VideoGenerationStatusResult> {\n return this.mapTask(await this.pollTask(handle.jobId));\n }\n\n /** Downloads a completed video while validating the initial URL and every redirect. */\n async fetchVideoGenerationResult(\n handle: VideoGenerationJob,\n ): Promise<VideoGenerationResult> {\n const task =\n this.completedSnapshot(handle.jobId) ??\n (await this.pollTask(handle.jobId));\n const status = this.mapTask(task);\n if (status.status !== 'succeeded' || !status.result?.url)\n throw new AIError(\n `Seevio job ${handle.jobId} has not succeeded (status: ${status.status})`,\n 'VIDEO_JOB_NOT_READY',\n 'seevio',\n handle.model,\n );\n try {\n const downloaded = await this.downloadReviewedResult(status.result.url);\n return { ...status.result, ...downloaded };\n } finally {\n // The handoff cache exists only to make an immediate status → fetch\n // flow cadence-safe. Do not retain expired signed URLs after any fetch\n // attempt; a later fetch will authenticate a fresh task snapshot.\n this.completedSnapshots.delete(handle.jobId);\n }\n }\n\n /** Reports cancellation as unsupported because Seevio documents no cancellation endpoint. */\n async cancelVideoGenerationJob(_handle: VideoGenerationJob): Promise<void> {\n throw new AIError(\n 'Seevio does not document a video-task cancellation endpoint',\n 'NOT_IMPLEMENTED',\n 'seevio',\n );\n }\n\n /** Performs an unbilled random task-id probe; an authenticated 404 is valid access. */\n async validateVideoGenerationAccess(): Promise<boolean> {\n const response = await this.rawRequest(\n `/v1/tasks/have-ai-access-probe-${crypto.randomUUID()}`,\n { allowNotFound: true },\n );\n return response.ok || response.status === 404;\n }\n\n private mapTask(task: SeevioTask): VideoGenerationStatusResult {\n const billing = normalizeBilling(task);\n if (task.status === 'queued')\n return { status: 'queued', ...(billing ? { billing } : {}) };\n if (task.status === 'generating')\n return { status: 'running', ...(billing ? { billing } : {}) };\n if (task.status === 'failed')\n return {\n status: 'failed',\n error: task.failed_reason || 'Seevio video generation failed',\n ...(billing ? { billing } : {}),\n };\n if (task.status !== 'completed')\n return {\n status: 'running',\n rawStatus: task.status,\n ...(billing ? { billing } : {}),\n };\n const url = task.data?.results?.[0];\n if (!url)\n return {\n status: 'failed',\n error: 'Seevio reported completion with no result URL',\n ...(billing ? { billing } : {}),\n };\n const result: VideoGenerationResult = {\n url: this.assertReviewedResultUrl(url),\n mimeType: 'video/mp4',\n ...(task.data?.video_expires_at\n ? { expiresAt: task.data.video_expires_at }\n : {}),\n ...(task.data?.last_frame_url\n ? {\n lastFrameUrl: this.assertReviewedResultUrl(\n task.data.last_frame_url,\n ),\n }\n : {}),\n };\n return { status: 'succeeded', result, ...(billing ? { billing } : {}) };\n }\n\n /** Lists the single intentional Seevio model selection. */\n async getModels(): Promise<AIModel[]> {\n return [\n {\n id: MODEL,\n name: 'Seedance 2.5',\n description: 'Seevio Seedance 2.5 async video generation',\n contextLength: 0,\n capabilities: ['video_generation'],\n supportsFunctions: false,\n supportsVision: true,\n },\n ];\n }\n /** Reports Seevio's video-only capability surface. */\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 ],\n };\n }\n\n private unsupported(): never {\n throw new AIError(\n 'This operation is not supported by the Seevio video-only provider.',\n 'NOT_IMPLEMENTED',\n 'seevio',\n );\n }\n /** @inheritdoc */ async chat(\n _messages: AIMessage[],\n _options?: ChatOptions,\n ): Promise<AIResponse> {\n return this.unsupported();\n }\n /** @inheritdoc */ async complete(\n _prompt: string,\n _options?: CompletionOptions,\n ): Promise<AIResponse> {\n return this.unsupported();\n }\n /** @inheritdoc */ async message(\n _text: string,\n _options?: MessageOptions,\n ): Promise<string> {\n return this.unsupported();\n }\n /** @inheritdoc */ async embed(\n _text: string | string[],\n _options?: EmbeddingOptions,\n ): Promise<EmbeddingResponse> {\n return this.unsupported();\n }\n /** @inheritdoc */ async embedImage(\n _image: string | Buffer,\n _options?: ImageEmbeddingOptions,\n ): Promise<EmbeddingResponse> {\n return this.unsupported();\n }\n /** @inheritdoc */ async describeImage(\n _image: string | Buffer,\n _prompt?: string,\n _options?: ImageDescriptionOptions,\n ): Promise<string> {\n return this.unsupported();\n }\n /** @inheritdoc */ async generateImage(\n _prompt: string,\n _options?: ImageGenerationOptions,\n ): Promise<ImageGenerationResponse> {\n return this.unsupported();\n }\n /** @inheritdoc */ stream(\n _messages: AIMessage[],\n _options?: ChatOptions,\n ): AsyncIterable<string> {\n const error = new AIError(\n 'Chat streaming is not supported by the Seevio video-only provider.',\n 'NOT_IMPLEMENTED',\n 'seevio',\n );\n return {\n [Symbol.asyncIterator]: () => ({ next: () => Promise.reject(error) }),\n };\n }\n /** @inheritdoc */ async countTokens(_text: string): Promise<number> {\n return this.unsupported();\n }\n /** @inheritdoc */ async synthesizeSpeech(\n _text: string,\n _options?: TTSOptions,\n ): Promise<TTSResponse> {\n return this.unsupported();\n }\n /** @inheritdoc */ streamSpeech(\n _text: string,\n _options?: TTSOptions,\n ): AsyncIterable<Buffer> {\n const error = new AIError(\n 'TTS streaming is not supported by the Seevio video-only provider.',\n 'NOT_IMPLEMENTED',\n 'seevio',\n );\n return {\n [Symbol.asyncIterator]: () => ({ next: () => Promise.reject(error) }),\n };\n }\n /** @inheritdoc */ async cloneVoice(\n _options: VoiceCloneOptions,\n ): Promise<Voice> {\n return this.unsupported();\n }\n /** @inheritdoc */ async designVoice(\n _options: VoiceDesignOptions,\n ): Promise<Voice> {\n return this.unsupported();\n }\n /** @inheritdoc */ async getVoices(\n _options?: VoiceListOptions,\n ): Promise<Voice[]> {\n return this.unsupported();\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA6CA,IAAM,mBAAmB;AACzB,IAAM,wBAAwB;AAC9B,IAAM,QAAQ;AACd,IAAM,uBAAuB;AAC7B,IAAM,gBAAgB;AACtB,IAAM,2BAA2B,MAAM,OAAO;AAC9C,IAAM,4BAA4B;AAClC,IAAM,0BAA0B;AA4ChC,SAAS,cAAc,SAAuC;CAC5D,MAAM,WAAW,QAAQ,WAAW,iBAAA,CACjC,KAAK,CAAC,CACN,QAAQ,QAAQ,EAAE;CAErB,IAAI,IADsB,IAAI,OAC1B,CAAA,CAAc,aAAa,UAC7B,MAAM,IAAI,gBAAgB,iCAAiC,EACzD,UAAU,SACZ,CAAC;CAEH,MAAM,UAAU,QAAQ,kBAAkB,SACtC,QAAQ,mBACR,CAAC,qBAAqB;CAC1B,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,SAAS,IAAI,IAAI,MAAM;EAC7B,IACE,OAAO,aAAa,YACpB,OAAO,WAAW,OAAO,QAAQ,QAAQ,EAAE,GAE3C,MAAM,IAAI,gBACR,2DACA,EACE,UAAU,SACZ,CACF;CAEJ;CACA,IACE,QAAQ,mBAAmB,KAAA,MAC1B,CAAC,OAAO,cAAc,QAAQ,cAAc,KAC3C,QAAQ,kBAAkB,IAE5B,MAAM,IAAI,gBACR,yDACA,EAAE,UAAU,SAAS,CACvB;CAEF,OAAO;EACL,GAAG;EACH;EACA,kBAAkB,QAAQ,KAAK,WAAW,OAAO,QAAQ,QAAQ,EAAE,CAAC;CACtE;AACF;AAEA,SAAS,kBAAkB,OAAe,OAAuB;CAC/D,IAAI;CACJ,IAAI;EACF,MAAM,IAAI,IAAI,KAAK;CACrB,QAAQ;EACN,MAAM,IAAI,gBAAgB,GAAG,MAAM,oCAAoC,EACrE,UAAU,SACZ,CAAC;CACH;CACA,IAAI,IAAI,aAAa,UACnB,MAAM,IAAI,gBAAgB,GAAG,MAAM,kBAAkB,EACnD,UAAU,SACZ,CAAC;CAEH,MAAM,OAAO,IAAI,SAAS,YAAY;CACtC,IACE,IAAI,YACJ,IAAI,YACJ,SAAS,eACT,KAAK,SAAS,YAAY,KAC1B,KAAK,SAAS,GAAG,KACjB,4BAA4B,KAAK,IAAI,GAErC,MAAM,IAAI,gBACR,GAAG,MAAM,8DACT,EAAE,UAAU,SAAS,CACvB;CAEF,OAAO,IAAI,SAAS;AACtB;AAEA,SAAS,iBACP,MAC4C;CAC5C,IAAI,KAAK,YAAY,KAAA,KAAa,CAAC,KAAK,gBAAgB,OAAO,KAAA;CAC/D,MAAM,aAAa;EACjB;EACA;EACA;EACA;CACF,CAAC,CAAC,SAAS,KAAK,kBAAkB,EAAE,IAC/B,KAAK,iBACN,KAAA;CACJ,OAAO;EACL,GAAI,KAAK,YAAY,KAAA,IAAY,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;EAC9D,GAAI,aAAa,EAAE,QAAQ,WAAW,IAAI,CAAC;EAC3C,GAAI,CAAC,cAAc,KAAK,iBACpB,EAAE,WAAW,KAAK,eAAe,IACjC,CAAC;CACP;AACF;;AAGA,IAAa,iBAAb,MAAmD;CACjD;CACA;CACA,6BAA8B,IAAI,IAAoB;CACtD,qCAAsC,IAAI,IAGxC;;CAGF,YAAY,SAAwB;EAClC,KAAK,UAAU,uBAAuB,cAAc,OAAO,CAAC;EAC5D,KAAK,gBAAgB,IAAI,IAAI,KAAK,QAAQ,gBAAgB;CAC5D;CAEA,MAAc,QACZ,MACA,UAMI,CAAC,GACO;EAEZ,MAAM,OAAO,OAAM,MADI,KAAK,WAAW,MAAM,OAAO,EAAA,CACxB,KAAK;EACjC,OAAO,OAAQ,KAAK,MAAM,IAAI,IAAW,CAAC;CAC5C;CAEA,MAAc,WACZ,MACA,UAMI,CAAC,GACc;EACnB,IAAI,QAAQ,QAAQ,SAClB,MAAM,IAAI,QACR,oCACA,cACA,QACF;EACF,MAAM,aAAa,IAAI,gBAAgB;EACvC,MAAM,YAAY,QAAQ,WAAW,KAAK,QAAQ;EAClD,IAAI,WAAW;EACf,MAAM,QAAQ,iBAAiB;GAC7B,WAAW;GACX,WAAW,MAAM;EACnB,GAAG,SAAS;EACZ,MAAM,cAAc,WAAW,MAAM,QAAQ,QAAQ,MAAM;EAC3D,QAAQ,QAAQ,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;EAI/D,IAAI,QAAQ,QAAQ,SAAS,MAAM;EACnC,IAAI;GACF,IAAI;GACJ,IAAI;IACF,WAAW,MAAM,MAAM,GAAG,KAAK,QAAQ,UAAU,QAAQ;KACvD,QAAQ,QAAQ,UAAU;KAC1B,SAAS;MACP,eAAe,UAAU,KAAK,QAAQ,UAAU;MAChD,GAAI,QAAQ,SAAS,KAAA,IACjB,EAAE,gBAAgB,mBAAmB,IACrC,CAAC;MACL,GAAI,KAAK,QAAQ,WAAW,CAAC;KAC/B;KACA,MACE,QAAQ,SAAS,KAAA,IACb,KAAA,IACA,KAAK,UAAU,QAAQ,IAAI;KACjC,QAAQ,WAAW;IACrB,CAAC;GACH,SAAS,OAAO;IACd,IAAI,UACF,MAAM,IAAI,QACR,qBAAqB,KAAK,mBAAmB,UAAU,KACvD,cACA,UACA,KAAA,GACA,QAAQ,WAAW,MACrB;IACF,IAAI,QAAQ,QAAQ,SAClB,MAAM,IAAI,QACR,oCACA,cACA,QACF;IACF,MAAM,IAAI,QACR,gCAAgC,KAAK,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAC9F,iBACA,UACA,KAAA,GACA,QAAQ,WAAW,MACrB;GACF;GACA,IAAI,SAAS,WAAW,OAAO,QAAQ,eAAe,OAAO;GAC7D,IAAI,SAAS,IAAI,OAAO;GACxB,MAAM,YAAY,MAAM,SAAS,KAAK;GACtC,IAAI,UAA+B,CAAC;GACpC,IAAI;IACF,UAAU,KAAK,MAAM,SAAS;GAChC,QAAQ,CAER;GACA,MAAM,UACJ,QAAQ,OAAO,WACf,qBAAqB,KAAK,oBAAoB,SAAS;GACzD,IAAI,SAAS,WAAW,KAAK,MAAM,IAAI,oBAAoB,QAAQ;GACnE,IAAI,SAAS,WAAW,KACtB,MAAM,IAAI,QAAQ,SAAS,oBAAoB,QAAQ;GACzD,IAAI,SAAS,WAAW,KACtB,MAAM,IAAI,QAAQ,SAAS,wBAAwB,QAAQ;GAC7D,IAAI,SAAS,WAAW,KACtB,MAAM,mBACJ,UACA,EAAE,SAAS,SAAS,QAAQ,GAC5B,EAAE,QAAQ,QAAQ,OAAO,QAAQ,CACnC;GACF,MAAM,IAAI,QACR,SACA,yBACA,UACA,KAAA,GACA,SAAS,UAAU,OAAO,QAAQ,WAAW,MAC/C;EACF,UAAU;GACR,aAAa,KAAK;GAClB,QAAQ,QAAQ,oBAAoB,SAAS,KAAK;EACpD;CACF;CAEA,wBAAgC,OAAuB;EACrD,MAAM,MAAM,kBAAkB,OAAO,mBAAmB;EACxD,IAAI,CAAC,KAAK,cAAc,IAAI,IAAI,IAAI,GAAG,CAAC,CAAC,MAAM,GAC7C,MAAM,IAAI,QACR,oEACA,wBACA,QACF;EAEF,OAAO;CACT;CAEA,MAAc,uBACZ,YAC0D;EAC1D,IAAI,UAAU,KAAK,wBAAwB,UAAU;EACrD,MAAM,aAAa,IAAI,gBAAgB;EACvC,MAAM,QAAQ,iBAAiB,WAAW,MAAM,GAAG,KAAK,QAAQ,OAAO;EACvE,MAAM,WAAW,KAAK,QAAQ,kBAAkB;EAChD,IAAI;GACF,KAAK,IAAI,YAAY,GAAG,aAAa,eAAe,aAAa,GAAG;IAClE,MAAM,WAAW,MAAM,MAAM,SAAS;KACpC,UAAU;KACV,QAAQ,WAAW;IACrB,CAAC;IACD,IAAI,SAAS,UAAU,OAAO,SAAS,SAAS,KAAK;KACnD,MAAM,WAAW,SAAS,QAAQ,IAAI,UAAU;KAChD,IAAI,CAAC,UACH,MAAM,IAAI,QACR,2CACA,iCACA,QACF;KACF,UAAU,KAAK,wBACb,IAAI,IAAI,UAAU,OAAO,CAAC,CAAC,SAAS,CACtC;KACA;IACF;IACA,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,QACR,2CAA2C,SAAS,UACpD,iCACA,QACF;IACF,MAAM,cAAc,SAAS,QAAQ,IAAI,cAAc;IACvD,IAAI,CAAC,aAAa,YAAY,CAAC,CAAC,WAAW,QAAQ,GACjD,MAAM,IAAI,QACR,0DAA0D,eAC1D,8BACA,QACF;IACF,MAAM,gBAAgB,SAAS,QAAQ,IAAI,gBAAgB;IAC3D,IAAI,iBAAiB,OAAO,aAAa,IAAI,UAC3C,MAAM,IAAI,QACR,oCAAoC,SAAS,uBAC7C,2BACA,QACF;IACF,IAAI,CAAC,SAAS,MACZ,MAAM,IAAI,QACR,+CACA,iCACA,QACF;IACF,MAAM,SAAuB,CAAC;IAC9B,IAAI,OAAO;IACX,MAAM,SAAS,SAAS,KAAK,UAAU;IACvC,SAAS;KACP,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;KAC1C,IAAI,MAAM;KACV,QAAQ,MAAM;KACd,IAAI,OAAO,UAAU;MACnB,MAAM,OAAO,OAAO;MACpB,MAAM,IAAI,QACR,oCAAoC,SAAS,uBAC7C,2BACA,QACF;KACF;KACA,OAAO,KAAK,KAAK;IACnB;IACA,OAAO;KACL,MAAM,OAAO,OAAO,OAAO,KAAK,UAAU,OAAO,KAAK,KAAK,CAAC,CAAC;KAC7D,UAAU;KACV,KAAK;IACP;GACF;GACA,MAAM,IAAI,QACR,qDACA,iCACA,QACF;EACF,SAAS,OAAO;GACd,IAAI,WAAW,OAAO,SACpB,MAAM,IAAI,QACR,0CAA0C,KAAK,QAAQ,QAAQ,KAC/D,cACA,UACA,KAAA,GACA,IACF;GACF,MAAM;EACR,UAAU;GACR,aAAa,KAAK;EACpB;CACF;CAEA,UAAkB,SAIhB;EACA,MAAM,UAAU,QAAQ,mBAAmB,CAAC,EAAA,CAAG,KAAK,cAAc;GAChE,IACE,OAAO,UAAU,UAAU,YAC3B,UAAU,MAAM,WAAW,OAAO,GAElC,MAAM,IAAI,gBACR,2FACA,EAAE,UAAU,SAAS,CACvB;GAEF,OAAO,kBAAkB,UAAU,OAAO,wBAAwB;EACpE,CAAC;EACD,MAAM,aAAa,QAAQ,kBAAkB,CAAC;EAC9C,IAAI,WAAW,SAAS,OAAO,SAAS,IACtC,MAAM,IAAI,gBACR,oDACA,EAAE,UAAU,SAAS,CACvB;EACF,MAAM,WAAW,SACf,WACG,QAAQ,cAAc,UAAU,SAAS,IAAI,CAAC,CAC9C,KAAK,cACJ,kBAAkB,UAAU,KAAK,UAAU,KAAK,WAAW,CAC7D;EACJ,MAAM,cAAc,QAAQ,OAAO;EACnC,MAAM,SAAS,QAAQ,OAAO;EAC9B,MAAM,QAAQ,QAAQ,OAAO;EAC7B,IACE,OAAO,SAAS,YAAY,SAAS,MACrC,OAAO,SAAS,MAChB,MAAM,SAAS,IAEf,MAAM,IAAI,gBACR,wEACA,EAAE,UAAU,SAAS,CACvB;EAEF,KAAK,MAAM,aAAa,YACtB,KACG,UAAU,SAAS,WAAW,UAAU,SAAS,YAClD,UAAU,oBAAoB,KAAA,MAC7B,CAAC,OAAO,SAAS,UAAU,eAAe,KACzC,UAAU,kBAAkB,KAC5B,UAAU,kBAAkB,KAE9B,MAAM,IAAI,gBACR,6EACA,EAAE,UAAU,SAAS,CACvB;EAGJ,KAAK,MAAM,CAAC,OAAO,SAAS,CAC1B,CAAC,SAAS,OAAO,GACjB,CAAC,SAAS,OAAO,CACnB,GAQE,IAPkB,WACf,QAAQ,cAAc,UAAU,SAAS,IAAI,CAAC,CAC9C,SAAS,cACR,UAAU,oBAAoB,KAAA,IAC1B,CAAC,IACD,CAAC,UAAU,eAAe,CAE9B,CAAA,CAAU,QAAQ,OAAO,aAAa,QAAQ,UAAU,CAAC,IAAI,IAC/D,MAAM,IAAI,gBACR,UAAU,MAAM,0DAChB,EAAE,UAAU,SAAS,CACvB;EAGJ,OAAO;GAAE,QAAQ,CAAC,GAAG,QAAQ,GAAG,WAAW;GAAG;GAAQ;EAAM;CAC9D;CAEA,MAAc,SAAS,OAAoC;EACzD,MAAM,MAAM,KAAK,IAAI;EACrB,KAAK,MAAM,CAAC,YAAY,WAAW,KAAK,YACtC,IAAI,UAAU,KAAK,KAAK,WAAW,OAAO,UAAU;EAEtD,MAAM,OAAO,KAAK,WAAW,IAAI,KAAK;EACtC,IAAI,QAAQ,MAAM,MAChB,MAAM,IAAI,QACR,8EAA8E,KAAK,MAAM,OAAO,OAAO,GAAI,EAAE,WAC7G,2BACA,QACF;EACF,MAAM,SAAS,MAAM;EACrB,KAAK,WAAW,IAAI,OAAO,MAAM;EACjC,MAAM,UAAU,iBAAiB;GAC/B,IAAI,KAAK,WAAW,IAAI,KAAK,MAAM,QAAQ,KAAK,WAAW,OAAO,KAAK;EACzE,GAAG,oBAAoB;EAGvB,IAAI,OAAO,YAAY,YAAY,WAAW,SAAS,QAAQ,MAAM;EACrE,MAAM,OAAO,MAAM,KAAK,QACtB,aAAa,mBAAmB,KAAK,GACvC;EACA,IAAI,KAAK,WAAW,aAAa;GAI/B,IAAI,CAAC,KAAK,mBAAmB,IAAI,KAAK,GAAG;IACvC,MAAM,cAAc,KAAK,mBAAmB,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;IAC1D,IACE,KAAK,mBAAmB,QAAQ,2BAChC,gBAAgB,KAAA,GAEhB,KAAK,mBAAmB,OAAO,WAAW;GAE9C;GACA,KAAK,mBAAmB,IAAI,OAAO;IAAE;IAAM,YAAY,KAAK,IAAI;GAAE,CAAC;EACrE;EACA,OAAO;CACT;CAEA,kBAA0B,OAAuC;EAC/D,MAAM,WAAW,KAAK,mBAAmB,IAAI,KAAK;EAClD,IAAI,CAAC,UAAU,OAAO,KAAA;EACtB,IAAI,KAAK,IAAI,IAAI,SAAS,cAAc,2BAA2B;GACjE,KAAK,mBAAmB,OAAO,KAAK;GACpC;EACF;EACA,OAAO,SAAS;CAClB;;CAGA,MAAM,yBACJ,SAC6B;EAC7B,MAAM,YAAY,KAAK,IAAI;EAE3B,KADc,QAAQ,SAAS,KAAK,QAAQ,gBAAgB,WAC9C,OACZ,MAAM,IAAI,gBACR,6CAA6C,SAC7C,EAAE,UAAU,SAAS,CACvB;EACF,IAAI,CAAC,QAAQ,QAAQ,KAAK,GACxB,MAAM,IAAI,gBAAgB,sCAAsC,EAC9D,UAAU,SACZ,CAAC;EACH,IAAI,QAAQ,SAAS,KAAA,GACnB,MAAM,IAAI,gBACR,gDACA,EAAE,UAAU,SAAS,CACvB;EACF,MAAM,WAAW,QAAQ,mBAAmB;EAC5C,IAAI,CAAC,OAAO,UAAU,QAAQ,KAAK,WAAW,KAAK,WAAW,IAC5D,MAAM,IAAI,gBACR,+DACA,EAAE,UAAU,SAAS,CACvB;EACF,MAAM,aAAa,QAAQ,cAAc;EACzC,IAAI,eAAe,UAAU,eAAe,QAC1C,MAAM,IAAI,gBAAgB,0CAA0C,EAClE,UAAU,SACZ,CAAC;EACH,MAAM,EAAE,QAAQ,QAAQ,UAAU,KAAK,UAAU,OAAO;EACxD,MAAM,iBACJ,OAAO,UAAU,KACjB,OAAO,UAAU,KACjB,OAAO,WAAW,KAClB,MAAM,WAAW;EACnB,MAAM,iBAAiB,iBACnB,mBACA,OAAO,UAAU,OAAO,UAAU,MAAM,SACtC,uBACA;EACN,MAAM,cAAc,QAAQ,eAAe;EAU3C,IAAI,CAAC;GARH;GACA;GACA;GACA;GACA;GACA;GACA;EAEG,CAAA,CAAgB,SAAS,WAA0C,GACtE,MAAM,IAAI,gBAAgB,kCAAkC,EAC1D,UAAU,SACZ,CAAC;EACH,IAAI,kBAAkB,gBAAgB,YACpC,MAAM,IAAI,gBACR,wDACA,EAAE,UAAU,SAAS,CACvB;EACF,MAAM,QAAqB;GACzB,QAAQ,QAAQ,OAAO,KAAK;GAC5B,iBAAiB;GACjB;GACA,cAAc;GACF;GACZ,gBAAgB,QAAQ,iBAAiB;GACzC,mBAAmB,QAAQ,mBAAmB;GAC9C,GAAI,OAAO,SAAS,EAAE,YAAY,OAAO,IAAI,CAAC;GAC9C,GAAI,OAAO,SAAS,EAAE,YAAY,OAAO,IAAI,CAAC;GAC9C,GAAI,MAAM,SAAS,EAAE,YAAY,MAAM,IAAI,CAAC;EAC9C;EACA,MAAM,UAAU,MAAM,KAAK,QACzB,0BACA;GACE,QAAQ;GACR,MAAM;IAAE,OAAO;IAAO;GAAM;GAC5B,QAAQ,QAAQ;GAChB,SAAS,QAAQ;EACnB,CACF;EACA,IAAI,CAAC,QAAQ,QACX,MAAM,IAAI,QACR,+CACA,2BACA,QACF;EACF,UACE,KAAK,SACL,UACA,4BACA,OACA,KAAA,GACA,WACA;GACE,iBAAiB,OAAO,QAAQ;GAChC;GACA;GACA,GAAG,QAAQ;EACb,CACF;EACA,OAAO;GACL,OAAO,QAAQ;GACf,UAAU;GACV,OAAO;GACP,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;GAClC,KACE,QAAQ,YAAY,KAAA,IAChB,KAAA,IACA;IAAE,SAAS,QAAQ;IAAS,eAAe;GAAW;EAC9D;CACF;;CAGA,MAAM,sBACJ,QACsC;EACtC,OAAO,KAAK,QAAQ,MAAM,KAAK,SAAS,OAAO,KAAK,CAAC;CACvD;;CAGA,MAAM,2BACJ,QACgC;EAChC,MAAM,OACJ,KAAK,kBAAkB,OAAO,KAAK,KAClC,MAAM,KAAK,SAAS,OAAO,KAAK;EACnC,MAAM,SAAS,KAAK,QAAQ,IAAI;EAChC,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,QAAQ,KACnD,MAAM,IAAI,QACR,cAAc,OAAO,MAAM,8BAA8B,OAAO,OAAO,IACvE,uBACA,UACA,OAAO,KACT;EACF,IAAI;GACF,MAAM,aAAa,MAAM,KAAK,uBAAuB,OAAO,OAAO,GAAG;GACtE,OAAO;IAAE,GAAG,OAAO;IAAQ,GAAG;GAAW;EAC3C,UAAU;GAIR,KAAK,mBAAmB,OAAO,OAAO,KAAK;EAC7C;CACF;;CAGA,MAAM,yBAAyB,SAA4C;EACzE,MAAM,IAAI,QACR,+DACA,mBACA,QACF;CACF;;CAGA,MAAM,gCAAkD;EACtD,MAAM,WAAW,MAAM,KAAK,WAC1B,kCAAkC,OAAO,WAAW,KACpD,EAAE,eAAe,KAAK,CACxB;EACA,OAAO,SAAS,MAAM,SAAS,WAAW;CAC5C;CAEA,QAAgB,MAA+C;EAC7D,MAAM,UAAU,iBAAiB,IAAI;EACrC,IAAI,KAAK,WAAW,UAClB,OAAO;GAAE,QAAQ;GAAU,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAAG;EAC7D,IAAI,KAAK,WAAW,cAClB,OAAO;GAAE,QAAQ;GAAW,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAAG;EAC9D,IAAI,KAAK,WAAW,UAClB,OAAO;GACL,QAAQ;GACR,OAAO,KAAK,iBAAiB;GAC7B,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAC/B;EACF,IAAI,KAAK,WAAW,aAClB,OAAO;GACL,QAAQ;GACR,WAAW,KAAK;GAChB,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAC/B;EACF,MAAM,MAAM,KAAK,MAAM,UAAU;EACjC,IAAI,CAAC,KACH,OAAO;GACL,QAAQ;GACR,OAAO;GACP,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAC/B;EAeF,OAAO;GAAE,QAAQ;GAAa,QAAA;IAb5B,KAAK,KAAK,wBAAwB,GAAG;IACrC,UAAU;IACV,GAAI,KAAK,MAAM,mBACX,EAAE,WAAW,KAAK,KAAK,iBAAiB,IACxC,CAAC;IACL,GAAI,KAAK,MAAM,iBACX,EACE,cAAc,KAAK,wBACjB,KAAK,KAAK,cACZ,EACF,IACA,CAAC;GAEuB;GAAQ,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAAG;CACxE;;CAGA,MAAM,YAAgC;EACpC,OAAO,CACL;GACE,IAAI;GACJ,MAAM;GACN,aAAa;GACb,eAAe;GACf,cAAc,CAAC,kBAAkB;GACjC,mBAAmB;GACnB,gBAAgB;EAClB,CACF;CACF;;CAEA,MAAM,kBAA2C;EAC/C,OAAO;GACL,MAAM;GACN,YAAY;GACZ,YAAY;GACZ,WAAW;GACX,WAAW;GACX,QAAQ;GACR,YAAY;GACZ,iBAAiB;GACjB,iBAAiB;GACjB,iBAAiB;GACjB,KAAK;GACL,cAAc;GACd,aAAa;GACb,kBAAkB;GAClB,qBAAqB;IACnB;IACA;IACA;GACF;EACF;CACF;CAEA,cAA6B;EAC3B,MAAM,IAAI,QACR,sEACA,mBACA,QACF;CACF;oBACmB,MAAM,KACvB,WACA,UACqB;EACrB,OAAO,KAAK,YAAY;CAC1B;oBACmB,MAAM,SACvB,SACA,UACqB;EACrB,OAAO,KAAK,YAAY;CAC1B;oBACmB,MAAM,QACvB,OACA,UACiB;EACjB,OAAO,KAAK,YAAY;CAC1B;oBACmB,MAAM,MACvB,OACA,UAC4B;EAC5B,OAAO,KAAK,YAAY;CAC1B;oBACmB,MAAM,WACvB,QACA,UAC4B;EAC5B,OAAO,KAAK,YAAY;CAC1B;oBACmB,MAAM,cACvB,QACA,SACA,UACiB;EACjB,OAAO,KAAK,YAAY;CAC1B;oBACmB,MAAM,cACvB,SACA,UACkC;EAClC,OAAO,KAAK,YAAY;CAC1B;oBACmB,OACjB,WACA,UACuB;EACvB,MAAM,QAAQ,IAAI,QAChB,sEACA,mBACA,QACF;EACA,OAAO,GACJ,OAAO,uBAAuB,EAAE,YAAY,QAAQ,OAAO,KAAK,EAAE,GACrE;CACF;oBACmB,MAAM,YAAY,OAAgC;EACnE,OAAO,KAAK,YAAY;CAC1B;oBACmB,MAAM,iBACvB,OACA,UACsB;EACtB,OAAO,KAAK,YAAY;CAC1B;oBACmB,aACjB,OACA,UACuB;EACvB,MAAM,QAAQ,IAAI,QAChB,qEACA,mBACA,QACF;EACA,OAAO,GACJ,OAAO,uBAAuB,EAAE,YAAY,QAAQ,OAAO,KAAK,EAAE,GACrE;CACF;oBACmB,MAAM,WACvB,UACgB;EAChB,OAAO,KAAK,YAAY;CAC1B;oBACmB,MAAM,YACvB,UACgB;EAChB,OAAO,KAAK,YAAY;CAC1B;oBACmB,MAAM,UACvB,UACkB;EAClB,OAAO,KAAK,YAAY;CAC1B;AACF"}
|
|
1
|
+
{"version":3,"file":"seevio-Dt8kWgpg.js","names":[],"sources":["../../src/shared/providers/seevio.ts"],"sourcesContent":["/**\n * Seevio's native asynchronous Seedance 2.5 video-generation provider.\n *\n * Seevio is deliberately implemented as its own raw-HTTP adapter rather than\n * being shaped to ModelArk. Both services can run Seedance, but their task,\n * billing, and media contracts differ.\n *\n * @see https://seevio.ai/api-docs\n */\n\nimport { ValidationError } from '@happyvertical/utils';\nimport { rateLimitErrorFrom } from '../rate-limit';\nimport { normalizeBaseAIOptions } from '../safety';\nimport type {\n AICapabilities,\n AIInterface,\n AIMessage,\n AIModel,\n AIResponse,\n ChatOptions,\n CompletionOptions,\n EmbeddingOptions,\n EmbeddingResponse,\n ImageDescriptionOptions,\n ImageEmbeddingOptions,\n ImageGenerationOptions,\n ImageGenerationResponse,\n MessageOptions,\n SeevioOptions,\n TTSOptions,\n TTSResponse,\n VideoGenerationBillingMetadata,\n VideoGenerationJob,\n VideoGenerationOptions,\n VideoGenerationReferenceMedia,\n VideoGenerationResult,\n VideoGenerationStatusResult,\n Voice,\n VoiceCloneOptions,\n VoiceDesignOptions,\n VoiceListOptions,\n} from '../types';\nimport { AIError, AuthenticationError } from '../types';\nimport { emitUsage } from './usage';\n\nconst DEFAULT_BASE_URL = 'https://api.seevio.ai';\nconst DEFAULT_RESULT_ORIGIN = 'https://cdn.seevio.ai';\nconst MODEL = 'seedance-2-5';\nconst MIN_POLL_INTERVAL_MS = 10_000;\nconst MAX_REDIRECTS = 5;\nconst DEFAULT_MAX_RESULT_BYTES = 200 * 1024 * 1024;\nconst COMPLETED_SNAPSHOT_TTL_MS = MIN_POLL_INTERVAL_MS;\nconst MAX_COMPLETED_SNAPSHOTS = 128;\n\ninterface SeevioTask {\n id: string;\n status: string;\n model?: string;\n billing_status?: string;\n credits?: number;\n failed_reason?: string | null;\n data?: {\n results?: string[];\n video_expires_at?: string;\n last_frame_url?: string | null;\n processing_time?: number;\n };\n}\n\ninterface SeevioErrorResponse {\n error?: {\n code?: string;\n message?: string;\n required?: number;\n available?: number;\n };\n}\n\ninterface SeevioCreateResponse {\n taskId: string;\n credits?: number;\n}\n\ninterface SeevioInput {\n prompt: string;\n generation_type: 'text-to-video' | 'image-to-video' | 'reference-to-video';\n image_urls?: string[];\n video_urls?: string[];\n audio_urls?: string[];\n duration: number;\n aspect_ratio: 'adaptive' | '16:9' | '9:16' | '1:1' | '4:3' | '3:4' | '21:9';\n resolution: '480p' | '720p';\n generate_audio: boolean;\n return_last_frame: boolean;\n}\n\nfunction ensureOptions(options: SeevioOptions): SeevioOptions {\n const baseUrl = (options.baseUrl || DEFAULT_BASE_URL)\n .trim()\n .replace(/\\/+$/, '');\n const parsedBaseUrl = new URL(baseUrl);\n if (parsedBaseUrl.protocol !== 'https:') {\n throw new ValidationError('seevio baseUrl must use HTTPS', {\n provider: 'seevio',\n });\n }\n const origins = options.resultUrlOrigins?.length\n ? options.resultUrlOrigins\n : [DEFAULT_RESULT_ORIGIN];\n for (const origin of origins) {\n const parsed = new URL(origin);\n if (\n parsed.protocol !== 'https:' ||\n parsed.origin !== origin.replace(/\\/+$/, '')\n ) {\n throw new ValidationError(\n 'seevio resultUrlOrigins must contain HTTPS origins only',\n {\n provider: 'seevio',\n },\n );\n }\n }\n if (\n options.maxResultBytes !== undefined &&\n (!Number.isSafeInteger(options.maxResultBytes) ||\n options.maxResultBytes <= 0)\n ) {\n throw new ValidationError(\n 'seevio maxResultBytes must be a positive safe integer',\n { provider: 'seevio' },\n );\n }\n return {\n ...options,\n baseUrl,\n resultUrlOrigins: origins.map((origin) => origin.replace(/\\/+$/, '')),\n };\n}\n\nfunction validatedHttpsUrl(value: string, label: string): string {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new ValidationError(`${label} must be a valid public HTTPS URL`, {\n provider: 'seevio',\n });\n }\n if (url.protocol !== 'https:') {\n throw new ValidationError(`${label} must use HTTPS`, {\n provider: 'seevio',\n });\n }\n const host = url.hostname.toLowerCase();\n if (\n url.username ||\n url.password ||\n host === 'localhost' ||\n host.endsWith('.localhost') ||\n host.includes(':') ||\n /^\\d{1,3}(?:\\.\\d{1,3}){3}$/.test(host)\n ) {\n throw new ValidationError(\n `${label} must not use credentials, localhost, or an IP-literal host`,\n { provider: 'seevio' },\n );\n }\n return url.toString();\n}\n\nfunction normalizeBilling(\n task: SeevioTask,\n): VideoGenerationBillingMetadata | undefined {\n if (task.credits === undefined && !task.billing_status) return undefined;\n const normalized = [\n 'reserved',\n 'charged',\n 'refunded',\n 'refund_failed',\n ].includes(task.billing_status || '')\n ? (task.billing_status as VideoGenerationBillingMetadata['status'])\n : undefined;\n return {\n ...(task.credits !== undefined ? { credits: task.credits } : {}),\n ...(normalized ? { status: normalized } : {}),\n ...(!normalized && task.billing_status\n ? { rawStatus: task.billing_status }\n : {}),\n };\n}\n\n/** Native Seevio adapter for the pinned `seedance-2-5` model. */\nexport class SeevioProvider implements AIInterface {\n private readonly options: SeevioOptions;\n private readonly resultOrigins: Set<string>;\n private readonly nextPollAt = new Map<string, number>();\n private readonly completedSnapshots = new Map<\n string,\n { task: SeevioTask; recordedAt: number }\n >();\n\n /** Creates a Seevio task client using the documented HTTPS API root. */\n constructor(options: SeevioOptions) {\n this.options = normalizeBaseAIOptions(ensureOptions(options));\n this.resultOrigins = new Set(this.options.resultUrlOrigins);\n }\n\n private async request<T>(\n path: string,\n options: {\n method?: 'GET' | 'POST';\n body?: unknown;\n signal?: AbortSignal;\n timeout?: number;\n allowNotFound?: boolean;\n } = {},\n ): Promise<T> {\n const response = await this.rawRequest(path, options);\n const text = await response.text();\n return text ? (JSON.parse(text) as T) : ({} as T);\n }\n\n private async rawRequest(\n path: string,\n options: {\n method?: 'GET' | 'POST';\n body?: unknown;\n signal?: AbortSignal;\n timeout?: number;\n allowNotFound?: boolean;\n } = {},\n ): Promise<Response> {\n if (options.signal?.aborted)\n throw new AIError(\n 'Seevio request aborted by caller',\n 'AI_ABORTED',\n 'seevio',\n );\n const controller = new AbortController();\n const timeoutMs = options.timeout ?? this.options.timeout;\n let timedOut = false;\n const timer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, timeoutMs);\n const abort = () => controller.abort(options.signal?.reason);\n options.signal?.addEventListener('abort', abort, { once: true });\n // An abort may occur between the preflight check and listener\n // registration. Re-check after registration so that race still reaches\n // the fetch signal rather than allowing a paid request to continue.\n if (options.signal?.aborted) abort();\n try {\n let response: Response;\n try {\n response = await fetch(`${this.options.baseUrl}${path}`, {\n method: options.method || 'GET',\n headers: {\n Authorization: `Bearer ${this.options.apiKey ?? ''}`,\n ...(options.body !== undefined\n ? { 'Content-Type': 'application/json' }\n : {}),\n ...(this.options.headers || {}),\n },\n body:\n options.body === undefined\n ? undefined\n : JSON.stringify(options.body),\n signal: controller.signal,\n });\n } catch (error) {\n if (timedOut)\n throw new AIError(\n `Seevio request to ${path} timed out after ${timeoutMs}ms`,\n 'AI_TIMEOUT',\n 'seevio',\n undefined,\n options.method !== 'POST',\n );\n if (options.signal?.aborted)\n throw new AIError(\n 'Seevio request aborted by caller',\n 'AI_ABORTED',\n 'seevio',\n );\n throw new AIError(\n `Network error calling Seevio ${path}: ${error instanceof Error ? error.message : String(error)}`,\n 'NETWORK_ERROR',\n 'seevio',\n undefined,\n options.method !== 'POST',\n );\n }\n if (response.status === 404 && options.allowNotFound) return response;\n if (response.ok) return response;\n const errorText = await response.text();\n let payload: SeevioErrorResponse = {};\n try {\n payload = JSON.parse(errorText) as SeevioErrorResponse;\n } catch {\n /* preserve status fallback below */\n }\n const message =\n payload.error?.message ||\n `Seevio request to ${path} failed with HTTP ${response.status}`;\n if (response.status === 401) throw new AuthenticationError('seevio');\n if (response.status === 403)\n throw new AIError(message, 'SEEVIO_FORBIDDEN', 'seevio');\n if (response.status === 402)\n throw new AIError(message, 'INSUFFICIENT_CREDITS', 'seevio');\n if (response.status === 429)\n throw rateLimitErrorFrom(\n 'seevio',\n { headers: response.headers },\n { reason: payload.error?.message },\n );\n throw new AIError(\n message,\n 'SEEVIO_REQUEST_FAILED',\n 'seevio',\n undefined,\n response.status >= 500 && options.method !== 'POST',\n );\n } finally {\n clearTimeout(timer);\n options.signal?.removeEventListener('abort', abort);\n }\n }\n\n private assertReviewedResultUrl(value: string): string {\n const url = validatedHttpsUrl(value, 'Seevio result URL');\n if (!this.resultOrigins.has(new URL(url).origin)) {\n throw new AIError(\n 'Seevio returned a result URL outside configured reviewed origins',\n 'UNTRUSTED_RESULT_URL',\n 'seevio',\n );\n }\n return url;\n }\n\n private async downloadReviewedResult(\n initialUrl: string,\n ): Promise<{ data: Buffer; mimeType: string; url: string }> {\n let current = this.assertReviewedResultUrl(initialUrl);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.options.timeout);\n const maxBytes = this.options.maxResultBytes ?? DEFAULT_MAX_RESULT_BYTES;\n try {\n for (let redirects = 0; redirects <= MAX_REDIRECTS; redirects += 1) {\n const response = await fetch(current, {\n redirect: 'manual',\n signal: controller.signal,\n });\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get('location');\n if (!location)\n throw new AIError(\n 'Seevio result redirect omitted Location',\n 'SEEVIO_RESULT_DOWNLOAD_FAILED',\n 'seevio',\n );\n current = this.assertReviewedResultUrl(\n new URL(location, current).toString(),\n );\n continue;\n }\n if (!response.ok)\n throw new AIError(\n `Seevio result download failed with HTTP ${response.status}`,\n 'SEEVIO_RESULT_DOWNLOAD_FAILED',\n 'seevio',\n );\n const contentType = response.headers.get('content-type');\n if (!contentType?.toLowerCase().startsWith('video/'))\n throw new AIError(\n `Seevio result download returned non-video content type ${contentType}`,\n 'SEEVIO_RESULT_MIME_INVALID',\n 'seevio',\n );\n const contentLength = response.headers.get('content-length');\n if (contentLength && Number(contentLength) > maxBytes)\n throw new AIError(\n `Seevio result exceeds configured ${maxBytes}-byte download limit`,\n 'SEEVIO_RESULT_TOO_LARGE',\n 'seevio',\n );\n if (!response.body)\n throw new AIError(\n 'Seevio result download had no response body',\n 'SEEVIO_RESULT_DOWNLOAD_FAILED',\n 'seevio',\n );\n const chunks: Uint8Array[] = [];\n let size = 0;\n const reader = response.body.getReader();\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n size += value.byteLength;\n if (size > maxBytes) {\n await reader.cancel();\n throw new AIError(\n `Seevio result exceeds configured ${maxBytes}-byte download limit`,\n 'SEEVIO_RESULT_TOO_LARGE',\n 'seevio',\n );\n }\n chunks.push(value);\n }\n return {\n data: Buffer.concat(chunks.map((chunk) => Buffer.from(chunk))),\n mimeType: contentType,\n url: current,\n };\n }\n throw new AIError(\n 'Seevio result exceeded the maximum redirect count',\n 'SEEVIO_RESULT_DOWNLOAD_FAILED',\n 'seevio',\n );\n } catch (error) {\n if (controller.signal.aborted)\n throw new AIError(\n `Seevio result download timed out after ${this.options.timeout}ms`,\n 'AI_TIMEOUT',\n 'seevio',\n undefined,\n true,\n );\n throw error;\n } finally {\n clearTimeout(timer);\n }\n }\n\n private mediaUrls(options: VideoGenerationOptions): {\n images: string[];\n videos: string[];\n audio: string[];\n } {\n const images = (options.referenceImages ?? []).map((reference) => {\n if (\n typeof reference.image !== 'string' ||\n reference.image.startsWith('data:')\n ) {\n throw new ValidationError(\n 'Seevio referenceImages must be public HTTPS URLs; it cannot retrieve bytes or data URLs',\n { provider: 'seevio' },\n );\n }\n return validatedHttpsUrl(reference.image, 'Seevio reference image');\n });\n const references = options.referenceMedia ?? [];\n if (references.length + images.length > 50)\n throw new ValidationError(\n 'Seevio accepts at most 50 total reference assets',\n { provider: 'seevio' },\n );\n const collect = (type: VideoGenerationReferenceMedia['type']) =>\n references\n .filter((reference) => reference.type === type)\n .map((reference) =>\n validatedHttpsUrl(reference.url, `Seevio ${type} reference`),\n );\n const mediaImages = collect('image');\n const videos = collect('video');\n const audio = collect('audio');\n if (\n images.length + mediaImages.length > 30 ||\n videos.length > 10 ||\n audio.length > 10\n ) {\n throw new ValidationError(\n 'Seevio reference limits are 30 images, 10 videos, and 10 audio files',\n { provider: 'seevio' },\n );\n }\n for (const reference of references) {\n if (\n (reference.type === 'video' || reference.type === 'audio') &&\n reference.durationSeconds !== undefined &&\n (!Number.isFinite(reference.durationSeconds) ||\n reference.durationSeconds < 2 ||\n reference.durationSeconds > 30)\n ) {\n throw new ValidationError(\n 'Seevio video/audio reference durations must be 2-30 seconds when supplied',\n { provider: 'seevio' },\n );\n }\n }\n for (const [label, type] of [\n ['video', 'video'],\n ['audio', 'audio'],\n ] as const) {\n const durations = references\n .filter((reference) => reference.type === type)\n .flatMap((reference) =>\n reference.durationSeconds === undefined\n ? []\n : [reference.durationSeconds],\n );\n if (durations.reduce((total, duration) => total + duration, 0) > 30) {\n throw new ValidationError(\n `Seevio ${label} reference durations must total no more than 30 seconds`,\n { provider: 'seevio' },\n );\n }\n }\n return { images: [...images, ...mediaImages], videos, audio };\n }\n\n private async pollTask(jobId: string): Promise<SeevioTask> {\n const now = Date.now();\n for (const [knownJobId, expiry] of this.nextPollAt) {\n if (expiry <= now) this.nextPollAt.delete(knownJobId);\n }\n const next = this.nextPollAt.get(jobId);\n if (next && now < next)\n throw new AIError(\n `Seevio tasks may be polled no more than once every 10 seconds; retry after ${Math.ceil((next - now) / 1000)} seconds`,\n 'VIDEO_POLL_TOO_FREQUENT',\n 'seevio',\n );\n const expiry = now + MIN_POLL_INTERVAL_MS;\n this.nextPollAt.set(jobId, expiry);\n const cleanup = setTimeout(() => {\n if (this.nextPollAt.get(jobId) === expiry) this.nextPollAt.delete(jobId);\n }, MIN_POLL_INTERVAL_MS);\n // Let idle Node workers exit rather than keeping the process alive for a\n // cadence cleanup timer. Browser timer handles do not expose `unref`.\n if (typeof cleanup === 'object' && 'unref' in cleanup) cleanup.unref();\n const task = await this.request<SeevioTask>(\n `/v1/tasks/${encodeURIComponent(jobId)}`,\n );\n if (task.status === 'completed') {\n // This is a bounded, short-lived handoff cache, not task history. A\n // completed task can be fetched without another status request only\n // within the provider's ten-second cadence window.\n if (!this.completedSnapshots.has(jobId)) {\n const oldestJobId = this.completedSnapshots.keys().next().value;\n if (\n this.completedSnapshots.size >= MAX_COMPLETED_SNAPSHOTS &&\n oldestJobId !== undefined\n ) {\n this.completedSnapshots.delete(oldestJobId);\n }\n }\n this.completedSnapshots.set(jobId, { task, recordedAt: Date.now() });\n }\n return task;\n }\n\n private completedSnapshot(jobId: string): SeevioTask | undefined {\n const snapshot = this.completedSnapshots.get(jobId);\n if (!snapshot) return undefined;\n if (Date.now() - snapshot.recordedAt >= COMPLETED_SNAPSHOT_TTL_MS) {\n this.completedSnapshots.delete(jobId);\n return undefined;\n }\n return snapshot.task;\n }\n\n /** Submits one unambiguous, non-retried Seedance 2.5 task. */\n async submitVideoGenerationJob(\n options: VideoGenerationOptions,\n ): Promise<VideoGenerationJob> {\n const startTime = Date.now();\n const model = options.model || this.options.defaultModel || MODEL;\n if (model !== MODEL)\n throw new ValidationError(\n `Seevio provider supports the pinned model ${MODEL}`,\n { provider: 'seevio' },\n );\n if (!options.prompt?.trim())\n throw new ValidationError('Seevio requires a non-empty prompt', {\n provider: 'seevio',\n });\n if (options.seed !== undefined)\n throw new ValidationError(\n 'seed is not supported by Seevio Seedance 2.5',\n { provider: 'seevio' },\n );\n const duration = options.durationSeconds ?? 5;\n if (!Number.isInteger(duration) || duration < 4 || duration > 30)\n throw new ValidationError(\n 'Seevio durationSeconds must be an integer from 4 through 30',\n { provider: 'seevio' },\n );\n const resolution = options.resolution ?? '720p';\n if (resolution !== '480p' && resolution !== '720p')\n throw new ValidationError('Seevio resolution must be 480p or 720p', {\n provider: 'seevio',\n });\n const { images, videos, audio } = this.mediaUrls(options);\n const isImageToVideo =\n images.length >= 1 &&\n images.length <= 2 &&\n videos.length === 0 &&\n audio.length === 0;\n const generationType = isImageToVideo\n ? 'image-to-video'\n : images.length || videos.length || audio.length\n ? 'reference-to-video'\n : 'text-to-video';\n const aspectRatio = options.aspectRatio ?? 'adaptive';\n const supportedRatios: SeevioInput['aspect_ratio'][] = [\n 'adaptive',\n '16:9',\n '9:16',\n '1:1',\n '4:3',\n '3:4',\n '21:9',\n ];\n if (!supportedRatios.includes(aspectRatio as SeevioInput['aspect_ratio']))\n throw new ValidationError('Unsupported Seevio aspectRatio', {\n provider: 'seevio',\n });\n if (isImageToVideo && aspectRatio !== 'adaptive')\n throw new ValidationError(\n 'Seevio image-to-video requires aspectRatio: adaptive',\n { provider: 'seevio' },\n );\n const input: SeevioInput = {\n prompt: options.prompt.trim(),\n generation_type: generationType,\n duration,\n aspect_ratio: aspectRatio as SeevioInput['aspect_ratio'],\n resolution: resolution as '480p' | '720p',\n generate_audio: options.generateAudio ?? true,\n return_last_frame: options.returnLastFrame ?? false,\n ...(images.length ? { image_urls: images } : {}),\n ...(videos.length ? { video_urls: videos } : {}),\n ...(audio.length ? { audio_urls: audio } : {}),\n };\n const created = await this.request<SeevioCreateResponse>(\n '/v1/videos/generations',\n {\n method: 'POST',\n body: { model: MODEL, input },\n signal: options.signal,\n timeout: options.timeout,\n },\n );\n if (!created.taskId)\n throw new AIError(\n 'Seevio accepted submission without a taskId',\n 'SEEVIO_INVALID_RESPONSE',\n 'seevio',\n );\n emitUsage(\n this.options,\n 'seevio',\n 'submitVideoGenerationJob',\n MODEL,\n undefined,\n startTime,\n {\n durationSeconds: String(duration),\n resolution,\n aspectRatio,\n ...options.usageTags,\n },\n );\n return {\n jobId: created.taskId,\n provider: 'seevio',\n model: MODEL,\n createdAt: new Date().toISOString(),\n raw:\n created.credits === undefined\n ? undefined\n : { credits: created.credits, billingStatus: 'reserved' },\n };\n }\n\n /** Returns a normalized task snapshot, enforcing Seevio's 10-second poll cadence. */\n async getVideoGenerationJob(\n handle: VideoGenerationJob,\n ): Promise<VideoGenerationStatusResult> {\n return this.mapTask(await this.pollTask(handle.jobId));\n }\n\n /** Downloads a completed video while validating the initial URL and every redirect. */\n async fetchVideoGenerationResult(\n handle: VideoGenerationJob,\n ): Promise<VideoGenerationResult> {\n const task =\n this.completedSnapshot(handle.jobId) ??\n (await this.pollTask(handle.jobId));\n const status = this.mapTask(task);\n if (status.status !== 'succeeded' || !status.result?.url)\n throw new AIError(\n `Seevio job ${handle.jobId} has not succeeded (status: ${status.status})`,\n 'VIDEO_JOB_NOT_READY',\n 'seevio',\n handle.model,\n );\n try {\n const downloaded = await this.downloadReviewedResult(status.result.url);\n return { ...status.result, ...downloaded };\n } finally {\n // The handoff cache exists only to make an immediate status → fetch\n // flow cadence-safe. Do not retain expired signed URLs after any fetch\n // attempt; a later fetch will authenticate a fresh task snapshot.\n this.completedSnapshots.delete(handle.jobId);\n }\n }\n\n /** Reports cancellation as unsupported because Seevio documents no cancellation endpoint. */\n async cancelVideoGenerationJob(_handle: VideoGenerationJob): Promise<void> {\n throw new AIError(\n 'Seevio does not document a video-task cancellation endpoint',\n 'NOT_IMPLEMENTED',\n 'seevio',\n );\n }\n\n /** Performs an unbilled random task-id probe; an authenticated 404 is valid access. */\n async validateVideoGenerationAccess(): Promise<boolean> {\n const response = await this.rawRequest(\n `/v1/tasks/have-ai-access-probe-${crypto.randomUUID()}`,\n { allowNotFound: true },\n );\n return response.ok || response.status === 404;\n }\n\n private mapTask(task: SeevioTask): VideoGenerationStatusResult {\n const billing = normalizeBilling(task);\n if (task.status === 'queued')\n return { status: 'queued', ...(billing ? { billing } : {}) };\n if (task.status === 'generating')\n return { status: 'running', ...(billing ? { billing } : {}) };\n if (task.status === 'failed')\n return {\n status: 'failed',\n error: task.failed_reason || 'Seevio video generation failed',\n ...(billing ? { billing } : {}),\n };\n if (task.status !== 'completed')\n return {\n status: 'running',\n rawStatus: task.status,\n ...(billing ? { billing } : {}),\n };\n const url = task.data?.results?.[0];\n if (!url)\n return {\n status: 'failed',\n error: 'Seevio reported completion with no result URL',\n ...(billing ? { billing } : {}),\n };\n const result: VideoGenerationResult = {\n url: this.assertReviewedResultUrl(url),\n mimeType: 'video/mp4',\n ...(task.data?.video_expires_at\n ? { expiresAt: task.data.video_expires_at }\n : {}),\n ...(task.data?.last_frame_url\n ? {\n lastFrameUrl: this.assertReviewedResultUrl(\n task.data.last_frame_url,\n ),\n }\n : {}),\n };\n return { status: 'succeeded', result, ...(billing ? { billing } : {}) };\n }\n\n /** Lists the single intentional Seevio model selection. */\n async getModels(): Promise<AIModel[]> {\n return [\n {\n id: MODEL,\n name: 'Seedance 2.5',\n description: 'Seevio Seedance 2.5 async video generation',\n contextLength: 0,\n capabilities: ['video_generation'],\n supportsFunctions: false,\n supportsVision: true,\n },\n ];\n }\n /** Reports Seevio's video-only capability surface. */\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 ],\n };\n }\n\n private unsupported(): never {\n throw new AIError(\n 'This operation is not supported by the Seevio video-only provider.',\n 'NOT_IMPLEMENTED',\n 'seevio',\n );\n }\n /** @inheritdoc */ async chat(\n _messages: AIMessage[],\n _options?: ChatOptions,\n ): Promise<AIResponse> {\n return this.unsupported();\n }\n /** @inheritdoc */ async complete(\n _prompt: string,\n _options?: CompletionOptions,\n ): Promise<AIResponse> {\n return this.unsupported();\n }\n /** @inheritdoc */ async message(\n _text: string,\n _options?: MessageOptions,\n ): Promise<string> {\n return this.unsupported();\n }\n /** @inheritdoc */ async embed(\n _text: string | string[],\n _options?: EmbeddingOptions,\n ): Promise<EmbeddingResponse> {\n return this.unsupported();\n }\n /** @inheritdoc */ async embedImage(\n _image: string | Buffer,\n _options?: ImageEmbeddingOptions,\n ): Promise<EmbeddingResponse> {\n return this.unsupported();\n }\n /** @inheritdoc */ async describeImage(\n _image: string | Buffer,\n _prompt?: string,\n _options?: ImageDescriptionOptions,\n ): Promise<string> {\n return this.unsupported();\n }\n /** @inheritdoc */ async generateImage(\n _prompt: string,\n _options?: ImageGenerationOptions,\n ): Promise<ImageGenerationResponse> {\n return this.unsupported();\n }\n /** @inheritdoc */ stream(\n _messages: AIMessage[],\n _options?: ChatOptions,\n ): AsyncIterable<string> {\n const error = new AIError(\n 'Chat streaming is not supported by the Seevio video-only provider.',\n 'NOT_IMPLEMENTED',\n 'seevio',\n );\n return {\n [Symbol.asyncIterator]: () => ({ next: () => Promise.reject(error) }),\n };\n }\n /** @inheritdoc */ async countTokens(_text: string): Promise<number> {\n return this.unsupported();\n }\n /** @inheritdoc */ async synthesizeSpeech(\n _text: string,\n _options?: TTSOptions,\n ): Promise<TTSResponse> {\n return this.unsupported();\n }\n /** @inheritdoc */ streamSpeech(\n _text: string,\n _options?: TTSOptions,\n ): AsyncIterable<Buffer> {\n const error = new AIError(\n 'TTS streaming is not supported by the Seevio video-only provider.',\n 'NOT_IMPLEMENTED',\n 'seevio',\n );\n return {\n [Symbol.asyncIterator]: () => ({ next: () => Promise.reject(error) }),\n };\n }\n /** @inheritdoc */ async cloneVoice(\n _options: VoiceCloneOptions,\n ): Promise<Voice> {\n return this.unsupported();\n }\n /** @inheritdoc */ async designVoice(\n _options: VoiceDesignOptions,\n ): Promise<Voice> {\n return this.unsupported();\n }\n /** @inheritdoc */ async getVoices(\n _options?: VoiceListOptions,\n ): Promise<Voice[]> {\n return this.unsupported();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,160 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import {
|
|
3
|
-
import { n as rateLimitErrorFrom, t as createRateLimitedAI } from "./rate-limit-
|
|
4
|
-
import { t as
|
|
2
|
+
import { _ as ModelNotFoundError, c as normalizeChatOptions, d as AIError, f as AI_PROVIDER_TYPES, h as ContextLengthError, l as normalizeImageGenerationOptions, m as ContentFilterError, n as AI_ATTEMPT_RECORDER, o as createObservedAI, p as AuthenticationError, s as normalizeBaseAIOptions, t as emitUsage, u as prepareRequestControls } from "./usage-yIT5lpQm.js";
|
|
3
|
+
import { n as rateLimitErrorFrom, t as createRateLimitedAI } from "./rate-limit-B3Kz6v4j.js";
|
|
4
|
+
import { n as reportReasoning, t as readReasoningText } from "./reasoning-DCCN0I3I.js";
|
|
5
|
+
import { t as createContinuingAI } from "./continuation-B-b0Iqgj.js";
|
|
5
6
|
import { ApiError, ValidationError, loadEnvConfig } from "@happyvertical/utils";
|
|
6
7
|
import OpenAI from "openai";
|
|
8
|
+
//#region src/shared/chat-template-kwargs.ts
|
|
9
|
+
/**
|
|
10
|
+
* Validation for the per-request `chatTemplateKwargs` chat option.
|
|
11
|
+
*
|
|
12
|
+
* The value is forwarded as the `chat_template_kwargs` request field that
|
|
13
|
+
* vLLM (and SGLang) apply to the model's chat template, for example
|
|
14
|
+
* `{ enable_thinking: false }`. It is caller data, so it is bounded and copied
|
|
15
|
+
* here in one pass: every property is read exactly once, and the value that is
|
|
16
|
+
* validated is the value that is sent, so a getter cannot answer differently
|
|
17
|
+
* between validation and transport. Error messages locate the offending entry
|
|
18
|
+
* by position (`{#2}` is an object's third key, `[0]` an array's first item)
|
|
19
|
+
* and never include a key name or a value.
|
|
20
|
+
*/
|
|
21
|
+
/** Largest accepted `chatTemplateKwargs`, measured as serialized JSON bytes. */
|
|
22
|
+
var MAX_CHAT_TEMPLATE_KWARGS_BYTES = 8192;
|
|
23
|
+
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set([
|
|
24
|
+
"__proto__",
|
|
25
|
+
"constructor",
|
|
26
|
+
"prototype"
|
|
27
|
+
]);
|
|
28
|
+
var ROOT = "chatTemplateKwargs";
|
|
29
|
+
function isPlainObject(value) {
|
|
30
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
31
|
+
const proto = Object.getPrototypeOf(value);
|
|
32
|
+
return proto === Object.prototype || proto === null;
|
|
33
|
+
}
|
|
34
|
+
function invalid(reason, provider, model) {
|
|
35
|
+
return new AIError(`chatTemplateKwargs ${reason}`, "AI_INVALID_CHAT_TEMPLATE_KWARGS", provider, model);
|
|
36
|
+
}
|
|
37
|
+
function spend(budget, bytes) {
|
|
38
|
+
budget.bytes += bytes;
|
|
39
|
+
if (budget.bytes > 8192) throw tooLarge(budget.provider, budget.model);
|
|
40
|
+
}
|
|
41
|
+
function tooLarge(provider, model) {
|
|
42
|
+
return invalid(`exceeds ${MAX_CHAT_TEMPLATE_KWARGS_BYTES} bytes of JSON`, provider, model);
|
|
43
|
+
}
|
|
44
|
+
/** Validate `value` and return its JSON copy, reading each property once. */
|
|
45
|
+
function copy(value, path, depth, budget) {
|
|
46
|
+
const { provider, model } = budget;
|
|
47
|
+
if (value === null || typeof value === "boolean") {
|
|
48
|
+
spend(budget, value === false ? 5 : 4);
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
if (typeof value === "string") {
|
|
52
|
+
spend(budget, value.length + 2);
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
if (typeof value === "number") {
|
|
56
|
+
if (!Number.isFinite(value)) throw invalid(`${path} must be a finite number`, provider, model);
|
|
57
|
+
spend(budget, 1);
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
if (typeof value !== "object") throw invalid(`${path} must be JSON data`, provider, model);
|
|
61
|
+
if (depth > 4) throw invalid(`${path} nests deeper than 4 levels`, provider, model);
|
|
62
|
+
if (Array.isArray(value)) {
|
|
63
|
+
if (value.length > 32) throw invalid(`${path} has more than 32 items`, provider, model);
|
|
64
|
+
spend(budget, 2);
|
|
65
|
+
const items = [];
|
|
66
|
+
for (let index = 0; index < value.length; index++) items.push(copy(value[index], `${path}[${index}]`, depth + 1, budget));
|
|
67
|
+
return items;
|
|
68
|
+
}
|
|
69
|
+
if (!isPlainObject(value)) throw invalid(`${path} must be a plain JSON object`, provider, model);
|
|
70
|
+
const keys = Object.keys(value);
|
|
71
|
+
if (keys.length > 32) throw invalid(`${path} has more than 32 keys`, provider, model);
|
|
72
|
+
spend(budget, 2);
|
|
73
|
+
const result = {};
|
|
74
|
+
keys.forEach((key, index) => {
|
|
75
|
+
const entryPath = `${path}{#${index}}`;
|
|
76
|
+
if (FORBIDDEN_KEYS.has(key)) throw invalid(`${entryPath} uses a reserved key`, provider, model);
|
|
77
|
+
spend(budget, key.length + 3);
|
|
78
|
+
result[key] = copy(value[key], entryPath, depth + 1, budget);
|
|
79
|
+
});
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Validate `chatTemplateKwargs` and return a detached JSON copy to send as
|
|
84
|
+
* `chat_template_kwargs`, or `undefined` when the option is absent or empty.
|
|
85
|
+
* Objects may have `Object.prototype` or a null prototype
|
|
86
|
+
* (`Object.create(null)`); the copy is always an ordinary object.
|
|
87
|
+
*
|
|
88
|
+
* @throws {AIError} `AI_INVALID_CHAT_TEMPLATE_KWARGS` when the value is not a
|
|
89
|
+
* plain JSON object within the size, depth, and entry bounds.
|
|
90
|
+
*/
|
|
91
|
+
function prepareChatTemplateKwargs(value, provider, model) {
|
|
92
|
+
if (value === void 0) return void 0;
|
|
93
|
+
if (!isPlainObject(value)) throw invalid("must be a plain JSON object", provider, model);
|
|
94
|
+
const prepared = copy(value, ROOT, 1, {
|
|
95
|
+
bytes: 0,
|
|
96
|
+
provider,
|
|
97
|
+
model
|
|
98
|
+
});
|
|
99
|
+
if (Object.keys(prepared).length === 0) return void 0;
|
|
100
|
+
if (new TextEncoder().encode(JSON.stringify(prepared)).byteLength > 8192) throw tooLarge(provider, model);
|
|
101
|
+
return prepared;
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/shared/providers/connection-retry.ts
|
|
105
|
+
/**
|
|
106
|
+
* One-shot retry for stale keep-alive connections in the OpenAI-compatible
|
|
107
|
+
* providers (#1467).
|
|
108
|
+
*
|
|
109
|
+
* A pooled socket the server closed while idle fails the next request
|
|
110
|
+
* (ECONNRESET, EPIPE, undici `UND_ERR_SOCKET`, "socket hang up"). Callers
|
|
111
|
+
* decide when a resend is safe: `chat()` only before response headers,
|
|
112
|
+
* `stream()` only before its first yielded chunk. HTTP status responses,
|
|
113
|
+
* timeouts, and caller aborts are never retried.
|
|
114
|
+
*/
|
|
115
|
+
var STALE_SOCKET_CODES = /* @__PURE__ */ new Set([
|
|
116
|
+
"ECONNRESET",
|
|
117
|
+
"EPIPE",
|
|
118
|
+
"UND_ERR_SOCKET"
|
|
119
|
+
]);
|
|
120
|
+
var MAX_CAUSE_DEPTH = 8;
|
|
121
|
+
/**
|
|
122
|
+
* True when `error` (or anything on its `cause` chain) is a stale
|
|
123
|
+
* connection failure: the OpenAI SDK's `APIConnectionError` (not its timeout
|
|
124
|
+
* subclass) or a socket error coded ECONNRESET / EPIPE / UND_ERR_SOCKET /
|
|
125
|
+
* "socket hang up". Any timeout, abort, or HTTP status on the chain wins and
|
|
126
|
+
* makes the error non-retryable.
|
|
127
|
+
*/
|
|
128
|
+
function isStaleConnectionError(error) {
|
|
129
|
+
let matched = false;
|
|
130
|
+
const seen = /* @__PURE__ */ new Set();
|
|
131
|
+
let current = error;
|
|
132
|
+
for (let depth = 0; depth < MAX_CAUSE_DEPTH && current && typeof current === "object"; depth += 1) {
|
|
133
|
+
if (seen.has(current)) break;
|
|
134
|
+
seen.add(current);
|
|
135
|
+
if (current instanceof OpenAI.APIConnectionTimeoutError || current instanceof OpenAI.APIUserAbortError) return false;
|
|
136
|
+
if (current instanceof OpenAI.APIError && current.status !== void 0) return false;
|
|
137
|
+
const { name, code, message } = current;
|
|
138
|
+
if (name === "AbortError" || name === "TimeoutError") return false;
|
|
139
|
+
if (current instanceof OpenAI.APIConnectionError || typeof code === "string" && STALE_SOCKET_CODES.has(code) || typeof message === "string" && /socket hang up/i.test(message)) matched = true;
|
|
140
|
+
current = current.cause;
|
|
141
|
+
}
|
|
142
|
+
return matched;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Decide whether a failed attempt gets its single transparent retry, and
|
|
146
|
+
* record it on the per-call attempt recorder when it does.
|
|
147
|
+
*/
|
|
148
|
+
function shouldRetryConnection(error, state) {
|
|
149
|
+
if (state.enabled === false || state.alreadyRetried || state.signal.aborted || state.callerSignal?.aborted || state.didTimeout() || !isStaleConnectionError(error)) return false;
|
|
150
|
+
if (state.recorder) state.recorder.connectionRetries += 1;
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
/** The per-call attempt recorder the observed client attaches, if any. */
|
|
154
|
+
function attemptRecorderFrom(options) {
|
|
155
|
+
return options[AI_ATTEMPT_RECORDER];
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
7
158
|
//#region src/shared/providers/openai.ts
|
|
8
159
|
/**
|
|
9
160
|
* OpenAI provider implementation
|
|
@@ -220,27 +371,49 @@ var OpenAIProvider = class {
|
|
|
220
371
|
max_tokens: options.reasoning?.maxTokens,
|
|
221
372
|
include_thoughts: options.reasoning?.includeThoughts
|
|
222
373
|
};
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
374
|
+
const chatTemplateKwargs = prepareChatTemplateKwargs(options.chatTemplateKwargs, this.profile.providerName, model);
|
|
375
|
+
if (chatTemplateKwargs) {
|
|
376
|
+
this.assertChatTemplateKwargsSupported(model);
|
|
377
|
+
request.chat_template_kwargs = chatTemplateKwargs;
|
|
378
|
+
}
|
|
379
|
+
let responded = false;
|
|
380
|
+
const send = () => {
|
|
381
|
+
const pending = this.client.chat.completions.create(request, {
|
|
382
|
+
signal: controls?.signal,
|
|
383
|
+
timeout: controls?.timeout
|
|
384
|
+
});
|
|
385
|
+
if (typeof pending.asResponse === "function") pending.asResponse().then(() => {
|
|
386
|
+
responded = true;
|
|
387
|
+
}, () => void 0);
|
|
388
|
+
return pending;
|
|
389
|
+
};
|
|
390
|
+
const response = await send().catch((error) => {
|
|
391
|
+
if (!responded && this.retriesConnection(error, options, controls, false)) return send();
|
|
392
|
+
throw error;
|
|
226
393
|
});
|
|
227
394
|
const choice = response.choices[0];
|
|
228
395
|
if (!choice) throw new AIError(`No choices returned from ${this.profile.providerLabel}`, "NO_CHOICES", this.profile.providerName);
|
|
229
396
|
const usage = this.mapUsage(response.usage);
|
|
230
397
|
emitUsage(this.options, this.profile.providerName, "chat", response.model || model, usage, startTime, options.usageTags);
|
|
398
|
+
const toolCalls = choice.message.tool_calls?.filter((call) => call.type === "function").map((call) => ({
|
|
399
|
+
id: call.id,
|
|
400
|
+
type: "function",
|
|
401
|
+
function: {
|
|
402
|
+
name: call.function.name,
|
|
403
|
+
arguments: call.function.arguments
|
|
404
|
+
}
|
|
405
|
+
}));
|
|
406
|
+
let finishReason = this.mapFinishReason(choice.finish_reason);
|
|
407
|
+
if (toolCalls?.length && finishReason === "stop") finishReason = "tool_calls";
|
|
408
|
+
const reasoning = readReasoningText(choice.message);
|
|
231
409
|
return {
|
|
232
410
|
content: choice.message.content || "",
|
|
233
411
|
usage,
|
|
234
412
|
model: response.model,
|
|
235
|
-
finishReason
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
function: {
|
|
240
|
-
name: call.function.name,
|
|
241
|
-
arguments: call.function.arguments
|
|
242
|
-
}
|
|
243
|
-
}))
|
|
413
|
+
finishReason,
|
|
414
|
+
...finishReason === "other" && choice.finish_reason ? { rawFinishReason: String(choice.finish_reason) } : {},
|
|
415
|
+
...reasoning !== void 0 ? { reasoning } : {},
|
|
416
|
+
toolCalls
|
|
244
417
|
};
|
|
245
418
|
} catch (error) {
|
|
246
419
|
if (controls?.didTimeout()) throw new AIError(`AI request timed out after ${controls.timeout}ms`, "AI_TIMEOUT", this.profile.providerName, options.model);
|
|
@@ -553,6 +726,8 @@ var OpenAIProvider = class {
|
|
|
553
726
|
async *stream(messages, options = {}) {
|
|
554
727
|
const startTime = Date.now();
|
|
555
728
|
let controls;
|
|
729
|
+
let reasoning = "";
|
|
730
|
+
const onReasoning = options.onReasoning;
|
|
556
731
|
try {
|
|
557
732
|
const model = options.model || this.options.defaultModel || this.profile.defaultModel;
|
|
558
733
|
options = normalizeChatOptions(this.options, options, this.profile.providerName, model);
|
|
@@ -574,26 +749,43 @@ var OpenAIProvider = class {
|
|
|
574
749
|
max_tokens: options.reasoning?.maxTokens,
|
|
575
750
|
include_thoughts: options.reasoning?.includeThoughts
|
|
576
751
|
};
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
752
|
+
const chatTemplateKwargs = prepareChatTemplateKwargs(options.chatTemplateKwargs, this.profile.providerName, model);
|
|
753
|
+
if (chatTemplateKwargs) {
|
|
754
|
+
this.assertChatTemplateKwargsSupported(model);
|
|
755
|
+
request.chat_template_kwargs = chatTemplateKwargs;
|
|
756
|
+
}
|
|
581
757
|
let streamFinish = null;
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
758
|
+
let yielded = false;
|
|
759
|
+
for (let retried = false;; retried = true) try {
|
|
760
|
+
const stream = await this.client.chat.completions.create(request, {
|
|
761
|
+
signal: controls.signal,
|
|
762
|
+
timeout: controls.timeout
|
|
763
|
+
});
|
|
764
|
+
for await (const chunk of stream) {
|
|
765
|
+
streamFinish = chunk.choices[0]?.finish_reason ?? streamFinish;
|
|
766
|
+
reasoning += readReasoningText(chunk.choices[0]?.delta) ?? "";
|
|
767
|
+
const content = chunk.choices[0]?.delta?.content;
|
|
768
|
+
if (content) {
|
|
769
|
+
if (options.onProgress) options.onProgress(content);
|
|
770
|
+
yielded = true;
|
|
771
|
+
yield content;
|
|
772
|
+
}
|
|
588
773
|
}
|
|
774
|
+
break;
|
|
775
|
+
} catch (error) {
|
|
776
|
+
if (yielded || !this.retriesConnection(error, options, controls, retried)) throw error;
|
|
777
|
+
streamFinish = null;
|
|
778
|
+
reasoning = "";
|
|
589
779
|
}
|
|
590
|
-
|
|
780
|
+
if (controls.signal.aborted) throw controls.signal.reason;
|
|
781
|
+
options.onFinishReason?.(this.mapFinishReason(streamFinish));
|
|
591
782
|
emitUsage(this.options, this.profile.providerName, "stream", model, void 0, startTime, options.usageTags);
|
|
592
783
|
} catch (error) {
|
|
593
784
|
if (controls?.didTimeout()) throw new AIError(`AI request timed out after ${controls.timeout}ms`, "AI_TIMEOUT", this.profile.providerName, options.model);
|
|
594
785
|
if (options.signal?.aborted) throw new AIError("AI request aborted by caller", "AI_ABORTED", this.profile.providerName, options.model);
|
|
595
786
|
throw this.mapError(error);
|
|
596
787
|
} finally {
|
|
788
|
+
reportReasoning(onReasoning, reasoning);
|
|
597
789
|
controls?.cleanup();
|
|
598
790
|
}
|
|
599
791
|
}
|
|
@@ -742,21 +934,60 @@ var OpenAIProvider = class {
|
|
|
742
934
|
};
|
|
743
935
|
}
|
|
744
936
|
/**
|
|
745
|
-
*
|
|
937
|
+
* Reject `chatTemplateKwargs` on the hosted OpenAI API, which does not
|
|
938
|
+
* accept `chat_template_kwargs`. Throwing (rather than dropping the field)
|
|
939
|
+
* keeps a caller from silently losing a template switch it asked for, such
|
|
940
|
+
* as `enable_thinking: false`, and fails before the request is billed.
|
|
941
|
+
* The resolved client URL is checked, so `OPENAI_BASE_URL` pointing at a
|
|
942
|
+
* vLLM server still works when `baseUrl` is unset.
|
|
943
|
+
* @private
|
|
944
|
+
*/
|
|
945
|
+
assertChatTemplateKwargsSupported(model) {
|
|
946
|
+
if (this.profile.providerName !== "openai") return;
|
|
947
|
+
let host;
|
|
948
|
+
try {
|
|
949
|
+
host = new URL(this.client.baseURL).hostname.toLowerCase();
|
|
950
|
+
} catch {
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
if (host === "api.openai.com") throw new AIError("chatTemplateKwargs is not supported by the hosted OpenAI API; set baseUrl to a server that accepts chat_template_kwargs (e.g. vLLM)", "AI_UNSUPPORTED_OPTION", this.profile.providerName, model);
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Maps OpenAI finish reason to internal finish reason format. An absent
|
|
957
|
+
* reason is `'stop'`; a value outside the normalised set (e.g. vLLM
|
|
958
|
+
* `abort`, or legacy `function_call`, which carries no `tool_calls`) is
|
|
959
|
+
* `'other'` so a partial reply is not mistaken for a complete one.
|
|
746
960
|
* @param reason - OpenAI finish reason from API response
|
|
747
961
|
* @returns Internal finish reason
|
|
748
962
|
* @private
|
|
749
963
|
*/
|
|
750
964
|
mapFinishReason(reason) {
|
|
751
965
|
switch (reason) {
|
|
966
|
+
case null:
|
|
967
|
+
case void 0:
|
|
752
968
|
case "stop": return "stop";
|
|
753
969
|
case "length": return "length";
|
|
754
970
|
case "tool_calls": return "tool_calls";
|
|
755
971
|
case "content_filter": return "content_filter";
|
|
756
|
-
default: return "
|
|
972
|
+
default: return "other";
|
|
757
973
|
}
|
|
758
974
|
}
|
|
759
975
|
/**
|
|
976
|
+
* Whether a failed chat/stream attempt gets its one transparent
|
|
977
|
+
* stale-connection retry (#1467). Records the retry for `attempts`.
|
|
978
|
+
*/
|
|
979
|
+
retriesConnection(error, options, controls, alreadyRetried) {
|
|
980
|
+
if (!controls) return false;
|
|
981
|
+
return shouldRetryConnection(error, {
|
|
982
|
+
enabled: this.options.retryConnectionErrors,
|
|
983
|
+
alreadyRetried,
|
|
984
|
+
signal: controls.signal,
|
|
985
|
+
callerSignal: options.signal,
|
|
986
|
+
didTimeout: controls.didTimeout,
|
|
987
|
+
recorder: attemptRecorderFrom(options)
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
760
991
|
* Gets the context length for a given OpenAI model
|
|
761
992
|
* @param modelId - The OpenAI model identifier
|
|
762
993
|
* @returns Maximum context length in tokens
|
|
@@ -1224,49 +1455,49 @@ async function getAI(options = {}) {
|
|
|
1224
1455
|
const { ChatGPTPlanProvider } = await Promise.resolve().then(() => chatgpt_plan_exports);
|
|
1225
1456
|
client = new ChatGPTPlanProvider(options);
|
|
1226
1457
|
} else if (isLiteLLMOptions(options)) {
|
|
1227
|
-
const { LiteLLMProvider } = await import("./litellm-
|
|
1458
|
+
const { LiteLLMProvider } = await import("./litellm-CV2uEz8Q.js");
|
|
1228
1459
|
client = new LiteLLMProvider(options);
|
|
1229
1460
|
} else if (isBifrostOptions(options)) {
|
|
1230
|
-
const { BifrostProvider } = await import("./bifrost-
|
|
1461
|
+
const { BifrostProvider } = await import("./bifrost-DSeAoUz-.js");
|
|
1231
1462
|
client = new BifrostProvider(options);
|
|
1232
1463
|
} else if (isOllamaOptions(options)) {
|
|
1233
|
-
const { OllamaProvider } = await import("./ollama-
|
|
1464
|
+
const { OllamaProvider } = await import("./ollama-pL2WCpkS.js");
|
|
1234
1465
|
client = new OllamaProvider(options);
|
|
1235
1466
|
} else if (isGeminiOptions(options)) {
|
|
1236
|
-
const { GeminiProvider } = await import("./gemini-
|
|
1467
|
+
const { GeminiProvider } = await import("./gemini-BHdv492T.js");
|
|
1237
1468
|
client = new GeminiProvider(options);
|
|
1238
1469
|
} else if (isAnthropicOptions(options)) {
|
|
1239
|
-
const { AnthropicProvider } = await import("./anthropic-
|
|
1470
|
+
const { AnthropicProvider } = await import("./anthropic-DQjBd3wu.js");
|
|
1240
1471
|
client = new AnthropicProvider(options);
|
|
1241
1472
|
} else if (isHuggingFaceOptions(options)) {
|
|
1242
|
-
const { HuggingFaceProvider } = await import("./huggingface-
|
|
1473
|
+
const { HuggingFaceProvider } = await import("./huggingface-vooWYknE.js");
|
|
1243
1474
|
client = new HuggingFaceProvider(options);
|
|
1244
1475
|
} else if (isBedrockOptions(options)) {
|
|
1245
|
-
const { BedrockProvider } = await import("./bedrock-
|
|
1476
|
+
const { BedrockProvider } = await import("./bedrock-gtE6OU5Z.js");
|
|
1246
1477
|
client = new BedrockProvider(options);
|
|
1247
1478
|
} else if (isClaudeCliOptions(options)) {
|
|
1248
|
-
const { ClaudeCliProvider } = await import("./claude-cli-
|
|
1479
|
+
const { ClaudeCliProvider } = await import("./claude-cli-DysSNpKY.js");
|
|
1249
1480
|
client = new ClaudeCliProvider(options);
|
|
1250
1481
|
} else if (isQwen3TTSOptions(options)) {
|
|
1251
|
-
const { Qwen3TTSProvider } = await import("./qwen-tts-
|
|
1482
|
+
const { Qwen3TTSProvider } = await import("./qwen-tts-BbGkn1oW.js");
|
|
1252
1483
|
client = new Qwen3TTSProvider(options);
|
|
1253
1484
|
} else if (isOpenAICompatVideoOptions(options)) {
|
|
1254
|
-
const { OpenAICompatVideoProvider } = await import("./openai-compat-video-
|
|
1485
|
+
const { OpenAICompatVideoProvider } = await import("./openai-compat-video-BoLwWXJM.js");
|
|
1255
1486
|
client = new OpenAICompatVideoProvider(options);
|
|
1256
1487
|
} else if (isByteplusModelArkOptions(options)) {
|
|
1257
|
-
const { ByteplusModelArkProvider } = await import("./byteplus-modelark-
|
|
1488
|
+
const { ByteplusModelArkProvider } = await import("./byteplus-modelark-C0rRxYwh.js");
|
|
1258
1489
|
client = new ByteplusModelArkProvider(options);
|
|
1259
1490
|
} else if (isSeevioOptions(options)) {
|
|
1260
|
-
const { SeevioProvider } = await import("./seevio-
|
|
1491
|
+
const { SeevioProvider } = await import("./seevio-Dt8kWgpg.js");
|
|
1261
1492
|
client = new SeevioProvider(options);
|
|
1262
1493
|
} else if (isTypeSafeOptions(options)) {
|
|
1263
|
-
const { TypeSafeProvider } = await import("./typesafe-
|
|
1494
|
+
const { TypeSafeProvider } = await import("./typesafe-DDVSVUR_.js");
|
|
1264
1495
|
client = new TypeSafeProvider(options);
|
|
1265
1496
|
} else if (isLayaOptions(options)) {
|
|
1266
|
-
const { LayaProvider } = await import("./laya-
|
|
1497
|
+
const { LayaProvider } = await import("./laya-B7aCsYqu.js");
|
|
1267
1498
|
client = new LayaProvider(options);
|
|
1268
1499
|
} else if (isWebLLMOptions(options)) {
|
|
1269
|
-
const { WebLLMProvider } = await import("./webllm-
|
|
1500
|
+
const { WebLLMProvider } = await import("./webllm-DrwR5V-y.js").then((n) => n.o);
|
|
1270
1501
|
client = new WebLLMProvider(options);
|
|
1271
1502
|
} else throw new ValidationError("Unsupported AI provider type", {
|
|
1272
1503
|
supportedTypes: [...AI_PROVIDER_TYPES],
|
|
@@ -1976,4 +2207,4 @@ var PACKAGE_VERSION_INITIALIZED = true;
|
|
|
1976
2207
|
//#endregion
|
|
1977
2208
|
export { OpenAIProvider as _, ChatGPTPlanSessionRevokedError as a, ChatGPTPlanWorkspaceError as c, getAI as d, getAIAuto as f, getOpenAI as g, getAIClient as h, ChatGPTPlanProvider as i, AIMessage as l, OpenAIClient as m, AIThread as n, ChatGPTPlanUsageLimitError as o, AIClient as p, ChatGPTPlanError as r, ChatGPTPlanUsageUnavailableError as s, PACKAGE_VERSION_INITIALIZED as t, factory_exports as u };
|
|
1978
2209
|
|
|
1979
|
-
//# sourceMappingURL=src-
|
|
2210
|
+
//# sourceMappingURL=src-mOOK32QB.js.map
|