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