Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,7 @@ Required:
Optional:

- `ANTHROPIC_API_KEY`: For the Anthropic API. Necessary for the coding agent.
- `MINIMAX_API_KEY`: For the [MiniMax](https://www.minimax.io) API. Supports MiniMax-M2.7 and MiniMax-M2.7-highspeed models.

> We strongly recommend providing both an OpenAI and Anthropic key, as some examples require both. And it's important to
> try to find the best LLM for a given task, rather than automatically choose a familiar provider.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2024-2026 Embabel Pty Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.embabel.agent.api.models

/**
* Provides constants for MiniMax AI model identifiers.
* MiniMax offers large language models with up to 1M token context windows
* via an OpenAI-compatible API.
*
* @see <a href="https://www.minimax.io">MiniMax AI</a>
*/
class MiniMaxModels {

companion object {

const val MINIMAX_M2_7 = "MiniMax-M2.7"
const val MINIMAX_M2_7_HIGHSPEED = "MiniMax-M2.7-highspeed"

const val PROVIDER = "MiniMax"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.embabel.agent</groupId>
<artifactId>embabel-agent-autoconfigure</artifactId>
<version>0.3.5-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

<artifactId>embabel-agent-minimax-autoconfigure</artifactId>
<packaging>jar</packaging>
<name>Embabel Agent Autoconfiguration Models MiniMax</name>
<description>MiniMax Models Auto-Configuration for Embabel Agent API</description>
<url>https://github.com/embabel/embabel-agent</url>

<scm>
<url>https://github.com/embabel/embabel-agent</url>
<connection>scm:git:https://github.com/embabel/embabel-agent.git</connection>
<developerConnection>scm:git:https://github.com/embabel/embabel-agent.git</developerConnection>
<tag>HEAD</tag>
</scm>

<dependencies>
<dependency>
<groupId>com.embabel.agent</groupId>
<artifactId>embabel-agent-api</artifactId>
</dependency>

<dependency>
<groupId>com.embabel.agent</groupId>
<artifactId>embabel-agent-openai</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai</artifactId>
</dependency>

<dependency>
<groupId>com.embabel.agent</groupId>
<artifactId>embabel-agent-test-internal</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2024-2026 Embabel Pty Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.embabel.agent.autoconfigure.models.minimax;

import com.embabel.agent.config.models.minimax.MiniMaxModelsConfig;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Import;

/**
* Autoconfiguration for MiniMax AI models in the Embabel Agent system.
* <p>
* This class serves as a Spring Boot autoconfiguration entry point that:
* - Imports the {@link MiniMaxModelsConfig} configuration to register MiniMax model beans
* <p>
* The configuration is automatically activated when the MiniMax
* dependencies are present on the classpath and the MINIMAX_API_KEY
* environment variable is set.
*/
@AutoConfiguration
@AutoConfigureBefore(name = {"com.embabel.agent.autoconfigure.platform.AgentPlatformAutoConfiguration"})
@Import(MiniMaxModelsConfig.class)
public class AgentMiniMaxAutoConfiguration {
private static final Logger logger = LoggerFactory.getLogger(AgentMiniMaxAutoConfiguration.class);

@PostConstruct
public void logEvent() {
logger.info("AgentMiniMaxAutoConfiguration about to proceed...");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
* Copyright 2024-2026 Embabel Pty Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.embabel.agent.config.models.minimax

import com.embabel.agent.api.models.MiniMaxModels
import com.embabel.agent.openai.OpenAiCompatibleModelFactory
import com.embabel.agent.spi.LlmService
import com.embabel.agent.spi.common.RetryProperties
import com.embabel.agent.spi.support.springai.SpringAiLlmService
import com.embabel.common.ai.model.LlmOptions
import com.embabel.common.ai.model.OptionsConverter
import com.embabel.common.ai.model.PerTokenPricingModel
import com.embabel.common.util.ExcludeFromJacocoGeneratedReport
import com.embabel.common.util.loggerFor
import io.micrometer.observation.ObservationRegistry
import org.springframework.ai.openai.OpenAiChatOptions
import org.springframework.beans.factory.ObjectProvider
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.client.ClientHttpRequestFactory
import java.time.LocalDate

/**
* Configuration properties for MiniMax models.
* These properties are bound from the Spring configuration with the prefix
* "embabel.agent.platform.models.minimax" and control retry behavior
* when calling MiniMax APIs.
*/
@ConfigurationProperties(prefix = "embabel.agent.platform.models.minimax")
class MiniMaxProperties : RetryProperties {
/**
* Base URL for MiniMax API requests.
*/
var baseUrl: String = "https://api.minimax.io/v1"

/**
* API key for authenticating with MiniMax services.
*/
var apiKey: String? = null

/**
* Maximum number of attempts.
*/
override var maxAttempts: Int = 4

/**
* Initial backoff interval (in milliseconds).
*/
override var backoffMillis: Long = 1500L

/**
* Backoff interval multiplier.
*/
override var backoffMultiplier: Double = 2.0

/**
* Maximum backoff interval (in milliseconds).
*/
override var backoffMaxInterval: Long = 60000L
}

/**
* Configuration class for MiniMax models.
* This class provides beans for MiniMax models (M2.7, M2.7-highspeed)
* via the OpenAI-compatible API provided by MiniMax.
*
* MiniMax models require temperature values in the range (0.0, 1.0].
* The [MiniMaxOptionsConverter] handles clamping temperature to this range.
*
* To use, set the following environment variables:
* ```
* MINIMAX_API_KEY=your-api-key
* ```
*
* @see <a href="https://www.minimax.io">MiniMax AI</a>
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(MiniMaxProperties::class)
@ExcludeFromJacocoGeneratedReport(reason = "MiniMax configuration can't be unit tested")
class MiniMaxModelsConfig(
@param:Value("\${MINIMAX_BASE_URL:#{null}}")
private val envBaseUrl: String?,
@param:Value("\${MINIMAX_API_KEY:#{null}}")
private val envApiKey: String?,
observationRegistry: ObjectProvider<ObservationRegistry>,
private val properties: MiniMaxProperties,
requestFactory: ObjectProvider<ClientHttpRequestFactory>,
) : OpenAiCompatibleModelFactory(
baseUrl = envBaseUrl ?: properties.baseUrl,
apiKey = envApiKey?.trim()?.takeIf { it.isNotEmpty() }
?: properties.apiKey?.trim()?.takeIf { it.isNotEmpty() }
?: error("MiniMax API key required: set MINIMAX_API_KEY env var or embabel.agent.platform.models.minimax.api-key"),
completionsPath = null,
embeddingsPath = null,
observationRegistry = observationRegistry.getIfUnique { ObservationRegistry.NOOP },
requestFactory = requestFactory,
) {

init {
logger.info("MiniMax models are available: {}", properties)
}

@Bean
fun miniMaxM27(): LlmService<*> {
return openAiCompatibleLlm(
model = MiniMaxModels.MINIMAX_M2_7,
provider = MiniMaxModels.PROVIDER,
knowledgeCutoffDate = LocalDate.of(2025, 6, 1),
optionsConverter = MiniMaxOptionsConverter,
pricingModel = PerTokenPricingModel(
usdPer1mInputTokens = 1.10,
usdPer1mOutputTokens = 4.40,
),
retryTemplate = properties.retryTemplate(MiniMaxModels.MINIMAX_M2_7),
)
}

@Bean
fun miniMaxM27Highspeed(): LlmService<*> {
return openAiCompatibleLlm(
model = MiniMaxModels.MINIMAX_M2_7_HIGHSPEED,
provider = MiniMaxModels.PROVIDER,
knowledgeCutoffDate = LocalDate.of(2025, 6, 1),
optionsConverter = MiniMaxOptionsConverter,
pricingModel = PerTokenPricingModel(
usdPer1mInputTokens = 0.55,
usdPer1mOutputTokens = 2.20,
),
retryTemplate = properties.retryTemplate(MiniMaxModels.MINIMAX_M2_7_HIGHSPEED),
)
}
}

/**
* Options converter for MiniMax models.
* MiniMax requires temperature to be in the range (0.0, 1.0].
* Values outside this range are clamped accordingly.
*/
object MiniMaxOptionsConverter : OptionsConverter<OpenAiChatOptions> {

private const val MIN_TEMPERATURE = 0.01
private const val MAX_TEMPERATURE = 1.0

override fun convertOptions(options: LlmOptions): OpenAiChatOptions {
val temperature = options.temperature?.let { temp ->
temp.coerceIn(MIN_TEMPERATURE, MAX_TEMPERATURE).also { clamped ->
if (clamped != temp) {
loggerFor<MiniMaxOptionsConverter>().debug(
"MiniMax temperature clamped from {} to {} (valid range: ({}, {}])",
temp, clamped, 0.0, MAX_TEMPERATURE
)
}
}
}
return OpenAiChatOptions.builder()
.temperature(temperature)
.topP(options.topP)
.maxTokens(options.maxTokens)
.presencePenalty(options.presencePenalty)
.frequencyPenalty(options.frequencyPenalty)
.build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#
# Copyright 2025-2025 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
com.embabel.agent.autoconfigure.models.minimax.AgentMiniMaxAutoConfiguration
Loading