@core-ai/mistral 0.16.0 → 0.18.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.
Files changed (2) hide show
  1. package/dist/index.js +192 -32
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  // src/chat-adapter.ts
14
14
  import {
15
15
  asObject,
16
+ getProviderMetadata,
16
17
  safeParseJsonObject,
17
18
  zodSchemaToJsonSchema
18
19
  } from "@core-ai/core-ai";
@@ -49,13 +50,31 @@ function parseMistralEmbedProviderOptions(providerOptions) {
49
50
  }
50
51
  var mistralProviderOptionsSchema = mistralGenerateProviderOptionsSchema;
51
52
 
53
+ // src/model-capabilities.ts
54
+ var NO_REASONING_CAPABILITIES = {
55
+ reasoning: {
56
+ mode: "unsupported",
57
+ supportedEfforts: [],
58
+ restrictsSamplingParams: false,
59
+ supportedToolChoices: ["auto", "none", "required", "tool"]
60
+ }
61
+ };
62
+ function getMistralModelCapabilities(_modelId) {
63
+ return NO_REASONING_CAPABILITIES;
64
+ }
65
+
52
66
  // src/chat-adapter.ts
67
+ var MISTRAL_REASONING_METADATA_NAMESPACE = "mistral";
53
68
  var DEFAULT_STRUCTURED_OUTPUT_TOOL_NAME = "core_ai_generate_object";
54
69
  var DEFAULT_STRUCTURED_OUTPUT_TOOL_DESCRIPTION = "Return a JSON object that matches the requested schema.";
55
- function convertMessages(messages) {
56
- return messages.map(convertMessage);
70
+ function convertMessages(messages, options = {}) {
71
+ const includeReasoning = options.includeReasoning ?? true;
72
+ return messages.flatMap((message) => {
73
+ const convertedMessage = convertMessage(message, includeReasoning);
74
+ return convertedMessage ? [convertedMessage] : [];
75
+ });
57
76
  }
58
- function convertMessage(message) {
77
+ function convertMessage(message, includeReasoning) {
59
78
  if (message.role === "system") {
60
79
  return {
61
80
  role: "system",
@@ -77,12 +96,26 @@ function convertMessage(message) {
77
96
  if (part.type === "text") {
78
97
  contentChunks.push({ type: "text", text: part.text });
79
98
  } else if (part.type === "reasoning" && part.text.length > 0) {
80
- contentChunks.push({
81
- type: "thinking",
82
- thinking: [{ type: "text", text: part.text }]
83
- });
99
+ const isNativeMistralReasoning = getProviderMetadata(
100
+ part.providerMetadata,
101
+ MISTRAL_REASONING_METADATA_NAMESPACE
102
+ ) != null;
103
+ if (includeReasoning && isNativeMistralReasoning) {
104
+ contentChunks.push({
105
+ type: "thinking",
106
+ thinking: [{ type: "text", text: part.text }]
107
+ });
108
+ } else {
109
+ contentChunks.push({
110
+ type: "text",
111
+ text: `<thinking>${part.text}</thinking>`
112
+ });
113
+ }
84
114
  }
85
115
  }
116
+ if (contentChunks.length === 0 && toolCalls.length === 0) {
117
+ return void 0;
118
+ }
86
119
  return {
87
120
  role: "assistant",
88
121
  content: contentChunks.length > 0 ? contentChunks : null,
@@ -195,9 +228,12 @@ function createStreamRequest(modelId, options) {
195
228
  return mapMistralProviderOptionsToRequest(baseRequest, mistralOptions);
196
229
  }
197
230
  function createRequestBase(modelId, options) {
231
+ const capabilities = getMistralModelCapabilities(modelId);
198
232
  return {
199
233
  model: modelId,
200
- messages: convertMessages(options.messages),
234
+ messages: convertMessages(options.messages, {
235
+ includeReasoning: capabilities.reasoning.mode !== "unsupported"
236
+ }),
201
237
  ...options.tools && Object.keys(options.tools).length > 0 ? { tools: convertTools(options.tools) } : {},
202
238
  ...options.toolChoice ? { toolChoice: convertToolChoice(options.toolChoice) } : {},
203
239
  ...mapSamplingToRequestFields(options)
@@ -549,39 +585,163 @@ function toObject(value) {
549
585
  // src/mistral-error.ts
550
586
  import {
551
587
  RequestAbortedError,
588
+ RequestTimeoutError,
552
589
  MistralError
553
590
  } from "@mistralai/mistralai/models/errors";
554
- import { AbortedError, ProviderError } from "@core-ai/core-ai";
591
+ import {
592
+ AbortedError,
593
+ ContextLengthExceededError,
594
+ ModelOverloadedError,
595
+ ProviderError,
596
+ RateLimitError,
597
+ ServiceUnavailableError,
598
+ asRecord,
599
+ getErrorMessage,
600
+ getHttpStatusCode,
601
+ getRetryAfterSecondsFromError,
602
+ getString,
603
+ isAbortErrorByName,
604
+ isRateLimitStatus,
605
+ isTransientUnavailableStatus
606
+ } from "@core-ai/core-ai";
607
+ var OVERLOAD_MESSAGE_ELIGIBLE_STATUS_CODES = /* @__PURE__ */ new Set([500, 502, 503, 504]);
555
608
  function wrapMistralError(error) {
556
- if (error instanceof RequestAbortedError || error instanceof Error && error.name === "AbortError") {
609
+ if (isMistralAbortError(error)) {
557
610
  return new AbortedError(error, "mistral");
558
611
  }
559
- if (error instanceof MistralError) {
560
- return new ProviderError(
561
- error.message,
562
- "mistral",
563
- error.statusCode,
564
- error
565
- );
612
+ if (isMistralTimeoutError(error)) {
613
+ return new ServiceUnavailableError(getErrorMessage(error), "mistral", {
614
+ cause: error
615
+ });
616
+ }
617
+ const message = getErrorMessage(error);
618
+ const statusCode = error instanceof MistralError ? error.statusCode : getHttpStatusCode(error, ["statusCode", "status"]);
619
+ const body = parseErrorBody(error);
620
+ const options = { statusCode, cause: error };
621
+ const contextLength = getContextLengthDetails(body, message);
622
+ if (contextLength) {
623
+ return new ContextLengthExceededError(message, "mistral", {
624
+ ...options,
625
+ ...contextLength
626
+ });
627
+ }
628
+ if (indicatesMistralOverload(message, statusCode)) {
629
+ return new ModelOverloadedError(message, "mistral", options);
630
+ }
631
+ if (isMistralRateLimit(statusCode, body?.type)) {
632
+ return new RateLimitError(message, "mistral", {
633
+ ...options,
634
+ retryAfterSeconds: getRetryAfterSecondsFromError(error)
635
+ });
636
+ }
637
+ if (isTransientUnavailableStatus(statusCode)) {
638
+ return new ServiceUnavailableError(message, "mistral", options);
639
+ }
640
+ return new ProviderError(message, "mistral", options);
641
+ }
642
+ function isMistralAbortError(error) {
643
+ return error instanceof RequestAbortedError || isAbortErrorByName(error);
644
+ }
645
+ function isMistralTimeoutError(error) {
646
+ return error instanceof RequestTimeoutError || error instanceof Error && error.name === "RequestTimeoutError";
647
+ }
648
+ function isMistralRateLimit(statusCode, errorType) {
649
+ return isRateLimitStatus(statusCode) || errorType === "rate_limit_error";
650
+ }
651
+ function indicatesMistralOverload(text, statusCode) {
652
+ if (statusCode !== void 0 && !OVERLOAD_MESSAGE_ELIGIBLE_STATUS_CODES.has(statusCode)) {
653
+ return false;
654
+ }
655
+ return /\boverloaded\b/i.test(text);
656
+ }
657
+ function getContextLengthDetails(body, message) {
658
+ const errorMessage = resolveMistralMessageText(body?.message ?? message);
659
+ const errorType = body?.type;
660
+ const isContextType = errorType === "invalid_request_error" || errorType === "invalid_request_invalid_args" || errorType === void 0;
661
+ if (!isContextType) {
662
+ return void 0;
663
+ }
664
+ const lower = errorMessage.toLowerCase();
665
+ const isContextWording = lower.includes("too large for model") || /exceeds the model's maximum context length/i.test(errorMessage);
666
+ if (!isContextWording) {
667
+ return void 0;
566
668
  }
567
- return new ProviderError(
568
- error instanceof Error ? error.message : String(error),
569
- "mistral",
570
- void 0,
571
- error
669
+ const match = errorMessage.match(
670
+ /(\d+)\s*tokens.*too large for model with (\d+) maximum context length/
572
671
  );
672
+ if (!match) {
673
+ const alternate = errorMessage.match(
674
+ /exceeds the model's maximum context length of (\d+)/i
675
+ );
676
+ if (alternate?.[1]) {
677
+ return { maxTokens: parseInt(alternate[1], 10) };
678
+ }
679
+ return {};
680
+ }
681
+ const actualTokens = match[1];
682
+ const maxTokens = match[2];
683
+ if (actualTokens === void 0 || maxTokens === void 0) {
684
+ return {};
685
+ }
686
+ return {
687
+ maxTokens: parseInt(maxTokens, 10),
688
+ actualTokens: parseInt(actualTokens, 10)
689
+ };
573
690
  }
574
-
575
- // src/model-capabilities.ts
576
- var NO_REASONING_CAPABILITIES = {
577
- reasoning: {
578
- supported: false,
579
- supportedEfforts: [],
580
- restrictsSamplingParams: false
691
+ function resolveMistralMessageText(message) {
692
+ const trimmed = message.trim();
693
+ if (!trimmed.startsWith("{")) {
694
+ return message;
581
695
  }
582
- };
583
- function getMistralModelCapabilities(_modelId) {
584
- return NO_REASONING_CAPABILITIES;
696
+ try {
697
+ const parsed = JSON.parse(trimmed);
698
+ const record = asRecord(parsed);
699
+ const nestedMessage = getString(record, "message");
700
+ if (nestedMessage) {
701
+ return nestedMessage;
702
+ }
703
+ } catch {
704
+ }
705
+ return message;
706
+ }
707
+ function parseErrorBody(error) {
708
+ const record = asRecord(error);
709
+ if (!record || !("body" in record)) {
710
+ const type = getString(record, "type");
711
+ const message = getString(record, "message");
712
+ if (type || message) {
713
+ return {
714
+ type,
715
+ message: message ? resolveMistralMessageText(message) : void 0
716
+ };
717
+ }
718
+ return void 0;
719
+ }
720
+ const body = record.body;
721
+ if (typeof body !== "string") {
722
+ if (body && typeof body === "object") {
723
+ const bodyRecord = asRecord(body);
724
+ const message = getString(bodyRecord, "message");
725
+ return {
726
+ message: message ? resolveMistralMessageText(message) : void 0,
727
+ type: getString(bodyRecord, "type")
728
+ };
729
+ }
730
+ return void 0;
731
+ }
732
+ try {
733
+ const parsed = JSON.parse(body);
734
+ const parsedRecord = asRecord(parsed);
735
+ if (parsedRecord) {
736
+ const message = getString(parsedRecord, "message");
737
+ return {
738
+ message: message ? resolveMistralMessageText(message) : void 0,
739
+ type: getString(parsedRecord, "type")
740
+ };
741
+ }
742
+ } catch {
743
+ }
744
+ return void 0;
585
745
  }
586
746
 
587
747
  // src/chat-model.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@core-ai/mistral",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "description": "Mistral provider package for @core-ai/core-ai",
5
5
  "license": "MIT",
6
6
  "author": "Omnifact (https://omnifact.ai)",
@@ -43,7 +43,7 @@
43
43
  "test:watch": "vitest"
44
44
  },
45
45
  "dependencies": {
46
- "@core-ai/core-ai": "^0.16.0",
46
+ "@core-ai/core-ai": "^0.18.0",
47
47
  "@mistralai/mistralai": "^2.4.1"
48
48
  },
49
49
  "peerDependencies": {