UNPKG

sarvam-ai-sdk

Version:

Sarvam provider support for Vercel's AI-SDK

1,298 lines (1,278 loc) 35.2 kB
import { combineHeaders, createEventSourceResponseHandler, createJsonErrorResponseHandler, createJsonResponseHandler, generateId, isParsableJson, loadApiKey, parseProviderOptions, postFormDataToApi, postJsonToApi, withoutTrailingSlash } from "@ai-sdk/provider-utils"; import { InvalidResponseDataError, UnsupportedFunctionalityError } from "@ai-sdk/provider"; import { z } from "zod"; //#region src/error.ts const sarvamErrorDataSchema = z.object({ error: z.object({ request_id: z.string().nullable().optional(), message: z.string(), code: z.string() }) }); const sarvamFailedResponseHandler = createJsonErrorResponseHandler({ errorSchema: sarvamErrorDataSchema, errorToMessage: (data) => data.error.message }); //#endregion //#region src/chat/prepare-tools.ts function prepareTools({ tools, toolChoice }, addWarning) { const finalTools = tools?.length ? tools : void 0; if (finalTools == null) return { tools: void 0, tool_choice: void 0 }; const sarvamTools = []; for (const tool of finalTools) if (tool.type === "provider") addWarning({ type: "unsupported", feature: tool.name }); else sarvamTools.push({ type: "function", function: { name: tool.name, description: tool.description, parameters: tool.inputSchema } }); if (toolChoice == null) return { tools: sarvamTools, tool_choice: void 0 }; const type = toolChoice.type; switch (type) { case "auto": case "none": case "required": return { tools: sarvamTools, tool_choice: type }; case "tool": return { tools: sarvamTools, tool_choice: { type: "function", function: { name: toolChoice.toolName } } }; default: throw new UnsupportedFunctionalityError({ functionality: `Unsupported tool choice type: ${type}` }); } } function getToolResultContent(output) { switch (output.type) { case "text": case "error-text": return output.value; case "json": case "error-json": case "content": return JSON.stringify(output.value); case "execution-denied": return output.reason ?? "Tool execution was denied."; default: { const _exhaustiveCheck = output; return JSON.stringify(_exhaustiveCheck); } } } function prepareResponseFormat(responseFormat) { if (!responseFormat || responseFormat.type !== "json") return; if (responseFormat.schema) return { type: "json_schema", json_schema: { name: responseFormat.name ?? "response", description: responseFormat.description, schema: responseFormat.schema, strict: true } }; return { type: "json_object" }; } function prepareResponseFormatAsTool(responseFormat) { return { tool_choice: { type: "function", function: { name: responseFormat.name ?? "response" } }, tools: [{ type: "function", function: { name: responseFormat.name ?? "response", description: responseFormat.description, parameters: responseFormat.schema } }] }; } //#endregion //#region src/chat/convert-to-chat-messages.ts function convertToChatMessages(prompt) { const messages = []; for (const message of prompt) switch (message.role) { case "system": messages.push({ role: "system", content: message.content }); break; case "user": for (const part of message.content) { if (part.type !== "text") throw new UnsupportedFunctionalityError({ functionality: `Unsupported content part type: ${part.type}` }); if (part.text.trim()) messages.push({ role: "user", content: part.text }); } break; case "assistant": { let text = ""; const toolCalls = []; for (const part of message.content) switch (part.type) { case "text": text += part.text; break; case "tool-call": toolCalls.push({ id: part.toolCallId, type: "function", function: { name: part.toolName, arguments: typeof part.input === "string" ? part.input : JSON.stringify(part.input) } }); break; case "tool-result": break; } messages.push({ role: "assistant", content: text, tool_calls: toolCalls.length > 0 ? toolCalls : void 0 }); break; } case "tool": for (const part of message.content) if (part.type === "tool-result") messages.push({ role: "tool", tool_call_id: part.toolCallId, content: getToolResultContent(part.output) }); break; default: { const _exhaustiveCheck = message; throw new Error(`Unsupported role: ${_exhaustiveCheck}`); } } return messages; } //#endregion //#region src/chat/settings.ts const chatSettingsSchema = z.object({ reasoning_effort: z.enum([ "none", "low", "medium", "high" ]).transform((re) => re === "none" ? null : re).nullish(), wiki_grounding: z.boolean().nullish(), n: z.number().min(1).max(128).nullish() }); const chatResponseSchema = z.object({ id: z.string().nullish(), created: z.number().nullish(), model: z.string().nullish(), object: z.string().nullish(), service_tier: z.string().nullish(), system_fingerprint: z.string().nullish(), choices: z.array(z.object({ index: z.number(), finish_reason: z.string().nullish(), logprobs: z.object({}).nullish(), message: z.object({ content: z.string().nullish(), reasoning_content: z.string().nullish(), refusal: z.string().nullish(), tool_calls: z.array(z.object({ id: z.string().nullish(), type: z.literal("function"), function: z.object({ name: z.string(), arguments: z.string() }) })).nullish() }) })), usage: z.object({ completion_tokens: z.number().nullish(), prompt_tokens: z.number().nullish(), total_tokens: z.number().nullish() }).nullish() }); const chatChunkSchema = z.union([z.object({ id: z.string().nullish(), created: z.number().nullish(), model: z.string().nullish(), choices: z.array(z.object({ delta: z.object({ content: z.string().nullish(), reasoning: z.string().nullish(), tool_calls: z.array(z.object({ index: z.number(), id: z.string().nullish(), type: z.literal("function").optional(), function: z.object({ name: z.string().nullish(), arguments: z.string().nullish() }) })).nullish() }).nullish(), finish_reason: z.string().nullable().optional(), index: z.number() })), x_sarvam: z.object({ usage: z.object({ prompt_tokens: z.number().nullish(), completion_tokens: z.number().nullish() }).nullish() }).nullish() }), sarvamErrorDataSchema]); //#endregion //#region src/chat/utils.ts function mapFinishReason(finishReason) { switch (finishReason) { case "stop": return "stop"; case "length": return "length"; case "content_filter": return "content-filter"; case "function_call": case "tool_calls": return "tool-calls"; default: return "other"; } } function getResponseMetadata({ id, model, created }) { return { id: id ?? void 0, modelId: model ?? void 0, timestamp: created != null ? /* @__PURE__ */ new Date(created * 1e3) : void 0 }; } //#endregion //#region src/chat/language-model.ts var SarvamChatLanguageModel = class { constructor(modelId, settings, config) { this.specificationVersion = "v4"; this.modelId = modelId; this.settings = settings; this.config = config; } get provider() { return this.config.provider; } get supportedUrls() { return {}; } async getArgs(options, stream) { const { prompt, maxOutputTokens, temperature, topP, topK, frequencyPenalty, presencePenalty, stopSequences, responseFormat, seed, tools, toolChoice, providerOptions, reasoning } = options; const warnings = []; if (topK) warnings.push({ type: "unsupported", feature: "topK" }); const sarvamOptions = await parseProviderOptions({ provider: "sarvam", providerOptions: { sarvam: { reasoning_effort: reasoning, ...providerOptions?.sarvam, ...this.settings } }, schema: chatSettingsSchema }); const jsonSchemaAsTool = responseFormat?.type === "json" && !stream && !this.settings.experimental_json_mode; const jsonMode = this.settings.experimental_json_mode && !stream; if (jsonMode) warnings.push({ type: "other", message: "Experimental json_mode is enabled" }); const baseArgs = { model: this.modelId, messages: convertToChatMessages(prompt), response_format: jsonMode ? prepareResponseFormat(responseFormat) : void 0, max_tokens: maxOutputTokens, temperature, top_p: topP, frequency_penalty: frequencyPenalty, presence_penalty: presencePenalty, stop: stopSequences, seed, ...sarvamOptions }; const toolsArg = jsonSchemaAsTool ? prepareResponseFormatAsTool(responseFormat) : prepareTools({ tools, toolChoice }, (w) => warnings.push(w)); return { args: { ...baseArgs, ...toolsArg }, warnings, jsonSchemaAsTool }; } async doGenerate(options) { const { args, warnings, jsonSchemaAsTool } = await this.getArgs(options, false); const { responseHeaders, value: response, rawValue: rawResponse } = await postJsonToApi({ url: this.config.url({ path: "/chat/completions", modelId: this.modelId }), headers: combineHeaders(this.config.headers?.(), options.headers), body: args, failedResponseHandler: sarvamFailedResponseHandler, successfulResponseHandler: createJsonResponseHandler(chatResponseSchema), abortSignal: options.abortSignal, fetch: this.config.fetch }); const choice = response.choices[0]; if (!choice) throw new InvalidResponseDataError({ data: response, message: "No choices returned in response" }); const content = []; if (choice.message.content) content.push({ type: "text", text: choice.message.content }); if (choice.message.reasoning_content) content.push({ type: "reasoning", text: choice.message.reasoning_content }); if (choice.message.tool_calls && choice.message.tool_calls.length > 0) for (const toolCall of choice.message.tool_calls) if (jsonSchemaAsTool) content.push({ type: "text", text: toolCall.function.arguments }); else content.push({ type: "tool-call", toolCallId: toolCall.id ?? (this.config.generateId ?? generateId)(), toolName: toolCall.function.name, input: toolCall.function.arguments }); const finishReason = mapFinishReason(choice.finish_reason); return { content, finishReason: { unified: jsonSchemaAsTool && finishReason === "tool-calls" ? "stop" : finishReason, raw: choice.finish_reason ?? void 0 }, usage: { inputTokens: { total: response.usage?.prompt_tokens ?? void 0, noCache: void 0, cacheRead: void 0, cacheWrite: void 0 }, outputTokens: { total: response.usage?.completion_tokens ?? void 0, text: void 0, reasoning: void 0 } }, providerMetadata: { sarvam: { system_fingerprint: response.system_fingerprint, service_tier: response.service_tier } }, warnings, request: { body: args }, response: { headers: responseHeaders, body: rawResponse, id: response.id ?? void 0, modelId: response.model ?? void 0, timestamp: response.created ? /* @__PURE__ */ new Date(response.created * 1e3) : void 0 } }; } async doStream(options) { const { args } = await this.getArgs(options, true); const { responseHeaders, value: response } = await postJsonToApi({ url: this.config.url({ path: "/chat/completions", modelId: this.modelId }), headers: combineHeaders(this.config.headers?.(), options.headers), body: { ...args, stream: true }, failedResponseHandler: sarvamFailedResponseHandler, successfulResponseHandler: createEventSourceResponseHandler(chatChunkSchema), abortSignal: options.abortSignal, fetch: this.config.fetch }); const toolCalls = []; let finishReason = { unified: "other", raw: void 0 }; let usage = { inputTokens: { total: void 0, noCache: void 0, cacheRead: void 0, cacheWrite: void 0 }, outputTokens: { total: void 0, text: void 0, reasoning: void 0 } }; let isFirstChunk = true; return { stream: response.pipeThrough(new TransformStream({ transform(chunk, controller) { if (!chunk.success) { finishReason = { unified: "error", raw: void 0 }; controller.enqueue({ type: "error", error: chunk.error }); return; } const value = chunk.value; if ("error" in value) { finishReason = { unified: "error", raw: void 0 }; controller.enqueue({ type: "error", error: value.error }); return; } if (isFirstChunk) { isFirstChunk = false; const metadata = getResponseMetadata(value); if (metadata.id || metadata.timestamp || metadata.modelId) controller.enqueue({ type: "response-metadata", ...metadata }); } if (value.x_sarvam?.usage != null) usage = { inputTokens: { total: value.x_sarvam.usage.prompt_tokens ?? void 0, noCache: void 0, cacheRead: void 0, cacheWrite: void 0 }, outputTokens: { total: value.x_sarvam.usage.completion_tokens ?? void 0, text: void 0, reasoning: void 0 } }; const choice = value.choices[0]; if (choice?.finish_reason != null) finishReason = { unified: mapFinishReason(choice.finish_reason), raw: choice.finish_reason }; if (choice?.delta == null) return; const delta = choice.delta; if (delta.reasoning != null && delta.reasoning.length > 0) controller.enqueue({ type: "reasoning-delta", id: "reasoning-0", delta: delta.reasoning }); if (delta.content != null && delta.content.length > 0) controller.enqueue({ type: "text-delta", id: "text-0", delta: delta.content }); if (delta.tool_calls != null) for (const toolCallDelta of delta.tool_calls) { const index = toolCallDelta.index; if (toolCalls[index] == null) { if (toolCallDelta.type !== "function") throw new InvalidResponseDataError({ data: toolCallDelta, message: `Expected 'function' type.` }); if (toolCallDelta.id == null) throw new InvalidResponseDataError({ data: toolCallDelta, message: `Expected 'id' to be a string.` }); if (toolCallDelta.function?.name == null) throw new InvalidResponseDataError({ data: toolCallDelta, message: `Expected 'function.name' to be a string.` }); toolCalls[index] = { id: toolCallDelta.id, name: toolCallDelta.function.name, arguments: toolCallDelta.function.arguments ?? "", hasFinished: false }; const toolCall$1 = toolCalls[index]; if (toolCall$1.name != null && toolCall$1.arguments != null) { controller.enqueue({ type: "tool-input-start", id: toolCall$1.id, toolName: toolCall$1.name }); if (toolCall$1.arguments.length > 0) controller.enqueue({ type: "tool-input-delta", id: toolCall$1.id, delta: toolCall$1.arguments }); if (isParsableJson(toolCall$1.arguments)) { controller.enqueue({ type: "tool-input-end", id: toolCall$1.id }); controller.enqueue({ type: "tool-call", toolCallId: toolCall$1.id, toolName: toolCall$1.name, input: toolCall$1.arguments }); toolCall$1.hasFinished = true; } } continue; } const toolCall = toolCalls[index]; if (toolCall.hasFinished) continue; if (toolCallDelta.function?.arguments != null) toolCall.arguments += toolCallDelta.function?.arguments ?? ""; controller.enqueue({ type: "tool-input-delta", id: toolCall.id, delta: toolCallDelta.function.arguments ?? "" }); if (toolCall.name != null && toolCall.arguments != null && isParsableJson(toolCall.arguments)) { controller.enqueue({ type: "tool-input-end", id: toolCall.id }); controller.enqueue({ type: "tool-call", toolCallId: toolCall.id, toolName: toolCall.name, input: toolCall.arguments }); toolCall.hasFinished = true; } } }, flush(controller) { controller.enqueue({ type: "finish", finishReason, usage }); } })), request: { body: args }, response: { headers: responseHeaders } }; } }; //#endregion //#region src/config.ts const SarvamLanguageCodeSchema = z.enum([ "hi-IN", "bn-IN", "kn-IN", "ml-IN", "mr-IN", "od-IN", "pa-IN", "ta-IN", "te-IN", "en-IN", "gu-IN" ]); const MoreSarvamLanguageCodeSchema = z.enum([ "as-IN", "ur-IN", "ne-IN", "kok-IN", "ks-IN", "sd-IN", "sa-IN", "sat-IN", "mni-IN", "brx-IN", "mai-IN", "doi-IN" ]); const SarvamScriptCodeSchema = z.enum([ "Latn", "Deva", "Beng", "Gujr", "Knda", "Mlym", "Orya", "Guru", "Taml", "Telu" ]); //#endregion //#region src/stt/transcription-settings.ts const transcriptionProviderOptionsSchema = z.object({ mode: z.enum([ "transcribe", "translate", "verbatim", "translit", "codemix" ]).nullish(), with_timestamps: z.boolean().nullish() }); const transcriptionResponseSchema = z.object({ request_id: z.string().nullish(), transcript: z.string(), language_code: z.string().nullish(), language_probability: z.number().nullish(), timestamps: z.object({ words: z.array(z.string()), start_time_seconds: z.array(z.number()), end_time_seconds: z.array(z.number()) }).nullish(), diarized_transcript: z.object({ entries: z.array(z.object({ end_time_seconds: z.number(), start_time_seconds: z.number(), transcript: z.string(), speaker_id: z.string() })) }).nullish() }); //#endregion //#region src/stt/transcription-model.ts var SarvamTranscriptionModel = class { constructor(modelId, languageCode, config) { this.modelId = modelId; this.languageCode = languageCode; this.config = config; this.specificationVersion = "v4"; } get provider() { return this.config.provider; } get supportedUrls() { return {}; } async getArgs(options) { const { audio, mediaType, providerOptions } = options; const sarvamOptions = await parseProviderOptions({ provider: "sarvam", providerOptions: { sarvam: { ...providerOptions?.sarvam, ...this.config.transcription } }, schema: transcriptionProviderOptionsSchema }); const formData = new FormData(); formData.append("model", this.modelId); formData.append("language_code", z.union([ SarvamLanguageCodeSchema, MoreSarvamLanguageCodeSchema, z.literal("unknown") ]).parse(this.languageCode)); const blob = audio instanceof Blob ? audio : new Blob([audio], { type: mediaType }); formData.append("file", blob); if (sarvamOptions) Object.entries(sarvamOptions).forEach(([key, value]) => { if (value !== void 0 && value !== null) formData.append(key, String(value)); }); return { formData, warnings: [] }; } async doGenerate(options) { const currentDate = this.config._internal?.currentDate?.() ?? /* @__PURE__ */ new Date(); const { formData, warnings } = await this.getArgs({ ...options, stream: false }); const { value: response, responseHeaders, rawValue: rawResponse } = await postFormDataToApi({ url: this.config.url({ path: "/speech-to-text", modelId: this.modelId }), headers: combineHeaders(this.config.headers?.(), options.headers), formData, failedResponseHandler: sarvamFailedResponseHandler, successfulResponseHandler: createJsonResponseHandler(transcriptionResponseSchema), abortSignal: options.abortSignal, fetch: this.config.fetch }); return { text: response.transcript, segments: response.diarized_transcript?.entries.map((e) => ({ text: e.transcript, speakerId: e.speaker_id, startSecond: e.start_time_seconds, endSecond: e.end_time_seconds })) ?? [], language: response.language_code ?? void 0, durationInSeconds: response.timestamps?.end_time_seconds[response.timestamps.end_time_seconds.length - 1] ?? void 0, warnings, providerMetadata: { sarvam: { request_id: response.request_id, transcript: response.transcript, language_code: response.language_code, language_probability: response.language_probability } }, response: { timestamp: currentDate, modelId: this.modelId, headers: responseHeaders, body: rawResponse } }; } }; //#endregion //#region src/tts/speech-settings.ts const SpeakerSchema = z.enum([ "shubh", "aditya", "rahul", "rohan", "amit", "dev", "ratan", "varun", "manan", "sumit", "kabir", "aayan", "ashutosh", "advait", "anand", "tarun", "sunny", "mani", "gokul", "vijay", "mohit", "rehan", "soham", "ritu", "priya", "neha", "pooja", "simran", "kavya", "ishita", "shreya", "roopa", "amelia", "sophia", "tanya", "shruti", "suhani", "kavitha", "rupali" ]); const outputAudioCodecSchema = z.enum([ "mp3", "linear16", "mulaw", "alaw", "opus", "flac", "aac", "wav" ]); const speechOptionsSchema = z.object({ speaker: SpeakerSchema, pace: z.number().min(.5).max(2), speech_sample_rate: z.union([ z.literal(8e3), z.literal(16e3), z.literal(22050), z.literal(24e3), z.literal(32e3), z.literal(44100), z.literal(48e3) ]), output_audio_codec: outputAudioCodecSchema, temperature: z.number().min(.01).max(2), dict_id: z.string() }).partial(); const speechResponseSchema = z.object({ request_id: z.string().nullish(), audios: z.array(z.string()) }); //#endregion //#region src/tts/speech-model.ts var SarvamSpeechModel = class { get provider() { return this.config.provider; } get supportedUrls() { return {}; } constructor(modelId, languageCode, config) { this.modelId = modelId; this.languageCode = languageCode; this.config = config; this.specificationVersion = "v4"; } async getArgs(options) { const { text, voice, outputFormat = "wav", speed, providerOptions } = options; const sarvamOptions = await parseProviderOptions({ provider: "sarvam", providerOptions: { sarvam: { speaker: voice, pace: speed, output_audio_codec: outputFormat, ...providerOptions?.sarvam, ...this.config.speech } }, schema: speechOptionsSchema }); const requestBody = { model: this.modelId, text, target_language_code: SarvamLanguageCodeSchema.parse(this.languageCode) }; if (sarvamOptions) Object.entries(sarvamOptions).forEach(([key, value]) => { if (value !== void 0 && value !== null) requestBody[key] = value; }); return { requestBody, warnings: [] }; } async doGenerate(options) { const currentDate = this.config._internal?.currentDate?.() ?? /* @__PURE__ */ new Date(); const { requestBody, warnings } = await this.getArgs({ ...options, stream: false }); const { value, responseHeaders, rawValue: rawResponse } = await postJsonToApi({ url: this.config.url({ path: "/text-to-speech", modelId: this.modelId }), headers: combineHeaders(this.config.headers?.(), options.headers), body: requestBody, failedResponseHandler: sarvamFailedResponseHandler, successfulResponseHandler: createJsonResponseHandler(speechResponseSchema), abortSignal: options.abortSignal, fetch: this.config.fetch }); const audio = value.audios[0]; if (audio == null) throw new Error("No audio returned in response"); return { audio, warnings, providerMetadata: { sarvam: { request_id: value.request_id } }, request: { body: requestBody }, response: { timestamp: currentDate, modelId: this.modelId, headers: responseHeaders, body: rawResponse } }; } }; //#endregion //#region src/ttt/lid-settings.ts const sarvamLidResponseSchema = z.object({ script_code: SarvamScriptCodeSchema.nullish(), language_code: SarvamLanguageCodeSchema.nullish(), request_id: z.string().nullish() }); //#endregion //#region src/ttt/utils.ts const convertPromptToInput = (prompt) => { return prompt.filter((m) => m.role === "user").flatMap((m) => m.content.filter((c) => c.type === "text").map((c) => c.text.trim()).filter((text) => text.length > 0)).join("\n"); }; //#endregion //#region src/ttt/lid-model.ts var SarvamLidModel = class { constructor(config) { this.specificationVersion = "v4"; this.modelId = "unknown"; this.config = config; } get provider() { return this.config.provider; } get supportedUrls() { return {}; } getArgs(options) { const { prompt } = options; return { args: { input: convertPromptToInput(prompt) }, warnings: [] }; } async doGenerate(options) { const { args } = this.getArgs({ ...options, stream: false }); const { responseHeaders, value: response, rawValue: rawResponse } = await postJsonToApi({ url: this.config.url({ path: "/text-lid", modelId: this.modelId }), headers: combineHeaders(this.config.headers?.(), options.headers), body: args, failedResponseHandler: sarvamFailedResponseHandler, successfulResponseHandler: createJsonResponseHandler(sarvamLidResponseSchema), abortSignal: options.abortSignal, fetch: this.config.fetch }); return { content: [{ type: "text", text: response.language_code ?? void 0 ?? "unknown" }], finishReason: { unified: "stop", raw: void 0 }, usage: { inputTokens: { total: void 0, noCache: void 0, cacheRead: void 0, cacheWrite: void 0 }, outputTokens: { total: void 0, text: void 0, reasoning: void 0 } }, request: { body: args }, response: { id: response.request_id ?? void 0, headers: responseHeaders, body: rawResponse }, providerMetadata: { sarvam: { request_id: response.request_id, script_code: response.script_code, language_code: response.language_code } }, warnings: [] }; } async doStream(_options) { throw new Error("Language Identification feature doesn't support streaming yet"); } }; //#endregion //#region src/ttt/translation-settings.ts const translationSettingsSchema = z.object({ from: z.union([ SarvamLanguageCodeSchema, MoreSarvamLanguageCodeSchema, z.literal("auto") ]).default("auto"), to: z.union([SarvamLanguageCodeSchema, MoreSarvamLanguageCodeSchema]), numerals_format: z.enum(["native", "international"]).nullish(), speaker_gender: z.enum(["Male", "Female"]).nullish(), mode: z.enum([ "formal", "modern-colloquial", "classic-colloquial", "code-mixed" ]).nullish(), enable_preprocessing: z.boolean().nullish(), output_script: z.enum([ "roman", "fully-native", "spoken-form-in-native" ]).nullish() }); const translationResponseSchema = z.object({ translated_text: z.string().nullish(), source_language_code: z.string().nullish(), request_id: z.string().nullish() }); //#endregion //#region src/ttt/translation-model.ts var SarvamTranslationModel = class { constructor(modelId, settings, config) { this.specificationVersion = "v4"; this.modelId = modelId; this.settings = settings; this.config = config; } get provider() { return this.config.provider; } get supportedUrls() { return {}; } async getArgs(options) { const { prompt, providerOptions } = options; const sarvamOptions = await parseProviderOptions({ provider: "sarvam", providerOptions: { sarvam: { ...providerOptions?.sarvam, ...this.settings } }, schema: translationSettingsSchema }); if (!sarvamOptions) throw new Error("Translation Settings is not provided"); const { from = "auto", to,...restOptions } = sarvamOptions; if (from === to) throw new Error("Source and target languages code must be different."); if (this.modelId === "sarvam-translate:v1") { if ((sarvamOptions.mode ?? "formal") !== "formal") throw new Error("Sarvam 'sarvam-translate:v1' only support mode formal."); if (from === "auto") throw new Error("Sarvam 'sarvam-translate:v1' requires source language code."); } return { args: { input: convertPromptToInput(prompt), model: this.modelId, ...restOptions, source_language_code: from, target_language_code: to }, warnings: [] }; } async doGenerate(options) { const { args } = await this.getArgs({ ...options, stream: false }); const { responseHeaders, value: response, rawValue: rawResponse } = await postJsonToApi({ url: this.config.url({ path: "/translate", modelId: this.modelId }), headers: combineHeaders(this.config.headers?.(), options.headers), body: args, failedResponseHandler: sarvamFailedResponseHandler, successfulResponseHandler: createJsonResponseHandler(translationResponseSchema), abortSignal: options.abortSignal, fetch: this.config.fetch }); return { content: [{ type: "text", text: response.translated_text ?? "" }], finishReason: { unified: "stop", raw: void 0 }, usage: { inputTokens: { total: void 0, noCache: void 0, cacheRead: void 0, cacheWrite: void 0 }, outputTokens: { total: void 0, text: void 0, reasoning: void 0 } }, request: { body: args }, response: { id: response.request_id ?? void 0, headers: responseHeaders, body: rawResponse }, providerMetadata: { sarvam: { request_id: response.request_id, source_language_code: response.source_language_code, translated_text: response.translated_text } }, warnings: [] }; } async doStream(_options) { throw new Error("Translation feature doesn't support streaming yet"); } }; //#endregion //#region src/ttt/transliterate-settings.ts const transliterateSettingsSchema = z.object({ from: z.union([SarvamLanguageCodeSchema, z.literal("auto")]).default("auto"), to: SarvamLanguageCodeSchema, numerals_format: z.enum(["native", "international"]).nullish(), spoken_form: z.boolean().nullish(), spoken_form_numerals_language: z.enum(["english", "native"]).nullish() }); const transliterateResponseSchema = z.object({ transliterated_text: z.string().nullish(), source_language_code: SarvamLanguageCodeSchema.nullish(), request_id: z.string().nullish() }); //#endregion //#region src/ttt/transliterate-model.ts var SarvamTransliterateModel = class { constructor(settings, config) { this.specificationVersion = "v4"; this.modelId = "unknown"; this.settings = settings; this.config = config; } get provider() { return this.config.provider; } get supportedUrls() { return {}; } async getArgs(options) { const { prompt, providerOptions } = options; const sarvamOptions = await parseProviderOptions({ provider: "sarvam", providerOptions: { sarvam: { ...providerOptions?.sarvam, ...this.settings } }, schema: transliterateSettingsSchema }); if (!sarvamOptions) throw new Error("Transliterate Settings is not provided"); const { from = "auto", to,...restOptions } = sarvamOptions; if (from !== "auto") { if (to !== "en-IN" && from !== "en-IN") { if (to !== from) throw new Error("Sarvam doesn't support Indic-Indic Transliteration yet"); } } return { args: { input: convertPromptToInput(prompt), ...restOptions, source_language_code: from, target_language_code: to, spoken_form_numerals_language: sarvamOptions.spoken_form ? sarvamOptions.spoken_form_numerals_language ?? "english" : void 0 }, warnings: [] }; } async doGenerate(options) { const { args } = await this.getArgs({ ...options, stream: false }); const { responseHeaders, value: response, rawValue: rawResponse } = await postJsonToApi({ url: this.config.url({ path: "/transliterate", modelId: this.modelId }), headers: combineHeaders(this.config.headers?.(), options.headers), body: args, failedResponseHandler: sarvamFailedResponseHandler, successfulResponseHandler: createJsonResponseHandler(transliterateResponseSchema), abortSignal: options.abortSignal, fetch: this.config.fetch }); return { content: [{ type: "text", text: response.transliterated_text ?? "" }], finishReason: { unified: "stop", raw: void 0 }, usage: { inputTokens: { total: void 0, noCache: void 0, cacheRead: void 0, cacheWrite: void 0 }, outputTokens: { total: void 0, text: void 0, reasoning: void 0 } }, request: { body: args }, response: { id: response.request_id ?? void 0, headers: responseHeaders, body: rawResponse }, providerMetadata: { sarvam: { request_id: response.request_id, source_language_code: response.source_language_code, transliterated_text: response.transliterated_text } }, warnings: [] }; } async doStream(_options) { throw new Error("Transliterate feature doesn't support streaming yet"); } }; //#endregion //#region src/provider.ts /** * Create an Sarvam provider instance. */ function createSarvam(options = {}) { const baseURL = withoutTrailingSlash(options.baseURL) ?? "https://api.sarvam.ai"; const getApiKey = () => loadApiKey({ apiKey: options.apiKey, environmentVariableName: "SARVAM_API_KEY", description: "Sarvam" }); const getHeaders = () => { const apiKey = getApiKey(); return { Authorization: `Bearer ${apiKey}`, "api-subscription-key": apiKey, ...options.headers }; }; const createChatModel = (modelId, settings = {}) => new SarvamChatLanguageModel(modelId, settings, { provider: "sarvam.chat", url: ({ path }) => `${baseURL}/v1${path}`, headers: getHeaders, fetch: options.fetch }); const createLanguageModel = (modelId, settings) => { if (new.target) throw new Error("The Sarvam model function cannot be called with the new keyword."); return createChatModel(modelId, settings); }; const provider = (modelId, settings) => createLanguageModel(modelId, settings); provider.chat = createChatModel; provider.languageModel = createLanguageModel; provider.speech = (modelId, languageCode, settings) => new SarvamSpeechModel(modelId, languageCode, { provider: "sarvam.speech", url: ({ path }) => `${baseURL}${path}`, headers: getHeaders, fetch: options.fetch, speech: settings }); provider.transcription = (modelId, languageCode, settings) => new SarvamTranscriptionModel(modelId, languageCode ?? "unknown", { provider: "sarvam.transcription", url: ({ path }) => `${baseURL}${path}`, headers: getHeaders, fetch: options.fetch, transcription: settings }); provider.transliterate = (settings) => new SarvamTransliterateModel(settings, { provider: "sarvam.transliterate", url: ({ path }) => `${baseURL}${path}`, headers: getHeaders, fetch: options.fetch }); provider.translation = (model, settings) => new SarvamTranslationModel(model, settings, { provider: "sarvam.translation", url: ({ path }) => `${baseURL}${path}`, headers: getHeaders, fetch: options.fetch }); provider.languageIdentification = () => new SarvamLidModel({ provider: "sarvam.lid", url: ({ path }) => `${baseURL}${path}`, headers: getHeaders, fetch: options.fetch }); return provider; } /** * Default Sarvam provider instance. */ const sarvam = createSarvam(); //#endregion export { createSarvam, sarvam };