infomaniak-ai-provider
Version:
> the project is under active development, for now only text and embedding models are supported.
403 lines (396 loc) • 14.4 kB
JavaScript
;
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;
}
specificationVersion = "v2";
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/1/ai/${getProductId()}/openai`;
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