UNPKG

infomaniak-ai-provider

Version:

The Infomaniak provider for the [AI SDK](https://ai-sdk.dev/docs/introduction) contains language model support for the [Infomaniak AI Tools API](https://www.infomaniak.com/en/hosting/ai-services/open-source-models).

405 lines (398 loc) 14.4 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { createInfomaniak: () => createInfomaniak, infomaniak: () => infomaniak }); module.exports = __toCommonJS(index_exports); // src/infomaniak-provider.ts var import_openai_compatible = require("@ai-sdk/openai-compatible"); var import_provider_utils3 = require("@ai-sdk/provider-utils"); // src/transcription/infomaniak-transcription-model.ts var import_provider_utils2 = require("@ai-sdk/provider-utils"); var import_v43 = require("zod/v4"); // src/infomaniak-error.ts var import_provider_utils = require("@ai-sdk/provider-utils"); var import_v4 = require("zod/v4"); var infomaniakErrorDataSchema = import_v4.z.object({ result: import_v4.z.string(), error: import_v4.z.object({ code: import_v4.z.string(), description: import_v4.z.string(), errors: import_v4.z.array(import_v4.z.any()) }).optional() }); var infomaniakFailedResponseHandler = (0, import_provider_utils.createJsonErrorResponseHandler)({ errorSchema: infomaniakErrorDataSchema, errorToMessage: (data) => { var _a; return ((_a = data.error) == null ? void 0 : _a.description) || data.result; } }); // src/transcription/infomaniak-transcription-options.ts var import_v42 = require("zod/v4"); var infomaniakTranscriptionProviderOptions = import_v42.z.object({ /** * Only if timestamp_granularities[]:word is True, merge these punctuation symbols with the previous word */ appendPunctuations: import_v42.z.array(import_v42.z.string()).optional(), /** * Defines the maximum duration for an active segment in sec. For subtitle tasks, it's recommended to set this to a short duration (5-10 seconds) to avoid long sentences. */ chunkLength: import_v42.z.number().min(2).max(30).optional(), /** * Subtitle task. Underline each word as it is spoken in srt and vtt output formats (requires timestamp_granularities[]:word) */ highlightWords: import_v42.z.boolean().optional(), /** * The language of the input audio in ISO-639-1 format. */ language: import_v42.z.string().optional(), /** * Subtitle task. The maximum number of lines in a segment in srt and vtt output formats (requires timestamp_granularities[]:word) */ maxLineCount: import_v42.z.number().min(1).max(1e3).optional(), /** * Subtitle task. The maximum number of characters in a line before breaking the line in srt and vtt output formats (requires timestamp_granularities[]:word) */ maxLineWidth: import_v42.z.number().min(1).max(1e3).optional(), /** * Subtitle task. The maximum number of words in a segment (requires timestamp_granularities[]:word) */ maxWordsPerLine: import_v42.z.number().min(1).max(1e3).optional(), /** * If the no_speech probability is higher than this value AND the average log probability over sampled tokens is below log_prob_threshold, consider the segment as silent. */ noSpeechThreshold: import_v42.z.number().optional(), /** * Only if timestamp_granularities[]:word is True, merge these punctuation symbols with the next word */ prependPunctuations: import_v42.z.array(import_v42.z.string()).optional(), /** * An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language. */ prompt: import_v42.z.string().optional(), /** * The format of the transcript output * @default 'json' */ responseFormat: import_v42.z.enum(["text", "json", "srt", "verbose_json", "vtt"]).default("json").optional(), /** * The timestamp granularities to populate for this transcription. * @default ['segment'] */ timestampGranularities: import_v42.z.array(import_v42.z.enum(["word", "segment"])).default(["segment"]).optional() }); // src/transcription/infomaniak-transcription-model.ts var languageMap = { afrikaans: "af", arabic: "ar", armenian: "hy", azerbaijani: "az", belarusian: "be", bosnian: "bs", bulgarian: "bg", catalan: "ca", chinese: "zh", croatian: "hr", czech: "cs", danish: "da", dutch: "nl", english: "en", estonian: "et", finnish: "fi", french: "fr", galician: "gl", german: "de", greek: "el", hebrew: "he", hindi: "hi", hungarian: "hu", icelandic: "is", indonesian: "id", italian: "it", japanese: "ja", kannada: "kn", kazakh: "kk", korean: "ko", latvian: "lv", lithuanian: "lt", macedonian: "mk", malay: "ms", marathi: "mr", maori: "mi", nepali: "ne", norwegian: "no", persian: "fa", polish: "pl", portuguese: "pt", romanian: "ro", russian: "ru", serbian: "sr", slovak: "sk", slovenian: "sl", spanish: "es", swahili: "sw", swedish: "sv", tagalog: "tl", tamil: "ta", thai: "th", turkish: "tr", ukrainian: "uk", urdu: "ur", vietnamese: "vi", welsh: "cy" }; var infomaniakTranscriptionResponseSchema = import_v43.z.object({ batch_id: import_v43.z.uuid() }); var infomaniakBatchResponseSchema = import_v43.z.object({ status: import_v43.z.enum(["pending", "success", "error"]), url: import_v43.z.url().nullable(), file_name: import_v43.z.string().nullable(), file_size: import_v43.z.number().nullable(), data: import_v43.z.json().nullable() }); var infomaniakTranscriptionDataSchema = import_v43.z.object({ duration: import_v43.z.number().nullish(), language: import_v43.z.string().nullish(), segments: import_v43.z.array(import_v43.z.object({ avg_logprob: import_v43.z.number(), compression_ratio: import_v43.z.number(), end: import_v43.z.number(), id: import_v43.z.number(), no_speech_prob: import_v43.z.number().min(0).max(1), seek: import_v43.z.number(), start: import_v43.z.number(), temperature: import_v43.z.number().min(0).max(1), text: import_v43.z.string(), tokens: import_v43.z.array(import_v43.z.number()) })).nullish(), task: import_v43.z.string().nullish(), text: import_v43.z.string(), words: import_v43.z.array(import_v43.z.object({ end: import_v43.z.number(), start: import_v43.z.number(), word: import_v43.z.string() })).nullish() }); var InfomaniakTranscriptionModel = class { constructor(modelId, config) { this.modelId = modelId; this.config = config; } modelId; config; specificationVersion = "v3"; get provider() { return this.config.provider; } async getArgs({ audio, mediaType, providerOptions }) { const warnings = []; const infomaniakOptions = await (0, import_provider_utils2.parseProviderOptions)({ provider: "infomaniak", providerOptions, schema: infomaniakTranscriptionProviderOptions }); const formData = new FormData(); const blob = audio instanceof Uint8Array ? new Blob([audio]) : new Blob([(0, import_provider_utils2.convertBase64ToUint8Array)(audio)]); formData.append("model", this.modelId); formData.append("file", new File([blob], "audio", { type: mediaType })); if (infomaniakOptions) { const transcriptionModelOptions = { append_punctuations: infomaniakOptions.appendPunctuations, chunk_length: infomaniakOptions.chunkLength, highlight_words: infomaniakOptions.highlightWords, language: infomaniakOptions.language, max_line_count: infomaniakOptions.maxLineCount, max_line_width: infomaniakOptions.maxLineWidth, max_words_per_line: infomaniakOptions.maxWordsPerLine, no_speech_threshold: infomaniakOptions.noSpeechThreshold, prepend_punctuations: infomaniakOptions.prependPunctuations, prompt: infomaniakOptions.prompt, response_format: "verbose_json", // always use verbose_json to get correct data shape timestamp_granularities: infomaniakOptions.timestampGranularities || ["segment"] }; for (const [key, value] of Object.entries(transcriptionModelOptions)) { if (value != null) { if (Array.isArray(value)) { value.forEach((item) => formData.append(`${key}[]`, String(item))); } else { formData.append(key, String(value)); } } } } return { formData, warnings }; } async pollForResults(batchId, options, maxRetries = 60, delayMs = 2e3) { for (let attempt = 0; attempt < maxRetries; attempt++) { const url = this.config.url({ path: `/results/${batchId}` }); const { value: response, responseHeaders } = await (0, import_provider_utils2.getFromApi)({ url: url.replace("/openai", ""), // remove the /openai part, maybe remove it from the base anyway headers: (0, import_provider_utils2.combineHeaders)(this.config.headers(), options.headers), failedResponseHandler: infomaniakFailedResponseHandler, successfulResponseHandler: (0, import_provider_utils2.createJsonResponseHandler)( infomaniakBatchResponseSchema ), abortSignal: options.abortSignal, fetch: this.config.fetch }); if (response.status === "success" && response.data) { const parsedData = infomaniakTranscriptionDataSchema.parse( JSON.parse(response.data) ); return { result: parsedData, responseHeaders, rawResponse: JSON.stringify(response) }; } if (attempt < maxRetries - 1) { await new Promise((resolve) => setTimeout(resolve, delayMs)); } } throw new Error(`Transcription timed out after ${maxRetries} attempts`); } async doGenerate(options) { var _a, _b, _c, _d; const currentDate = ((_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) ?? /* @__PURE__ */ new Date(); const { formData, warnings } = await this.getArgs(options); const { value: batchResponse } = await (0, import_provider_utils2.postFormDataToApi)({ url: this.config.url({ path: "/audio/transcriptions" }), headers: (0, import_provider_utils2.combineHeaders)(this.config.headers(), options.headers), formData, failedResponseHandler: infomaniakFailedResponseHandler, successfulResponseHandler: (0, import_provider_utils2.createJsonResponseHandler)( infomaniakTranscriptionResponseSchema ), abortSignal: options.abortSignal, fetch: this.config.fetch }); const { result, responseHeaders, rawResponse } = await this.pollForResults( batchResponse.batch_id, options ); const language = result.language != null && result.language in languageMap ? languageMap[result.language] : void 0; return { text: result.text, segments: ((_c = result.segments) == null ? void 0 : _c.map((segment) => ({ text: segment.text, startSecond: segment.start, endSecond: segment.end }))) ?? ((_d = result.words) == null ? void 0 : _d.map((word) => ({ text: word.word, startSecond: word.start, endSecond: word.end }))) ?? [], language, durationInSeconds: result.duration ?? void 0, warnings, response: { timestamp: currentDate, modelId: this.modelId, headers: responseHeaders, body: rawResponse }, providerMetadata: { infomaniak: { batchId: batchResponse.batch_id } } }; } }; // src/infomaniak-provider.ts function createInfomaniak(options = {}) { const getProductId = () => (0, import_provider_utils3.loadSetting)({ environmentVariableName: "INFOMANIAK_PRODUCT_ID", settingName: "productId", description: "Infomaniak product ID", settingValue: options.productId }); const getApiUrl = () => `https://api.infomaniak.com/2/ai/${getProductId()}/openai/v1`; const getHeaders = () => ({ Authorization: `Bearer ${(0, import_provider_utils3.loadApiKey)({ apiKey: options.apiKey, environmentVariableName: "INFOMANIAK_API_KEY", description: "Infomaniak API key" })}`, ...options.headers }); const getCommonModelConfig = (modelType) => ({ provider: `infomaniak.${modelType}`, url: ({ path }) => { const url = new URL(`${getApiUrl()}${path}`); return url.toString(); }, headers: getHeaders, fetch: options.fetch }); const createChatModel = (modelId) => { return new import_openai_compatible.OpenAICompatibleChatLanguageModel( modelId, { ...getCommonModelConfig("chat"), includeUsage: options.includeUsage ?? false } ); }; const createTextEmbeddingModel = (modelId) => { return new import_openai_compatible.OpenAICompatibleEmbeddingModel( modelId, { ...getCommonModelConfig("text_embedding"), url: ({ path }) => { const url = new URL(`${getApiUrl()}/v1${path}`); return url.toString(); } } ); }; const createImageModel = (modelId) => new import_openai_compatible.OpenAICompatibleImageModel(modelId, getCommonModelConfig("image")); const createTranscriptionModel = (modelId) => new InfomaniakTranscriptionModel(modelId, { ...getCommonModelConfig("transcription") }); const provider = (modelId) => createChatModel(modelId); provider.chatModel = createChatModel; provider.languageModel = createChatModel; provider.textEmbeddingModel = createTextEmbeddingModel; provider.imageModel = createImageModel; provider.transcription = createTranscriptionModel; return provider; } var infomaniak = createInfomaniak(); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { createInfomaniak, infomaniak }); //# sourceMappingURL=index.js.map