sarvam-ai-sdk
Version:
Sarvam provider support for Vercel's AI-SDK
1,298 lines (1,278 loc) • 35.2 kB
JavaScript
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 };