diff --git a/embabel-agent-api/src/main/kotlin/com/embabel/agent/api/models/AtlasCloudModels.kt b/embabel-agent-api/src/main/kotlin/com/embabel/agent/api/models/AtlasCloudModels.kt
new file mode 100644
index 000000000..5cedee1f6
--- /dev/null
+++ b/embabel-agent-api/src/main/kotlin/com/embabel/agent/api/models/AtlasCloudModels.kt
@@ -0,0 +1,33 @@
+/*
+ * 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 Atlas Cloud model identifiers.
+ * Atlas Cloud exposes models from multiple vendors through an OpenAI-compatible API.
+ *
+ * @see Atlas Cloud Models
+ */
+class AtlasCloudModels {
+
+ companion object {
+
+ const val QWEN3_5_FLASH = "qwen/qwen3.5-flash"
+ const val QWEN3_8_MAX = "qwen/qwen3.8-max"
+
+ const val PROVIDER = "Atlas Cloud"
+ }
+}
diff --git a/embabel-agent-common/embabel-agent-byok/src/main/kotlin/com/embabel/common/byok/ProviderDetection.kt b/embabel-agent-common/embabel-agent-byok/src/main/kotlin/com/embabel/common/byok/ProviderDetection.kt
index 72dcb9134..41853afc1 100644
--- a/embabel-agent-common/embabel-agent-byok/src/main/kotlin/com/embabel/common/byok/ProviderDetection.kt
+++ b/embabel-agent-common/embabel-agent-byok/src/main/kotlin/com/embabel/common/byok/ProviderDetection.kt
@@ -31,6 +31,7 @@ import java.util.concurrent.Executors
* OpenAiCompatibleModelFactory.deepSeek(userKey),
* OpenAiCompatibleModelFactory.mistral(userKey),
* OpenAiCompatibleModelFactory.gemini(userKey),
+ * OpenAiCompatibleModelFactory.atlasCloud(userKey),
* )
* val detectedProvider = service.provider
* ```
diff --git a/embabel-agent-docs/src/main/asciidoc/reference/customizing/page.adoc b/embabel-agent-docs/src/main/asciidoc/reference/customizing/page.adoc
index 7352f6256..65c86b668 100644
--- a/embabel-agent-docs/src/main/asciidoc/reference/customizing/page.adoc
+++ b/embabel-agent-docs/src/main/asciidoc/reference/customizing/page.adoc
@@ -224,6 +224,9 @@ val service: LlmService<*> = OpenAiCompatibleModelFactory.mistral(userKey).build
// Gemini (OpenAI-compatible endpoint)
val service: LlmService<*> = OpenAiCompatibleModelFactory.gemini(userKey).buildValidated()
+
+// Atlas Cloud (OpenAI-compatible endpoint)
+val service: LlmService<*> = OpenAiCompatibleModelFactory.atlasCloud(userKey).buildValidated()
----
`buildValidated()` makes a single probe call with no retries.
@@ -258,6 +261,7 @@ val service: LlmService<*> = detectProvider(
OpenAiCompatibleModelFactory.deepSeek(userKey),
OpenAiCompatibleModelFactory.mistral(userKey),
OpenAiCompatibleModelFactory.gemini(userKey),
+ OpenAiCompatibleModelFactory.atlasCloud(userKey),
)
val detectedProvider: String = service.provider
----
@@ -543,4 +547,3 @@ Remove the `embabel.agent.logging.personality` key to disable personality-based
As all logging results from listening to events via an `AgenticEventListener`, you can also easily create your own customized logging.
-
diff --git a/embabel-agent-openai/src/main/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactory.kt b/embabel-agent-openai/src/main/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactory.kt
index 62500d173..38fcabcc9 100644
--- a/embabel-agent-openai/src/main/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactory.kt
+++ b/embabel-agent-openai/src/main/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactory.kt
@@ -15,6 +15,7 @@
*/
package com.embabel.agent.openai
+import com.embabel.agent.api.models.AtlasCloudModels
import com.embabel.agent.api.models.DeepSeekModels
import com.embabel.agent.api.models.GoogleGenAiModels
import com.embabel.agent.api.models.MistralAiModels
@@ -127,6 +128,18 @@ open class OpenAiCompatibleModelFactory(
GoogleGenAiModels.PROVIDER,
)
+ /**
+ * Returns a [ByokSpec] for Atlas Cloud (OpenAI-compatible endpoint).
+ * Validates against [AtlasCloudModels.QWEN3_5_FLASH] by default.
+ */
+ fun atlasCloud(apiKey: String): ByokSpec =
+ ByokSpec(
+ "https://api.atlascloud.ai/v1",
+ apiKey,
+ AtlasCloudModels.QWEN3_5_FLASH,
+ AtlasCloudModels.PROVIDER,
+ )
+
/**
* Returns a [ByokSpec] for a custom OpenAI-compatible provider.
*
@@ -191,8 +204,9 @@ open class OpenAiCompatibleModelFactory(
* so it can be passed directly to [com.embabel.common.byok.detectProvider].
*
* Obtained via the companion factory methods ([openAi], [deepSeek], [mistral], [gemini],
- * or [byok] for custom providers). Use [validating] to override the default validation
- * model and provider — for example if the key only grants access to a specific model tier.
+ * [atlasCloud], or [byok] for custom providers). Use [validating] to override the default
+ * validation model and provider — for example if the key only grants access to a specific
+ * model tier.
*/
class ByokSpec internal constructor(
private val baseUrl: String?,
diff --git a/embabel-agent-openai/src/test/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactoryBuildValidatedTest.kt b/embabel-agent-openai/src/test/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactoryBuildValidatedTest.kt
index 948400041..eeaade590 100644
--- a/embabel-agent-openai/src/test/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactoryBuildValidatedTest.kt
+++ b/embabel-agent-openai/src/test/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactoryBuildValidatedTest.kt
@@ -157,6 +157,7 @@ class OpenAiCompatibleModelFactoryBuildValidatedTest {
OpenAiCompatibleModelFactory.deepSeek(""),
OpenAiCompatibleModelFactory.mistral("\t"),
OpenAiCompatibleModelFactory.gemini(" "),
+ OpenAiCompatibleModelFactory.atlasCloud("\n"),
).forEach { spec ->
val e = assertThrows { spec.buildValidated() }
assertEquals(BLANK_API_KEY_MESSAGE, e.message)
diff --git a/embabel-agent-openai/src/test/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactoryByokIT.kt b/embabel-agent-openai/src/test/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactoryByokIT.kt
index f13ea8e7f..d76b72039 100644
--- a/embabel-agent-openai/src/test/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactoryByokIT.kt
+++ b/embabel-agent-openai/src/test/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactoryByokIT.kt
@@ -60,6 +60,15 @@ class OpenAiCompatibleModelFactoryByokIT {
assertNotNull(service)
}
+ @Test
+ @EnabledIfEnvironmentVariable(named = "ATLASCLOUD_API_KEY", matches = ".+")
+ fun `atlasCloud buildValidated succeeds with valid key`() {
+ val service = OpenAiCompatibleModelFactory.atlasCloud(System.getenv("ATLASCLOUD_API_KEY"))
+ .buildValidated()
+ assertNotNull(service)
+ assertEquals("Atlas Cloud", service.provider)
+ }
+
@Test
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+")
fun `openAiEmbedding buildValidated succeeds with valid key`() {
diff --git a/embabel-agent-openai/src/test/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactoryByokSpecTest.kt b/embabel-agent-openai/src/test/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactoryByokSpecTest.kt
index a8e36e2e1..b50e1316d 100644
--- a/embabel-agent-openai/src/test/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactoryByokSpecTest.kt
+++ b/embabel-agent-openai/src/test/kotlin/com/embabel/agent/openai/OpenAiCompatibleModelFactoryByokSpecTest.kt
@@ -15,6 +15,7 @@
*/
package com.embabel.agent.openai
+import com.embabel.agent.api.models.AtlasCloudModels
import com.embabel.agent.api.models.DeepSeekModels
import com.embabel.agent.api.models.GoogleGenAiModels
import com.embabel.agent.api.models.MistralAiModels
@@ -45,6 +46,11 @@ class OpenAiCompatibleModelFactoryByokSpecTest {
assertInstanceOf(ByokFactory::class.java, OpenAiCompatibleModelFactory.gemini("key"))
}
+ @Test
+ fun `atlasCloud returns ByokFactory`() {
+ assertInstanceOf(ByokFactory::class.java, OpenAiCompatibleModelFactory.atlasCloud("key"))
+ }
+
@Test
fun `byok with explicit params returns ByokFactory`() {
assertInstanceOf(
@@ -74,11 +80,16 @@ class OpenAiCompatibleModelFactoryByokSpecTest {
val deepSeekSpec = OpenAiCompatibleModelFactory.deepSeek("key")
val mistralSpec = OpenAiCompatibleModelFactory.mistral("key")
val geminiSpec = OpenAiCompatibleModelFactory.gemini("key")
+ val atlasCloudSpec = OpenAiCompatibleModelFactory.atlasCloud("key")
// Each spec should construct without error (no network call at this stage)
assertInstanceOf(ByokFactory::class.java, openAiSpec)
assertInstanceOf(ByokFactory::class.java, deepSeekSpec)
assertInstanceOf(ByokFactory::class.java, mistralSpec)
assertInstanceOf(ByokFactory::class.java, geminiSpec)
+ assertInstanceOf(ByokFactory::class.java, atlasCloudSpec)
+
+ val overridden = atlasCloudSpec.validating(AtlasCloudModels.QWEN3_8_MAX, AtlasCloudModels.PROVIDER)
+ assertInstanceOf(ByokFactory::class.java, overridden)
}
}