@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio
173 lines • 7.9 kB
JavaScript
import { createMistral } from "@ai-sdk/mistral";
import { stepCountIs, streamText } from "ai";
import { BaseProvider } from "../core/baseProvider.js";
import { DEFAULT_MAX_STEPS } from "../core/constants.js";
import { streamAnalyticsCollector } from "../core/streamAnalytics.js";
import { isNeuroLink } from "../neurolink.js";
import { createProxyFetch } from "../proxy/proxyFetch.js";
import { AuthenticationError, NetworkError, ProviderError, RateLimitError, } from "../types/index.js";
import { emitToolEndFromStepFinish } from "../utils/toolEndEmitter.js";
import { logger } from "../utils/logger.js";
import { createMistralConfig, getProviderModel, validateApiKey, } from "../utils/providerConfig.js";
import { composeAbortSignals, createTimeoutController, TimeoutError, } from "../utils/timeout.js";
import { resolveToolChoice } from "../utils/toolChoice.js";
import { toAnalyticsStreamResult } from "./providerTypeUtils.js";
// Configuration helpers - now using consolidated utility
const getMistralApiKey = () => {
return validateApiKey(createMistralConfig());
};
const getDefaultMistralModel = () => {
// Default to vision-capable Mistral Small 2506 (June 2025) with multimodal support
return getProviderModel("MISTRAL_MODEL", "mistral-small-2506");
};
/**
* Mistral AI Provider v2 - BaseProvider Implementation
* Supports official AI-SDK integration with all Mistral models
*/
export class MistralProvider extends BaseProvider {
model;
constructor(modelName, sdk, _region, credentials) {
// Type guard for NeuroLink parameter validation
const validatedNeurolink = isNeuroLink(sdk) ? sdk : undefined;
super(modelName, "mistral", validatedNeurolink);
// Initialize Mistral model with API key validation and proxy support
const apiKey = credentials?.apiKey ?? getMistralApiKey();
const mistral = createMistral({
apiKey: apiKey,
fetch: createProxyFetch(),
});
this.model = mistral(this.modelName);
logger.debug("Mistral Provider v2 initialized", {
modelName: this.modelName,
providerName: this.providerName,
});
}
// generate() method is inherited from BaseProvider; this provider uses the base implementation for generation with tools
async executeStream(options, _analysisSchema) {
this.validateStreamOptions(options);
const startTime = Date.now();
const timeout = this.getTimeout(options);
const timeoutController = createTimeoutController(timeout, this.providerName, "stream");
try {
// Get tools - options.tools is pre-merged by BaseProvider.stream()
const shouldUseTools = !options.disableTools && this.supportsTools();
const tools = shouldUseTools
? options.tools || (await this.getAllTools())
: {};
// Build message array from options with multimodal support
// Using protected helper from BaseProvider to eliminate code duplication
const messages = await this.buildMessagesForStream(options);
const model = await this.getAISDKModelWithMiddleware(options); // This is where network connection happens!
// Reviewer follow-up: capture upstream provider errors via onError
// so the post-stream NoOutput sentinel carries the real cause.
let capturedProviderError;
const result = await streamText({
model,
messages: messages,
temperature: options.temperature,
maxOutputTokens: options.maxTokens, // No default limit - unlimited unless specified
tools,
stopWhen: stepCountIs(options.maxSteps || DEFAULT_MAX_STEPS),
toolChoice: resolveToolChoice(options, tools, shouldUseTools),
abortSignal: composeAbortSignals(options.abortSignal, timeoutController?.controller.signal),
experimental_telemetry: this.telemetryHandler.getTelemetryConfig(options),
experimental_repairToolCall: this.getToolCallRepairFn(options),
onError: (event) => {
capturedProviderError = event.error;
logger.error("Mistral: Stream error", {
error: event.error instanceof Error
? event.error.message
: String(event.error),
});
},
onStepFinish: ({ toolCalls, toolResults }) => {
emitToolEndFromStepFinish(this.neurolink?.getEventEmitter(), toolResults);
this.handleToolExecutionStorage(toolCalls, toolResults, options, new Date()).catch((error) => {
logger.warn("[MistralProvider] Failed to store tool executions", {
provider: this.providerName,
error: error instanceof Error ? error.message : String(error),
});
});
},
});
timeoutController?.cleanup();
// Transform string stream to content object stream using BaseProvider method
const transformedStream = this.createTextStream(result, () => capturedProviderError);
// Create analytics promise that resolves after stream completion
const analyticsPromise = streamAnalyticsCollector.createAnalytics(this.providerName, this.modelName, toAnalyticsStreamResult(result), Date.now() - startTime, {
requestId: `mistral-stream-${Date.now()}`,
streamingMode: true,
});
return {
stream: transformedStream,
provider: this.providerName,
model: this.modelName,
analytics: analyticsPromise,
metadata: {
startTime,
streamId: `mistral-${Date.now()}`,
},
};
}
catch (error) {
timeoutController?.cleanup();
throw this.handleProviderError(error);
}
}
// ===================
// ABSTRACT METHOD IMPLEMENTATIONS
// ===================
getProviderName() {
return this.providerName;
}
getDefaultModel() {
return getDefaultMistralModel();
}
/**
* Returns the Vercel AI SDK model instance for Mistral
*/
getAISDKModel() {
return this.model;
}
formatProviderError(error) {
if (error instanceof TimeoutError) {
return new NetworkError(`Request timed out: ${error.message}`, "mistral");
}
const errorRecord = error;
const message = typeof errorRecord?.message === "string"
? errorRecord.message
: "Unknown error";
if (message.includes("API_KEY_INVALID") ||
message.includes("Invalid API key")) {
return new AuthenticationError("Invalid Mistral API key. Please check your MISTRAL_API_KEY environment variable.", "mistral");
}
if (message.includes("Rate limit exceeded")) {
return new RateLimitError("Mistral rate limit exceeded", "mistral");
}
return new ProviderError(`Mistral error: ${message}`, "mistral");
}
/**
* Validate provider configuration
*/
async validateConfiguration() {
try {
getMistralApiKey();
return true;
}
catch {
return false;
}
}
/**
* Get provider-specific configuration
*/
getConfiguration() {
return {
provider: this.providerName,
model: this.modelName,
defaultModel: getDefaultMistralModel(),
};
}
}
export default MistralProvider;
//# sourceMappingURL=mistral.js.map