class Builder extends MistralAiClient.Builder<DefaultMistralAiClient, Builder> {

        public DefaultMistralAiClient build() {
            return new DefaultMistralAiClient(this);
        }
    }

    DefaultMistralAiClient(Builder builder) {
        OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder()
                .callTimeout(builder.timeout)
                .connectTimeout(builder.timeout)
                .readTimeout(builder.timeout)
                .writeTimeout(builder.timeout);

        okHttpClientBuilder.addInterceptor(new MistralAiApiKeyInterceptor(builder.apiKey));

        if (builder.logRequests) {
            okHttpClientBuilder.addInterceptor(new MistralAiRequestLoggingInterceptor());
        }

        if (builder.logResponses) {
            okHttpClientBuilder.addInterceptor(new MistralAiResponseLoggingInterceptor());
        }

        this.logStreamingResponses = builder.logResponses;
        this.okHttpClient = okHttpClientBuilder.build();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(formattedUrlForRetrofit(builder.baseUrl))
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create(GSON))
                .build();

        mistralAiApi = retrofit.create(MistralAiApi.class);
    }

    private static String formattedUrlForRetrofit(String baseUrl) {
        return baseUrl.endsWith("/") ? baseUrl : baseUrl + "/";
    }

    @Override
    public MistralAiChatCompletionResponse chatCompletion(MistralAiChatCompletionRequest request) {
        try {
            retrofit2.Response<MistralAiChatCompletionResponse> retrofitResponse
                    = mistralAiApi.chatCompletion(request).execute();
            if (retrofitResponse.isSuccessful()) {
                return retrofitResponse.body();
            } else {
                throw toException(retrofitResponse);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void streamingChatCompletion(MistralAiChatCompletionRequest request, StreamingResponseHandler<AiMessage> handler) {
        EventSourceListener eventSourceListener = new EventSourceListener() {
            final StringBuffer contentBuilder = new StringBuffer();
            List<ToolExecutionRequest> toolExecutionRequests;
            TokenUsage tokenUsage;
            FinishReason finishReason;

            @Override
            public void onOpen(EventSource eventSource, okhttp3.Response response) {
                if (logStreamingResponses) {
                    LOGGER.debug("onOpen()");
                }
            }

            @Override
            public void onEvent(EventSource eventSource, String id, String type, String data) {<FILL_FUNCTION_BODY>

                if (logStreamingResponses) {
                    LOGGER.debug("onEvent() {}", data);
                }
                if ("[DONE]".equals(data)) {
                    AiMessage aiMessage;
                    if (!isNullOrEmpty(toolExecutionRequests)){
                        aiMessage = AiMessage.from(toolExecutionRequests);
                    } else {
                        aiMessage = AiMessage.from(contentBuilder.toString());
                    }

                    Response<AiMessage> response = Response.from(
                            aiMessage,
                            tokenUsage,
                            finishReason
                    );
                    handler.onComplete(response);
                } else {
                    try {
                        MistralAiChatCompletionResponse chatCompletionResponse = GSON.fromJson(data, MistralAiChatCompletionResponse.class);
                        MistralAiChatCompletionChoice choice = chatCompletionResponse.getChoices().get(0);

                        String chunk = choice.getDelta().getContent();
                        if (isNotNullOrBlank(chunk)) {
                            contentBuilder.append(chunk);
                            handler.onNext(chunk);
                        }

                        List<MistralAiToolCall> toolCalls = choice.getDelta().getToolCalls();
                        if (!isNullOrEmpty(toolCalls)) {
                            toolExecutionRequests = toToolExecutionRequests(toolCalls);
                        }

                        MistralAiUsage usageInfo = chatCompletionResponse.getUsage();
                        if (usageInfo != null) {
                            this.tokenUsage = tokenUsageFrom(usageInfo);
                        }

                        String finishReasonString = choice.getFinishReason();
                        if (finishReasonString != null) {
                            this.finishReason = finishReasonFrom(finishReasonString);
                        }
                    } catch (Exception e) {
                        handler.onError(e);
                        throw new RuntimeException(e);
                    }
                }
            
<methods>public non-sealed void <init>() ,public static Builder#RAW builder() ,public abstract dev.langchain4j.model.mistralai.MistralAiChatCompletionResponse chatCompletion(dev.langchain4j.model.mistralai.MistralAiChatCompletionRequest) ,public abstract dev.langchain4j.model.mistralai.MistralAiEmbeddingResponse embedding(dev.langchain4j.model.mistralai.MistralAiEmbeddingRequest) ,public abstract dev.langchain4j.model.mistralai.MistralAiModelResponse listModels() ,public abstract void streamingChatCompletion(dev.langchain4j.model.mistralai.MistralAiChatCompletionRequest, StreamingResponseHandler<dev.langchain4j.data.message.AiMessage>) <variables>