krutrim-ai-sdk 0.1.0
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.
- package/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +365 -0
- package/dist/index.cjs +1594 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +442 -0
- package/dist/index.d.ts +442 -0
- package/dist/index.js +1572 -0
- package/dist/index.js.map +1 -0
- package/package.json +100 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1594 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var providerUtils = require('@ai-sdk/provider-utils');
|
|
4
|
+
var zod = require('zod');
|
|
5
|
+
var provider = require('@ai-sdk/provider');
|
|
6
|
+
|
|
7
|
+
// src/provider.ts
|
|
8
|
+
var krutrimErrorDataSchema = zod.z.object({
|
|
9
|
+
error: zod.z.union([
|
|
10
|
+
zod.z.string(),
|
|
11
|
+
zod.z.object({
|
|
12
|
+
message: zod.z.string().optional(),
|
|
13
|
+
type: zod.z.string().optional(),
|
|
14
|
+
code: zod.z.union([zod.z.string(), zod.z.number()]).optional(),
|
|
15
|
+
param: zod.z.string().nullable().optional()
|
|
16
|
+
})
|
|
17
|
+
]).optional(),
|
|
18
|
+
message: zod.z.string().optional(),
|
|
19
|
+
status: zod.z.union([zod.z.string(), zod.z.number()]).optional(),
|
|
20
|
+
detail: zod.z.object({
|
|
21
|
+
info: zod.z.string().optional()
|
|
22
|
+
}).passthrough().optional(),
|
|
23
|
+
// OpenAI-compatible sometimes nests differently
|
|
24
|
+
code: zod.z.union([zod.z.string(), zod.z.number()]).optional()
|
|
25
|
+
});
|
|
26
|
+
function extractRawMessage(data) {
|
|
27
|
+
if (typeof data.error === "string" && data.error.length > 0) {
|
|
28
|
+
return data.error;
|
|
29
|
+
}
|
|
30
|
+
if (data.error && typeof data.error === "object" && typeof data.error.message === "string" && data.error.message.length > 0) {
|
|
31
|
+
return data.error.message;
|
|
32
|
+
}
|
|
33
|
+
if (typeof data.message === "string" && data.message.length > 0) {
|
|
34
|
+
return data.message;
|
|
35
|
+
}
|
|
36
|
+
if (data.detail?.info) {
|
|
37
|
+
return data.detail.info;
|
|
38
|
+
}
|
|
39
|
+
return "Unknown Krutrim API error";
|
|
40
|
+
}
|
|
41
|
+
function enhanceKrutrimErrorMessage(rawMessage, statusCode) {
|
|
42
|
+
const lower = rawMessage.toLowerCase();
|
|
43
|
+
const tips = [];
|
|
44
|
+
if (statusCode === 401 || lower.includes("unauthorized") || lower.includes("invalid api key") || lower.includes("authentication") || lower.includes("api key")) {
|
|
45
|
+
tips.push(
|
|
46
|
+
"Check KRUTRIM_API_KEY or KRUTRIM_CLOUD_API_KEY (console: https://cloud.olakrutrim.com).",
|
|
47
|
+
"Keys can be regenerated under Key Management \u2192 Model API Keys."
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (statusCode === 403 || lower.includes("forbidden") || lower.includes("region")) {
|
|
51
|
+
tips.push(
|
|
52
|
+
"This model or endpoint may be restricted to supported regions / account tiers.",
|
|
53
|
+
"Confirm your Krutrim Cloud account has access in the console."
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (statusCode === 429 || lower.includes("rate limit") || lower.includes("too many requests") || lower.includes("quota")) {
|
|
57
|
+
tips.push(
|
|
58
|
+
"Rate limit or quota exceeded. Back off and retry with exponential delay.",
|
|
59
|
+
"Billing is typically INR-based on Krutrim Cloud \u2014 top up credits under Usage & Transactions if balance is low."
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
if (lower.includes("insufficient") || lower.includes("credit") || lower.includes("balance") || lower.includes("payment") || lower.includes("billing")) {
|
|
63
|
+
tips.push(
|
|
64
|
+
"Krutrim Cloud usage is billed in INR. Add credits at https://cloud.olakrutrim.com (Usage & Transactions)."
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (lower.includes("model") && (lower.includes("not found") || lower.includes("invalid") || lower.includes("does not exist") || lower.includes("unknown"))) {
|
|
68
|
+
tips.push(
|
|
69
|
+
"Copy the exact model string from the Model Catalogue (case-sensitive).",
|
|
70
|
+
'Try aliases like "krutrim-2" \u2192 "Krutrim-2", or pass the catalogue ID directly.'
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
if (statusCode === 404) {
|
|
74
|
+
tips.push(
|
|
75
|
+
"Verify baseURL is https://cloud.olakrutrim.com/v1 for chat/embeddings.",
|
|
76
|
+
"Language Labs (Bhashik) uses https://cloud.olakrutrim.com/api/v1/..."
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (statusCode != null && statusCode >= 500) {
|
|
80
|
+
tips.push(
|
|
81
|
+
"Krutrim API returned a server error. Retry shortly; check status in the console if it persists."
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
if (tips.length === 0) {
|
|
85
|
+
return rawMessage;
|
|
86
|
+
}
|
|
87
|
+
return `${rawMessage}
|
|
88
|
+
|
|
89
|
+
\u{1F4A1} Krutrim tips:
|
|
90
|
+
${tips.map((t) => ` \u2022 ${t}`).join("\n")}`;
|
|
91
|
+
}
|
|
92
|
+
var krutrimFailedResponseHandler = providerUtils.createJsonErrorResponseHandler({
|
|
93
|
+
errorSchema: krutrimErrorDataSchema,
|
|
94
|
+
errorToMessage: (data) => enhanceKrutrimErrorMessage(extractRawMessage(data))
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// src/bhashik/language-detection.ts
|
|
98
|
+
var lidResponseSchema = zod.z.object({
|
|
99
|
+
status: zod.z.string().nullish(),
|
|
100
|
+
data: zod.z.array(
|
|
101
|
+
zod.z.object({
|
|
102
|
+
label: zod.z.string().nullish(),
|
|
103
|
+
value: zod.z.string().nullish()
|
|
104
|
+
})
|
|
105
|
+
).nullish()
|
|
106
|
+
});
|
|
107
|
+
function extractUserText(options) {
|
|
108
|
+
const prompt = options.prompt;
|
|
109
|
+
if (typeof prompt === "string") {
|
|
110
|
+
return prompt;
|
|
111
|
+
}
|
|
112
|
+
if (!Array.isArray(prompt)) {
|
|
113
|
+
return "";
|
|
114
|
+
}
|
|
115
|
+
const parts = [];
|
|
116
|
+
for (const message of prompt) {
|
|
117
|
+
if (message.role === "user") {
|
|
118
|
+
for (const part of message.content) {
|
|
119
|
+
if (part.type === "text") {
|
|
120
|
+
parts.push(part.text);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return parts.join("\n");
|
|
126
|
+
}
|
|
127
|
+
var KrutrimLanguageDetectionModel = class {
|
|
128
|
+
constructor(config) {
|
|
129
|
+
this.specificationVersion = "v3";
|
|
130
|
+
this.modelId = "bhashik-language-detection";
|
|
131
|
+
this.config = config;
|
|
132
|
+
this.provider = config.provider;
|
|
133
|
+
}
|
|
134
|
+
get supportedUrls() {
|
|
135
|
+
return {};
|
|
136
|
+
}
|
|
137
|
+
async doGenerate(options) {
|
|
138
|
+
const query = extractUserText(options);
|
|
139
|
+
if (!query.trim()) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
"Language detection requires a non-empty user prompt or message."
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
const {
|
|
145
|
+
responseHeaders,
|
|
146
|
+
value: response,
|
|
147
|
+
rawValue
|
|
148
|
+
} = await providerUtils.postJsonToApi({
|
|
149
|
+
url: this.config.languageLabsUrl("/languagelabs/language-detection"),
|
|
150
|
+
headers: providerUtils.combineHeaders(this.config.headers(), options.headers),
|
|
151
|
+
body: { query },
|
|
152
|
+
failedResponseHandler: krutrimFailedResponseHandler,
|
|
153
|
+
successfulResponseHandler: providerUtils.createJsonResponseHandler(lidResponseSchema),
|
|
154
|
+
abortSignal: options.abortSignal,
|
|
155
|
+
fetch: this.config.fetch
|
|
156
|
+
});
|
|
157
|
+
const primary = response.data?.[0];
|
|
158
|
+
const text = primary?.value ?? primary?.label ?? response.data?.map((d) => d.value ?? d.label).filter(Boolean).join(", ") ?? "unknown";
|
|
159
|
+
const content = [{ type: "text", text }];
|
|
160
|
+
return {
|
|
161
|
+
content,
|
|
162
|
+
finishReason: { unified: "stop", raw: "stop" },
|
|
163
|
+
usage: {
|
|
164
|
+
inputTokens: {
|
|
165
|
+
total: void 0,
|
|
166
|
+
noCache: void 0,
|
|
167
|
+
cacheRead: void 0,
|
|
168
|
+
cacheWrite: void 0
|
|
169
|
+
},
|
|
170
|
+
outputTokens: {
|
|
171
|
+
total: void 0,
|
|
172
|
+
text: void 0,
|
|
173
|
+
reasoning: void 0
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
providerMetadata: {
|
|
177
|
+
krutrim: {
|
|
178
|
+
detections: response.data ?? []
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
warnings: [
|
|
182
|
+
{
|
|
183
|
+
type: "other",
|
|
184
|
+
message: "languageDetection only analyzes prompt / user messages (not system or assistant)."
|
|
185
|
+
}
|
|
186
|
+
],
|
|
187
|
+
request: { body: { query } },
|
|
188
|
+
response: {
|
|
189
|
+
headers: responseHeaders,
|
|
190
|
+
body: rawValue,
|
|
191
|
+
modelId: this.modelId,
|
|
192
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
async doStream(options) {
|
|
197
|
+
const result = await this.doGenerate(options);
|
|
198
|
+
const text = result.content.find((c) => c.type === "text")?.type === "text" ? result.content.find((c) => c.type === "text").text : "";
|
|
199
|
+
const stream = new ReadableStream({
|
|
200
|
+
start(controller) {
|
|
201
|
+
controller.enqueue({ type: "stream-start", warnings: result.warnings });
|
|
202
|
+
controller.enqueue({ type: "text-start", id: "text-0" });
|
|
203
|
+
controller.enqueue({ type: "text-delta", id: "text-0", delta: text });
|
|
204
|
+
controller.enqueue({ type: "text-end", id: "text-0" });
|
|
205
|
+
controller.enqueue({
|
|
206
|
+
type: "finish",
|
|
207
|
+
finishReason: result.finishReason,
|
|
208
|
+
usage: result.usage,
|
|
209
|
+
providerMetadata: result.providerMetadata
|
|
210
|
+
});
|
|
211
|
+
controller.close();
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
return {
|
|
215
|
+
stream,
|
|
216
|
+
request: result.request,
|
|
217
|
+
response: result.response
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// src/indic/languages.ts
|
|
223
|
+
var INDIC_LANGUAGES = [
|
|
224
|
+
{
|
|
225
|
+
code: "hi-IN",
|
|
226
|
+
bhashik: "hin",
|
|
227
|
+
name: "Hindi",
|
|
228
|
+
nativeName: "\u0939\u093F\u0928\u094D\u0926\u0940",
|
|
229
|
+
script: "Deva"
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
code: "bn-IN",
|
|
233
|
+
bhashik: "ben",
|
|
234
|
+
name: "Bengali",
|
|
235
|
+
nativeName: "\u09AC\u09BE\u0982\u09B2\u09BE",
|
|
236
|
+
script: "Beng"
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
code: "ta-IN",
|
|
240
|
+
bhashik: "tam",
|
|
241
|
+
name: "Tamil",
|
|
242
|
+
nativeName: "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD",
|
|
243
|
+
script: "Taml"
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
code: "te-IN",
|
|
247
|
+
bhashik: "tel",
|
|
248
|
+
name: "Telugu",
|
|
249
|
+
nativeName: "\u0C24\u0C46\u0C32\u0C41\u0C17\u0C41",
|
|
250
|
+
script: "Telu"
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
code: "mr-IN",
|
|
254
|
+
bhashik: "mar",
|
|
255
|
+
name: "Marathi",
|
|
256
|
+
nativeName: "\u092E\u0930\u093E\u0920\u0940",
|
|
257
|
+
script: "Deva"
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
code: "gu-IN",
|
|
261
|
+
bhashik: "guj",
|
|
262
|
+
name: "Gujarati",
|
|
263
|
+
nativeName: "\u0A97\u0AC1\u0A9C\u0AB0\u0ABE\u0AA4\u0AC0",
|
|
264
|
+
script: "Gujr"
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
code: "kn-IN",
|
|
268
|
+
bhashik: "kan",
|
|
269
|
+
name: "Kannada",
|
|
270
|
+
nativeName: "\u0C95\u0CA8\u0CCD\u0CA8\u0CA1",
|
|
271
|
+
script: "Knda"
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
code: "ml-IN",
|
|
275
|
+
bhashik: "mal",
|
|
276
|
+
name: "Malayalam",
|
|
277
|
+
nativeName: "\u0D2E\u0D32\u0D2F\u0D3E\u0D33\u0D02",
|
|
278
|
+
script: "Mlym"
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
code: "pa-IN",
|
|
282
|
+
bhashik: "pan",
|
|
283
|
+
name: "Punjabi",
|
|
284
|
+
nativeName: "\u0A2A\u0A70\u0A1C\u0A3E\u0A2C\u0A40",
|
|
285
|
+
script: "Guru"
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
code: "or-IN",
|
|
289
|
+
bhashik: null,
|
|
290
|
+
name: "Odia",
|
|
291
|
+
nativeName: "\u0B13\u0B21\u0B3C\u0B3F\u0B06",
|
|
292
|
+
script: "Orya",
|
|
293
|
+
notes: "Not all Bhashik endpoints list Odia yet."
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
code: "as-IN",
|
|
297
|
+
bhashik: null,
|
|
298
|
+
name: "Assamese",
|
|
299
|
+
nativeName: "\u0985\u09B8\u09AE\u09C0\u09AF\u09BC\u09BE",
|
|
300
|
+
script: "Beng"
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
code: "ur-IN",
|
|
304
|
+
bhashik: null,
|
|
305
|
+
name: "Urdu",
|
|
306
|
+
nativeName: "\u0627\u0631\u062F\u0648",
|
|
307
|
+
script: "Arab"
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
code: "en-IN",
|
|
311
|
+
bhashik: "eng",
|
|
312
|
+
name: "English (India)",
|
|
313
|
+
nativeName: "English",
|
|
314
|
+
script: "Latn"
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
code: "sa-IN",
|
|
318
|
+
bhashik: null,
|
|
319
|
+
name: "Sanskrit",
|
|
320
|
+
nativeName: "\u0938\u0902\u0938\u094D\u0915\u0943\u0924\u092E\u094D",
|
|
321
|
+
script: "Deva"
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
code: "ne-IN",
|
|
325
|
+
bhashik: null,
|
|
326
|
+
name: "Nepali",
|
|
327
|
+
nativeName: "\u0928\u0947\u092A\u093E\u0932\u0940",
|
|
328
|
+
script: "Deva"
|
|
329
|
+
}
|
|
330
|
+
];
|
|
331
|
+
function toBhashikLanguageCode(code) {
|
|
332
|
+
const entry = INDIC_LANGUAGES.find(
|
|
333
|
+
(l) => l.code === code || l.bhashik === code
|
|
334
|
+
);
|
|
335
|
+
return entry?.bhashik ?? null;
|
|
336
|
+
}
|
|
337
|
+
function toTranslationLanguageCode(code) {
|
|
338
|
+
const entry = INDIC_LANGUAGES.find(
|
|
339
|
+
(l) => l.code === code || l.bhashik === code
|
|
340
|
+
);
|
|
341
|
+
if (!entry?.bhashik) {
|
|
342
|
+
if (code.includes("_")) return code;
|
|
343
|
+
throw new Error(
|
|
344
|
+
`No Bhashik/translation mapping for language "${code}". Use a supported code or pass an explicit tag like "hin_Deva".`
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
return `${entry.bhashik}_${entry.script}`;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// src/bhashik/speech-model.ts
|
|
351
|
+
var ttsResponseSchema = zod.z.object({
|
|
352
|
+
status: zod.z.string().nullish(),
|
|
353
|
+
data: zod.z.object({
|
|
354
|
+
audio_file: zod.z.string().nullish(),
|
|
355
|
+
// Some deployments may return base64 directly
|
|
356
|
+
audio: zod.z.string().nullish(),
|
|
357
|
+
audio_base64: zod.z.string().nullish()
|
|
358
|
+
}).nullish()
|
|
359
|
+
});
|
|
360
|
+
var KrutrimSpeechModel = class {
|
|
361
|
+
constructor(language, settings = {}, config) {
|
|
362
|
+
this.specificationVersion = "v3";
|
|
363
|
+
this.modelId = "Krutrim-TTS";
|
|
364
|
+
const mapped = toBhashikLanguageCode(language);
|
|
365
|
+
if (!mapped) {
|
|
366
|
+
if (["eng", "guj", "ben", "hin", "kan", "mal", "mar", "tam", "tel"].includes(
|
|
367
|
+
language
|
|
368
|
+
)) {
|
|
369
|
+
this.languageCode = language;
|
|
370
|
+
} else {
|
|
371
|
+
throw new Error(
|
|
372
|
+
`Unsupported TTS language "${language}". Use hi-IN, ta-IN, eng, hin, etc.`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
} else {
|
|
376
|
+
this.languageCode = mapped;
|
|
377
|
+
}
|
|
378
|
+
this.settings = settings;
|
|
379
|
+
this.config = config;
|
|
380
|
+
}
|
|
381
|
+
get provider() {
|
|
382
|
+
return this.config.provider;
|
|
383
|
+
}
|
|
384
|
+
async doGenerate({
|
|
385
|
+
text,
|
|
386
|
+
headers,
|
|
387
|
+
abortSignal
|
|
388
|
+
}) {
|
|
389
|
+
const {
|
|
390
|
+
responseHeaders,
|
|
391
|
+
value: response,
|
|
392
|
+
rawValue
|
|
393
|
+
} = await providerUtils.postJsonToApi({
|
|
394
|
+
url: this.config.languageLabsUrl("/languagelabs/tts"),
|
|
395
|
+
headers: providerUtils.combineHeaders(this.config.headers(), headers),
|
|
396
|
+
body: {
|
|
397
|
+
input_text: text,
|
|
398
|
+
input_language: this.languageCode,
|
|
399
|
+
input_speaker: this.settings.speaker ?? "female"
|
|
400
|
+
},
|
|
401
|
+
failedResponseHandler: krutrimFailedResponseHandler,
|
|
402
|
+
successfulResponseHandler: providerUtils.createJsonResponseHandler(ttsResponseSchema),
|
|
403
|
+
abortSignal,
|
|
404
|
+
fetch: this.config.fetch
|
|
405
|
+
});
|
|
406
|
+
let audioData = response.data?.audio_base64 ?? response.data?.audio ?? void 0;
|
|
407
|
+
const audioUrl = response.data?.audio_file;
|
|
408
|
+
if (!audioData && audioUrl) {
|
|
409
|
+
const fetchFn = this.config.fetch ?? globalThis.fetch;
|
|
410
|
+
const audioResponse = await fetchFn(audioUrl, {
|
|
411
|
+
headers: this.config.headers(),
|
|
412
|
+
signal: abortSignal
|
|
413
|
+
});
|
|
414
|
+
if (!audioResponse.ok) {
|
|
415
|
+
throw new Error(
|
|
416
|
+
`Failed to download TTS audio from ${audioUrl}: ${audioResponse.status}`
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
const buffer = new Uint8Array(await audioResponse.arrayBuffer());
|
|
420
|
+
audioData = buffer;
|
|
421
|
+
}
|
|
422
|
+
if (!audioData) {
|
|
423
|
+
throw new Error(
|
|
424
|
+
"Krutrim TTS response did not include audio data or a downloadable audio_file URL."
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
audio: audioData,
|
|
429
|
+
warnings: [],
|
|
430
|
+
response: {
|
|
431
|
+
headers: responseHeaders,
|
|
432
|
+
body: rawValue,
|
|
433
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
434
|
+
modelId: this.modelId
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
var sttResponseSchema = zod.z.object({
|
|
440
|
+
status: zod.z.string().nullish(),
|
|
441
|
+
data: zod.z.object({
|
|
442
|
+
text: zod.z.union([zod.z.array(zod.z.string()), zod.z.string()]).nullish()
|
|
443
|
+
}).nullish()
|
|
444
|
+
});
|
|
445
|
+
var KrutrimTranscriptionModel = class {
|
|
446
|
+
constructor(language, config) {
|
|
447
|
+
this.specificationVersion = "v3";
|
|
448
|
+
this.modelId = "Krutrim-Dhwani";
|
|
449
|
+
const mapped = toBhashikLanguageCode(language);
|
|
450
|
+
if (!mapped) {
|
|
451
|
+
if (["eng", "guj", "ben", "hin", "kan", "mal", "mar", "tam", "tel"].includes(
|
|
452
|
+
language
|
|
453
|
+
)) {
|
|
454
|
+
this.languageCode = language;
|
|
455
|
+
} else {
|
|
456
|
+
throw new Error(
|
|
457
|
+
`Unsupported STT language "${language}". Use hi-IN, ta-IN, eng, hin, etc.`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
} else {
|
|
461
|
+
this.languageCode = mapped;
|
|
462
|
+
}
|
|
463
|
+
this.config = config;
|
|
464
|
+
}
|
|
465
|
+
get provider() {
|
|
466
|
+
return this.config.provider;
|
|
467
|
+
}
|
|
468
|
+
async doGenerate({
|
|
469
|
+
audio,
|
|
470
|
+
mediaType,
|
|
471
|
+
headers,
|
|
472
|
+
abortSignal
|
|
473
|
+
}) {
|
|
474
|
+
const formData = new FormData();
|
|
475
|
+
let blob;
|
|
476
|
+
if (typeof audio === "string") {
|
|
477
|
+
const binary = Buffer.from(audio, "base64");
|
|
478
|
+
blob = new Blob([binary], { type: mediaType ?? "audio/wav" });
|
|
479
|
+
} else {
|
|
480
|
+
blob = new Blob([audio], {
|
|
481
|
+
type: mediaType ?? "audio/wav"
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
formData.append("file", blob, "audio.wav");
|
|
485
|
+
formData.append("lang_code", this.languageCode);
|
|
486
|
+
const baseHeaders = { ...this.config.headers(), ...headers };
|
|
487
|
+
delete baseHeaders["Content-Type"];
|
|
488
|
+
delete baseHeaders["content-type"];
|
|
489
|
+
const {
|
|
490
|
+
responseHeaders,
|
|
491
|
+
value: response,
|
|
492
|
+
rawValue
|
|
493
|
+
} = await providerUtils.postToApi({
|
|
494
|
+
url: this.config.languageLabsUrl("/languagelabs/transcribe/upload"),
|
|
495
|
+
headers: providerUtils.combineHeaders(baseHeaders, void 0),
|
|
496
|
+
body: {
|
|
497
|
+
content: formData,
|
|
498
|
+
values: formData
|
|
499
|
+
},
|
|
500
|
+
failedResponseHandler: krutrimFailedResponseHandler,
|
|
501
|
+
successfulResponseHandler: providerUtils.createJsonResponseHandler(sttResponseSchema),
|
|
502
|
+
abortSignal,
|
|
503
|
+
fetch: this.config.fetch
|
|
504
|
+
});
|
|
505
|
+
const textField = response.data?.text;
|
|
506
|
+
const text = Array.isArray(textField) ? textField.join(" ") : textField ?? "";
|
|
507
|
+
return {
|
|
508
|
+
text,
|
|
509
|
+
segments: [],
|
|
510
|
+
language: this.languageCode,
|
|
511
|
+
durationInSeconds: void 0,
|
|
512
|
+
warnings: [],
|
|
513
|
+
response: {
|
|
514
|
+
headers: responseHeaders,
|
|
515
|
+
body: rawValue,
|
|
516
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
517
|
+
modelId: this.modelId
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
var translationResponseSchema = zod.z.object({
|
|
523
|
+
status: zod.z.string().nullish(),
|
|
524
|
+
data: zod.z.object({
|
|
525
|
+
translated_text: zod.z.string().nullish()
|
|
526
|
+
}).nullish()
|
|
527
|
+
});
|
|
528
|
+
function extractUserText2(options) {
|
|
529
|
+
const prompt = options.prompt;
|
|
530
|
+
if (!Array.isArray(prompt)) {
|
|
531
|
+
return "";
|
|
532
|
+
}
|
|
533
|
+
const parts = [];
|
|
534
|
+
for (const message of prompt) {
|
|
535
|
+
if (message.role === "user") {
|
|
536
|
+
for (const part of message.content) {
|
|
537
|
+
if (part.type === "text") {
|
|
538
|
+
parts.push(part.text);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return parts.join("\n");
|
|
544
|
+
}
|
|
545
|
+
var KrutrimTranslationModel = class {
|
|
546
|
+
constructor(settings, config) {
|
|
547
|
+
this.specificationVersion = "v3";
|
|
548
|
+
this.settings = settings;
|
|
549
|
+
this.config = config;
|
|
550
|
+
this.provider = config.provider;
|
|
551
|
+
this.modelId = settings.model ?? "krutrim-translate-v1.0";
|
|
552
|
+
}
|
|
553
|
+
get supportedUrls() {
|
|
554
|
+
return {};
|
|
555
|
+
}
|
|
556
|
+
async doGenerate(options) {
|
|
557
|
+
const text = extractUserText2(options);
|
|
558
|
+
if (!text.trim()) {
|
|
559
|
+
throw new Error("Translation requires a non-empty user prompt or message.");
|
|
560
|
+
}
|
|
561
|
+
const src = toTranslationLanguageCode(this.settings.from);
|
|
562
|
+
const tgt = toTranslationLanguageCode(this.settings.to);
|
|
563
|
+
const body = {
|
|
564
|
+
text,
|
|
565
|
+
src_language: src,
|
|
566
|
+
tgt_language: tgt,
|
|
567
|
+
model: this.modelId
|
|
568
|
+
};
|
|
569
|
+
const {
|
|
570
|
+
responseHeaders,
|
|
571
|
+
value: response,
|
|
572
|
+
rawValue
|
|
573
|
+
} = await providerUtils.postJsonToApi({
|
|
574
|
+
url: this.config.languageLabsUrl("/languagelabs/translation"),
|
|
575
|
+
headers: providerUtils.combineHeaders(this.config.headers(), options.headers),
|
|
576
|
+
body,
|
|
577
|
+
failedResponseHandler: krutrimFailedResponseHandler,
|
|
578
|
+
successfulResponseHandler: providerUtils.createJsonResponseHandler(
|
|
579
|
+
translationResponseSchema
|
|
580
|
+
),
|
|
581
|
+
abortSignal: options.abortSignal,
|
|
582
|
+
fetch: this.config.fetch
|
|
583
|
+
});
|
|
584
|
+
const translated = response.data?.translated_text ?? "";
|
|
585
|
+
const content = [
|
|
586
|
+
{ type: "text", text: translated }
|
|
587
|
+
];
|
|
588
|
+
return {
|
|
589
|
+
content,
|
|
590
|
+
finishReason: { unified: "stop", raw: "stop" },
|
|
591
|
+
usage: {
|
|
592
|
+
inputTokens: {
|
|
593
|
+
total: void 0,
|
|
594
|
+
noCache: void 0,
|
|
595
|
+
cacheRead: void 0,
|
|
596
|
+
cacheWrite: void 0
|
|
597
|
+
},
|
|
598
|
+
outputTokens: {
|
|
599
|
+
total: void 0,
|
|
600
|
+
text: void 0,
|
|
601
|
+
reasoning: void 0
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
providerMetadata: {
|
|
605
|
+
krutrim: {
|
|
606
|
+
src_language: src,
|
|
607
|
+
tgt_language: tgt,
|
|
608
|
+
model: this.modelId
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
warnings: [
|
|
612
|
+
{
|
|
613
|
+
type: "other",
|
|
614
|
+
message: "translation only processes prompt / user messages (not system or assistant)."
|
|
615
|
+
}
|
|
616
|
+
],
|
|
617
|
+
request: { body },
|
|
618
|
+
response: {
|
|
619
|
+
headers: responseHeaders,
|
|
620
|
+
body: rawValue,
|
|
621
|
+
modelId: this.modelId,
|
|
622
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
async doStream(options) {
|
|
627
|
+
const result = await this.doGenerate(options);
|
|
628
|
+
const textPart = result.content.find((c) => c.type === "text");
|
|
629
|
+
const text = textPart && textPart.type === "text" ? textPart.text : "";
|
|
630
|
+
const stream = new ReadableStream({
|
|
631
|
+
start(controller) {
|
|
632
|
+
controller.enqueue({ type: "stream-start", warnings: result.warnings });
|
|
633
|
+
controller.enqueue({ type: "text-start", id: "text-0" });
|
|
634
|
+
controller.enqueue({ type: "text-delta", id: "text-0", delta: text });
|
|
635
|
+
controller.enqueue({ type: "text-end", id: "text-0" });
|
|
636
|
+
controller.enqueue({
|
|
637
|
+
type: "finish",
|
|
638
|
+
finishReason: result.finishReason,
|
|
639
|
+
usage: result.usage,
|
|
640
|
+
providerMetadata: result.providerMetadata
|
|
641
|
+
});
|
|
642
|
+
controller.close();
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
return {
|
|
646
|
+
stream,
|
|
647
|
+
request: result.request,
|
|
648
|
+
response: result.response
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
// src/models.ts
|
|
654
|
+
var KRUTRIM_CHAT_MODELS = {
|
|
655
|
+
/** Krutrim's flagship Indic-capable model (v1). */
|
|
656
|
+
krutrim1: "Krutrim-1",
|
|
657
|
+
/** Krutrim's next-generation text model. */
|
|
658
|
+
krutrim2: "Krutrim-2",
|
|
659
|
+
/** Lowercase alias accepted by many OpenAI-compatible samples. */
|
|
660
|
+
krutrim1Lower: "krutrim-1",
|
|
661
|
+
krutrim2Lower: "krutrim-2",
|
|
662
|
+
// Open models commonly hosted on Krutrim Cloud
|
|
663
|
+
llama33_70b: "Llama-3.3-70B-Instruct",
|
|
664
|
+
llama32_11b_vision: "Llama-3.2-11B-Vision-Instruct",
|
|
665
|
+
llama4_maverick: "Llama-4-Maverick-17B-128E-Instruct",
|
|
666
|
+
llama3_8b: "Meta-Llama-3-8B-Instruct",
|
|
667
|
+
mistral7b: "Mistral-7B-v0.2",
|
|
668
|
+
mistral7bInstruct: "Mistral-7B-Instruct",
|
|
669
|
+
qwen3_32b: "Qwen3-32B",
|
|
670
|
+
qwen3_30b: "Qwen3-30B-A3B",
|
|
671
|
+
phi4_reasoning: "Phi-4-reasoning-plus",
|
|
672
|
+
deepseekR1: "DeepSeek-R1",
|
|
673
|
+
deepseekR1_70b: "DeepSeek-R1-Distill-Llama-70B",
|
|
674
|
+
deepseekR1_8b: "DeepSeek-R1-Distill-Llama-8B",
|
|
675
|
+
gemma3_27b: "gemma-3-27b-it",
|
|
676
|
+
spectreV2: "Krutrim-spectre-v2"
|
|
677
|
+
};
|
|
678
|
+
var KRUTRIM_EMBEDDING_MODELS = {
|
|
679
|
+
/** Krutrim embedding model. */
|
|
680
|
+
vyakyarth: "Vyakyarth",
|
|
681
|
+
/** Alternative Krutrim embedding model. */
|
|
682
|
+
bhasantarit: "Bhasantarit"
|
|
683
|
+
};
|
|
684
|
+
var KRUTRIM_SPEECH_MODELS = {
|
|
685
|
+
/** Krutrim Speech-to-Text. */
|
|
686
|
+
dhwani: "Krutrim-Dhwani",
|
|
687
|
+
/** Krutrim Text-to-Speech. */
|
|
688
|
+
tts: "Krutrim-TTS"
|
|
689
|
+
};
|
|
690
|
+
var KRUTRIM_VISION_MODELS = {
|
|
691
|
+
chitrapathak: "chitrapathak",
|
|
692
|
+
llama32_11b_vision: "Llama-3.2-11B-Vision-Instruct",
|
|
693
|
+
llama4_maverick: "Llama-4-Maverick-17B-128E-Instruct",
|
|
694
|
+
gemma3_27b: "gemma-3-27b-it"
|
|
695
|
+
};
|
|
696
|
+
var MODEL_ALIASES = {
|
|
697
|
+
// Native Krutrim
|
|
698
|
+
krutrim: "Krutrim-2",
|
|
699
|
+
"krutrim-1": "Krutrim-1",
|
|
700
|
+
"krutrim-2": "Krutrim-2",
|
|
701
|
+
krutrim1: "Krutrim-1",
|
|
702
|
+
krutrim2: "Krutrim-2",
|
|
703
|
+
"Krutrim-1": "Krutrim-1",
|
|
704
|
+
"Krutrim-2": "Krutrim-2",
|
|
705
|
+
// Speech
|
|
706
|
+
dhwani: "Krutrim-Dhwani",
|
|
707
|
+
"krutrim-dhwani": "Krutrim-Dhwani",
|
|
708
|
+
"Krutrim-Dhwani": "Krutrim-Dhwani",
|
|
709
|
+
tts: "Krutrim-TTS",
|
|
710
|
+
"krutrim-tts": "Krutrim-TTS",
|
|
711
|
+
"Krutrim-TTS": "Krutrim-TTS",
|
|
712
|
+
// Embeddings
|
|
713
|
+
vyakyarth: "Vyakyarth",
|
|
714
|
+
Vyakyarth: "Vyakyarth",
|
|
715
|
+
bhasantarit: "Bhasantarit",
|
|
716
|
+
Bhasantarit: "Bhasantarit",
|
|
717
|
+
// Common open-model shortcuts
|
|
718
|
+
"llama-3.3-70b": "Llama-3.3-70B-Instruct",
|
|
719
|
+
"llama3.3-70b": "Llama-3.3-70B-Instruct",
|
|
720
|
+
"llama-3.2-11b-vision": "Llama-3.2-11B-Vision-Instruct",
|
|
721
|
+
"mistral-7b": "Mistral-7B-v0.2",
|
|
722
|
+
"deepseek-r1": "DeepSeek-R1",
|
|
723
|
+
"qwen3-32b": "Qwen3-32B",
|
|
724
|
+
"phi-4": "Phi-4-reasoning-plus"
|
|
725
|
+
};
|
|
726
|
+
function resolveModelId(modelId) {
|
|
727
|
+
return MODEL_ALIASES[modelId] ?? MODEL_ALIASES[modelId.toLowerCase()] ?? modelId;
|
|
728
|
+
}
|
|
729
|
+
function convertToChatMessages(prompt) {
|
|
730
|
+
const messages = [];
|
|
731
|
+
for (const message of prompt) {
|
|
732
|
+
switch (message.role) {
|
|
733
|
+
case "system": {
|
|
734
|
+
messages.push({ role: "system", content: message.content });
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
case "user": {
|
|
738
|
+
if (message.content.length === 1 && message.content[0].type === "text") {
|
|
739
|
+
messages.push({
|
|
740
|
+
role: "user",
|
|
741
|
+
content: message.content[0].text
|
|
742
|
+
});
|
|
743
|
+
break;
|
|
744
|
+
}
|
|
745
|
+
messages.push({
|
|
746
|
+
role: "user",
|
|
747
|
+
content: message.content.map((part) => {
|
|
748
|
+
switch (part.type) {
|
|
749
|
+
case "text": {
|
|
750
|
+
return { type: "text", text: part.text };
|
|
751
|
+
}
|
|
752
|
+
case "file": {
|
|
753
|
+
let imageData;
|
|
754
|
+
if (typeof part.data === "string") {
|
|
755
|
+
if (part.data.startsWith("http://") || part.data.startsWith("https://") || part.data.startsWith("data:")) {
|
|
756
|
+
imageData = part.data;
|
|
757
|
+
} else {
|
|
758
|
+
imageData = `data:${part.mediaType};base64,${part.data}`;
|
|
759
|
+
}
|
|
760
|
+
} else if (part.data instanceof URL) {
|
|
761
|
+
imageData = part.data.toString();
|
|
762
|
+
} else {
|
|
763
|
+
imageData = `data:${part.mediaType};base64,${providerUtils.convertUint8ArrayToBase64(part.data)}`;
|
|
764
|
+
}
|
|
765
|
+
return {
|
|
766
|
+
type: "image_url",
|
|
767
|
+
image_url: { url: imageData }
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
default: {
|
|
771
|
+
const _exhaustive = part;
|
|
772
|
+
throw new provider.UnsupportedFunctionalityError({
|
|
773
|
+
functionality: `Unsupported content part type: ${JSON.stringify(_exhaustive)}`
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
})
|
|
778
|
+
});
|
|
779
|
+
break;
|
|
780
|
+
}
|
|
781
|
+
case "assistant": {
|
|
782
|
+
let text = "";
|
|
783
|
+
const toolCalls = [];
|
|
784
|
+
for (const part of message.content) {
|
|
785
|
+
switch (part.type) {
|
|
786
|
+
case "text": {
|
|
787
|
+
text += part.text;
|
|
788
|
+
break;
|
|
789
|
+
}
|
|
790
|
+
case "tool-call": {
|
|
791
|
+
toolCalls.push({
|
|
792
|
+
id: part.toolCallId,
|
|
793
|
+
type: "function",
|
|
794
|
+
function: {
|
|
795
|
+
name: part.toolName,
|
|
796
|
+
arguments: typeof part.input === "string" ? part.input : JSON.stringify(part.input)
|
|
797
|
+
}
|
|
798
|
+
});
|
|
799
|
+
break;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
messages.push({
|
|
804
|
+
role: "assistant",
|
|
805
|
+
content: text || null,
|
|
806
|
+
tool_calls: toolCalls.length > 0 ? toolCalls : void 0
|
|
807
|
+
});
|
|
808
|
+
break;
|
|
809
|
+
}
|
|
810
|
+
case "tool": {
|
|
811
|
+
for (const part of message.content) {
|
|
812
|
+
if (part.type === "tool-result") {
|
|
813
|
+
messages.push({
|
|
814
|
+
role: "tool",
|
|
815
|
+
tool_call_id: part.toolCallId,
|
|
816
|
+
content: JSON.stringify(part.output)
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
break;
|
|
821
|
+
}
|
|
822
|
+
default: {
|
|
823
|
+
const _exhaustive = message;
|
|
824
|
+
throw new Error(`Unsupported role: ${JSON.stringify(_exhaustive)}`);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
return messages;
|
|
829
|
+
}
|
|
830
|
+
function prepareTools({
|
|
831
|
+
tools,
|
|
832
|
+
toolChoice
|
|
833
|
+
}) {
|
|
834
|
+
const finalTools = tools?.length ? tools : void 0;
|
|
835
|
+
const toolWarnings = [];
|
|
836
|
+
if (finalTools == null) {
|
|
837
|
+
return { tools: void 0, tool_choice: void 0, toolWarnings };
|
|
838
|
+
}
|
|
839
|
+
const krutrimTools = [];
|
|
840
|
+
for (const tool of finalTools) {
|
|
841
|
+
if (tool.type === "provider") {
|
|
842
|
+
toolWarnings.push({ type: "unsupported", feature: tool.name });
|
|
843
|
+
} else {
|
|
844
|
+
krutrimTools.push({
|
|
845
|
+
type: "function",
|
|
846
|
+
function: {
|
|
847
|
+
name: tool.name,
|
|
848
|
+
description: tool.description,
|
|
849
|
+
parameters: tool.inputSchema
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
if (toolChoice == null) {
|
|
855
|
+
return { tools: krutrimTools, tool_choice: void 0, toolWarnings };
|
|
856
|
+
}
|
|
857
|
+
const type = toolChoice.type;
|
|
858
|
+
switch (type) {
|
|
859
|
+
case "auto":
|
|
860
|
+
case "none":
|
|
861
|
+
case "required":
|
|
862
|
+
return { tools: krutrimTools, tool_choice: type, toolWarnings };
|
|
863
|
+
case "tool":
|
|
864
|
+
return {
|
|
865
|
+
tools: krutrimTools,
|
|
866
|
+
tool_choice: {
|
|
867
|
+
type: "function",
|
|
868
|
+
function: { name: toolChoice.toolName }
|
|
869
|
+
},
|
|
870
|
+
toolWarnings
|
|
871
|
+
};
|
|
872
|
+
default: {
|
|
873
|
+
const _exhaustive = type;
|
|
874
|
+
throw new provider.UnsupportedFunctionalityError({
|
|
875
|
+
functionality: `Unsupported tool choice type: ${_exhaustive}`
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
var chatSettingsSchema = zod.z.object({
|
|
881
|
+
n: zod.z.number().min(1).max(128).nullish(),
|
|
882
|
+
user: zod.z.string().nullish(),
|
|
883
|
+
reasoningEffort: zod.z.enum(["low", "medium", "high"]).nullish(),
|
|
884
|
+
// snake_case passthrough for raw API fields if set via providerOptions
|
|
885
|
+
reasoning_effort: zod.z.enum(["low", "medium", "high"]).nullish()
|
|
886
|
+
});
|
|
887
|
+
var chatResponseSchema = zod.z.object({
|
|
888
|
+
id: zod.z.string().nullish(),
|
|
889
|
+
created: zod.z.number().nullish(),
|
|
890
|
+
model: zod.z.string().nullish(),
|
|
891
|
+
object: zod.z.string().nullish(),
|
|
892
|
+
system_fingerprint: zod.z.string().nullish(),
|
|
893
|
+
choices: zod.z.array(
|
|
894
|
+
zod.z.object({
|
|
895
|
+
index: zod.z.number(),
|
|
896
|
+
finish_reason: zod.z.string().nullish(),
|
|
897
|
+
logprobs: zod.z.object({}).nullish(),
|
|
898
|
+
message: zod.z.object({
|
|
899
|
+
content: zod.z.string().nullish(),
|
|
900
|
+
reasoning_content: zod.z.string().nullish(),
|
|
901
|
+
refusal: zod.z.string().nullish(),
|
|
902
|
+
role: zod.z.string().nullish(),
|
|
903
|
+
tool_calls: zod.z.array(
|
|
904
|
+
zod.z.object({
|
|
905
|
+
id: zod.z.string().nullish(),
|
|
906
|
+
type: zod.z.literal("function"),
|
|
907
|
+
function: zod.z.object({
|
|
908
|
+
name: zod.z.string(),
|
|
909
|
+
arguments: zod.z.string()
|
|
910
|
+
})
|
|
911
|
+
})
|
|
912
|
+
).nullish()
|
|
913
|
+
})
|
|
914
|
+
})
|
|
915
|
+
),
|
|
916
|
+
usage: zod.z.object({
|
|
917
|
+
completion_tokens: zod.z.number().nullish(),
|
|
918
|
+
prompt_tokens: zod.z.number().nullish(),
|
|
919
|
+
total_tokens: zod.z.number().nullish()
|
|
920
|
+
}).nullish()
|
|
921
|
+
});
|
|
922
|
+
var chatChunkSchema = zod.z.union([
|
|
923
|
+
zod.z.object({
|
|
924
|
+
id: zod.z.string().nullish(),
|
|
925
|
+
created: zod.z.number().nullish(),
|
|
926
|
+
model: zod.z.string().nullish(),
|
|
927
|
+
choices: zod.z.array(
|
|
928
|
+
zod.z.object({
|
|
929
|
+
delta: zod.z.object({
|
|
930
|
+
content: zod.z.string().nullish(),
|
|
931
|
+
reasoning: zod.z.string().nullish(),
|
|
932
|
+
reasoning_content: zod.z.string().nullish(),
|
|
933
|
+
role: zod.z.string().nullish(),
|
|
934
|
+
tool_calls: zod.z.array(
|
|
935
|
+
zod.z.object({
|
|
936
|
+
index: zod.z.number(),
|
|
937
|
+
id: zod.z.string().nullish(),
|
|
938
|
+
type: zod.z.literal("function").optional(),
|
|
939
|
+
function: zod.z.object({
|
|
940
|
+
name: zod.z.string().nullish(),
|
|
941
|
+
arguments: zod.z.string().nullish()
|
|
942
|
+
})
|
|
943
|
+
})
|
|
944
|
+
).nullish()
|
|
945
|
+
}).nullish(),
|
|
946
|
+
finish_reason: zod.z.string().nullable().optional(),
|
|
947
|
+
index: zod.z.number()
|
|
948
|
+
})
|
|
949
|
+
),
|
|
950
|
+
usage: zod.z.object({
|
|
951
|
+
prompt_tokens: zod.z.number().nullish(),
|
|
952
|
+
completion_tokens: zod.z.number().nullish(),
|
|
953
|
+
total_tokens: zod.z.number().nullish()
|
|
954
|
+
}).nullish()
|
|
955
|
+
}),
|
|
956
|
+
krutrimErrorDataSchema
|
|
957
|
+
]);
|
|
958
|
+
|
|
959
|
+
// src/chat/utils.ts
|
|
960
|
+
function mapFinishReason(finishReason) {
|
|
961
|
+
switch (finishReason) {
|
|
962
|
+
case "stop":
|
|
963
|
+
return "stop";
|
|
964
|
+
case "length":
|
|
965
|
+
return "length";
|
|
966
|
+
case "content_filter":
|
|
967
|
+
return "content-filter";
|
|
968
|
+
case "function_call":
|
|
969
|
+
case "tool_calls":
|
|
970
|
+
return "tool-calls";
|
|
971
|
+
default:
|
|
972
|
+
return "other";
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
function getResponseMetadata({
|
|
976
|
+
id,
|
|
977
|
+
model,
|
|
978
|
+
created
|
|
979
|
+
}) {
|
|
980
|
+
return {
|
|
981
|
+
id: id ?? void 0,
|
|
982
|
+
modelId: model ?? void 0,
|
|
983
|
+
timestamp: created != null ? new Date(created * 1e3) : void 0
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// src/chat/language-model.ts
|
|
988
|
+
var KrutrimChatLanguageModel = class {
|
|
989
|
+
constructor(modelId, settings, config) {
|
|
990
|
+
this.specificationVersion = "v3";
|
|
991
|
+
this.modelId = modelId;
|
|
992
|
+
this.resolvedModelId = resolveModelId(String(modelId));
|
|
993
|
+
this.settings = settings;
|
|
994
|
+
this.config = config;
|
|
995
|
+
}
|
|
996
|
+
get provider() {
|
|
997
|
+
return this.config.provider;
|
|
998
|
+
}
|
|
999
|
+
get supportedUrls() {
|
|
1000
|
+
return {
|
|
1001
|
+
"image/*": [/^https?:\/\/.+/i]
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
async getArgs(options) {
|
|
1005
|
+
const {
|
|
1006
|
+
prompt,
|
|
1007
|
+
maxOutputTokens,
|
|
1008
|
+
temperature,
|
|
1009
|
+
topP,
|
|
1010
|
+
topK,
|
|
1011
|
+
frequencyPenalty,
|
|
1012
|
+
presencePenalty,
|
|
1013
|
+
stopSequences,
|
|
1014
|
+
responseFormat,
|
|
1015
|
+
seed,
|
|
1016
|
+
tools,
|
|
1017
|
+
toolChoice,
|
|
1018
|
+
providerOptions,
|
|
1019
|
+
stream
|
|
1020
|
+
} = options;
|
|
1021
|
+
const warnings = [];
|
|
1022
|
+
if (topK != null) {
|
|
1023
|
+
warnings.push({ type: "unsupported", feature: "topK" });
|
|
1024
|
+
}
|
|
1025
|
+
const krutrimOptions = await providerUtils.parseProviderOptions({
|
|
1026
|
+
provider: "krutrim",
|
|
1027
|
+
providerOptions: {
|
|
1028
|
+
krutrim: {
|
|
1029
|
+
...providerOptions?.krutrim,
|
|
1030
|
+
...this.settings
|
|
1031
|
+
}
|
|
1032
|
+
},
|
|
1033
|
+
schema: chatSettingsSchema
|
|
1034
|
+
});
|
|
1035
|
+
const reasoningEffort = krutrimOptions?.reasoningEffort ?? krutrimOptions?.reasoning_effort;
|
|
1036
|
+
const baseArgs = {
|
|
1037
|
+
model: this.resolvedModelId,
|
|
1038
|
+
messages: convertToChatMessages(prompt),
|
|
1039
|
+
max_tokens: maxOutputTokens,
|
|
1040
|
+
temperature,
|
|
1041
|
+
top_p: topP,
|
|
1042
|
+
frequency_penalty: frequencyPenalty,
|
|
1043
|
+
presence_penalty: presencePenalty,
|
|
1044
|
+
stop: stopSequences,
|
|
1045
|
+
seed,
|
|
1046
|
+
n: krutrimOptions?.n ?? void 0,
|
|
1047
|
+
user: krutrimOptions?.user ?? void 0,
|
|
1048
|
+
response_format: stream === false && responseFormat?.type === "json" ? responseFormat.schema ? {
|
|
1049
|
+
type: "json_schema",
|
|
1050
|
+
json_schema: {
|
|
1051
|
+
name: responseFormat.name ?? "response",
|
|
1052
|
+
description: responseFormat.description,
|
|
1053
|
+
schema: responseFormat.schema,
|
|
1054
|
+
strict: true
|
|
1055
|
+
}
|
|
1056
|
+
} : { type: "json_object" } : void 0
|
|
1057
|
+
};
|
|
1058
|
+
if (reasoningEffort) {
|
|
1059
|
+
baseArgs.reasoning_effort = reasoningEffort;
|
|
1060
|
+
}
|
|
1061
|
+
let toolsArg = null;
|
|
1062
|
+
if (tools && tools.length > 0) {
|
|
1063
|
+
toolsArg = prepareTools({ tools, toolChoice });
|
|
1064
|
+
}
|
|
1065
|
+
if (responseFormat?.type === "json" && responseFormat.schema) ;
|
|
1066
|
+
return {
|
|
1067
|
+
args: {
|
|
1068
|
+
...baseArgs,
|
|
1069
|
+
...toolsArg ? {
|
|
1070
|
+
tools: toolsArg.tools,
|
|
1071
|
+
tool_choice: toolsArg.tool_choice
|
|
1072
|
+
} : {}
|
|
1073
|
+
},
|
|
1074
|
+
warnings: [...warnings, ...toolsArg?.toolWarnings ?? []]
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
async doGenerate(options) {
|
|
1078
|
+
const { args, warnings } = await this.getArgs({
|
|
1079
|
+
...options,
|
|
1080
|
+
stream: false
|
|
1081
|
+
});
|
|
1082
|
+
const {
|
|
1083
|
+
responseHeaders,
|
|
1084
|
+
value: response,
|
|
1085
|
+
rawValue: rawResponse
|
|
1086
|
+
} = await providerUtils.postJsonToApi({
|
|
1087
|
+
url: this.config.url({
|
|
1088
|
+
path: "/chat/completions",
|
|
1089
|
+
modelId: this.resolvedModelId
|
|
1090
|
+
}),
|
|
1091
|
+
headers: providerUtils.combineHeaders(this.config.headers(), options.headers),
|
|
1092
|
+
body: args,
|
|
1093
|
+
failedResponseHandler: krutrimFailedResponseHandler,
|
|
1094
|
+
successfulResponseHandler: providerUtils.createJsonResponseHandler(chatResponseSchema),
|
|
1095
|
+
abortSignal: options.abortSignal,
|
|
1096
|
+
fetch: this.config.fetch
|
|
1097
|
+
});
|
|
1098
|
+
const choice = response.choices[0];
|
|
1099
|
+
if (!choice) {
|
|
1100
|
+
throw new provider.InvalidResponseDataError({
|
|
1101
|
+
data: response,
|
|
1102
|
+
message: "No choices returned in Krutrim chat completion response"
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
const content = [];
|
|
1106
|
+
if (choice.message.content) {
|
|
1107
|
+
content.push({ type: "text", text: choice.message.content });
|
|
1108
|
+
}
|
|
1109
|
+
if (choice.message.reasoning_content) {
|
|
1110
|
+
content.push({ type: "reasoning", text: choice.message.reasoning_content });
|
|
1111
|
+
}
|
|
1112
|
+
if (choice.message.tool_calls?.length) {
|
|
1113
|
+
for (const toolCall of choice.message.tool_calls) {
|
|
1114
|
+
content.push({
|
|
1115
|
+
type: "tool-call",
|
|
1116
|
+
toolCallId: toolCall.id ?? (this.config.generateId ?? providerUtils.generateId)(),
|
|
1117
|
+
toolName: toolCall.function.name,
|
|
1118
|
+
input: toolCall.function.arguments
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
return {
|
|
1123
|
+
content,
|
|
1124
|
+
finishReason: {
|
|
1125
|
+
unified: mapFinishReason(choice.finish_reason),
|
|
1126
|
+
raw: choice.finish_reason ?? void 0
|
|
1127
|
+
},
|
|
1128
|
+
usage: {
|
|
1129
|
+
inputTokens: {
|
|
1130
|
+
total: response.usage?.prompt_tokens ?? void 0,
|
|
1131
|
+
noCache: void 0,
|
|
1132
|
+
cacheRead: void 0,
|
|
1133
|
+
cacheWrite: void 0
|
|
1134
|
+
},
|
|
1135
|
+
outputTokens: {
|
|
1136
|
+
total: response.usage?.completion_tokens ?? void 0,
|
|
1137
|
+
text: void 0,
|
|
1138
|
+
reasoning: void 0
|
|
1139
|
+
}
|
|
1140
|
+
},
|
|
1141
|
+
providerMetadata: {
|
|
1142
|
+
krutrim: {
|
|
1143
|
+
system_fingerprint: response.system_fingerprint,
|
|
1144
|
+
resolvedModelId: this.resolvedModelId
|
|
1145
|
+
}
|
|
1146
|
+
},
|
|
1147
|
+
warnings,
|
|
1148
|
+
request: { body: args },
|
|
1149
|
+
response: {
|
|
1150
|
+
headers: responseHeaders,
|
|
1151
|
+
body: rawResponse,
|
|
1152
|
+
id: response.id ?? void 0,
|
|
1153
|
+
modelId: response.model ?? void 0,
|
|
1154
|
+
timestamp: response.created ? new Date(response.created * 1e3) : void 0
|
|
1155
|
+
}
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
async doStream(options) {
|
|
1159
|
+
const { args, warnings } = await this.getArgs({ ...options, stream: true });
|
|
1160
|
+
const { responseHeaders, value: response } = await providerUtils.postJsonToApi({
|
|
1161
|
+
url: this.config.url({
|
|
1162
|
+
path: "/chat/completions",
|
|
1163
|
+
modelId: this.resolvedModelId
|
|
1164
|
+
}),
|
|
1165
|
+
headers: providerUtils.combineHeaders(this.config.headers(), options.headers),
|
|
1166
|
+
body: { ...args, stream: true },
|
|
1167
|
+
failedResponseHandler: krutrimFailedResponseHandler,
|
|
1168
|
+
successfulResponseHandler: providerUtils.createEventSourceResponseHandler(chatChunkSchema),
|
|
1169
|
+
abortSignal: options.abortSignal,
|
|
1170
|
+
fetch: this.config.fetch
|
|
1171
|
+
});
|
|
1172
|
+
const toolCalls = [];
|
|
1173
|
+
let finishReason = {
|
|
1174
|
+
unified: "other",
|
|
1175
|
+
raw: void 0
|
|
1176
|
+
};
|
|
1177
|
+
let usage = {
|
|
1178
|
+
inputTokens: {
|
|
1179
|
+
total: void 0,
|
|
1180
|
+
noCache: void 0,
|
|
1181
|
+
cacheRead: void 0,
|
|
1182
|
+
cacheWrite: void 0
|
|
1183
|
+
},
|
|
1184
|
+
outputTokens: {
|
|
1185
|
+
total: void 0,
|
|
1186
|
+
text: void 0,
|
|
1187
|
+
reasoning: void 0
|
|
1188
|
+
}
|
|
1189
|
+
};
|
|
1190
|
+
let isFirstChunk = true;
|
|
1191
|
+
let textStarted = false;
|
|
1192
|
+
let reasoningStarted = false;
|
|
1193
|
+
const self = this;
|
|
1194
|
+
return {
|
|
1195
|
+
stream: response.pipeThrough(
|
|
1196
|
+
new TransformStream({
|
|
1197
|
+
start(controller) {
|
|
1198
|
+
controller.enqueue({ type: "stream-start", warnings });
|
|
1199
|
+
},
|
|
1200
|
+
transform(chunk, controller) {
|
|
1201
|
+
if (!chunk.success) {
|
|
1202
|
+
finishReason = { unified: "error", raw: void 0 };
|
|
1203
|
+
controller.enqueue({ type: "error", error: chunk.error });
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
const value = chunk.value;
|
|
1207
|
+
if ("error" in value && value.error != null && !("choices" in value)) {
|
|
1208
|
+
finishReason = { unified: "error", raw: void 0 };
|
|
1209
|
+
controller.enqueue({ type: "error", error: value.error });
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
if (!("choices" in value)) {
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
if (isFirstChunk) {
|
|
1216
|
+
isFirstChunk = false;
|
|
1217
|
+
const metadata = getResponseMetadata(value);
|
|
1218
|
+
if (metadata.id || metadata.timestamp || metadata.modelId) {
|
|
1219
|
+
controller.enqueue({ type: "response-metadata", ...metadata });
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
if (value.usage != null) {
|
|
1223
|
+
usage = {
|
|
1224
|
+
inputTokens: {
|
|
1225
|
+
total: value.usage.prompt_tokens ?? void 0,
|
|
1226
|
+
noCache: void 0,
|
|
1227
|
+
cacheRead: void 0,
|
|
1228
|
+
cacheWrite: void 0
|
|
1229
|
+
},
|
|
1230
|
+
outputTokens: {
|
|
1231
|
+
total: value.usage.completion_tokens ?? void 0,
|
|
1232
|
+
text: void 0,
|
|
1233
|
+
reasoning: void 0
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
const choice = value.choices[0];
|
|
1238
|
+
if (choice?.finish_reason != null) {
|
|
1239
|
+
finishReason = {
|
|
1240
|
+
unified: mapFinishReason(choice.finish_reason),
|
|
1241
|
+
raw: choice.finish_reason
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1244
|
+
if (choice?.delta == null) {
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
const delta = choice.delta;
|
|
1248
|
+
const reasoningText = delta.reasoning ?? delta.reasoning_content;
|
|
1249
|
+
if (reasoningText != null && reasoningText.length > 0) {
|
|
1250
|
+
if (!reasoningStarted) {
|
|
1251
|
+
reasoningStarted = true;
|
|
1252
|
+
controller.enqueue({
|
|
1253
|
+
type: "reasoning-start",
|
|
1254
|
+
id: "reasoning-0"
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
controller.enqueue({
|
|
1258
|
+
type: "reasoning-delta",
|
|
1259
|
+
id: "reasoning-0",
|
|
1260
|
+
delta: reasoningText
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
if (delta.content != null && delta.content.length > 0) {
|
|
1264
|
+
if (!textStarted) {
|
|
1265
|
+
textStarted = true;
|
|
1266
|
+
controller.enqueue({ type: "text-start", id: "text-0" });
|
|
1267
|
+
}
|
|
1268
|
+
controller.enqueue({
|
|
1269
|
+
type: "text-delta",
|
|
1270
|
+
id: "text-0",
|
|
1271
|
+
delta: delta.content
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
if (delta.tool_calls != null) {
|
|
1275
|
+
for (const toolCallDelta of delta.tool_calls) {
|
|
1276
|
+
const index = toolCallDelta.index;
|
|
1277
|
+
if (toolCalls[index] == null) {
|
|
1278
|
+
if (toolCallDelta.id == null) {
|
|
1279
|
+
throw new provider.InvalidResponseDataError({
|
|
1280
|
+
data: toolCallDelta,
|
|
1281
|
+
message: `Expected tool call 'id' to be a string.`
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
if (toolCallDelta.function?.name == null) {
|
|
1285
|
+
throw new provider.InvalidResponseDataError({
|
|
1286
|
+
data: toolCallDelta,
|
|
1287
|
+
message: `Expected tool call 'function.name' to be a string.`
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
toolCalls[index] = {
|
|
1291
|
+
id: toolCallDelta.id,
|
|
1292
|
+
name: toolCallDelta.function.name,
|
|
1293
|
+
arguments: toolCallDelta.function.arguments ?? "",
|
|
1294
|
+
hasFinished: false
|
|
1295
|
+
};
|
|
1296
|
+
const toolCall2 = toolCalls[index];
|
|
1297
|
+
controller.enqueue({
|
|
1298
|
+
type: "tool-input-start",
|
|
1299
|
+
id: toolCall2.id,
|
|
1300
|
+
toolName: toolCall2.name
|
|
1301
|
+
});
|
|
1302
|
+
if (toolCall2.arguments.length > 0) {
|
|
1303
|
+
controller.enqueue({
|
|
1304
|
+
type: "tool-input-delta",
|
|
1305
|
+
id: toolCall2.id,
|
|
1306
|
+
delta: toolCall2.arguments
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
if (providerUtils.isParsableJson(toolCall2.arguments)) {
|
|
1310
|
+
controller.enqueue({
|
|
1311
|
+
type: "tool-input-end",
|
|
1312
|
+
id: toolCall2.id
|
|
1313
|
+
});
|
|
1314
|
+
controller.enqueue({
|
|
1315
|
+
type: "tool-call",
|
|
1316
|
+
toolCallId: toolCall2.id,
|
|
1317
|
+
toolName: toolCall2.name,
|
|
1318
|
+
input: toolCall2.arguments
|
|
1319
|
+
});
|
|
1320
|
+
toolCall2.hasFinished = true;
|
|
1321
|
+
}
|
|
1322
|
+
continue;
|
|
1323
|
+
}
|
|
1324
|
+
const toolCall = toolCalls[index];
|
|
1325
|
+
if (toolCall.hasFinished) continue;
|
|
1326
|
+
if (toolCallDelta.function?.arguments != null) {
|
|
1327
|
+
toolCall.arguments += toolCallDelta.function.arguments;
|
|
1328
|
+
controller.enqueue({
|
|
1329
|
+
type: "tool-input-delta",
|
|
1330
|
+
id: toolCall.id,
|
|
1331
|
+
delta: toolCallDelta.function.arguments
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
if (toolCall.name != null && toolCall.arguments != null && providerUtils.isParsableJson(toolCall.arguments)) {
|
|
1335
|
+
controller.enqueue({
|
|
1336
|
+
type: "tool-input-end",
|
|
1337
|
+
id: toolCall.id
|
|
1338
|
+
});
|
|
1339
|
+
controller.enqueue({
|
|
1340
|
+
type: "tool-call",
|
|
1341
|
+
toolCallId: toolCall.id,
|
|
1342
|
+
toolName: toolCall.name,
|
|
1343
|
+
input: toolCall.arguments
|
|
1344
|
+
});
|
|
1345
|
+
toolCall.hasFinished = true;
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
},
|
|
1350
|
+
flush(controller) {
|
|
1351
|
+
if (reasoningStarted) {
|
|
1352
|
+
controller.enqueue({ type: "reasoning-end", id: "reasoning-0" });
|
|
1353
|
+
}
|
|
1354
|
+
if (textStarted) {
|
|
1355
|
+
controller.enqueue({ type: "text-end", id: "text-0" });
|
|
1356
|
+
}
|
|
1357
|
+
controller.enqueue({
|
|
1358
|
+
type: "finish",
|
|
1359
|
+
finishReason,
|
|
1360
|
+
usage,
|
|
1361
|
+
providerMetadata: {
|
|
1362
|
+
krutrim: {
|
|
1363
|
+
resolvedModelId: self.resolvedModelId
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
})
|
|
1369
|
+
),
|
|
1370
|
+
request: { body: args },
|
|
1371
|
+
response: { headers: responseHeaders }
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
|
|
1376
|
+
// src/config.ts
|
|
1377
|
+
var KRUTRIM_DEFAULT_BASE_URL = "https://cloud.olakrutrim.com/v1";
|
|
1378
|
+
var KRUTRIM_DEFAULT_API_HOST = "https://cloud.olakrutrim.com";
|
|
1379
|
+
function resolveKrutrimApiKey(apiKey) {
|
|
1380
|
+
if (apiKey && apiKey.trim().length > 0) {
|
|
1381
|
+
return apiKey;
|
|
1382
|
+
}
|
|
1383
|
+
if (typeof process === "undefined" || !process.env) {
|
|
1384
|
+
return void 0;
|
|
1385
|
+
}
|
|
1386
|
+
return process.env.KRUTRIM_API_KEY?.trim() || process.env.KRUTRIM_CLOUD_API_KEY?.trim() || void 0;
|
|
1387
|
+
}
|
|
1388
|
+
var embeddingResponseSchema = zod.z.object({
|
|
1389
|
+
object: zod.z.string().nullish(),
|
|
1390
|
+
data: zod.z.array(
|
|
1391
|
+
zod.z.object({
|
|
1392
|
+
object: zod.z.string().nullish(),
|
|
1393
|
+
embedding: zod.z.array(zod.z.number()),
|
|
1394
|
+
index: zod.z.number()
|
|
1395
|
+
})
|
|
1396
|
+
),
|
|
1397
|
+
model: zod.z.string().nullish(),
|
|
1398
|
+
usage: zod.z.object({
|
|
1399
|
+
prompt_tokens: zod.z.number().nullish(),
|
|
1400
|
+
total_tokens: zod.z.number().nullish()
|
|
1401
|
+
}).nullish()
|
|
1402
|
+
});
|
|
1403
|
+
var KrutrimEmbeddingModel = class {
|
|
1404
|
+
constructor(modelId, config) {
|
|
1405
|
+
this.specificationVersion = "v3";
|
|
1406
|
+
this.maxEmbeddingsPerCall = 32;
|
|
1407
|
+
this.supportsParallelCalls = true;
|
|
1408
|
+
this.modelId = modelId;
|
|
1409
|
+
this.resolvedModelId = resolveModelId(modelId);
|
|
1410
|
+
this.config = config;
|
|
1411
|
+
}
|
|
1412
|
+
get provider() {
|
|
1413
|
+
return this.config.provider;
|
|
1414
|
+
}
|
|
1415
|
+
async doEmbed({
|
|
1416
|
+
values,
|
|
1417
|
+
headers,
|
|
1418
|
+
abortSignal,
|
|
1419
|
+
providerOptions
|
|
1420
|
+
}) {
|
|
1421
|
+
const krutrimOptions = providerOptions?.krutrim;
|
|
1422
|
+
const {
|
|
1423
|
+
responseHeaders,
|
|
1424
|
+
value: response,
|
|
1425
|
+
rawValue
|
|
1426
|
+
} = await providerUtils.postJsonToApi({
|
|
1427
|
+
url: this.config.url({
|
|
1428
|
+
path: "/embeddings",
|
|
1429
|
+
modelId: this.resolvedModelId
|
|
1430
|
+
}),
|
|
1431
|
+
headers: providerUtils.combineHeaders(this.config.headers(), headers),
|
|
1432
|
+
body: {
|
|
1433
|
+
model: this.resolvedModelId,
|
|
1434
|
+
input: values,
|
|
1435
|
+
dimensions: krutrimOptions?.dimensions,
|
|
1436
|
+
user: krutrimOptions?.user
|
|
1437
|
+
},
|
|
1438
|
+
failedResponseHandler: krutrimFailedResponseHandler,
|
|
1439
|
+
successfulResponseHandler: providerUtils.createJsonResponseHandler(embeddingResponseSchema),
|
|
1440
|
+
abortSignal,
|
|
1441
|
+
fetch: this.config.fetch
|
|
1442
|
+
});
|
|
1443
|
+
return {
|
|
1444
|
+
embeddings: response.data.slice().sort((a, b) => a.index - b.index).map((item) => item.embedding),
|
|
1445
|
+
usage: response.usage?.total_tokens ? { tokens: response.usage.total_tokens } : void 0,
|
|
1446
|
+
warnings: [],
|
|
1447
|
+
response: { headers: responseHeaders, body: rawValue }
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
};
|
|
1451
|
+
|
|
1452
|
+
// src/provider.ts
|
|
1453
|
+
function createKrutrim(options = {}) {
|
|
1454
|
+
const baseURL = providerUtils.withoutTrailingSlash(options.baseURL) ?? KRUTRIM_DEFAULT_BASE_URL;
|
|
1455
|
+
const apiHost = providerUtils.withoutTrailingSlash(options.apiHost) ?? KRUTRIM_DEFAULT_API_HOST;
|
|
1456
|
+
const getApiKey = () => {
|
|
1457
|
+
const resolved = resolveKrutrimApiKey(options.apiKey);
|
|
1458
|
+
return providerUtils.loadApiKey({
|
|
1459
|
+
apiKey: resolved,
|
|
1460
|
+
environmentVariableName: "KRUTRIM_API_KEY",
|
|
1461
|
+
description: "Krutrim Cloud"
|
|
1462
|
+
});
|
|
1463
|
+
};
|
|
1464
|
+
const getHeaders = () => {
|
|
1465
|
+
const apiKey = getApiKey();
|
|
1466
|
+
return {
|
|
1467
|
+
Authorization: `Bearer ${apiKey}`,
|
|
1468
|
+
"User-Agent": "krutrim-ai-sdk",
|
|
1469
|
+
...options.headers
|
|
1470
|
+
};
|
|
1471
|
+
};
|
|
1472
|
+
const sharedConfig = {
|
|
1473
|
+
headers: getHeaders,
|
|
1474
|
+
fetch: options.fetch,
|
|
1475
|
+
generateId: options.generateId ?? providerUtils.generateId,
|
|
1476
|
+
url: ({ path }) => `${baseURL}${path}`,
|
|
1477
|
+
languageLabsUrl: (path) => {
|
|
1478
|
+
const normalized = path.startsWith("/") ? path : `/${path}`;
|
|
1479
|
+
return `${apiHost}/api/v1${normalized}`;
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
const createChatModel = (modelId, settings = {}) => new KrutrimChatLanguageModel(modelId, settings, {
|
|
1483
|
+
...sharedConfig,
|
|
1484
|
+
provider: "krutrim.chat"
|
|
1485
|
+
});
|
|
1486
|
+
const createLanguageModel = (modelId, settings) => {
|
|
1487
|
+
if (new.target) {
|
|
1488
|
+
throw new Error(
|
|
1489
|
+
"The Krutrim model function cannot be called with the new keyword."
|
|
1490
|
+
);
|
|
1491
|
+
}
|
|
1492
|
+
return createChatModel(modelId, settings);
|
|
1493
|
+
};
|
|
1494
|
+
const createEmbeddingModel = (modelId) => new KrutrimEmbeddingModel(modelId, {
|
|
1495
|
+
...sharedConfig,
|
|
1496
|
+
provider: "krutrim.embedding"
|
|
1497
|
+
});
|
|
1498
|
+
const provider = ((modelId, settings) => createLanguageModel(modelId, settings));
|
|
1499
|
+
provider.languageModel = createLanguageModel;
|
|
1500
|
+
provider.chat = createChatModel;
|
|
1501
|
+
provider.embedding = createEmbeddingModel;
|
|
1502
|
+
provider.embeddingModel = createEmbeddingModel;
|
|
1503
|
+
provider.textEmbeddingModel = createEmbeddingModel;
|
|
1504
|
+
provider.speech = (language, settings) => new KrutrimSpeechModel(language, settings ?? {}, {
|
|
1505
|
+
...sharedConfig,
|
|
1506
|
+
provider: "krutrim.speech"
|
|
1507
|
+
});
|
|
1508
|
+
provider.transcription = (language) => new KrutrimTranscriptionModel(language, {
|
|
1509
|
+
...sharedConfig,
|
|
1510
|
+
provider: "krutrim.transcription"
|
|
1511
|
+
});
|
|
1512
|
+
provider.languageDetection = () => new KrutrimLanguageDetectionModel({
|
|
1513
|
+
...sharedConfig,
|
|
1514
|
+
provider: "krutrim.language-detection"
|
|
1515
|
+
});
|
|
1516
|
+
provider.translation = (settings) => new KrutrimTranslationModel(settings, {
|
|
1517
|
+
...sharedConfig,
|
|
1518
|
+
provider: "krutrim.translation"
|
|
1519
|
+
});
|
|
1520
|
+
return provider;
|
|
1521
|
+
}
|
|
1522
|
+
var krutrim = createKrutrim();
|
|
1523
|
+
|
|
1524
|
+
// src/indic/prompts.ts
|
|
1525
|
+
function languageLabel(code) {
|
|
1526
|
+
const entry = INDIC_LANGUAGES.find((l) => l.code === code);
|
|
1527
|
+
if (entry) {
|
|
1528
|
+
return `${entry.name} (${entry.nativeName})`;
|
|
1529
|
+
}
|
|
1530
|
+
return code;
|
|
1531
|
+
}
|
|
1532
|
+
function indicResponsePrompt(language, options) {
|
|
1533
|
+
const nativeScript = options?.nativeScript ?? true;
|
|
1534
|
+
const allowCodeMix = options?.allowCodeMix ?? false;
|
|
1535
|
+
const label = languageLabel(language);
|
|
1536
|
+
return [
|
|
1537
|
+
`You are a helpful assistant optimized for Indian languages and culture.`,
|
|
1538
|
+
`Respond primarily in ${label}.`,
|
|
1539
|
+
nativeScript ? `Prefer the language's native script over Latin transliteration unless the user writes in Latin script and clearly wants Romanized output.` : `Latin / Romanized script is acceptable when natural for the user.`,
|
|
1540
|
+
allowCodeMix ? `Light code-mixing with English is fine when it matches how speakers actually talk.` : `Avoid unnecessary English code-mixing unless the user does so first.`,
|
|
1541
|
+
`Be culturally aware of Indian context (festivals, geography, INR, regional nuance) without stereotyping.`
|
|
1542
|
+
].join(" ");
|
|
1543
|
+
}
|
|
1544
|
+
function languageDetectionPrompt() {
|
|
1545
|
+
return [
|
|
1546
|
+
"Identify the primary language of the user message.",
|
|
1547
|
+
"Reply with ONLY a BCP-47 language tag such as hi-IN, ta-IN, en-IN, bn-IN, ml-IN.",
|
|
1548
|
+
"If mixed, pick the dominant language. No explanation."
|
|
1549
|
+
].join(" ");
|
|
1550
|
+
}
|
|
1551
|
+
function transliterationNotes(targetLanguage) {
|
|
1552
|
+
const target = targetLanguage ? ` When responding, prefer native script for ${languageLabel(targetLanguage)} unless asked otherwise.` : "";
|
|
1553
|
+
return [
|
|
1554
|
+
'Users may type Indian languages in Latin script (e.g. Hinglish: "aap kaise ho", Tanglish, etc.).',
|
|
1555
|
+
"Interpret Romanized Indic phonetically; do not treat it as English only.",
|
|
1556
|
+
"When the meaning is clear, reply in the same language; offer native script when helpful.",
|
|
1557
|
+
target
|
|
1558
|
+
].join(" ").trim();
|
|
1559
|
+
}
|
|
1560
|
+
function indicSupportAgentPrompt(options) {
|
|
1561
|
+
const brand = options?.brandName ?? "our service";
|
|
1562
|
+
const langs = options?.languages?.map(languageLabel).join(", ") ?? "Hindi, English (India), and other major Indian languages";
|
|
1563
|
+
return [
|
|
1564
|
+
`You are a polite, efficient customer support agent for ${brand}.`,
|
|
1565
|
+
`You can assist in: ${langs}.`,
|
|
1566
|
+
`Mirror the customer's language and script when possible.`,
|
|
1567
|
+
`Keep answers clear, short, and actionable. Amounts should use \u20B9 / INR when relevant.`,
|
|
1568
|
+
`If you are unsure, ask a clarifying question instead of guessing personal data.`
|
|
1569
|
+
].join(" ");
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
exports.INDIC_LANGUAGES = INDIC_LANGUAGES;
|
|
1573
|
+
exports.KRUTRIM_CHAT_MODELS = KRUTRIM_CHAT_MODELS;
|
|
1574
|
+
exports.KRUTRIM_DEFAULT_API_HOST = KRUTRIM_DEFAULT_API_HOST;
|
|
1575
|
+
exports.KRUTRIM_DEFAULT_BASE_URL = KRUTRIM_DEFAULT_BASE_URL;
|
|
1576
|
+
exports.KRUTRIM_EMBEDDING_MODELS = KRUTRIM_EMBEDDING_MODELS;
|
|
1577
|
+
exports.KRUTRIM_SPEECH_MODELS = KRUTRIM_SPEECH_MODELS;
|
|
1578
|
+
exports.KRUTRIM_VISION_MODELS = KRUTRIM_VISION_MODELS;
|
|
1579
|
+
exports.MODEL_ALIASES = MODEL_ALIASES;
|
|
1580
|
+
exports.createKrutrim = createKrutrim;
|
|
1581
|
+
exports.enhanceKrutrimErrorMessage = enhanceKrutrimErrorMessage;
|
|
1582
|
+
exports.indicResponsePrompt = indicResponsePrompt;
|
|
1583
|
+
exports.indicSupportAgentPrompt = indicSupportAgentPrompt;
|
|
1584
|
+
exports.krutrim = krutrim;
|
|
1585
|
+
exports.krutrimErrorDataSchema = krutrimErrorDataSchema;
|
|
1586
|
+
exports.krutrimFailedResponseHandler = krutrimFailedResponseHandler;
|
|
1587
|
+
exports.languageDetectionPrompt = languageDetectionPrompt;
|
|
1588
|
+
exports.resolveKrutrimApiKey = resolveKrutrimApiKey;
|
|
1589
|
+
exports.resolveModelId = resolveModelId;
|
|
1590
|
+
exports.toBhashikLanguageCode = toBhashikLanguageCode;
|
|
1591
|
+
exports.toTranslationLanguageCode = toTranslationLanguageCode;
|
|
1592
|
+
exports.transliterationNotes = transliterationNotes;
|
|
1593
|
+
//# sourceMappingURL=index.cjs.map
|
|
1594
|
+
//# sourceMappingURL=index.cjs.map
|