@happyvertical/ai 0.102.8 → 0.103.1

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 (55) hide show
  1. package/AGENT.md +3 -2
  2. package/README.md +123 -4
  3. package/dist/chunks/{anthropic-CxOrttgj.js → anthropic-CdbK9Jl-.js} +3 -3
  4. package/dist/chunks/{anthropic-CxOrttgj.js.map → anthropic-CdbK9Jl-.js.map} +1 -1
  5. package/dist/chunks/{bedrock-B0cNEXFa.js → bedrock-DNkxz3ow.js} +3 -3
  6. package/dist/chunks/{bedrock-B0cNEXFa.js.map → bedrock-DNkxz3ow.js.map} +1 -1
  7. package/dist/chunks/{bifrost-BXiXdyb8.js → bifrost-DmbaXObX.js} +4 -4
  8. package/dist/chunks/{bifrost-BXiXdyb8.js.map → bifrost-DmbaXObX.js.map} +1 -1
  9. package/dist/chunks/{byteplus-modelark-DZ0YL2vl.js → byteplus-modelark-BAi0y9SK.js} +3 -3
  10. package/dist/chunks/{byteplus-modelark-DZ0YL2vl.js.map → byteplus-modelark-BAi0y9SK.js.map} +1 -1
  11. package/dist/chunks/{claude-cli-CtBp-Uk-.js → claude-cli-C40pIzrp.js} +3 -3
  12. package/dist/chunks/{claude-cli-CtBp-Uk-.js.map → claude-cli-C40pIzrp.js.map} +1 -1
  13. package/dist/chunks/{continuation-7o7400bp.js → continuation-L5KnnE9R.js} +2 -2
  14. package/dist/chunks/{continuation-7o7400bp.js.map → continuation-L5KnnE9R.js.map} +1 -1
  15. package/dist/chunks/{gateway-audio-models-BSZGgjd8.js → gateway-audio-models-B1tGNv1H.js} +2 -2
  16. package/dist/chunks/{gateway-audio-models-BSZGgjd8.js.map → gateway-audio-models-B1tGNv1H.js.map} +1 -1
  17. package/dist/chunks/{gemini-DUk4ssth.js → gemini-C-r6c4N9.js} +3 -3
  18. package/dist/chunks/{gemini-DUk4ssth.js.map → gemini-C-r6c4N9.js.map} +1 -1
  19. package/dist/chunks/{huggingface-C41BZOoI.js → huggingface-DtuYF1vb.js} +3 -3
  20. package/dist/chunks/{huggingface-C41BZOoI.js.map → huggingface-DtuYF1vb.js.map} +1 -1
  21. package/dist/chunks/laya-D-P82D0h.js +471 -0
  22. package/dist/chunks/laya-D-P82D0h.js.map +1 -0
  23. package/dist/chunks/{litellm-rMCXClTb.js → litellm-BaGgWzHR.js} +4 -4
  24. package/dist/chunks/{litellm-rMCXClTb.js.map → litellm-BaGgWzHR.js.map} +1 -1
  25. package/dist/chunks/{ollama--y88p8Bz.js → ollama-C3T3mn68.js} +4 -4
  26. package/dist/chunks/{ollama--y88p8Bz.js.map → ollama-C3T3mn68.js.map} +1 -1
  27. package/dist/chunks/{openai-compat-video-erarPEz-.js → openai-compat-video-CwFsKAxR.js} +3 -3
  28. package/dist/chunks/{openai-compat-video-erarPEz-.js.map → openai-compat-video-CwFsKAxR.js.map} +1 -1
  29. package/dist/chunks/{qwen-tts-C9w7teY6.js → qwen-tts-DqMxfA54.js} +2 -2
  30. package/dist/chunks/{qwen-tts-C9w7teY6.js.map → qwen-tts-DqMxfA54.js.map} +1 -1
  31. package/dist/chunks/{rate-limit-Bln_w0rH.js → rate-limit-BkK4-hXY.js} +2 -2
  32. package/dist/chunks/{rate-limit-Bln_w0rH.js.map → rate-limit-BkK4-hXY.js.map} +1 -1
  33. package/dist/chunks/{seevio-CosohVa0.js → seevio-EzC6eKz-.js} +3 -3
  34. package/dist/chunks/{seevio-CosohVa0.js.map → seevio-EzC6eKz-.js.map} +1 -1
  35. package/dist/chunks/{src-nB7u5Xl0.js → src-Cc5yFnnQ.js} +25 -18
  36. package/dist/chunks/{src-nB7u5Xl0.js.map → src-Cc5yFnnQ.js.map} +1 -1
  37. package/dist/chunks/{typesafe-BNM-QAJ2.js → typesafe-D7ZmI9-n.js} +3 -3
  38. package/dist/chunks/{typesafe-BNM-QAJ2.js.map → typesafe-D7ZmI9-n.js.map} +1 -1
  39. package/dist/chunks/{usage-BaQKo8bc.js → usage-B9gPCWSa.js} +2 -1
  40. package/dist/chunks/usage-B9gPCWSa.js.map +1 -0
  41. package/dist/chunks/{webllm-CWgY5nnP.js → webllm-DT31sqUY.js} +2 -2
  42. package/dist/chunks/{webllm-CWgY5nnP.js.map → webllm-DT31sqUY.js.map} +1 -1
  43. package/dist/index.js +2 -2
  44. package/dist/local.js +2 -2
  45. package/dist/node/factory.d.ts.map +1 -1
  46. package/dist/node.js +16 -3
  47. package/dist/node.js.map +1 -1
  48. package/dist/shared/factory.d.ts.map +1 -1
  49. package/dist/shared/providers/laya.d.ts +46 -0
  50. package/dist/shared/providers/laya.d.ts.map +1 -0
  51. package/dist/shared/types.d.ts +37 -3
  52. package/dist/shared/types.d.ts.map +1 -1
  53. package/metadata.json +2 -2
  54. package/package.json +4 -3
  55. package/dist/chunks/usage-BaQKo8bc.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"webllm-CWgY5nnP.js","names":[],"sources":["../../src/shared/providers/webllm.ts"],"sourcesContent":["// biome-ignore-all lint/style/useNamingConvention: WebLLM's OpenAI-shaped wire format and model records are snake_case.\n/**\n * WebLLM provider: runs a chat model entirely in the browser on WebGPU.\n *\n * Backed by `@mlc-ai/web-llm`, an optional peer dependency that is imported\n * lazily on first use, so neither the root entry nor `getAI()` for other\n * provider types ever loads it. Reachable through `getAI({ type: 'webllm' })`\n * and the `@happyvertical/ai/local` subpath.\n *\n * WebLLM's engine API is OpenAI-shaped, so requests and responses map almost\n * one to one. Structured output (`responseSchema`) becomes WebLLM's\n * grammar-constrained `response_format`, which keeps a small model from\n * emitting a value outside the schema.\n */\n\nimport {\n normalizeBaseAIOptions,\n normalizeChatOptions,\n type PreparedRequestControls,\n prepareRequestControls,\n} from '../safety';\nimport type {\n AICapabilities,\n AIInterface,\n AIMessage,\n AIModel,\n AIResponse,\n AIToolCall,\n ChatOptions,\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 WebLLMEngineLike,\n WebLLMLoadProgress,\n WebLLMOptions,\n} from '../types';\nimport {\n AIError,\n ContextLengthError,\n extractTextContent,\n ModelNotFoundError,\n WebGPUUnavailableError,\n WebLLMPeerMissingError,\n} from '../types';\nimport { emitUsage } from './usage';\n\nconst PROVIDER = 'webllm';\n\n/** Small enough for most GPUs (about 0.9 GB of VRAM); override with `defaultModel`. */\nexport const DEFAULT_WEBLLM_MODEL = 'Llama-3.2-1B-Instruct-q4f16_1-MLC';\n\n/** Context window assumed when a model record does not declare one. */\nconst DEFAULT_CONTEXT_LENGTH = 4096;\n\nconst WEBLLM_CAPABILITIES: AICapabilities = {\n chat: true,\n completion: true,\n embeddings: false,\n streaming: true,\n functions: true,\n vision: false,\n fineTuning: false,\n imageEmbeddings: false,\n imageGeneration: false,\n videoGeneration: false,\n tts: false,\n voiceCloning: false,\n voiceDesign: false,\n maxContextLength: DEFAULT_CONTEXT_LENGTH,\n supportedOperations: ['chat', 'completion', 'streaming', 'functions'],\n};\n\n/** Structural subset of the `@mlc-ai/web-llm` module this provider uses. */\ninterface WebLLMModule {\n CreateMLCEngine(\n model: string,\n config?: {\n appConfig?: unknown;\n initProgressCallback?: (report: WebLLMLoadProgress) => void;\n },\n ): Promise<WebLLMEngineLike>;\n prebuiltAppConfig: { model_list: WebLLMModelRecord[] };\n functionCallingModelIds?: string[];\n}\n\ninterface WebLLMModelRecord {\n model_id: string;\n vram_required_MB?: number;\n low_resource_required?: boolean;\n /** WebLLM `ModelType`: 0 or absent = LLM, 1 = embedding, 2 = VLM. */\n model_type?: number;\n overrides?: { context_window_size?: number };\n}\n\ninterface EngineEntry {\n promise: Promise<WebLLMEngineLike>;\n listeners: Set<(report: WebLLMLoadProgress) => void>;\n}\n\n/**\n * Per-engine request queues. WebLLM runs one generation at a time per engine\n * and `interruptGenerate()` stops whichever one is running, so requests are\n * serialized here and only the running request may interrupt.\n */\nconst engineQueues = new WeakMap<object, Promise<void>>();\n\n/** Engines shared by every provider instance that uses the prebuilt model list. */\nconst sharedEngines = new Map<string, EngineEntry>();\n\n/**\n * Imports the optional peer. Isolated so a missing install becomes a typed\n * {@link WebLLMPeerMissingError} instead of a bare module-resolution failure.\n */\nexport async function importWebLLM(): Promise<WebLLMModule> {\n try {\n const loaded = (await import('@mlc-ai/web-llm')) as unknown as\n | (WebLLMModule & { default?: WebLLMModule })\n | undefined;\n const module =\n typeof loaded?.CreateMLCEngine === 'function' ? loaded : loaded?.default;\n if (typeof module?.CreateMLCEngine !== 'function') {\n throw new Error('@mlc-ai/web-llm did not export CreateMLCEngine');\n }\n return module;\n } catch (error) {\n throw new WebLLMPeerMissingError(error);\n }\n}\n\n/** True when this runtime exposes WebGPU (`navigator.gpu`). */\nexport function isWebGPUAvailable(): boolean {\n const nav = (globalThis as { navigator?: { gpu?: unknown } }).navigator;\n return Boolean(nav?.gpu);\n}\n\n/**\n * Unloads every engine this module created and forgets them. Caller-supplied\n * engines are never touched.\n */\nexport async function disposeWebLLMEngines(): Promise<void> {\n const entries = [...sharedEngines.values()];\n sharedEngines.clear();\n await Promise.allSettled(\n entries.map(async (entry) => (await entry.promise).unload?.()),\n );\n}\n\nfunction unsupported(feature: string): never {\n throw new AIError(\n `${feature} is not supported by the webllm provider.`,\n 'NOT_SUPPORTED',\n PROVIDER,\n );\n}\n\nfunction stripUndefined<T extends Record<string, unknown>>(value: T): T {\n return Object.fromEntries(\n Object.entries(value).filter(([, entry]) => entry !== undefined),\n ) as T;\n}\n\n/**\n * In-browser chat provider on WebLLM (WebGPU). See the module header.\n */\nexport class WebLLMProvider implements AIInterface {\n private options: WebLLMOptions;\n /** Model the caller named, if any; a supplied engine is loaded with it already. */\n private readonly explicitModel?: string;\n private factoryEngine?: Promise<WebLLMEngineLike>;\n private readonly localEngines = new Map<string, EngineEntry>();\n\n constructor(options: WebLLMOptions) {\n this.explicitModel = options.defaultModel ?? options.model;\n this.options = normalizeBaseAIOptions({\n ...options,\n defaultModel: this.explicitModel ?? DEFAULT_WEBLLM_MODEL,\n });\n }\n\n // --------------------------------------------------------------------------\n // Engine lifecycle\n // --------------------------------------------------------------------------\n\n private async acquireEngine(\n model: string,\n signal?: AbortSignal,\n ): Promise<WebLLMEngineLike> {\n const supplied = this.options.engine;\n if (supplied) {\n if (typeof supplied !== 'function') return supplied;\n this.factoryEngine ??= supplied().catch((error) => {\n this.factoryEngine = undefined;\n throw error;\n });\n return abortable(this.factoryEngine, signal);\n }\n\n if (!isWebGPUAvailable()) {\n throw new WebGPUUnavailableError(PROVIDER);\n }\n\n // A custom appConfig changes what a model id means, so it must not share\n // an engine with other callers.\n const cache = this.options.appConfig ? this.localEngines : sharedEngines;\n let entry = cache.get(model);\n if (!entry) {\n const listeners = new Set<(report: WebLLMLoadProgress) => void>();\n const created: EngineEntry = {\n listeners,\n promise: this.createEngine(model, listeners),\n };\n created.promise.catch(() => {\n if (cache.get(model) === created) cache.delete(model);\n });\n cache.set(model, created);\n entry = created;\n }\n\n const onProgress = this.options.onLoadProgress;\n if (onProgress) entry.listeners.add(onProgress);\n try {\n return await abortable(entry.promise, signal);\n } finally {\n if (onProgress) entry.listeners.delete(onProgress);\n }\n }\n\n private async createEngine(\n model: string,\n listeners: Set<(report: WebLLMLoadProgress) => void>,\n ): Promise<WebLLMEngineLike> {\n const webllm = await importWebLLM();\n return webllm.CreateMLCEngine(model, {\n ...(this.options.appConfig ? { appConfig: this.options.appConfig } : {}),\n initProgressCallback: (report) => {\n for (const listener of listeners) {\n try {\n listener(report);\n } catch {\n // A progress listener must never break model loading.\n }\n }\n },\n });\n }\n\n // --------------------------------------------------------------------------\n // Request / response mapping\n // --------------------------------------------------------------------------\n\n private mapMessages(messages: AIMessage[]): Record<string, unknown>[] {\n return messages.map((message) => {\n if (message.role === 'user' && Array.isArray(message.content)) {\n return {\n role: 'user',\n content: message.content.map((part) =>\n part.type === 'text'\n ? { type: 'text', text: part.text }\n : { type: 'image_url', image_url: { url: part.image_url.url } },\n ),\n ...(message.name ? { name: message.name } : {}),\n };\n }\n const content = extractTextContent(message.content);\n if (message.role === 'tool' || message.role === 'function') {\n return {\n role: 'tool',\n content,\n tool_call_id: message.tool_call_id ?? message.name ?? '',\n };\n }\n if (message.role === 'assistant') {\n return stripUndefined({\n role: 'assistant',\n content: content || (message.tool_calls?.length ? null : ''),\n tool_calls: message.tool_calls?.map((call) => ({\n id: call.id,\n type: call.type,\n function: {\n name: call.function.name,\n arguments: call.function.arguments,\n },\n })),\n });\n }\n return {\n role: message.role,\n content,\n ...(message.name ? { name: message.name } : {}),\n };\n });\n }\n\n private mapResponseFormat(\n options: Pick<ChatOptions, 'responseFormat' | 'responseSchema'>,\n ): Record<string, unknown> | undefined {\n if (options.responseSchema !== undefined) {\n const schema =\n typeof options.responseSchema === 'string'\n ? options.responseSchema\n : JSON.stringify(options.responseSchema);\n // WebLLM compiles the schema to a grammar and masks logits, so the\n // output cannot leave it.\n return { type: 'json_object', schema };\n }\n return options.responseFormat?.type === 'json_object'\n ? { type: 'json_object' }\n : undefined;\n }\n\n private buildRequest(\n messages: AIMessage[],\n options: ChatOptions & { maxTokens: number },\n model: string | undefined,\n stream: boolean,\n includeTools: boolean,\n ): Record<string, unknown> {\n const tools =\n includeTools && options.tools?.length\n ? options.tools.map((tool) => ({\n type: 'function' as const,\n function: {\n name: tool.function.name,\n description: tool.function.description,\n parameters: tool.function.parameters,\n },\n }))\n : undefined;\n return stripUndefined({\n model,\n messages: this.mapMessages(messages),\n max_tokens: options.maxTokens,\n temperature: options.temperature,\n top_p: options.topP,\n n: options.n,\n stop: options.stop,\n frequency_penalty: options.frequencyPenalty,\n presence_penalty: options.presencePenalty,\n seed: options.seed,\n tools,\n tool_choice: tools ? options.toolChoice : undefined,\n response_format: this.mapResponseFormat(options),\n stream,\n stream_options: stream ? { include_usage: true } : undefined,\n });\n }\n\n /** Model id to send. A caller-supplied engine already has its model loaded. */\n private requestModel(\n model: string,\n options: ChatOptions,\n ): string | undefined {\n if (!this.options.engine) return model;\n return options.model ?? this.explicitModel;\n }\n\n private mapUsage(usage: unknown): TokenUsage | undefined {\n const raw = usage as\n | {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n }\n | undefined\n | null;\n if (!raw) return undefined;\n return {\n promptTokens: raw.prompt_tokens ?? 0,\n completionTokens: raw.completion_tokens ?? 0,\n totalTokens: raw.total_tokens ?? 0,\n };\n }\n\n private mapFinishReason(reason: string | null | undefined) {\n switch (reason) {\n case 'length':\n return 'length' as const;\n case 'tool_calls':\n return 'tool_calls' as const;\n default:\n return 'stop' as const;\n }\n }\n\n /**\n * WebLLM reports `abort` when generation was interrupted. The provider's own\n * aborts are raised before this point, so reaching it means someone else\n * interrupted the engine (for example a caller sharing a supplied engine);\n * a truncated reply must not look like a normal stop.\n */\n private assertNotInterrupted(\n reason: string | null | undefined,\n model: string,\n ): void {\n if (reason === 'abort') {\n throw new AIError(\n 'Generation was interrupted before it finished',\n 'AI_INTERRUPTED',\n PROVIDER,\n model,\n true,\n );\n }\n }\n\n private mapError(error: unknown, model?: string): AIError {\n if (error instanceof AIError) return error;\n const name = (error as { name?: string } | undefined)?.name ?? '';\n const message = error instanceof Error ? error.message : String(error);\n if (name === 'WebGPUNotAvailableError' || name === 'WebGPUNotFoundError') {\n return new WebGPUUnavailableError(PROVIDER, message);\n }\n if (\n name === 'ContextWindowSizeExceededError' ||\n name === 'PrefillChunkSizeSmallerThanImageError'\n ) {\n return new ContextLengthError(PROVIDER, model);\n }\n if (\n name === 'ModelNotFoundError' ||\n name === 'SpecifiedModelNotFoundError' ||\n name === 'UnsupportedModelIdError' ||\n name === 'ModelNotLoadedError'\n ) {\n return new ModelNotFoundError(model ?? message, PROVIDER);\n }\n const mapped = new AIError(\n message,\n name === 'DeviceLostError' ? 'WEBGPU_DEVICE_LOST' : 'API_ERROR',\n PROVIDER,\n model,\n );\n mapped.cause = error;\n return mapped;\n }\n\n /** Throws the right AI error when the request ended by timeout or caller abort. */\n private abortError(\n controls: PreparedRequestControls | undefined,\n options: ChatOptions,\n model?: string,\n ): AIError | undefined {\n if (controls?.didTimeout()) {\n return new AIError(\n `AI request timed out after ${controls.timeout}ms`,\n 'AI_TIMEOUT',\n PROVIDER,\n model,\n );\n }\n if (options.signal?.aborted) {\n return new AIError(\n 'AI request aborted by caller',\n 'AI_ABORTED',\n PROVIDER,\n model,\n );\n }\n return undefined;\n }\n\n /**\n * Waits for this request's turn on the engine; resolves to a release fn. A\n * queued request that is aborted or times out leaves the queue at once and\n * never interrupts the generation ahead of it.\n */\n private async lockEngine(\n engine: WebLLMEngineLike,\n signal: AbortSignal,\n ): Promise<() => void> {\n const previous = engineQueues.get(engine) ?? Promise.resolve();\n let release: () => void = () => {};\n const mine = new Promise<void>((resolve) => {\n release = resolve;\n });\n engineQueues.set(\n engine,\n previous.then(() => mine),\n );\n try {\n await abortable(previous, signal);\n } catch (error) {\n release();\n throw error;\n }\n return release;\n }\n\n /** Interrupts generation when the composed signal fires; returns a detach fn. */\n private bindInterrupt(\n engine: WebLLMEngineLike,\n signal: AbortSignal,\n ): () => void {\n const interrupt = () => {\n try {\n void Promise.resolve(engine.interruptGenerate()).catch(() => {});\n } catch {\n // Best effort: the request is already being abandoned.\n }\n };\n if (signal.aborted) interrupt();\n signal.addEventListener('abort', interrupt, { once: true });\n return () => signal.removeEventListener('abort', interrupt);\n }\n\n // --------------------------------------------------------------------------\n // Chat\n // --------------------------------------------------------------------------\n\n async chat(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): Promise<AIResponse> {\n const startTime = Date.now();\n let controls: PreparedRequestControls | undefined;\n let detach: (() => void) | undefined;\n let release: (() => void) | undefined;\n let pending: Promise<any> | undefined;\n let holdRelease: (() => Promise<unknown> | undefined) | undefined;\n const model =\n options.model || this.options.defaultModel || DEFAULT_WEBLLM_MODEL;\n try {\n options = normalizeChatOptions(this.options, options, PROVIDER, model);\n // Load before arming the request timeout: a multi-GB download must not\n // consume the generation budget.\n const engine = await this.acquireEngine(model, options.signal);\n controls = prepareRequestControls(this.options, options);\n release = await this.lockEngine(engine, controls.signal);\n detach = this.bindInterrupt(engine, controls.signal);\n\n pending = engine.chat.completions.create(\n this.buildRequest(\n messages,\n options as ChatOptions & { maxTokens: number },\n this.requestModel(model, options),\n false,\n true,\n ),\n );\n const started: Promise<unknown> = pending;\n let settled = false;\n const markSettled = () => {\n settled = true;\n };\n started.then(markSettled, markSettled);\n holdRelease = () => (settled ? undefined : started);\n const response = await abortable(pending, controls.signal);\n if (controls.signal.aborted) {\n throw (\n this.abortError(controls, options, model) ?? controls.signal.reason\n );\n }\n\n const choice = response?.choices?.[0];\n this.assertNotInterrupted(choice?.finish_reason, model);\n if (!choice) {\n throw new AIError(\n 'No choices returned from webllm',\n 'NO_CHOICES',\n PROVIDER,\n );\n }\n const usage = this.mapUsage(response.usage);\n emitUsage(\n this.options,\n PROVIDER,\n 'chat',\n response.model || model,\n usage,\n startTime,\n options.usageTags,\n );\n const toolCalls: AIToolCall[] | undefined =\n choice.message?.tool_calls?.map(\n (call: {\n id: string;\n function: { name: string; arguments: string };\n }) => ({\n id: call.id,\n type: 'function' as const,\n function: {\n name: call.function.name,\n arguments: call.function.arguments,\n },\n }),\n );\n return {\n content: choice.message?.content || '',\n usage,\n model: response.model || model,\n finishReason: this.mapFinishReason(choice.finish_reason),\n ...(toolCalls?.length ? { toolCalls } : {}),\n };\n } catch (error) {\n throw (\n this.abortError(controls, options, model) ?? this.mapError(error, model)\n );\n } finally {\n detach?.();\n releaseWhenIdle(release, holdRelease?.());\n controls?.cleanup();\n }\n }\n\n async complete(\n prompt: string,\n options: CompletionOptions = {},\n ): Promise<AIResponse> {\n return this.chat([{ role: 'user', content: prompt }], {\n model: options.model,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n topP: options.topP,\n n: options.n,\n stop: options.stop,\n stream: options.stream,\n onProgress: options.onProgress,\n signal: options.signal,\n timeout: options.timeout,\n reasoning: options.reasoning,\n usageTags: options.usageTags,\n continueOnLength: options.continueOnLength,\n });\n }\n\n async message(text: string, options: MessageOptions = {}): Promise<string> {\n const messages: AIMessage[] = [\n ...(options.history || []),\n { role: options.role || 'user', content: text },\n ];\n 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 responseSchema: options.responseSchema,\n seed: options.seed,\n tools: options.tools,\n toolChoice: options.toolChoice,\n onProgress: options.onProgress,\n signal: options.signal,\n timeout: options.timeout,\n reasoning: options.reasoning,\n usageTags: options.usageTags,\n continueOnLength: options.continueOnLength,\n });\n return response.content;\n }\n\n /**\n * Streams a chat completion as text chunks. Like the other providers, a\n * stream yields text only; use `chat()` for rounds that may call tools.\n * Breaking out of the loop interrupts generation on the engine.\n */\n async *stream(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): AsyncIterable<string> {\n const startTime = Date.now();\n let controls: PreparedRequestControls | undefined;\n let detach: (() => void) | undefined;\n let engine: WebLLMEngineLike | undefined;\n let release: (() => void) | undefined;\n let iterator: AsyncIterator<WebLLMChunk> | undefined;\n let iteratorDone = false;\n let holdRelease: Promise<unknown> | undefined;\n const model =\n options.model || this.options.defaultModel || DEFAULT_WEBLLM_MODEL;\n try {\n options = normalizeChatOptions(this.options, options, PROVIDER, model);\n engine = await this.acquireEngine(model, options.signal);\n controls = prepareRequestControls(this.options, options);\n release = await this.lockEngine(engine, controls.signal);\n detach = this.bindInterrupt(engine, controls.signal);\n\n const pending: Promise<AsyncIterable<WebLLMChunk>> =\n engine.chat.completions.create(\n this.buildRequest(\n messages,\n options as ChatOptions & { maxTokens: number },\n this.requestModel(model, options),\n true,\n false,\n ),\n );\n let chunks: AsyncIterable<WebLLMChunk>;\n try {\n chunks = await abortable(pending, controls.signal);\n } catch (error) {\n // The caller is gone but the engine may still start this request.\n // Keep our queue closed until it has been interrupted and drained, or\n // the next request would wait behind it and could interrupt it.\n holdRelease = pending.then(\n (late) => abandonStream(late[Symbol.asyncIterator](), engine),\n () => {},\n );\n throw error;\n }\n // Iterated by hand: WebLLM releases its per-model lock only when its\n // generator runs to completion, never on an early `return()`.\n iterator = chunks[Symbol.asyncIterator]();\n\n let finishReason: string | null | undefined;\n let usage: TokenUsage | undefined;\n for (;;) {\n const step = await iterator.next();\n if (step.done) {\n iteratorDone = true;\n break;\n }\n const chunk = step.value;\n const choice = chunk.choices?.[0];\n finishReason = choice?.finish_reason ?? finishReason;\n if (chunk.usage) usage = this.mapUsage(chunk.usage);\n const content = choice?.delta?.content;\n if (content) {\n options.onProgress?.(content);\n yield content;\n }\n }\n if (controls.signal.aborted) {\n throw (\n this.abortError(controls, options, model) ?? controls.signal.reason\n );\n }\n this.assertNotInterrupted(finishReason, model);\n options.onFinishReason?.(this.mapFinishReason(finishReason));\n emitUsage(\n this.options,\n PROVIDER,\n 'stream',\n model,\n usage,\n startTime,\n options.usageTags,\n );\n } catch (error) {\n throw (\n this.abortError(controls, options, model) ?? this.mapError(error, model)\n );\n } finally {\n if (iterator && !iteratorDone && engine) {\n // Consumer stopped early (or the loop threw): stop the GPU, then let\n // the engine's generator finish so its lock is released before the\n // next request is admitted.\n try {\n void Promise.resolve(engine.interruptGenerate()).catch(() => {});\n } catch {\n // Best effort.\n }\n await drainIterator(iterator);\n }\n detach?.();\n releaseWhenIdle(release, holdRelease);\n controls?.cleanup();\n }\n }\n\n async countTokens(text: string): Promise<number> {\n // WebLLM exposes no standalone tokenizer; ~4 characters per token.\n return Math.ceil(text.length / 4);\n }\n\n // --------------------------------------------------------------------------\n // Models and capabilities\n // --------------------------------------------------------------------------\n\n async getModels(): Promise<AIModel[]> {\n const webllm = await importWebLLM();\n const list =\n (\n this.options.appConfig as\n | { model_list?: WebLLMModelRecord[] }\n | undefined\n )?.model_list ?? webllm.prebuiltAppConfig.model_list;\n const functionCalling = new Set(webllm.functionCallingModelIds ?? []);\n return list\n .filter((record) => record.model_type !== 1) // embedding models\n .map((record) => {\n const vision = record.model_type === 2;\n const functions = functionCalling.has(record.model_id);\n return {\n id: record.model_id,\n name: record.model_id,\n description: record.vram_required_MB\n ? `Runs on-device via WebGPU (~${Math.round(record.vram_required_MB)} MB VRAM)`\n : 'Runs on-device via WebGPU',\n contextLength:\n record.overrides?.context_window_size ?? DEFAULT_CONTEXT_LENGTH,\n capabilities: [\n 'text',\n 'chat',\n ...(functions ? ['functions'] : []),\n ...(vision ? ['vision'] : []),\n ],\n supportsFunctions: functions,\n supportsVision: vision,\n };\n });\n }\n\n async getCapabilities(): Promise<AICapabilities> {\n return { ...WEBLLM_CAPABILITIES };\n }\n\n // --------------------------------------------------------------------------\n // No in-browser equivalent\n // --------------------------------------------------------------------------\n\n async embed(\n _text: string | string[],\n _options: EmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n return unsupported('Embeddings');\n }\n\n async embedImage(\n _image: string | Buffer,\n _options: ImageEmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n return unsupported('Image embeddings');\n }\n\n async describeImage(\n _image: string | Buffer,\n _prompt?: string,\n _options: ImageDescriptionOptions = {},\n ): Promise<string> {\n return unsupported('Image description');\n }\n\n async generateImage(\n _prompt: string,\n _options: ImageGenerationOptions = {},\n ): Promise<ImageGenerationResponse> {\n return unsupported('Image generation');\n }\n\n async submitVideoGenerationJob(\n _options: VideoGenerationOptions,\n ): Promise<VideoGenerationJob> {\n return unsupported('Video generation');\n }\n\n async getVideoGenerationJob(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationStatusResult> {\n return unsupported('Video generation');\n }\n\n async fetchVideoGenerationResult(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationResult> {\n return unsupported('Video generation');\n }\n\n async cancelVideoGenerationJob(_handle: VideoGenerationJob): Promise<void> {\n return unsupported('Video generation');\n }\n\n async validateVideoGenerationAccess(): Promise<boolean> {\n return unsupported('Video generation');\n }\n\n async synthesizeSpeech(\n _text: string,\n _options?: TTSOptions,\n ): Promise<TTSResponse> {\n return unsupported('Text-to-speech');\n }\n\n streamSpeech(_text: string, _options?: TTSOptions): AsyncIterable<Buffer> {\n const error = new AIError(\n 'Text-to-speech streaming is not supported by the webllm provider.',\n 'NOT_SUPPORTED',\n PROVIDER,\n );\n return {\n [Symbol.asyncIterator]: () => ({\n next: () => Promise.reject(error),\n }),\n };\n }\n\n async cloneVoice(_options: VoiceCloneOptions): Promise<Voice> {\n return unsupported('Voice cloning');\n }\n\n async designVoice(_options: VoiceDesignOptions): Promise<Voice> {\n return unsupported('Voice design');\n }\n\n async getVoices(_options?: VoiceListOptions): Promise<Voice[]> {\n return unsupported('Voice listing');\n }\n}\n\n/** One streamed WebLLM chunk (structural subset). */\ninterface WebLLMChunk {\n choices?: Array<{\n delta?: { content?: string | null };\n finish_reason?: string | null;\n }>;\n usage?: unknown;\n}\n\n/** Runs a WebLLM stream to its end, ignoring errors; used after interrupting it. */\nasync function drainIterator(\n iterator: AsyncIterator<WebLLMChunk>,\n): Promise<void> {\n try {\n while (!(await iterator.next()).done) {\n // discard\n }\n } catch {\n // The engine already stopped; nothing more to release.\n }\n}\n\n/** Releases an engine queue slot now, or once `idle` has settled. */\nfunction releaseWhenIdle(\n release: (() => void) | undefined,\n idle?: Promise<unknown>,\n): void {\n if (!release) return;\n if (idle) void idle.then(release, release);\n else release();\n}\n\n/**\n * Finishes a stream nobody is reading. WebLLM resets its interrupt flag on the\n * first `next()`, so interrupt only after pulling one chunk, then drain it so\n * the engine's lock is released.\n */\nasync function abandonStream(\n iterator: AsyncIterator<WebLLMChunk>,\n engine: WebLLMEngineLike | undefined,\n): Promise<void> {\n try {\n if ((await iterator.next()).done) return;\n await Promise.resolve(engine?.interruptGenerate());\n } catch {\n return;\n }\n await drainIterator(iterator);\n}\n\n/** Rejects with the signal's reason if it fires first; the underlying work continues. */\nfunction abortable<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {\n if (!signal) return promise;\n if (signal.aborted) {\n return Promise.reject(\n new AIError('AI request aborted by caller', 'AI_ABORTED', PROVIDER),\n );\n }\n return new Promise<T>((resolve, reject) => {\n const onAbort = () =>\n reject(\n new AIError('AI request aborted by caller', 'AI_ABORTED', PROVIDER),\n );\n signal.addEventListener('abort', onAbort, { once: true });\n promise.then(\n (value) => {\n signal.removeEventListener('abort', onAbort);\n resolve(value);\n },\n (error) => {\n signal.removeEventListener('abort', onAbort);\n reject(error);\n },\n );\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA8DA,IAAM,WAAW;;AAGjB,IAAa,uBAAuB;;AAGpC,IAAM,yBAAyB;AAE/B,IAAM,sBAAsC;CAC1C,MAAM;CACN,YAAY;CACZ,YAAY;CACZ,WAAW;CACX,WAAW;CACX,QAAQ;CACR,YAAY;CACZ,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CACjB,KAAK;CACL,cAAc;CACd,aAAa;CACb,kBAAkB;CAClB,qBAAqB;EAAC;EAAQ;EAAc;EAAa;CAAW;AACtE;;;;;;AAkCA,IAAM,+BAAe,IAAI,QAA+B;;AAGxD,IAAM,gCAAgB,IAAI,IAAyB;;;;;AAMnD,eAAsB,eAAsC;CAC1D,IAAI;EACF,MAAM,SAAU,MAAM,OAAO;EAG7B,MAAM,SACJ,OAAO,QAAQ,oBAAoB,aAAa,SAAS,QAAQ;EACnE,IAAI,OAAO,QAAQ,oBAAoB,YACrC,MAAM,IAAI,MAAM,gDAAgD;EAElE,OAAO;CACT,SAAS,OAAO;EACd,MAAM,IAAI,uBAAuB,KAAK;CACxC;AACF;;AAGA,SAAgB,oBAA6B;CAC3C,MAAM,MAAO,WAAiD;CAC9D,OAAO,QAAQ,KAAK,GAAG;AACzB;;;;;AAMA,eAAsB,uBAAsC;CAC1D,MAAM,UAAU,CAAC,GAAG,cAAc,OAAO,CAAC;CAC1C,cAAc,MAAM;CACpB,MAAM,QAAQ,WACZ,QAAQ,IAAI,OAAO,WAAW,MAAM,MAAM,QAAA,CAAS,SAAS,CAAC,CAC/D;AACF;AAEA,SAAS,YAAY,SAAwB;CAC3C,MAAM,IAAI,QACR,GAAG,QAAQ,4CACX,iBACA,QACF;AACF;AAEA,SAAS,eAAkD,OAAa;CACtE,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,CAAC,CAAC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CACjE;AACF;;;;AAKA,IAAa,iBAAb,MAAmD;CACjD;;CAEA;CACA;CACA,+BAAgC,IAAI,IAAyB;CAE7D,YAAY,SAAwB;EAClC,KAAK,gBAAgB,QAAQ,gBAAgB,QAAQ;EACrD,KAAK,UAAU,uBAAuB;GACpC,GAAG;GACH,cAAc,KAAK,iBAAA;EACrB,CAAC;CACH;CAMA,MAAc,cACZ,OACA,QAC2B;EAC3B,MAAM,WAAW,KAAK,QAAQ;EAC9B,IAAI,UAAU;GACZ,IAAI,OAAO,aAAa,YAAY,OAAO;GAC3C,KAAK,kBAAkB,SAAS,CAAC,CAAC,OAAO,UAAU;IACjD,KAAK,gBAAgB,KAAA;IACrB,MAAM;GACR,CAAC;GACD,OAAO,UAAU,KAAK,eAAe,MAAM;EAC7C;EAEA,IAAI,CAAC,kBAAkB,GACrB,MAAM,IAAI,uBAAuB,QAAQ;EAK3C,MAAM,QAAQ,KAAK,QAAQ,YAAY,KAAK,eAAe;EAC3D,IAAI,QAAQ,MAAM,IAAI,KAAK;EAC3B,IAAI,CAAC,OAAO;GACV,MAAM,4BAAY,IAAI,IAA0C;GAChE,MAAM,UAAuB;IAC3B;IACA,SAAS,KAAK,aAAa,OAAO,SAAS;GAC7C;GACA,QAAQ,QAAQ,YAAY;IAC1B,IAAI,MAAM,IAAI,KAAK,MAAM,SAAS,MAAM,OAAO,KAAK;GACtD,CAAC;GACD,MAAM,IAAI,OAAO,OAAO;GACxB,QAAQ;EACV;EAEA,MAAM,aAAa,KAAK,QAAQ;EAChC,IAAI,YAAY,MAAM,UAAU,IAAI,UAAU;EAC9C,IAAI;GACF,OAAO,MAAM,UAAU,MAAM,SAAS,MAAM;EAC9C,UAAU;GACR,IAAI,YAAY,MAAM,UAAU,OAAO,UAAU;EACnD;CACF;CAEA,MAAc,aACZ,OACA,WAC2B;EAE3B,QAAO,MADc,aAAa,EAAA,CACpB,gBAAgB,OAAO;GACnC,GAAI,KAAK,QAAQ,YAAY,EAAE,WAAW,KAAK,QAAQ,UAAU,IAAI,CAAC;GACtE,uBAAuB,WAAW;IAChC,KAAK,MAAM,YAAY,WACrB,IAAI;KACF,SAAS,MAAM;IACjB,QAAQ,CAER;GAEJ;EACF,CAAC;CACH;CAMA,YAAoB,UAAkD;EACpE,OAAO,SAAS,KAAK,YAAY;GAC/B,IAAI,QAAQ,SAAS,UAAU,MAAM,QAAQ,QAAQ,OAAO,GAC1D,OAAO;IACL,MAAM;IACN,SAAS,QAAQ,QAAQ,KAAK,SAC5B,KAAK,SAAS,SACV;KAAE,MAAM;KAAQ,MAAM,KAAK;IAAK,IAChC;KAAE,MAAM;KAAa,WAAW,EAAE,KAAK,KAAK,UAAU,IAAI;IAAE,CAClE;IACA,GAAI,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;GAC/C;GAEF,MAAM,UAAU,mBAAmB,QAAQ,OAAO;GAClD,IAAI,QAAQ,SAAS,UAAU,QAAQ,SAAS,YAC9C,OAAO;IACL,MAAM;IACN;IACA,cAAc,QAAQ,gBAAgB,QAAQ,QAAQ;GACxD;GAEF,IAAI,QAAQ,SAAS,aACnB,OAAO,eAAe;IACpB,MAAM;IACN,SAAS,YAAY,QAAQ,YAAY,SAAS,OAAO;IACzD,YAAY,QAAQ,YAAY,KAAK,UAAU;KAC7C,IAAI,KAAK;KACT,MAAM,KAAK;KACX,UAAU;MACR,MAAM,KAAK,SAAS;MACpB,WAAW,KAAK,SAAS;KAC3B;IACF,EAAE;GACJ,CAAC;GAEH,OAAO;IACL,MAAM,QAAQ;IACd;IACA,GAAI,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;GAC/C;EACF,CAAC;CACH;CAEA,kBACE,SACqC;EACrC,IAAI,QAAQ,mBAAmB,KAAA,GAO7B,OAAO;GAAE,MAAM;GAAe,QAL5B,OAAO,QAAQ,mBAAmB,WAC9B,QAAQ,iBACR,KAAK,UAAU,QAAQ,cAAc;EAGN;EAEvC,OAAO,QAAQ,gBAAgB,SAAS,gBACpC,EAAE,MAAM,cAAc,IACtB,KAAA;CACN;CAEA,aACE,UACA,SACA,OACA,QACA,cACyB;EACzB,MAAM,QACJ,gBAAgB,QAAQ,OAAO,SAC3B,QAAQ,MAAM,KAAK,UAAU;GAC3B,MAAM;GACN,UAAU;IACR,MAAM,KAAK,SAAS;IACpB,aAAa,KAAK,SAAS;IAC3B,YAAY,KAAK,SAAS;GAC5B;EACF,EAAE,IACF,KAAA;EACN,OAAO,eAAe;GACpB;GACA,UAAU,KAAK,YAAY,QAAQ;GACnC,YAAY,QAAQ;GACpB,aAAa,QAAQ;GACrB,OAAO,QAAQ;GACf,GAAG,QAAQ;GACX,MAAM,QAAQ;GACd,mBAAmB,QAAQ;GAC3B,kBAAkB,QAAQ;GAC1B,MAAM,QAAQ;GACd;GACA,aAAa,QAAQ,QAAQ,aAAa,KAAA;GAC1C,iBAAiB,KAAK,kBAAkB,OAAO;GAC/C;GACA,gBAAgB,SAAS,EAAE,eAAe,KAAK,IAAI,KAAA;EACrD,CAAC;CACH;;CAGA,aACE,OACA,SACoB;EACpB,IAAI,CAAC,KAAK,QAAQ,QAAQ,OAAO;EACjC,OAAO,QAAQ,SAAS,KAAK;CAC/B;CAEA,SAAiB,OAAwC;EACvD,MAAM,MAAM;EAQZ,IAAI,CAAC,KAAK,OAAO,KAAA;EACjB,OAAO;GACL,cAAc,IAAI,iBAAiB;GACnC,kBAAkB,IAAI,qBAAqB;GAC3C,aAAa,IAAI,gBAAgB;EACnC;CACF;CAEA,gBAAwB,QAAmC;EACzD,QAAQ,QAAR;GACE,KAAK,UACH,OAAO;GACT,KAAK,cACH,OAAO;GACT,SACE,OAAO;EACX;CACF;;;;;;;CAQA,qBACE,QACA,OACM;EACN,IAAI,WAAW,SACb,MAAM,IAAI,QACR,iDACA,kBACA,UACA,OACA,IACF;CAEJ;CAEA,SAAiB,OAAgB,OAAyB;EACxD,IAAI,iBAAiB,SAAS,OAAO;EACrC,MAAM,OAAQ,OAAyC,QAAQ;EAC/D,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACrE,IAAI,SAAS,6BAA6B,SAAS,uBACjD,OAAO,IAAI,uBAAuB,UAAU,OAAO;EAErD,IACE,SAAS,oCACT,SAAS,yCAET,OAAO,IAAI,mBAAmB,UAAU,KAAK;EAE/C,IACE,SAAS,wBACT,SAAS,iCACT,SAAS,6BACT,SAAS,uBAET,OAAO,IAAI,mBAAmB,SAAS,SAAS,QAAQ;EAE1D,MAAM,SAAS,IAAI,QACjB,SACA,SAAS,oBAAoB,uBAAuB,aACpD,UACA,KACF;EACA,OAAO,QAAQ;EACf,OAAO;CACT;;CAGA,WACE,UACA,SACA,OACqB;EACrB,IAAI,UAAU,WAAW,GACvB,OAAO,IAAI,QACT,8BAA8B,SAAS,QAAQ,KAC/C,cACA,UACA,KACF;EAEF,IAAI,QAAQ,QAAQ,SAClB,OAAO,IAAI,QACT,gCACA,cACA,UACA,KACF;CAGJ;;;;;;CAOA,MAAc,WACZ,QACA,QACqB;EACrB,MAAM,WAAW,aAAa,IAAI,MAAM,KAAK,QAAQ,QAAQ;EAC7D,IAAI,gBAA4B,CAAC;EACjC,MAAM,OAAO,IAAI,SAAe,YAAY;GAC1C,UAAU;EACZ,CAAC;EACD,aAAa,IACX,QACA,SAAS,WAAW,IAAI,CAC1B;EACA,IAAI;GACF,MAAM,UAAU,UAAU,MAAM;EAClC,SAAS,OAAO;GACd,QAAQ;GACR,MAAM;EACR;EACA,OAAO;CACT;;CAGA,cACE,QACA,QACY;EACZ,MAAM,kBAAkB;GACtB,IAAI;IACF,QAAa,QAAQ,OAAO,kBAAkB,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;GACjE,QAAQ,CAER;EACF;EACA,IAAI,OAAO,SAAS,UAAU;EAC9B,OAAO,iBAAiB,SAAS,WAAW,EAAE,MAAM,KAAK,CAAC;EAC1D,aAAa,OAAO,oBAAoB,SAAS,SAAS;CAC5D;CAMA,MAAM,KACJ,UACA,UAAuB,CAAC,GACH;EACrB,MAAM,YAAY,KAAK,IAAI;EAC3B,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,MAAM,QACJ,QAAQ,SAAS,KAAK,QAAQ,gBAAA;EAChC,IAAI;GACF,UAAU,qBAAqB,KAAK,SAAS,SAAS,UAAU,KAAK;GAGrE,MAAM,SAAS,MAAM,KAAK,cAAc,OAAO,QAAQ,MAAM;GAC7D,WAAW,uBAAuB,KAAK,SAAS,OAAO;GACvD,UAAU,MAAM,KAAK,WAAW,QAAQ,SAAS,MAAM;GACvD,SAAS,KAAK,cAAc,QAAQ,SAAS,MAAM;GAEnD,UAAU,OAAO,KAAK,YAAY,OAChC,KAAK,aACH,UACA,SACA,KAAK,aAAa,OAAO,OAAO,GAChC,OACA,IACF,CACF;GACA,MAAM,UAA4B;GAClC,IAAI,UAAU;GACd,MAAM,oBAAoB;IACxB,UAAU;GACZ;GACA,QAAQ,KAAK,aAAa,WAAW;GACrC,oBAAqB,UAAU,KAAA,IAAY;GAC3C,MAAM,WAAW,MAAM,UAAU,SAAS,SAAS,MAAM;GACzD,IAAI,SAAS,OAAO,SAClB,MACE,KAAK,WAAW,UAAU,SAAS,KAAK,KAAK,SAAS,OAAO;GAIjE,MAAM,SAAS,UAAU,UAAU;GACnC,KAAK,qBAAqB,QAAQ,eAAe,KAAK;GACtD,IAAI,CAAC,QACH,MAAM,IAAI,QACR,mCACA,cACA,QACF;GAEF,MAAM,QAAQ,KAAK,SAAS,SAAS,KAAK;GAC1C,UACE,KAAK,SACL,UACA,QACA,SAAS,SAAS,OAClB,OACA,WACA,QAAQ,SACV;GACA,MAAM,YACJ,OAAO,SAAS,YAAY,KACzB,UAGM;IACL,IAAI,KAAK;IACT,MAAM;IACN,UAAU;KACR,MAAM,KAAK,SAAS;KACpB,WAAW,KAAK,SAAS;IAC3B;GACF,EACF;GACF,OAAO;IACL,SAAS,OAAO,SAAS,WAAW;IACpC;IACA,OAAO,SAAS,SAAS;IACzB,cAAc,KAAK,gBAAgB,OAAO,aAAa;IACvD,GAAI,WAAW,SAAS,EAAE,UAAU,IAAI,CAAC;GAC3C;EACF,SAAS,OAAO;GACd,MACE,KAAK,WAAW,UAAU,SAAS,KAAK,KAAK,KAAK,SAAS,OAAO,KAAK;EAE3E,UAAU;GACR,SAAS;GACT,gBAAgB,SAAS,cAAc,CAAC;GACxC,UAAU,QAAQ;EACpB;CACF;CAEA,MAAM,SACJ,QACA,UAA6B,CAAC,GACT;EACrB,OAAO,KAAK,KAAK,CAAC;GAAE,MAAM;GAAQ,SAAS;EAAO,CAAC,GAAG;GACpD,OAAO,QAAQ;GACf,WAAW,QAAQ;GACnB,aAAa,QAAQ;GACrB,MAAM,QAAQ;GACd,GAAG,QAAQ;GACX,MAAM,QAAQ;GACd,QAAQ,QAAQ;GAChB,YAAY,QAAQ;GACpB,QAAQ,QAAQ;GAChB,SAAS,QAAQ;GACjB,WAAW,QAAQ;GACnB,WAAW,QAAQ;GACnB,kBAAkB,QAAQ;EAC5B,CAAC;CACH;CAEA,MAAM,QAAQ,MAAc,UAA0B,CAAC,GAAoB;EACzE,MAAM,WAAwB,CAC5B,GAAI,QAAQ,WAAW,CAAC,GACxB;GAAE,MAAM,QAAQ,QAAQ;GAAQ,SAAS;EAAK,CAChD;EAsBA,QAAO,MArBgB,KAAK,KAAK,UAAU;GACzC,OAAO,QAAQ;GACf,WAAW,QAAQ;GACnB,aAAa,QAAQ;GACrB,MAAM,QAAQ;GACd,MAAM,QAAQ;GACd,QAAQ,QAAQ;GAChB,kBAAkB,QAAQ;GAC1B,iBAAiB,QAAQ;GACzB,gBAAgB,QAAQ;GACxB,gBAAgB,QAAQ;GACxB,MAAM,QAAQ;GACd,OAAO,QAAQ;GACf,YAAY,QAAQ;GACpB,YAAY,QAAQ;GACpB,QAAQ,QAAQ;GAChB,SAAS,QAAQ;GACjB,WAAW,QAAQ;GACnB,WAAW,QAAQ;GACnB,kBAAkB,QAAQ;EAC5B,CAAC,EAAA,CACe;CAClB;;;;;;CAOA,OAAO,OACL,UACA,UAAuB,CAAC,GACD;EACvB,MAAM,YAAY,KAAK,IAAI;EAC3B,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI,eAAe;EACnB,IAAI;EACJ,MAAM,QACJ,QAAQ,SAAS,KAAK,QAAQ,gBAAA;EAChC,IAAI;GACF,UAAU,qBAAqB,KAAK,SAAS,SAAS,UAAU,KAAK;GACrE,SAAS,MAAM,KAAK,cAAc,OAAO,QAAQ,MAAM;GACvD,WAAW,uBAAuB,KAAK,SAAS,OAAO;GACvD,UAAU,MAAM,KAAK,WAAW,QAAQ,SAAS,MAAM;GACvD,SAAS,KAAK,cAAc,QAAQ,SAAS,MAAM;GAEnD,MAAM,UACJ,OAAO,KAAK,YAAY,OACtB,KAAK,aACH,UACA,SACA,KAAK,aAAa,OAAO,OAAO,GAChC,MACA,KACF,CACF;GACF,IAAI;GACJ,IAAI;IACF,SAAS,MAAM,UAAU,SAAS,SAAS,MAAM;GACnD,SAAS,OAAO;IAId,cAAc,QAAQ,MACnB,SAAS,cAAc,KAAK,OAAO,cAAc,CAAC,GAAG,MAAM,SACtD,CAAC,CACT;IACA,MAAM;GACR;GAGA,WAAW,OAAO,OAAO,cAAc,CAAC;GAExC,IAAI;GACJ,IAAI;GACJ,SAAS;IACP,MAAM,OAAO,MAAM,SAAS,KAAK;IACjC,IAAI,KAAK,MAAM;KACb,eAAe;KACf;IACF;IACA,MAAM,QAAQ,KAAK;IACnB,MAAM,SAAS,MAAM,UAAU;IAC/B,eAAe,QAAQ,iBAAiB;IACxC,IAAI,MAAM,OAAO,QAAQ,KAAK,SAAS,MAAM,KAAK;IAClD,MAAM,UAAU,QAAQ,OAAO;IAC/B,IAAI,SAAS;KACX,QAAQ,aAAa,OAAO;KAC5B,MAAM;IACR;GACF;GACA,IAAI,SAAS,OAAO,SAClB,MACE,KAAK,WAAW,UAAU,SAAS,KAAK,KAAK,SAAS,OAAO;GAGjE,KAAK,qBAAqB,cAAc,KAAK;GAC7C,QAAQ,iBAAiB,KAAK,gBAAgB,YAAY,CAAC;GAC3D,UACE,KAAK,SACL,UACA,UACA,OACA,OACA,WACA,QAAQ,SACV;EACF,SAAS,OAAO;GACd,MACE,KAAK,WAAW,UAAU,SAAS,KAAK,KAAK,KAAK,SAAS,OAAO,KAAK;EAE3E,UAAU;GACR,IAAI,YAAY,CAAC,gBAAgB,QAAQ;IAIvC,IAAI;KACF,QAAa,QAAQ,OAAO,kBAAkB,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IACjE,QAAQ,CAER;IACA,MAAM,cAAc,QAAQ;GAC9B;GACA,SAAS;GACT,gBAAgB,SAAS,WAAW;GACpC,UAAU,QAAQ;EACpB;CACF;CAEA,MAAM,YAAY,MAA+B;EAE/C,OAAO,KAAK,KAAK,KAAK,SAAS,CAAC;CAClC;CAMA,MAAM,YAAgC;EACpC,MAAM,SAAS,MAAM,aAAa;EAClC,MAAM,OAEF,KAAK,QAAQ,WAGZ,cAAc,OAAO,kBAAkB;EAC5C,MAAM,kBAAkB,IAAI,IAAI,OAAO,2BAA2B,CAAC,CAAC;EACpE,OAAO,KACJ,QAAQ,WAAW,OAAO,eAAe,CAAC,CAAC,CAC3C,KAAK,WAAW;GACf,MAAM,SAAS,OAAO,eAAe;GACrC,MAAM,YAAY,gBAAgB,IAAI,OAAO,QAAQ;GACrD,OAAO;IACL,IAAI,OAAO;IACX,MAAM,OAAO;IACb,aAAa,OAAO,mBAChB,+BAA+B,KAAK,MAAM,OAAO,gBAAgB,EAAE,aACnE;IACJ,eACE,OAAO,WAAW,uBAAuB;IAC3C,cAAc;KACZ;KACA;KACA,GAAI,YAAY,CAAC,WAAW,IAAI,CAAC;KACjC,GAAI,SAAS,CAAC,QAAQ,IAAI,CAAC;IAC7B;IACA,mBAAmB;IACnB,gBAAgB;GAClB;EACF,CAAC;CACL;CAEA,MAAM,kBAA2C;EAC/C,OAAO,EAAE,GAAG,oBAAoB;CAClC;CAMA,MAAM,MACJ,OACA,WAA6B,CAAC,GACF;EAC5B,OAAO,YAAY,YAAY;CACjC;CAEA,MAAM,WACJ,QACA,WAAkC,CAAC,GACP;EAC5B,OAAO,YAAY,kBAAkB;CACvC;CAEA,MAAM,cACJ,QACA,SACA,WAAoC,CAAC,GACpB;EACjB,OAAO,YAAY,mBAAmB;CACxC;CAEA,MAAM,cACJ,SACA,WAAmC,CAAC,GACF;EAClC,OAAO,YAAY,kBAAkB;CACvC;CAEA,MAAM,yBACJ,UAC6B;EAC7B,OAAO,YAAY,kBAAkB;CACvC;CAEA,MAAM,sBACJ,SACsC;EACtC,OAAO,YAAY,kBAAkB;CACvC;CAEA,MAAM,2BACJ,SACgC;EAChC,OAAO,YAAY,kBAAkB;CACvC;CAEA,MAAM,yBAAyB,SAA4C;EACzE,OAAO,YAAY,kBAAkB;CACvC;CAEA,MAAM,gCAAkD;EACtD,OAAO,YAAY,kBAAkB;CACvC;CAEA,MAAM,iBACJ,OACA,UACsB;EACtB,OAAO,YAAY,gBAAgB;CACrC;CAEA,aAAa,OAAe,UAA8C;EACxE,MAAM,QAAQ,IAAI,QAChB,qEACA,iBACA,QACF;EACA,OAAO,GACJ,OAAO,uBAAuB,EAC7B,YAAY,QAAQ,OAAO,KAAK,EAClC,GACF;CACF;CAEA,MAAM,WAAW,UAA6C;EAC5D,OAAO,YAAY,eAAe;CACpC;CAEA,MAAM,YAAY,UAA8C;EAC9D,OAAO,YAAY,cAAc;CACnC;CAEA,MAAM,UAAU,UAA+C;EAC7D,OAAO,YAAY,eAAe;CACpC;AACF;;AAYA,eAAe,cACb,UACe;CACf,IAAI;EACF,OAAO,EAAE,MAAM,SAAS,KAAK,EAAA,CAAG;CAGlC,QAAQ,CAER;AACF;;AAGA,SAAS,gBACP,SACA,MACM;CACN,IAAI,CAAC,SAAS;CACd,IAAI,MAAM,KAAU,KAAK,SAAS,OAAO;MACpC,QAAQ;AACf;;;;;;AAOA,eAAe,cACb,UACA,QACe;CACf,IAAI;EACF,KAAK,MAAM,SAAS,KAAK,EAAA,CAAG,MAAM;EAClC,MAAM,QAAQ,QAAQ,QAAQ,kBAAkB,CAAC;CACnD,QAAQ;EACN;CACF;CACA,MAAM,cAAc,QAAQ;AAC9B;;AAGA,SAAS,UAAa,SAAqB,QAAkC;CAC3E,IAAI,CAAC,QAAQ,OAAO;CACpB,IAAI,OAAO,SACT,OAAO,QAAQ,OACb,IAAI,QAAQ,gCAAgC,cAAc,QAAQ,CACpE;CAEF,OAAO,IAAI,SAAY,SAAS,WAAW;EACzC,MAAM,gBACJ,OACE,IAAI,QAAQ,gCAAgC,cAAc,QAAQ,CACpE;EACF,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EACxD,QAAQ,MACL,UAAU;GACT,OAAO,oBAAoB,SAAS,OAAO;GAC3C,QAAQ,KAAK;EACf,IACC,UAAU;GACT,OAAO,oBAAoB,SAAS,OAAO;GAC3C,OAAO,KAAK;EACd,CACF;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"webllm-DT31sqUY.js","names":[],"sources":["../../src/shared/providers/webllm.ts"],"sourcesContent":["// biome-ignore-all lint/style/useNamingConvention: WebLLM's OpenAI-shaped wire format and model records are snake_case.\n/**\n * WebLLM provider: runs a chat model entirely in the browser on WebGPU.\n *\n * Backed by `@mlc-ai/web-llm`, an optional peer dependency that is imported\n * lazily on first use, so neither the root entry nor `getAI()` for other\n * provider types ever loads it. Reachable through `getAI({ type: 'webllm' })`\n * and the `@happyvertical/ai/local` subpath.\n *\n * WebLLM's engine API is OpenAI-shaped, so requests and responses map almost\n * one to one. Structured output (`responseSchema`) becomes WebLLM's\n * grammar-constrained `response_format`, which keeps a small model from\n * emitting a value outside the schema.\n */\n\nimport {\n normalizeBaseAIOptions,\n normalizeChatOptions,\n type PreparedRequestControls,\n prepareRequestControls,\n} from '../safety';\nimport type {\n AICapabilities,\n AIInterface,\n AIMessage,\n AIModel,\n AIResponse,\n AIToolCall,\n ChatOptions,\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 WebLLMEngineLike,\n WebLLMLoadProgress,\n WebLLMOptions,\n} from '../types';\nimport {\n AIError,\n ContextLengthError,\n extractTextContent,\n ModelNotFoundError,\n WebGPUUnavailableError,\n WebLLMPeerMissingError,\n} from '../types';\nimport { emitUsage } from './usage';\n\nconst PROVIDER = 'webllm';\n\n/** Small enough for most GPUs (about 0.9 GB of VRAM); override with `defaultModel`. */\nexport const DEFAULT_WEBLLM_MODEL = 'Llama-3.2-1B-Instruct-q4f16_1-MLC';\n\n/** Context window assumed when a model record does not declare one. */\nconst DEFAULT_CONTEXT_LENGTH = 4096;\n\nconst WEBLLM_CAPABILITIES: AICapabilities = {\n chat: true,\n completion: true,\n embeddings: false,\n streaming: true,\n functions: true,\n vision: false,\n fineTuning: false,\n imageEmbeddings: false,\n imageGeneration: false,\n videoGeneration: false,\n tts: false,\n voiceCloning: false,\n voiceDesign: false,\n maxContextLength: DEFAULT_CONTEXT_LENGTH,\n supportedOperations: ['chat', 'completion', 'streaming', 'functions'],\n};\n\n/** Structural subset of the `@mlc-ai/web-llm` module this provider uses. */\ninterface WebLLMModule {\n CreateMLCEngine(\n model: string,\n config?: {\n appConfig?: unknown;\n initProgressCallback?: (report: WebLLMLoadProgress) => void;\n },\n ): Promise<WebLLMEngineLike>;\n prebuiltAppConfig: { model_list: WebLLMModelRecord[] };\n functionCallingModelIds?: string[];\n}\n\ninterface WebLLMModelRecord {\n model_id: string;\n vram_required_MB?: number;\n low_resource_required?: boolean;\n /** WebLLM `ModelType`: 0 or absent = LLM, 1 = embedding, 2 = VLM. */\n model_type?: number;\n overrides?: { context_window_size?: number };\n}\n\ninterface EngineEntry {\n promise: Promise<WebLLMEngineLike>;\n listeners: Set<(report: WebLLMLoadProgress) => void>;\n}\n\n/**\n * Per-engine request queues. WebLLM runs one generation at a time per engine\n * and `interruptGenerate()` stops whichever one is running, so requests are\n * serialized here and only the running request may interrupt.\n */\nconst engineQueues = new WeakMap<object, Promise<void>>();\n\n/** Engines shared by every provider instance that uses the prebuilt model list. */\nconst sharedEngines = new Map<string, EngineEntry>();\n\n/**\n * Imports the optional peer. Isolated so a missing install becomes a typed\n * {@link WebLLMPeerMissingError} instead of a bare module-resolution failure.\n */\nexport async function importWebLLM(): Promise<WebLLMModule> {\n try {\n const loaded = (await import('@mlc-ai/web-llm')) as unknown as\n | (WebLLMModule & { default?: WebLLMModule })\n | undefined;\n const module =\n typeof loaded?.CreateMLCEngine === 'function' ? loaded : loaded?.default;\n if (typeof module?.CreateMLCEngine !== 'function') {\n throw new Error('@mlc-ai/web-llm did not export CreateMLCEngine');\n }\n return module;\n } catch (error) {\n throw new WebLLMPeerMissingError(error);\n }\n}\n\n/** True when this runtime exposes WebGPU (`navigator.gpu`). */\nexport function isWebGPUAvailable(): boolean {\n const nav = (globalThis as { navigator?: { gpu?: unknown } }).navigator;\n return Boolean(nav?.gpu);\n}\n\n/**\n * Unloads every engine this module created and forgets them. Caller-supplied\n * engines are never touched.\n */\nexport async function disposeWebLLMEngines(): Promise<void> {\n const entries = [...sharedEngines.values()];\n sharedEngines.clear();\n await Promise.allSettled(\n entries.map(async (entry) => (await entry.promise).unload?.()),\n );\n}\n\nfunction unsupported(feature: string): never {\n throw new AIError(\n `${feature} is not supported by the webllm provider.`,\n 'NOT_SUPPORTED',\n PROVIDER,\n );\n}\n\nfunction stripUndefined<T extends Record<string, unknown>>(value: T): T {\n return Object.fromEntries(\n Object.entries(value).filter(([, entry]) => entry !== undefined),\n ) as T;\n}\n\n/**\n * In-browser chat provider on WebLLM (WebGPU). See the module header.\n */\nexport class WebLLMProvider implements AIInterface {\n private options: WebLLMOptions;\n /** Model the caller named, if any; a supplied engine is loaded with it already. */\n private readonly explicitModel?: string;\n private factoryEngine?: Promise<WebLLMEngineLike>;\n private readonly localEngines = new Map<string, EngineEntry>();\n\n constructor(options: WebLLMOptions) {\n this.explicitModel = options.defaultModel ?? options.model;\n this.options = normalizeBaseAIOptions({\n ...options,\n defaultModel: this.explicitModel ?? DEFAULT_WEBLLM_MODEL,\n });\n }\n\n // --------------------------------------------------------------------------\n // Engine lifecycle\n // --------------------------------------------------------------------------\n\n private async acquireEngine(\n model: string,\n signal?: AbortSignal,\n ): Promise<WebLLMEngineLike> {\n const supplied = this.options.engine;\n if (supplied) {\n if (typeof supplied !== 'function') return supplied;\n this.factoryEngine ??= supplied().catch((error) => {\n this.factoryEngine = undefined;\n throw error;\n });\n return abortable(this.factoryEngine, signal);\n }\n\n if (!isWebGPUAvailable()) {\n throw new WebGPUUnavailableError(PROVIDER);\n }\n\n // A custom appConfig changes what a model id means, so it must not share\n // an engine with other callers.\n const cache = this.options.appConfig ? this.localEngines : sharedEngines;\n let entry = cache.get(model);\n if (!entry) {\n const listeners = new Set<(report: WebLLMLoadProgress) => void>();\n const created: EngineEntry = {\n listeners,\n promise: this.createEngine(model, listeners),\n };\n created.promise.catch(() => {\n if (cache.get(model) === created) cache.delete(model);\n });\n cache.set(model, created);\n entry = created;\n }\n\n const onProgress = this.options.onLoadProgress;\n if (onProgress) entry.listeners.add(onProgress);\n try {\n return await abortable(entry.promise, signal);\n } finally {\n if (onProgress) entry.listeners.delete(onProgress);\n }\n }\n\n private async createEngine(\n model: string,\n listeners: Set<(report: WebLLMLoadProgress) => void>,\n ): Promise<WebLLMEngineLike> {\n const webllm = await importWebLLM();\n return webllm.CreateMLCEngine(model, {\n ...(this.options.appConfig ? { appConfig: this.options.appConfig } : {}),\n initProgressCallback: (report) => {\n for (const listener of listeners) {\n try {\n listener(report);\n } catch {\n // A progress listener must never break model loading.\n }\n }\n },\n });\n }\n\n // --------------------------------------------------------------------------\n // Request / response mapping\n // --------------------------------------------------------------------------\n\n private mapMessages(messages: AIMessage[]): Record<string, unknown>[] {\n return messages.map((message) => {\n if (message.role === 'user' && Array.isArray(message.content)) {\n return {\n role: 'user',\n content: message.content.map((part) =>\n part.type === 'text'\n ? { type: 'text', text: part.text }\n : { type: 'image_url', image_url: { url: part.image_url.url } },\n ),\n ...(message.name ? { name: message.name } : {}),\n };\n }\n const content = extractTextContent(message.content);\n if (message.role === 'tool' || message.role === 'function') {\n return {\n role: 'tool',\n content,\n tool_call_id: message.tool_call_id ?? message.name ?? '',\n };\n }\n if (message.role === 'assistant') {\n return stripUndefined({\n role: 'assistant',\n content: content || (message.tool_calls?.length ? null : ''),\n tool_calls: message.tool_calls?.map((call) => ({\n id: call.id,\n type: call.type,\n function: {\n name: call.function.name,\n arguments: call.function.arguments,\n },\n })),\n });\n }\n return {\n role: message.role,\n content,\n ...(message.name ? { name: message.name } : {}),\n };\n });\n }\n\n private mapResponseFormat(\n options: Pick<ChatOptions, 'responseFormat' | 'responseSchema'>,\n ): Record<string, unknown> | undefined {\n if (options.responseSchema !== undefined) {\n const schema =\n typeof options.responseSchema === 'string'\n ? options.responseSchema\n : JSON.stringify(options.responseSchema);\n // WebLLM compiles the schema to a grammar and masks logits, so the\n // output cannot leave it.\n return { type: 'json_object', schema };\n }\n return options.responseFormat?.type === 'json_object'\n ? { type: 'json_object' }\n : undefined;\n }\n\n private buildRequest(\n messages: AIMessage[],\n options: ChatOptions & { maxTokens: number },\n model: string | undefined,\n stream: boolean,\n includeTools: boolean,\n ): Record<string, unknown> {\n const tools =\n includeTools && options.tools?.length\n ? options.tools.map((tool) => ({\n type: 'function' as const,\n function: {\n name: tool.function.name,\n description: tool.function.description,\n parameters: tool.function.parameters,\n },\n }))\n : undefined;\n return stripUndefined({\n model,\n messages: this.mapMessages(messages),\n max_tokens: options.maxTokens,\n temperature: options.temperature,\n top_p: options.topP,\n n: options.n,\n stop: options.stop,\n frequency_penalty: options.frequencyPenalty,\n presence_penalty: options.presencePenalty,\n seed: options.seed,\n tools,\n tool_choice: tools ? options.toolChoice : undefined,\n response_format: this.mapResponseFormat(options),\n stream,\n stream_options: stream ? { include_usage: true } : undefined,\n });\n }\n\n /** Model id to send. A caller-supplied engine already has its model loaded. */\n private requestModel(\n model: string,\n options: ChatOptions,\n ): string | undefined {\n if (!this.options.engine) return model;\n return options.model ?? this.explicitModel;\n }\n\n private mapUsage(usage: unknown): TokenUsage | undefined {\n const raw = usage as\n | {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n }\n | undefined\n | null;\n if (!raw) return undefined;\n return {\n promptTokens: raw.prompt_tokens ?? 0,\n completionTokens: raw.completion_tokens ?? 0,\n totalTokens: raw.total_tokens ?? 0,\n };\n }\n\n private mapFinishReason(reason: string | null | undefined) {\n switch (reason) {\n case 'length':\n return 'length' as const;\n case 'tool_calls':\n return 'tool_calls' as const;\n default:\n return 'stop' as const;\n }\n }\n\n /**\n * WebLLM reports `abort` when generation was interrupted. The provider's own\n * aborts are raised before this point, so reaching it means someone else\n * interrupted the engine (for example a caller sharing a supplied engine);\n * a truncated reply must not look like a normal stop.\n */\n private assertNotInterrupted(\n reason: string | null | undefined,\n model: string,\n ): void {\n if (reason === 'abort') {\n throw new AIError(\n 'Generation was interrupted before it finished',\n 'AI_INTERRUPTED',\n PROVIDER,\n model,\n true,\n );\n }\n }\n\n private mapError(error: unknown, model?: string): AIError {\n if (error instanceof AIError) return error;\n const name = (error as { name?: string } | undefined)?.name ?? '';\n const message = error instanceof Error ? error.message : String(error);\n if (name === 'WebGPUNotAvailableError' || name === 'WebGPUNotFoundError') {\n return new WebGPUUnavailableError(PROVIDER, message);\n }\n if (\n name === 'ContextWindowSizeExceededError' ||\n name === 'PrefillChunkSizeSmallerThanImageError'\n ) {\n return new ContextLengthError(PROVIDER, model);\n }\n if (\n name === 'ModelNotFoundError' ||\n name === 'SpecifiedModelNotFoundError' ||\n name === 'UnsupportedModelIdError' ||\n name === 'ModelNotLoadedError'\n ) {\n return new ModelNotFoundError(model ?? message, PROVIDER);\n }\n const mapped = new AIError(\n message,\n name === 'DeviceLostError' ? 'WEBGPU_DEVICE_LOST' : 'API_ERROR',\n PROVIDER,\n model,\n );\n mapped.cause = error;\n return mapped;\n }\n\n /** Throws the right AI error when the request ended by timeout or caller abort. */\n private abortError(\n controls: PreparedRequestControls | undefined,\n options: ChatOptions,\n model?: string,\n ): AIError | undefined {\n if (controls?.didTimeout()) {\n return new AIError(\n `AI request timed out after ${controls.timeout}ms`,\n 'AI_TIMEOUT',\n PROVIDER,\n model,\n );\n }\n if (options.signal?.aborted) {\n return new AIError(\n 'AI request aborted by caller',\n 'AI_ABORTED',\n PROVIDER,\n model,\n );\n }\n return undefined;\n }\n\n /**\n * Waits for this request's turn on the engine; resolves to a release fn. A\n * queued request that is aborted or times out leaves the queue at once and\n * never interrupts the generation ahead of it.\n */\n private async lockEngine(\n engine: WebLLMEngineLike,\n signal: AbortSignal,\n ): Promise<() => void> {\n const previous = engineQueues.get(engine) ?? Promise.resolve();\n let release: () => void = () => {};\n const mine = new Promise<void>((resolve) => {\n release = resolve;\n });\n engineQueues.set(\n engine,\n previous.then(() => mine),\n );\n try {\n await abortable(previous, signal);\n } catch (error) {\n release();\n throw error;\n }\n return release;\n }\n\n /** Interrupts generation when the composed signal fires; returns a detach fn. */\n private bindInterrupt(\n engine: WebLLMEngineLike,\n signal: AbortSignal,\n ): () => void {\n const interrupt = () => {\n try {\n void Promise.resolve(engine.interruptGenerate()).catch(() => {});\n } catch {\n // Best effort: the request is already being abandoned.\n }\n };\n if (signal.aborted) interrupt();\n signal.addEventListener('abort', interrupt, { once: true });\n return () => signal.removeEventListener('abort', interrupt);\n }\n\n // --------------------------------------------------------------------------\n // Chat\n // --------------------------------------------------------------------------\n\n async chat(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): Promise<AIResponse> {\n const startTime = Date.now();\n let controls: PreparedRequestControls | undefined;\n let detach: (() => void) | undefined;\n let release: (() => void) | undefined;\n let pending: Promise<any> | undefined;\n let holdRelease: (() => Promise<unknown> | undefined) | undefined;\n const model =\n options.model || this.options.defaultModel || DEFAULT_WEBLLM_MODEL;\n try {\n options = normalizeChatOptions(this.options, options, PROVIDER, model);\n // Load before arming the request timeout: a multi-GB download must not\n // consume the generation budget.\n const engine = await this.acquireEngine(model, options.signal);\n controls = prepareRequestControls(this.options, options);\n release = await this.lockEngine(engine, controls.signal);\n detach = this.bindInterrupt(engine, controls.signal);\n\n pending = engine.chat.completions.create(\n this.buildRequest(\n messages,\n options as ChatOptions & { maxTokens: number },\n this.requestModel(model, options),\n false,\n true,\n ),\n );\n const started: Promise<unknown> = pending;\n let settled = false;\n const markSettled = () => {\n settled = true;\n };\n started.then(markSettled, markSettled);\n holdRelease = () => (settled ? undefined : started);\n const response = await abortable(pending, controls.signal);\n if (controls.signal.aborted) {\n throw (\n this.abortError(controls, options, model) ?? controls.signal.reason\n );\n }\n\n const choice = response?.choices?.[0];\n this.assertNotInterrupted(choice?.finish_reason, model);\n if (!choice) {\n throw new AIError(\n 'No choices returned from webllm',\n 'NO_CHOICES',\n PROVIDER,\n );\n }\n const usage = this.mapUsage(response.usage);\n emitUsage(\n this.options,\n PROVIDER,\n 'chat',\n response.model || model,\n usage,\n startTime,\n options.usageTags,\n );\n const toolCalls: AIToolCall[] | undefined =\n choice.message?.tool_calls?.map(\n (call: {\n id: string;\n function: { name: string; arguments: string };\n }) => ({\n id: call.id,\n type: 'function' as const,\n function: {\n name: call.function.name,\n arguments: call.function.arguments,\n },\n }),\n );\n return {\n content: choice.message?.content || '',\n usage,\n model: response.model || model,\n finishReason: this.mapFinishReason(choice.finish_reason),\n ...(toolCalls?.length ? { toolCalls } : {}),\n };\n } catch (error) {\n throw (\n this.abortError(controls, options, model) ?? this.mapError(error, model)\n );\n } finally {\n detach?.();\n releaseWhenIdle(release, holdRelease?.());\n controls?.cleanup();\n }\n }\n\n async complete(\n prompt: string,\n options: CompletionOptions = {},\n ): Promise<AIResponse> {\n return this.chat([{ role: 'user', content: prompt }], {\n model: options.model,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n topP: options.topP,\n n: options.n,\n stop: options.stop,\n stream: options.stream,\n onProgress: options.onProgress,\n signal: options.signal,\n timeout: options.timeout,\n reasoning: options.reasoning,\n usageTags: options.usageTags,\n continueOnLength: options.continueOnLength,\n });\n }\n\n async message(text: string, options: MessageOptions = {}): Promise<string> {\n const messages: AIMessage[] = [\n ...(options.history || []),\n { role: options.role || 'user', content: text },\n ];\n 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 responseSchema: options.responseSchema,\n seed: options.seed,\n tools: options.tools,\n toolChoice: options.toolChoice,\n onProgress: options.onProgress,\n signal: options.signal,\n timeout: options.timeout,\n reasoning: options.reasoning,\n usageTags: options.usageTags,\n continueOnLength: options.continueOnLength,\n });\n return response.content;\n }\n\n /**\n * Streams a chat completion as text chunks. Like the other providers, a\n * stream yields text only; use `chat()` for rounds that may call tools.\n * Breaking out of the loop interrupts generation on the engine.\n */\n async *stream(\n messages: AIMessage[],\n options: ChatOptions = {},\n ): AsyncIterable<string> {\n const startTime = Date.now();\n let controls: PreparedRequestControls | undefined;\n let detach: (() => void) | undefined;\n let engine: WebLLMEngineLike | undefined;\n let release: (() => void) | undefined;\n let iterator: AsyncIterator<WebLLMChunk> | undefined;\n let iteratorDone = false;\n let holdRelease: Promise<unknown> | undefined;\n const model =\n options.model || this.options.defaultModel || DEFAULT_WEBLLM_MODEL;\n try {\n options = normalizeChatOptions(this.options, options, PROVIDER, model);\n engine = await this.acquireEngine(model, options.signal);\n controls = prepareRequestControls(this.options, options);\n release = await this.lockEngine(engine, controls.signal);\n detach = this.bindInterrupt(engine, controls.signal);\n\n const pending: Promise<AsyncIterable<WebLLMChunk>> =\n engine.chat.completions.create(\n this.buildRequest(\n messages,\n options as ChatOptions & { maxTokens: number },\n this.requestModel(model, options),\n true,\n false,\n ),\n );\n let chunks: AsyncIterable<WebLLMChunk>;\n try {\n chunks = await abortable(pending, controls.signal);\n } catch (error) {\n // The caller is gone but the engine may still start this request.\n // Keep our queue closed until it has been interrupted and drained, or\n // the next request would wait behind it and could interrupt it.\n holdRelease = pending.then(\n (late) => abandonStream(late[Symbol.asyncIterator](), engine),\n () => {},\n );\n throw error;\n }\n // Iterated by hand: WebLLM releases its per-model lock only when its\n // generator runs to completion, never on an early `return()`.\n iterator = chunks[Symbol.asyncIterator]();\n\n let finishReason: string | null | undefined;\n let usage: TokenUsage | undefined;\n for (;;) {\n const step = await iterator.next();\n if (step.done) {\n iteratorDone = true;\n break;\n }\n const chunk = step.value;\n const choice = chunk.choices?.[0];\n finishReason = choice?.finish_reason ?? finishReason;\n if (chunk.usage) usage = this.mapUsage(chunk.usage);\n const content = choice?.delta?.content;\n if (content) {\n options.onProgress?.(content);\n yield content;\n }\n }\n if (controls.signal.aborted) {\n throw (\n this.abortError(controls, options, model) ?? controls.signal.reason\n );\n }\n this.assertNotInterrupted(finishReason, model);\n options.onFinishReason?.(this.mapFinishReason(finishReason));\n emitUsage(\n this.options,\n PROVIDER,\n 'stream',\n model,\n usage,\n startTime,\n options.usageTags,\n );\n } catch (error) {\n throw (\n this.abortError(controls, options, model) ?? this.mapError(error, model)\n );\n } finally {\n if (iterator && !iteratorDone && engine) {\n // Consumer stopped early (or the loop threw): stop the GPU, then let\n // the engine's generator finish so its lock is released before the\n // next request is admitted.\n try {\n void Promise.resolve(engine.interruptGenerate()).catch(() => {});\n } catch {\n // Best effort.\n }\n await drainIterator(iterator);\n }\n detach?.();\n releaseWhenIdle(release, holdRelease);\n controls?.cleanup();\n }\n }\n\n async countTokens(text: string): Promise<number> {\n // WebLLM exposes no standalone tokenizer; ~4 characters per token.\n return Math.ceil(text.length / 4);\n }\n\n // --------------------------------------------------------------------------\n // Models and capabilities\n // --------------------------------------------------------------------------\n\n async getModels(): Promise<AIModel[]> {\n const webllm = await importWebLLM();\n const list =\n (\n this.options.appConfig as\n | { model_list?: WebLLMModelRecord[] }\n | undefined\n )?.model_list ?? webllm.prebuiltAppConfig.model_list;\n const functionCalling = new Set(webllm.functionCallingModelIds ?? []);\n return list\n .filter((record) => record.model_type !== 1) // embedding models\n .map((record) => {\n const vision = record.model_type === 2;\n const functions = functionCalling.has(record.model_id);\n return {\n id: record.model_id,\n name: record.model_id,\n description: record.vram_required_MB\n ? `Runs on-device via WebGPU (~${Math.round(record.vram_required_MB)} MB VRAM)`\n : 'Runs on-device via WebGPU',\n contextLength:\n record.overrides?.context_window_size ?? DEFAULT_CONTEXT_LENGTH,\n capabilities: [\n 'text',\n 'chat',\n ...(functions ? ['functions'] : []),\n ...(vision ? ['vision'] : []),\n ],\n supportsFunctions: functions,\n supportsVision: vision,\n };\n });\n }\n\n async getCapabilities(): Promise<AICapabilities> {\n return { ...WEBLLM_CAPABILITIES };\n }\n\n // --------------------------------------------------------------------------\n // No in-browser equivalent\n // --------------------------------------------------------------------------\n\n async embed(\n _text: string | string[],\n _options: EmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n return unsupported('Embeddings');\n }\n\n async embedImage(\n _image: string | Buffer,\n _options: ImageEmbeddingOptions = {},\n ): Promise<EmbeddingResponse> {\n return unsupported('Image embeddings');\n }\n\n async describeImage(\n _image: string | Buffer,\n _prompt?: string,\n _options: ImageDescriptionOptions = {},\n ): Promise<string> {\n return unsupported('Image description');\n }\n\n async generateImage(\n _prompt: string,\n _options: ImageGenerationOptions = {},\n ): Promise<ImageGenerationResponse> {\n return unsupported('Image generation');\n }\n\n async submitVideoGenerationJob(\n _options: VideoGenerationOptions,\n ): Promise<VideoGenerationJob> {\n return unsupported('Video generation');\n }\n\n async getVideoGenerationJob(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationStatusResult> {\n return unsupported('Video generation');\n }\n\n async fetchVideoGenerationResult(\n _handle: VideoGenerationJob,\n ): Promise<VideoGenerationResult> {\n return unsupported('Video generation');\n }\n\n async cancelVideoGenerationJob(_handle: VideoGenerationJob): Promise<void> {\n return unsupported('Video generation');\n }\n\n async validateVideoGenerationAccess(): Promise<boolean> {\n return unsupported('Video generation');\n }\n\n async synthesizeSpeech(\n _text: string,\n _options?: TTSOptions,\n ): Promise<TTSResponse> {\n return unsupported('Text-to-speech');\n }\n\n streamSpeech(_text: string, _options?: TTSOptions): AsyncIterable<Buffer> {\n const error = new AIError(\n 'Text-to-speech streaming is not supported by the webllm provider.',\n 'NOT_SUPPORTED',\n PROVIDER,\n );\n return {\n [Symbol.asyncIterator]: () => ({\n next: () => Promise.reject(error),\n }),\n };\n }\n\n async cloneVoice(_options: VoiceCloneOptions): Promise<Voice> {\n return unsupported('Voice cloning');\n }\n\n async designVoice(_options: VoiceDesignOptions): Promise<Voice> {\n return unsupported('Voice design');\n }\n\n async getVoices(_options?: VoiceListOptions): Promise<Voice[]> {\n return unsupported('Voice listing');\n }\n}\n\n/** One streamed WebLLM chunk (structural subset). */\ninterface WebLLMChunk {\n choices?: Array<{\n delta?: { content?: string | null };\n finish_reason?: string | null;\n }>;\n usage?: unknown;\n}\n\n/** Runs a WebLLM stream to its end, ignoring errors; used after interrupting it. */\nasync function drainIterator(\n iterator: AsyncIterator<WebLLMChunk>,\n): Promise<void> {\n try {\n while (!(await iterator.next()).done) {\n // discard\n }\n } catch {\n // The engine already stopped; nothing more to release.\n }\n}\n\n/** Releases an engine queue slot now, or once `idle` has settled. */\nfunction releaseWhenIdle(\n release: (() => void) | undefined,\n idle?: Promise<unknown>,\n): void {\n if (!release) return;\n if (idle) void idle.then(release, release);\n else release();\n}\n\n/**\n * Finishes a stream nobody is reading. WebLLM resets its interrupt flag on the\n * first `next()`, so interrupt only after pulling one chunk, then drain it so\n * the engine's lock is released.\n */\nasync function abandonStream(\n iterator: AsyncIterator<WebLLMChunk>,\n engine: WebLLMEngineLike | undefined,\n): Promise<void> {\n try {\n if ((await iterator.next()).done) return;\n await Promise.resolve(engine?.interruptGenerate());\n } catch {\n return;\n }\n await drainIterator(iterator);\n}\n\n/** Rejects with the signal's reason if it fires first; the underlying work continues. */\nfunction abortable<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {\n if (!signal) return promise;\n if (signal.aborted) {\n return Promise.reject(\n new AIError('AI request aborted by caller', 'AI_ABORTED', PROVIDER),\n );\n }\n return new Promise<T>((resolve, reject) => {\n const onAbort = () =>\n reject(\n new AIError('AI request aborted by caller', 'AI_ABORTED', PROVIDER),\n );\n signal.addEventListener('abort', onAbort, { once: true });\n promise.then(\n (value) => {\n signal.removeEventListener('abort', onAbort);\n resolve(value);\n },\n (error) => {\n signal.removeEventListener('abort', onAbort);\n reject(error);\n },\n );\n });\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { _ as RateLimitError, b as extractTextContent, d as AI_PROVIDER_TYPES, f as AuthenticationError, g as ModelNotFoundError, h as DEFAULT_RATE_LIMIT_MAX_RETRY_DELAY_MS, i as DEFAULT_AI_TIMEOUT_MS, m as ContextLengthError, n as DEFAULT_AI_GENERATION_LIMITS, p as ContentFilterError, r as DEFAULT_AI_MAX_RETRIES, u as AIError, v as WebGPUUnavailableError, x as isRateLimitRetryableSoon, y as WebLLMPeerMissingError } from "./chunks/usage-BaQKo8bc.js";
2
- import { a as getAI, c as OpenAIClient, l as getAIClient, n as AIThread, o as getAIAuto, r as AIMessage, s as AIClient, t as PACKAGE_VERSION_INITIALIZED, u as getOpenAI } from "./chunks/src-nB7u5Xl0.js";
1
+ import { _ as RateLimitError, b as extractTextContent, d as AI_PROVIDER_TYPES, f as AuthenticationError, g as ModelNotFoundError, h as DEFAULT_RATE_LIMIT_MAX_RETRY_DELAY_MS, i as DEFAULT_AI_TIMEOUT_MS, m as ContextLengthError, n as DEFAULT_AI_GENERATION_LIMITS, p as ContentFilterError, r as DEFAULT_AI_MAX_RETRIES, u as AIError, v as WebGPUUnavailableError, x as isRateLimitRetryableSoon, y as WebLLMPeerMissingError } from "./chunks/usage-B9gPCWSa.js";
2
+ import { a as getAI, c as OpenAIClient, l as getAIClient, n as AIThread, o as getAIAuto, r as AIMessage, s as AIClient, t as PACKAGE_VERSION_INITIALIZED, u as getOpenAI } from "./chunks/src-Cc5yFnnQ.js";
3
3
  export { AIClient, AIError, AIMessage as AIMessageClass, AIThread, AI_PROVIDER_TYPES, AuthenticationError, ContentFilterError, ContextLengthError, DEFAULT_AI_GENERATION_LIMITS, DEFAULT_AI_MAX_RETRIES, DEFAULT_AI_TIMEOUT_MS, DEFAULT_RATE_LIMIT_MAX_RETRY_DELAY_MS, ModelNotFoundError, OpenAIClient, PACKAGE_VERSION_INITIALIZED, RateLimitError, WebGPUUnavailableError, WebLLMPeerMissingError, extractTextContent, getAI, getAIAuto, getAIClient, getOpenAI, isRateLimitRetryableSoon };
package/dist/local.js CHANGED
@@ -1,3 +1,3 @@
1
- import { v as WebGPUUnavailableError, y as WebLLMPeerMissingError } from "./chunks/usage-BaQKo8bc.js";
2
- import { a as isWebGPUAvailable, i as importWebLLM, n as WebLLMProvider, r as disposeWebLLMEngines, t as DEFAULT_WEBLLM_MODEL } from "./chunks/webllm-CWgY5nnP.js";
1
+ import { v as WebGPUUnavailableError, y as WebLLMPeerMissingError } from "./chunks/usage-B9gPCWSa.js";
2
+ import { a as isWebGPUAvailable, i as importWebLLM, n as WebLLMProvider, r as disposeWebLLMEngines, t as DEFAULT_WEBLLM_MODEL } from "./chunks/webllm-DT31sqUY.js";
3
3
  export { DEFAULT_WEBLLM_MODEL, WebGPUUnavailableError, WebLLMPeerMissingError, WebLLMProvider, disposeWebLLMEngines, importWebLLM, isWebGPUAvailable };
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/node/factory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EACV,WAAW,EAeZ,MAAM,oBAAiB,CAAC;AAGzB;;GAEG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAmB,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,SAAS,CAC7B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAChC,OAAO,CAAC,WAAW,CAAC,CAyUtB"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/node/factory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EACV,WAAW,EAgBZ,MAAM,oBAAiB,CAAC;AAGzB;;GAEG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAmB,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,SAAS,CAC7B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAChC,OAAO,CAAC,WAAW,CAAC,CA4VtB"}
package/dist/node.js CHANGED
@@ -1,5 +1,5 @@
1
- import { _ as RateLimitError, b as extractTextContent, d as AI_PROVIDER_TYPES, f as AuthenticationError, g as ModelNotFoundError, h as DEFAULT_RATE_LIMIT_MAX_RETRY_DELAY_MS, i as DEFAULT_AI_TIMEOUT_MS, m as ContextLengthError, n as DEFAULT_AI_GENERATION_LIMITS, p as ContentFilterError, r as DEFAULT_AI_MAX_RETRIES, u as AIError, v as WebGPUUnavailableError, x as isRateLimitRetryableSoon, y as WebLLMPeerMissingError } from "./chunks/usage-BaQKo8bc.js";
2
- import { a as getAI, c as OpenAIClient, l as getAIClient, n as AIThread, r as AIMessage, s as AIClient, t as PACKAGE_VERSION_INITIALIZED, u as getOpenAI } from "./chunks/src-nB7u5Xl0.js";
1
+ import { _ as RateLimitError, b as extractTextContent, d as AI_PROVIDER_TYPES, f as AuthenticationError, g as ModelNotFoundError, h as DEFAULT_RATE_LIMIT_MAX_RETRY_DELAY_MS, i as DEFAULT_AI_TIMEOUT_MS, m as ContextLengthError, n as DEFAULT_AI_GENERATION_LIMITS, p as ContentFilterError, r as DEFAULT_AI_MAX_RETRIES, u as AIError, v as WebGPUUnavailableError, x as isRateLimitRetryableSoon, y as WebLLMPeerMissingError } from "./chunks/usage-B9gPCWSa.js";
2
+ import { a as getAI, c as OpenAIClient, l as getAIClient, n as AIThread, r as AIMessage, s as AIClient, t as PACKAGE_VERSION_INITIALIZED, u as getOpenAI } from "./chunks/src-Cc5yFnnQ.js";
3
3
  import { ValidationError, loadEnvConfig } from "@happyvertical/utils";
4
4
  //#region src/node/factory.ts
5
5
  /**
@@ -81,6 +81,11 @@ async function getAIAuto(options = {}) {
81
81
  apiKey: config.apiKey || process.env.TYPESAFE_API_KEY,
82
82
  baseUrl: config.baseUrl || process.env.TYPESAFE_BASE_URL
83
83
  });
84
+ if (config.type === "laya") return getAI({
85
+ ...config,
86
+ apiKey: config.apiKey || process.env.LAYA_API_KEY,
87
+ baseUrl: config.baseUrl || process.env.LAYA_BASE_URL
88
+ });
84
89
  if (config.type === "bifrost") {
85
90
  const resolvedAdminUrl = callerAdminUrl || process.env.BIFROST_ADMIN_URL || process.env.BIFROST_ADMIN_BASE_URL;
86
91
  const resolvedAdminUser = callerAdminUser || process.env.BIFROST_ADMIN_USER || process.env.BIFROST_ADMIN_USERNAME;
@@ -99,7 +104,7 @@ async function getAIAuto(options = {}) {
99
104
  return getAI(config);
100
105
  }
101
106
  try {
102
- return await import("./chunks/src-nB7u5Xl0.js").then((n) => n.i).then((m) => m.getAIAuto(config));
107
+ return await import("./chunks/src-Cc5yFnnQ.js").then((n) => n.i).then((m) => m.getAIAuto(config));
103
108
  } catch (_error) {}
104
109
  const hasLiteLLMSignal = Boolean(process.env.LITELLM_BASE_URL || process.env.LITELLM_API_KEY);
105
110
  if (Boolean(process.env.BIFROST_BASE_URL) && !config.type) return getAI({
@@ -164,6 +169,7 @@ async function getAIAuto(options = {}) {
164
169
  const hasOpenAICompatVideoSignal = Boolean(process.env.OPENAI_COMPAT_VIDEO_BASE_URL);
165
170
  const hasSeevioSignal = Boolean(process.env.SEEVIO_API_KEY || process.env.SEEVIO_BASE_URL);
166
171
  const hasTypeSafeSignal = Boolean(process.env.TYPESAFE_API_KEY || process.env.TYPESAFE_BASE_URL);
172
+ const hasLayaSignal = Boolean(process.env.LAYA_BASE_URL);
167
173
  if (hasModelArkSignal && !config.type) return getAI({
168
174
  ...config,
169
175
  type: "byteplus-modelark",
@@ -188,6 +194,12 @@ async function getAIAuto(options = {}) {
188
194
  apiKey: config.apiKey || process.env.TYPESAFE_API_KEY,
189
195
  baseUrl: config.baseUrl || process.env.TYPESAFE_BASE_URL
190
196
  });
197
+ if (hasLayaSignal && !config.type) return getAI({
198
+ ...config,
199
+ type: "laya",
200
+ apiKey: config.apiKey || process.env.LAYA_API_KEY,
201
+ baseUrl: config.baseUrl || process.env.LAYA_BASE_URL
202
+ });
191
203
  throw new ValidationError("Could not auto-detect AI provider from options or environment", {
192
204
  hint: "Please specify a \"type\" field in options or provide provider-specific credentials/environment variables",
193
205
  supportedTypes: [...AI_PROVIDER_TYPES],
@@ -210,6 +222,7 @@ async function getAIAuto(options = {}) {
210
222
  "AWS_DEFAULT_REGION",
211
223
  "TYPESAFE_API_KEY",
212
224
  "TYPESAFE_BASE_URL",
225
+ "LAYA_BASE_URL",
213
226
  "OPENAI_COMPAT_VIDEO_BASE_URL",
214
227
  "OPENAI_COMPAT_VIDEO_API_KEY",
215
228
  "MODELARK_API_KEY",
package/dist/node.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"node.js","names":[],"sources":["../src/node/factory.ts"],"sourcesContent":["/**\n * Node.js-specific factory functions for creating AI provider instances\n * Includes support for environment variable detection\n */\n\nimport { loadEnvConfig, ValidationError } from '@happyvertical/utils';\nimport { getAI as getAIUniversal } from '../shared/factory';\n\nimport type {\n AIInterface,\n AIProviderType,\n AnthropicOptions,\n BedrockOptions,\n BifrostOptions,\n ByteplusModelArkOptions,\n GeminiOptions,\n GetAIOptions,\n HuggingFaceOptions,\n LiteLLMOptions,\n OllamaOptions,\n OpenAICompatVideoOptions,\n OpenAIOptions,\n SeevioOptions,\n TypeSafeOptions,\n} from '../shared/types';\nimport { AI_PROVIDER_TYPES } from '../shared/types';\n\n/**\n * Re-export the universal getAI function\n */\nexport { getAI } from '../shared/factory';\n\n/**\n * Node.js-enhanced auto-detection of AI provider based on available credentials\n * Includes support for environment variables\n *\n * Supports both HAVE_AI_* environment variables and provider-specific variables:\n * - HAVE_AI_PROVIDER / HAVE_AI_TYPE → provider type\n * - HAVE_AI_API_KEY → fallback API key\n * - LITELLM_BASE_URL / LITELLM_API_KEY / LITELLM_ADMIN_API_KEY → LiteLLM-specific gateway config\n * - BIFROST_BASE_URL / BIFROST_API_KEY / BIFROST_ADMIN_URL / BIFROST_ADMIN_USER / BIFROST_ADMIN_PASSWORD → Bifrost gateway config\n * - OLLAMA_HOST / OLLAMA_BASE_URL / OLLAMA_API_KEY → Ollama host/auth config\n * - OPENAI_API_KEY → OpenAI-specific key\n * - ANTHROPIC_API_KEY → Anthropic-specific key\n * - GEMINI_API_KEY / GOOGLE_API_KEY → Gemini-specific key\n * - HF_TOKEN → Hugging Face token\n * - AWS_* → AWS Bedrock credentials\n * - OPENAI_COMPAT_VIDEO_BASE_URL / OPENAI_COMPAT_VIDEO_API_KEY → openai-compat-video gateway config (checked last)\n * - MODELARK_API_KEY / ARK_API_KEY / MODELARK_BASE_URL → BytePlus ModelArk (Seedance) config (checked last)\n * - SEEVIO_API_KEY / SEEVIO_BASE_URL → Seevio Seedance config (checked last)\n *\n * The two video-only provider types above are intentionally checked *after*\n * every general-purpose provider: they throw `NOT_IMPLEMENTED` for chat()\n * and everything else, so their env signals must never hijack a bare\n * `getAIAuto()` call away from a general-purpose provider. Prefer explicit\n * `type: 'byteplus-modelark'` / `type: 'openai-compat-video'` / `type: 'seevio'` when you want\n * one of these on purpose.\n *\n * @param options - Configuration options that may contain provider-specific credentials\n * @returns Promise resolving to an AI provider instance\n * @throws ValidationError if no provider can be detected from the options\n */\nexport async function getAIAuto(\n options: Record<string, any> = {},\n): Promise<AIInterface> {\n // Load HAVE_AI_* environment variables first\n const config = loadEnvConfig(options, {\n packageName: 'ai',\n schema: {\n provider: 'string',\n type: 'string',\n model: 'string',\n defaultModel: 'string',\n timeout: 'number',\n maxRetries: 'number',\n apiKey: 'string',\n baseUrl: 'string',\n adminApiKey: 'string',\n adminBaseUrl: 'string',\n adminUrl: 'string',\n adminUser: 'string',\n adminUsername: 'string',\n adminPassword: 'string',\n },\n }) as GetAIOptions;\n\n // Normalize 'provider' field to 'type'\n if ('provider' in config && !config.type) {\n (config as any).type = (config as any).provider;\n }\n\n const adminConfig = config as {\n adminApiKey?: string;\n adminBaseUrl?: string;\n adminUrl?: string;\n adminUser?: string;\n adminUsername?: string;\n adminPassword?: string;\n };\n\n // Caller options always win over env: collapse alias pairs (adminUrl/adminBaseUrl\n // and adminUser/adminUsername) on the caller side first, so that an env override\n // for one alias cannot mask an explicit caller value supplied via the other.\n const callerAdminUrl = adminConfig.adminUrl || adminConfig.adminBaseUrl;\n const callerAdminUser = adminConfig.adminUser || adminConfig.adminUsername;\n\n // If type is specified (either from options or env vars), use getAI directly\n if (config.type) {\n if (config.type === 'byteplus-modelark') {\n return getAIUniversal({\n ...config,\n apiKey:\n config.apiKey ||\n process.env.MODELARK_API_KEY ||\n process.env.ARK_API_KEY,\n baseUrl: config.baseUrl || process.env.MODELARK_BASE_URL,\n } as ByteplusModelArkOptions);\n }\n\n if (config.type === 'openai-compat-video') {\n return getAIUniversal({\n ...config,\n apiKey: config.apiKey || process.env.OPENAI_COMPAT_VIDEO_API_KEY,\n baseUrl: config.baseUrl || process.env.OPENAI_COMPAT_VIDEO_BASE_URL,\n } as OpenAICompatVideoOptions);\n }\n\n if (config.type === 'seevio') {\n return getAIUniversal({\n ...config,\n apiKey: config.apiKey || process.env.SEEVIO_API_KEY,\n baseUrl: config.baseUrl || process.env.SEEVIO_BASE_URL,\n } as SeevioOptions);\n }\n\n if (config.type === 'typesafe') {\n return getAIUniversal({\n ...config,\n apiKey: config.apiKey || process.env.TYPESAFE_API_KEY,\n baseUrl: config.baseUrl || process.env.TYPESAFE_BASE_URL,\n } as TypeSafeOptions);\n }\n\n if (config.type === 'bifrost') {\n const resolvedAdminUrl =\n callerAdminUrl ||\n process.env.BIFROST_ADMIN_URL ||\n process.env.BIFROST_ADMIN_BASE_URL;\n const resolvedAdminUser =\n callerAdminUser ||\n process.env.BIFROST_ADMIN_USER ||\n process.env.BIFROST_ADMIN_USERNAME;\n\n return getAIUniversal({\n ...config,\n baseUrl: config.baseUrl || process.env.BIFROST_BASE_URL,\n apiKey: config.apiKey || process.env.BIFROST_API_KEY,\n adminApiKey:\n adminConfig.adminApiKey || process.env.BIFROST_ADMIN_API_KEY,\n adminBaseUrl: resolvedAdminUrl,\n adminUrl: resolvedAdminUrl,\n adminUser: resolvedAdminUser,\n adminUsername: resolvedAdminUser,\n adminPassword:\n adminConfig.adminPassword || process.env.BIFROST_ADMIN_PASSWORD,\n } as BifrostOptions);\n }\n\n return getAIUniversal(config);\n }\n\n // First try universal detection with loaded config\n try {\n return await import('../shared/factory.js').then((m) =>\n m.getAIAuto(config),\n );\n } catch (_error) {\n // If universal detection fails, try Node.js-specific environment variables\n }\n\n // Auto-detect provider based on available credentials including environment variables\n const hasLiteLLMSignal = Boolean(\n process.env.LITELLM_BASE_URL || process.env.LITELLM_API_KEY,\n );\n const hasBifrostSignal = Boolean(process.env.BIFROST_BASE_URL);\n\n if (hasBifrostSignal && !config.type) {\n return getAIUniversal({\n ...config,\n type: 'bifrost',\n baseUrl: config.baseUrl || process.env.BIFROST_BASE_URL,\n apiKey: config.apiKey || process.env.BIFROST_API_KEY,\n adminApiKey: adminConfig.adminApiKey || process.env.BIFROST_ADMIN_API_KEY,\n adminBaseUrl:\n adminConfig.adminBaseUrl || process.env.BIFROST_ADMIN_BASE_URL,\n adminUrl: adminConfig.adminUrl || process.env.BIFROST_ADMIN_URL,\n adminUser:\n adminConfig.adminUser ||\n adminConfig.adminUsername ||\n process.env.BIFROST_ADMIN_USER ||\n process.env.BIFROST_ADMIN_USERNAME,\n adminPassword:\n adminConfig.adminPassword || process.env.BIFROST_ADMIN_PASSWORD,\n } as BifrostOptions);\n }\n\n if (hasLiteLLMSignal && !config.type) {\n return getAIUniversal({\n ...config,\n type: 'litellm',\n baseUrl: config.baseUrl || process.env.LITELLM_BASE_URL,\n apiKey: config.apiKey || process.env.LITELLM_API_KEY,\n adminApiKey: adminConfig.adminApiKey || process.env.LITELLM_ADMIN_API_KEY,\n adminBaseUrl:\n adminConfig.adminBaseUrl || process.env.LITELLM_ADMIN_BASE_URL,\n adminUrl: adminConfig.adminUrl || process.env.LITELLM_ADMIN_URL,\n } as LiteLLMOptions);\n }\n\n if (\n (process.env.OLLAMA_HOST ||\n process.env.OLLAMA_BASE_URL ||\n (process.env.OLLAMA_API_KEY && !process.env.OPENAI_API_KEY)) &&\n !config.type\n ) {\n return getAIUniversal({\n ...config,\n type: 'ollama',\n baseUrl:\n config.baseUrl ||\n process.env.OLLAMA_BASE_URL ||\n process.env.OLLAMA_HOST,\n apiKey: (config as any).apiKey || process.env.OLLAMA_API_KEY,\n } as OllamaOptions);\n }\n\n if ((config.apiKey || process.env.OPENAI_API_KEY) && !config.type) {\n // Default to OpenAI if apiKey is provided without explicit type\n return getAIUniversal({\n ...config,\n type: 'openai',\n apiKey: config.apiKey || process.env.OPENAI_API_KEY,\n } as OpenAIOptions);\n }\n\n if (process.env.ANTHROPIC_API_KEY) {\n return getAIUniversal({\n ...config,\n type: 'anthropic',\n apiKey: process.env.ANTHROPIC_API_KEY,\n } as AnthropicOptions);\n }\n\n if (process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY) {\n return getAIUniversal({\n ...config,\n type: 'gemini',\n apiKey: process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY,\n } as GeminiOptions);\n }\n\n if ((config as any).apiToken || process.env.HF_TOKEN) {\n // Hugging Face uses apiToken or HF_TOKEN\n return getAIUniversal({\n ...config,\n type: 'huggingface',\n apiToken: (config as any).apiToken || process.env.HF_TOKEN,\n } as HuggingFaceOptions);\n }\n\n if (\n ((config as any).region || process.env.AWS_DEFAULT_REGION) &&\n ((config as any).credentials || process.env.AWS_ACCESS_KEY_ID)\n ) {\n // AWS Bedrock uses region and AWS credentials (explicit or from env)\n const bedrockOptions: BedrockOptions = {\n ...config,\n type: 'bedrock',\n region: (config as any).region || process.env.AWS_DEFAULT_REGION,\n };\n\n // Add credentials if available in environment\n if (\n !(config as any).credentials &&\n process.env.AWS_ACCESS_KEY_ID &&\n process.env.AWS_SECRET_ACCESS_KEY\n ) {\n bedrockOptions.credentials = {\n accessKeyId: process.env.AWS_ACCESS_KEY_ID,\n secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,\n };\n }\n\n return getAIUniversal(bedrockOptions);\n }\n\n // Video-only provider detection runs last, deliberately after every\n // general-purpose provider check above: these providers throw\n // NOT_IMPLEMENTED for chat() and every other non-video method, so a bare\n // getAIAuto() must never let a video-only credential (e.g. MODELARK_API_KEY\n // set alongside OPENAI_API_KEY for an unrelated pipeline step) hijack\n // selection away from a general-purpose provider. Explicit\n // `type: 'byteplus-modelark'` / `type: 'openai-compat-video'` selection is\n // the intended way to reach these providers; auto-detection is a\n // last-resort fallback for when nothing general-purpose matches.\n const hasModelArkSignal = Boolean(\n process.env.MODELARK_API_KEY ||\n process.env.ARK_API_KEY ||\n process.env.MODELARK_BASE_URL,\n );\n const hasOpenAICompatVideoSignal = Boolean(\n process.env.OPENAI_COMPAT_VIDEO_BASE_URL,\n );\n const hasSeevioSignal = Boolean(\n process.env.SEEVIO_API_KEY || process.env.SEEVIO_BASE_URL,\n );\n const hasTypeSafeSignal = Boolean(\n process.env.TYPESAFE_API_KEY || process.env.TYPESAFE_BASE_URL,\n );\n\n if (hasModelArkSignal && !config.type) {\n return getAIUniversal({\n ...config,\n type: 'byteplus-modelark',\n apiKey:\n config.apiKey ||\n process.env.MODELARK_API_KEY ||\n process.env.ARK_API_KEY,\n baseUrl: config.baseUrl || process.env.MODELARK_BASE_URL,\n } as ByteplusModelArkOptions);\n }\n\n if (hasOpenAICompatVideoSignal && !config.type) {\n return getAIUniversal({\n ...config,\n type: 'openai-compat-video',\n apiKey: config.apiKey || process.env.OPENAI_COMPAT_VIDEO_API_KEY,\n baseUrl: config.baseUrl || process.env.OPENAI_COMPAT_VIDEO_BASE_URL,\n } as OpenAICompatVideoOptions);\n }\n\n if (hasSeevioSignal && !config.type) {\n return getAIUniversal({\n ...config,\n type: 'seevio',\n apiKey: config.apiKey || process.env.SEEVIO_API_KEY,\n baseUrl: config.baseUrl || process.env.SEEVIO_BASE_URL,\n } as SeevioOptions);\n }\n\n if (hasTypeSafeSignal && !config.type) {\n return getAIUniversal({\n ...config,\n type: 'typesafe',\n apiKey: config.apiKey || process.env.TYPESAFE_API_KEY,\n baseUrl: config.baseUrl || process.env.TYPESAFE_BASE_URL,\n } as TypeSafeOptions);\n }\n\n throw new ValidationError(\n 'Could not auto-detect AI provider from options or environment',\n {\n hint: 'Please specify a \"type\" field in options or provide provider-specific credentials/environment variables',\n supportedTypes: [...AI_PROVIDER_TYPES] as AIProviderType[],\n providedOptions: Object.keys(config),\n checkedEnvVars: [\n 'HAVE_AI_PROVIDER',\n 'HAVE_AI_TYPE',\n 'HAVE_AI_API_KEY',\n 'LITELLM_BASE_URL',\n 'LITELLM_API_KEY',\n 'OLLAMA_HOST',\n 'OLLAMA_BASE_URL',\n 'OLLAMA_API_KEY',\n 'OPENAI_API_KEY',\n 'ANTHROPIC_API_KEY',\n 'GEMINI_API_KEY',\n 'GOOGLE_API_KEY',\n 'HF_TOKEN',\n 'AWS_ACCESS_KEY_ID',\n 'AWS_DEFAULT_REGION',\n 'TYPESAFE_API_KEY',\n 'TYPESAFE_BASE_URL',\n 'OPENAI_COMPAT_VIDEO_BASE_URL',\n 'OPENAI_COMPAT_VIDEO_API_KEY',\n 'MODELARK_API_KEY',\n 'ARK_API_KEY',\n 'MODELARK_BASE_URL',\n 'SEEVIO_API_KEY',\n 'SEEVIO_BASE_URL',\n ],\n },\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"node.js","names":[],"sources":["../src/node/factory.ts"],"sourcesContent":["/**\n * Node.js-specific factory functions for creating AI provider instances\n * Includes support for environment variable detection\n */\n\nimport { loadEnvConfig, ValidationError } from '@happyvertical/utils';\nimport { getAI as getAIUniversal } from '../shared/factory';\n\nimport type {\n AIInterface,\n AIProviderType,\n AnthropicOptions,\n BedrockOptions,\n BifrostOptions,\n ByteplusModelArkOptions,\n GeminiOptions,\n GetAIOptions,\n HuggingFaceOptions,\n LayaOptions,\n LiteLLMOptions,\n OllamaOptions,\n OpenAICompatVideoOptions,\n OpenAIOptions,\n SeevioOptions,\n TypeSafeOptions,\n} from '../shared/types';\nimport { AI_PROVIDER_TYPES } from '../shared/types';\n\n/**\n * Re-export the universal getAI function\n */\nexport { getAI } from '../shared/factory';\n\n/**\n * Node.js-enhanced auto-detection of AI provider based on available credentials\n * Includes support for environment variables\n *\n * Supports both HAVE_AI_* environment variables and provider-specific variables:\n * - HAVE_AI_PROVIDER / HAVE_AI_TYPE → provider type\n * - HAVE_AI_API_KEY → fallback API key\n * - LITELLM_BASE_URL / LITELLM_API_KEY / LITELLM_ADMIN_API_KEY → LiteLLM-specific gateway config\n * - BIFROST_BASE_URL / BIFROST_API_KEY / BIFROST_ADMIN_URL / BIFROST_ADMIN_USER / BIFROST_ADMIN_PASSWORD → Bifrost gateway config\n * - OLLAMA_HOST / OLLAMA_BASE_URL / OLLAMA_API_KEY → Ollama host/auth config\n * - OPENAI_API_KEY → OpenAI-specific key\n * - ANTHROPIC_API_KEY → Anthropic-specific key\n * - GEMINI_API_KEY / GOOGLE_API_KEY → Gemini-specific key\n * - HF_TOKEN → Hugging Face token\n * - AWS_* → AWS Bedrock credentials\n * - OPENAI_COMPAT_VIDEO_BASE_URL / OPENAI_COMPAT_VIDEO_API_KEY → openai-compat-video gateway config (checked last)\n * - MODELARK_API_KEY / ARK_API_KEY / MODELARK_BASE_URL → BytePlus ModelArk (Seedance) config (checked last)\n * - SEEVIO_API_KEY / SEEVIO_BASE_URL → Seevio Seedance config (checked last)\n *\n * The two video-only provider types above are intentionally checked *after*\n * every general-purpose provider: they throw `NOT_IMPLEMENTED` for chat()\n * and everything else, so their env signals must never hijack a bare\n * `getAIAuto()` call away from a general-purpose provider. Prefer explicit\n * `type: 'byteplus-modelark'` / `type: 'openai-compat-video'` / `type: 'seevio'` when you want\n * one of these on purpose.\n *\n * @param options - Configuration options that may contain provider-specific credentials\n * @returns Promise resolving to an AI provider instance\n * @throws ValidationError if no provider can be detected from the options\n */\nexport async function getAIAuto(\n options: Record<string, any> = {},\n): Promise<AIInterface> {\n // Load HAVE_AI_* environment variables first\n const config = loadEnvConfig(options, {\n packageName: 'ai',\n schema: {\n provider: 'string',\n type: 'string',\n model: 'string',\n defaultModel: 'string',\n timeout: 'number',\n maxRetries: 'number',\n apiKey: 'string',\n baseUrl: 'string',\n adminApiKey: 'string',\n adminBaseUrl: 'string',\n adminUrl: 'string',\n adminUser: 'string',\n adminUsername: 'string',\n adminPassword: 'string',\n },\n }) as GetAIOptions;\n\n // Normalize 'provider' field to 'type'\n if ('provider' in config && !config.type) {\n (config as any).type = (config as any).provider;\n }\n\n const adminConfig = config as {\n adminApiKey?: string;\n adminBaseUrl?: string;\n adminUrl?: string;\n adminUser?: string;\n adminUsername?: string;\n adminPassword?: string;\n };\n\n // Caller options always win over env: collapse alias pairs (adminUrl/adminBaseUrl\n // and adminUser/adminUsername) on the caller side first, so that an env override\n // for one alias cannot mask an explicit caller value supplied via the other.\n const callerAdminUrl = adminConfig.adminUrl || adminConfig.adminBaseUrl;\n const callerAdminUser = adminConfig.adminUser || adminConfig.adminUsername;\n\n // If type is specified (either from options or env vars), use getAI directly\n if (config.type) {\n if (config.type === 'byteplus-modelark') {\n return getAIUniversal({\n ...config,\n apiKey:\n config.apiKey ||\n process.env.MODELARK_API_KEY ||\n process.env.ARK_API_KEY,\n baseUrl: config.baseUrl || process.env.MODELARK_BASE_URL,\n } as ByteplusModelArkOptions);\n }\n\n if (config.type === 'openai-compat-video') {\n return getAIUniversal({\n ...config,\n apiKey: config.apiKey || process.env.OPENAI_COMPAT_VIDEO_API_KEY,\n baseUrl: config.baseUrl || process.env.OPENAI_COMPAT_VIDEO_BASE_URL,\n } as OpenAICompatVideoOptions);\n }\n\n if (config.type === 'seevio') {\n return getAIUniversal({\n ...config,\n apiKey: config.apiKey || process.env.SEEVIO_API_KEY,\n baseUrl: config.baseUrl || process.env.SEEVIO_BASE_URL,\n } as SeevioOptions);\n }\n\n if (config.type === 'typesafe') {\n return getAIUniversal({\n ...config,\n apiKey: config.apiKey || process.env.TYPESAFE_API_KEY,\n baseUrl: config.baseUrl || process.env.TYPESAFE_BASE_URL,\n } as TypeSafeOptions);\n }\n\n if (config.type === 'laya') {\n return getAIUniversal({\n ...config,\n apiKey: config.apiKey || process.env.LAYA_API_KEY,\n baseUrl: config.baseUrl || process.env.LAYA_BASE_URL,\n } as LayaOptions);\n }\n\n if (config.type === 'bifrost') {\n const resolvedAdminUrl =\n callerAdminUrl ||\n process.env.BIFROST_ADMIN_URL ||\n process.env.BIFROST_ADMIN_BASE_URL;\n const resolvedAdminUser =\n callerAdminUser ||\n process.env.BIFROST_ADMIN_USER ||\n process.env.BIFROST_ADMIN_USERNAME;\n\n return getAIUniversal({\n ...config,\n baseUrl: config.baseUrl || process.env.BIFROST_BASE_URL,\n apiKey: config.apiKey || process.env.BIFROST_API_KEY,\n adminApiKey:\n adminConfig.adminApiKey || process.env.BIFROST_ADMIN_API_KEY,\n adminBaseUrl: resolvedAdminUrl,\n adminUrl: resolvedAdminUrl,\n adminUser: resolvedAdminUser,\n adminUsername: resolvedAdminUser,\n adminPassword:\n adminConfig.adminPassword || process.env.BIFROST_ADMIN_PASSWORD,\n } as BifrostOptions);\n }\n\n return getAIUniversal(config);\n }\n\n // First try universal detection with loaded config\n try {\n return await import('../shared/factory.js').then((m) =>\n m.getAIAuto(config),\n );\n } catch (_error) {\n // If universal detection fails, try Node.js-specific environment variables\n }\n\n // Auto-detect provider based on available credentials including environment variables\n const hasLiteLLMSignal = Boolean(\n process.env.LITELLM_BASE_URL || process.env.LITELLM_API_KEY,\n );\n const hasBifrostSignal = Boolean(process.env.BIFROST_BASE_URL);\n\n if (hasBifrostSignal && !config.type) {\n return getAIUniversal({\n ...config,\n type: 'bifrost',\n baseUrl: config.baseUrl || process.env.BIFROST_BASE_URL,\n apiKey: config.apiKey || process.env.BIFROST_API_KEY,\n adminApiKey: adminConfig.adminApiKey || process.env.BIFROST_ADMIN_API_KEY,\n adminBaseUrl:\n adminConfig.adminBaseUrl || process.env.BIFROST_ADMIN_BASE_URL,\n adminUrl: adminConfig.adminUrl || process.env.BIFROST_ADMIN_URL,\n adminUser:\n adminConfig.adminUser ||\n adminConfig.adminUsername ||\n process.env.BIFROST_ADMIN_USER ||\n process.env.BIFROST_ADMIN_USERNAME,\n adminPassword:\n adminConfig.adminPassword || process.env.BIFROST_ADMIN_PASSWORD,\n } as BifrostOptions);\n }\n\n if (hasLiteLLMSignal && !config.type) {\n return getAIUniversal({\n ...config,\n type: 'litellm',\n baseUrl: config.baseUrl || process.env.LITELLM_BASE_URL,\n apiKey: config.apiKey || process.env.LITELLM_API_KEY,\n adminApiKey: adminConfig.adminApiKey || process.env.LITELLM_ADMIN_API_KEY,\n adminBaseUrl:\n adminConfig.adminBaseUrl || process.env.LITELLM_ADMIN_BASE_URL,\n adminUrl: adminConfig.adminUrl || process.env.LITELLM_ADMIN_URL,\n } as LiteLLMOptions);\n }\n\n if (\n (process.env.OLLAMA_HOST ||\n process.env.OLLAMA_BASE_URL ||\n (process.env.OLLAMA_API_KEY && !process.env.OPENAI_API_KEY)) &&\n !config.type\n ) {\n return getAIUniversal({\n ...config,\n type: 'ollama',\n baseUrl:\n config.baseUrl ||\n process.env.OLLAMA_BASE_URL ||\n process.env.OLLAMA_HOST,\n apiKey: (config as any).apiKey || process.env.OLLAMA_API_KEY,\n } as OllamaOptions);\n }\n\n if ((config.apiKey || process.env.OPENAI_API_KEY) && !config.type) {\n // Default to OpenAI if apiKey is provided without explicit type\n return getAIUniversal({\n ...config,\n type: 'openai',\n apiKey: config.apiKey || process.env.OPENAI_API_KEY,\n } as OpenAIOptions);\n }\n\n if (process.env.ANTHROPIC_API_KEY) {\n return getAIUniversal({\n ...config,\n type: 'anthropic',\n apiKey: process.env.ANTHROPIC_API_KEY,\n } as AnthropicOptions);\n }\n\n if (process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY) {\n return getAIUniversal({\n ...config,\n type: 'gemini',\n apiKey: process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY,\n } as GeminiOptions);\n }\n\n if ((config as any).apiToken || process.env.HF_TOKEN) {\n // Hugging Face uses apiToken or HF_TOKEN\n return getAIUniversal({\n ...config,\n type: 'huggingface',\n apiToken: (config as any).apiToken || process.env.HF_TOKEN,\n } as HuggingFaceOptions);\n }\n\n if (\n ((config as any).region || process.env.AWS_DEFAULT_REGION) &&\n ((config as any).credentials || process.env.AWS_ACCESS_KEY_ID)\n ) {\n // AWS Bedrock uses region and AWS credentials (explicit or from env)\n const bedrockOptions: BedrockOptions = {\n ...config,\n type: 'bedrock',\n region: (config as any).region || process.env.AWS_DEFAULT_REGION,\n };\n\n // Add credentials if available in environment\n if (\n !(config as any).credentials &&\n process.env.AWS_ACCESS_KEY_ID &&\n process.env.AWS_SECRET_ACCESS_KEY\n ) {\n bedrockOptions.credentials = {\n accessKeyId: process.env.AWS_ACCESS_KEY_ID,\n secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,\n };\n }\n\n return getAIUniversal(bedrockOptions);\n }\n\n // Video-only provider detection runs last, deliberately after every\n // general-purpose provider check above: these providers throw\n // NOT_IMPLEMENTED for chat() and every other non-video method, so a bare\n // getAIAuto() must never let a video-only credential (e.g. MODELARK_API_KEY\n // set alongside OPENAI_API_KEY for an unrelated pipeline step) hijack\n // selection away from a general-purpose provider. Explicit\n // `type: 'byteplus-modelark'` / `type: 'openai-compat-video'` selection is\n // the intended way to reach these providers; auto-detection is a\n // last-resort fallback for when nothing general-purpose matches.\n const hasModelArkSignal = Boolean(\n process.env.MODELARK_API_KEY ||\n process.env.ARK_API_KEY ||\n process.env.MODELARK_BASE_URL,\n );\n const hasOpenAICompatVideoSignal = Boolean(\n process.env.OPENAI_COMPAT_VIDEO_BASE_URL,\n );\n const hasSeevioSignal = Boolean(\n process.env.SEEVIO_API_KEY || process.env.SEEVIO_BASE_URL,\n );\n const hasTypeSafeSignal = Boolean(\n process.env.TYPESAFE_API_KEY || process.env.TYPESAFE_BASE_URL,\n );\n const hasLayaSignal = Boolean(process.env.LAYA_BASE_URL);\n\n if (hasModelArkSignal && !config.type) {\n return getAIUniversal({\n ...config,\n type: 'byteplus-modelark',\n apiKey:\n config.apiKey ||\n process.env.MODELARK_API_KEY ||\n process.env.ARK_API_KEY,\n baseUrl: config.baseUrl || process.env.MODELARK_BASE_URL,\n } as ByteplusModelArkOptions);\n }\n\n if (hasOpenAICompatVideoSignal && !config.type) {\n return getAIUniversal({\n ...config,\n type: 'openai-compat-video',\n apiKey: config.apiKey || process.env.OPENAI_COMPAT_VIDEO_API_KEY,\n baseUrl: config.baseUrl || process.env.OPENAI_COMPAT_VIDEO_BASE_URL,\n } as OpenAICompatVideoOptions);\n }\n\n if (hasSeevioSignal && !config.type) {\n return getAIUniversal({\n ...config,\n type: 'seevio',\n apiKey: config.apiKey || process.env.SEEVIO_API_KEY,\n baseUrl: config.baseUrl || process.env.SEEVIO_BASE_URL,\n } as SeevioOptions);\n }\n\n if (hasTypeSafeSignal && !config.type) {\n return getAIUniversal({\n ...config,\n type: 'typesafe',\n apiKey: config.apiKey || process.env.TYPESAFE_API_KEY,\n baseUrl: config.baseUrl || process.env.TYPESAFE_BASE_URL,\n } as TypeSafeOptions);\n }\n\n if (hasLayaSignal && !config.type) {\n return getAIUniversal({\n ...config,\n type: 'laya',\n apiKey: config.apiKey || process.env.LAYA_API_KEY,\n baseUrl: config.baseUrl || process.env.LAYA_BASE_URL,\n } as LayaOptions);\n }\n\n throw new ValidationError(\n 'Could not auto-detect AI provider from options or environment',\n {\n hint: 'Please specify a \"type\" field in options or provide provider-specific credentials/environment variables',\n supportedTypes: [...AI_PROVIDER_TYPES] as AIProviderType[],\n providedOptions: Object.keys(config),\n checkedEnvVars: [\n 'HAVE_AI_PROVIDER',\n 'HAVE_AI_TYPE',\n 'HAVE_AI_API_KEY',\n 'LITELLM_BASE_URL',\n 'LITELLM_API_KEY',\n 'OLLAMA_HOST',\n 'OLLAMA_BASE_URL',\n 'OLLAMA_API_KEY',\n 'OPENAI_API_KEY',\n 'ANTHROPIC_API_KEY',\n 'GEMINI_API_KEY',\n 'GOOGLE_API_KEY',\n 'HF_TOKEN',\n 'AWS_ACCESS_KEY_ID',\n 'AWS_DEFAULT_REGION',\n 'TYPESAFE_API_KEY',\n 'TYPESAFE_BASE_URL',\n 'LAYA_BASE_URL',\n 'OPENAI_COMPAT_VIDEO_BASE_URL',\n 'OPENAI_COMPAT_VIDEO_API_KEY',\n 'MODELARK_API_KEY',\n 'ARK_API_KEY',\n 'MODELARK_BASE_URL',\n 'SEEVIO_API_KEY',\n 'SEEVIO_BASE_URL',\n ],\n },\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/shared/factory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAU,CAAC;AAIhD,OAAO,KAAK,EACV,WAAW,EAQX,YAAY,EAUb,MAAM,YAAS,CAAC;AAgKjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAsB,KAAK,CACzB,OAAO,GAAE,YAAY,GAAG,eAAoB,GAC3C,OAAO,CAAC,WAAW,CAAC,CAoGtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC3B,OAAO,CAAC,WAAW,CAAC,CA6CtB"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/shared/factory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAU,CAAC;AAIhD,OAAO,KAAK,EACV,WAAW,EAQX,YAAY,EAWb,MAAM,YAAS,CAAC;AAuKjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAsB,KAAK,CACzB,OAAO,GAAE,YAAY,GAAG,eAAoB,GAC3C,OAAO,CAAC,WAAW,CAAC,CAuGtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC3B,OAAO,CAAC,WAAW,CAAC,CA6CtB"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Laya typed-decision provider.
3
+ *
4
+ * Laya (https://github.com/NandhaKishorM/laya) is a self-hosted,
5
+ * non-autoregressive decision engine. Its `laya-serve` HTTP server answers the
6
+ * Jev `POST /v1/systemone` wire shape, with differences this adapter handles
7
+ * itself rather than inheriting from the TypeSafe adapter: optional auth, a
8
+ * checkpoint chosen by the server's router, a `max_len` token window,
9
+ * probabilities rounded to four decimals one at a time, and truncation facts.
10
+ *
11
+ * The public SDK contract calls Laya's `noul` primitive a `predicate`; the
12
+ * server's vocabulary stays confined to this wire translation.
13
+ */
14
+ import type { AICapabilities, AIInterface, AIMessage, AIModel, AIResponse, ChatOptions, CompletionOptions, DecisionRequest, DecisionResult, EmbeddingOptions, EmbeddingResponse, ImageDescriptionOptions, ImageEmbeddingOptions, ImageGenerationOptions, ImageGenerationResponse, LayaDecisionOptions, LayaOptions, MessageOptions, TTSOptions, TTSResponse, VideoGenerationJob, VideoGenerationOptions, VideoGenerationResult, VideoGenerationStatusResult, Voice, VoiceCloneOptions, VoiceDesignOptions, VoiceListOptions } from "../types.js";
15
+ /** A decision-only adapter for a `laya-serve` `/v1/systemone` endpoint. */
16
+ export declare class LayaProvider implements AIInterface {
17
+ private readonly options;
18
+ private readonly root;
19
+ private readonly maxLen?;
20
+ constructor(options: LayaOptions);
21
+ decide(request: DecisionRequest, options?: LayaDecisionOptions): Promise<DecisionResult>;
22
+ private result;
23
+ getModels(): Promise<AIModel[]>;
24
+ getCapabilities(): Promise<AICapabilities>;
25
+ private unsupported;
26
+ chat(_a: AIMessage[], _b?: ChatOptions): Promise<AIResponse>;
27
+ complete(_a: string, _b?: CompletionOptions): Promise<AIResponse>;
28
+ message(_a: string, _b?: MessageOptions): Promise<string>;
29
+ embed(_a: string | string[], _b?: EmbeddingOptions): Promise<EmbeddingResponse>;
30
+ embedImage(_a: string | Buffer, _b?: ImageEmbeddingOptions): Promise<EmbeddingResponse>;
31
+ describeImage(_a: string | Buffer, _b?: string, _c?: ImageDescriptionOptions): Promise<string>;
32
+ generateImage(_a: string, _b?: ImageGenerationOptions): Promise<ImageGenerationResponse>;
33
+ stream(_a: AIMessage[], _b?: ChatOptions): AsyncIterable<string>;
34
+ countTokens(_a: string): Promise<number>;
35
+ submitVideoGenerationJob(_a: VideoGenerationOptions): Promise<VideoGenerationJob>;
36
+ getVideoGenerationJob(_a: VideoGenerationJob): Promise<VideoGenerationStatusResult>;
37
+ fetchVideoGenerationResult(_a: VideoGenerationJob): Promise<VideoGenerationResult>;
38
+ cancelVideoGenerationJob(_a: VideoGenerationJob): Promise<void>;
39
+ validateVideoGenerationAccess(): Promise<boolean>;
40
+ synthesizeSpeech(_a: string, _b?: TTSOptions): Promise<TTSResponse>;
41
+ streamSpeech(_a: string, _b?: TTSOptions): AsyncIterable<Buffer>;
42
+ cloneVoice(_a: VoiceCloneOptions): Promise<Voice>;
43
+ designVoice(_a: VoiceDesignOptions): Promise<Voice>;
44
+ getVoices(_a?: VoiceListOptions): Promise<Voice[]>;
45
+ }
46
+ //# sourceMappingURL=laya.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"laya.d.ts","sourceRoot":"","sources":["../../../src/shared/providers/laya.ts"],"names":[],"mappings":"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"}
@@ -86,7 +86,7 @@ export interface AIRequestEvent {
86
86
  /**
87
87
  * Supported AI provider types
88
88
  */
89
- export declare const AI_PROVIDER_TYPES: readonly ["openai", "litellm", "bifrost", "ollama", "gemini", "anthropic", "huggingface", "bedrock", "claude-cli", "qwen3-tts", "openai-compat-video", "byteplus-modelark", "seevio", "typesafe", "webllm"];
89
+ export declare const AI_PROVIDER_TYPES: readonly ["openai", "litellm", "bifrost", "ollama", "gemini", "anthropic", "huggingface", "bedrock", "claude-cli", "qwen3-tts", "openai-compat-video", "byteplus-modelark", "seevio", "typesafe", "laya", "webllm"];
90
90
  /**
91
91
  * Supported AI provider type union
92
92
  */
@@ -1123,10 +1123,17 @@ export interface DecisionResult {
1123
1123
  /** Actual model returned by the provider. */
1124
1124
  model: string;
1125
1125
  usage?: TokenUsage;
1126
- /** Provider/model provenance. Probabilities are not cross-provider calibrated. */
1126
+ /**
1127
+ * Provider/model provenance. Probabilities are not cross-provider calibrated.
1128
+ *
1129
+ * `details` carries provider-specific JSON facts (for example the requested
1130
+ * model, truncation, or raw values replaced during normalization). Callers
1131
+ * must not depend on keys that a provider does not document.
1132
+ */
1127
1133
  provenance: {
1128
1134
  provider: string;
1129
1135
  model: string;
1136
+ details?: Record<string, DecisionValue>;
1130
1137
  };
1131
1138
  answers: Record<string, DecisionAnswer>;
1132
1139
  }
@@ -2016,10 +2023,37 @@ export interface WebLLMOptions extends BaseAIOptions {
2016
2023
  /** WebLLM `AppConfig` (custom `model_list`, cache backend). Defaults to the prebuilt list. */
2017
2024
  appConfig?: Record<string, any>;
2018
2025
  }
2026
+ /**
2027
+ * Laya decision provider (`laya-serve`, the Jev-compatible `/v1/systemone`
2028
+ * server). `defaultModel` selects a Laya checkpoint (`typed-decisions`,
2029
+ * `english`, `multilingual`, or a name the server registered); omit it to let
2030
+ * the server route by language and question IDs.
2031
+ */
2032
+ export interface LayaOptions extends BaseAIOptions {
2033
+ type: 'laya';
2034
+ /**
2035
+ * Server root, for example `http://localhost:8000`. Required: there is no
2036
+ * hosted Laya service. A trailing `/v1` or `/v1/systemone` is accepted.
2037
+ */
2038
+ baseUrl?: string;
2039
+ /** Sent as a Bearer token. Needed only when the server sets `LAYA_API_KEY`. */
2040
+ apiKey?: string;
2041
+ /**
2042
+ * Default total token window (`max_len`) for each request. Omitted means the
2043
+ * checkpoint's own window. The server rejects values above its
2044
+ * `LAYA_MAX_TOKEN_BUDGET`.
2045
+ */
2046
+ maxLen?: number;
2047
+ }
2048
+ /** Per-request controls for {@link LayaOptions} providers. */
2049
+ export interface LayaDecisionOptions extends DecisionOptions {
2050
+ /** Overrides `maxLen` for this request. */
2051
+ maxLen?: number;
2052
+ }
2019
2053
  /**
2020
2054
  * Union type for all provider options
2021
2055
  */
2022
- export type GetAIOptions = OpenAIOptions | LiteLLMOptions | BifrostOptions | OllamaOptions | GeminiOptions | AnthropicOptions | HuggingFaceOptions | BedrockOptions | ClaudeCliOptions | Qwen3TTSOptions | OpenAICompatVideoOptions | ByteplusModelArkOptions | SeevioOptions | TypeSafeOptions | WebLLMOptions;
2056
+ export type GetAIOptions = OpenAIOptions | LiteLLMOptions | BifrostOptions | OllamaOptions | GeminiOptions | AnthropicOptions | HuggingFaceOptions | BedrockOptions | ClaudeCliOptions | Qwen3TTSOptions | OpenAICompatVideoOptions | ByteplusModelArkOptions | SeevioOptions | TypeSafeOptions | LayaOptions | WebLLMOptions;
2023
2057
  /**
2024
2058
  * Base error class for all AI operations.
2025
2059
  * Provider-specific errors are mapped to subclasses for structured error handling.