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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ A-Evolve is a **framework**, not a standalone agent. Every axis is pluggable:
| **Agent (BYOA)** | `BaseAgent.solve()` | Any agent architecture — ReAct, Plan-and-Solve, custom | `SweAgent`, `McpAgent` |
| **Benchmark (BYOE)** | `BenchmarkAdapter.get_tasks()` / `.evaluate()` | Any domain with task + evaluation signal | SWE-bench, MCP-Atlas, Terminal-Bench 2.0, SkillsBench, CL-bench |
| **Algorithm (BYO-Algo)** | `EvolutionEngine.step()` | Any evolution strategy | `AEvolveEngine` (LLM-driven mutation) |
| **LLM Provider** | `LLMProvider.complete()` | Any model API | Anthropic, OpenAI, AWS Bedrock |
| **LLM Provider** | `LLMProvider.complete()` | Any model API | Anthropic, OpenAI, Atlas Cloud, AWS Bedrock |

### Built-in Evolution Algorithms

Expand Down
10 changes: 10 additions & 0 deletions agent_evolve/algorithms/adaptive_evolve/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ def bash(command: str) -> str:
def _create_default_llm(config: EvolveConfig) -> LLMProvider:
"""Create default LLM provider based on config."""
model = config.evolver_model
from ...llm.model_resolver import is_atlascloud_model, strip_atlascloud_prefix

if is_atlascloud_model(model):
from ...llm.atlascloud import AtlasCloudProvider

return AtlasCloudProvider(
model=strip_atlascloud_prefix(model),
api_key=config.extra.get("atlascloud_api_key"),
base_url=config.extra.get("atlascloud_api_base"),
)
if "." in model and ("anthropic" in model or "amazon" in model or "meta" in model):
from ...llm.bedrock import BedrockProvider
return BedrockProvider(model_id=model, region=config.extra.get("region", "us-west-2"))
Expand Down
10 changes: 10 additions & 0 deletions agent_evolve/algorithms/adaptive_skill/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ def bash(command: str) -> str:
def create_default_llm(config: EvolveConfig) -> LLMProvider:
"""Create the default LLM provider based on the evolver_model config string."""
model = config.evolver_model
from ...llm.model_resolver import is_atlascloud_model, strip_atlascloud_prefix

if is_atlascloud_model(model):
from ...llm.atlascloud import AtlasCloudProvider

return AtlasCloudProvider(
model=strip_atlascloud_prefix(model),
api_key=config.extra.get("atlascloud_api_key"),
base_url=config.extra.get("atlascloud_api_base"),
)

if "." in model and ("anthropic" in model or "amazon" in model or "meta" in model):
from ...llm.bedrock import BedrockProvider
Expand Down
10 changes: 10 additions & 0 deletions agent_evolve/algorithms/navigation/_evolver_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,16 @@ def make_workspace_bash(
def create_default_llm(config: EvolveConfig) -> LLMProvider:
"""Create the default LLM provider based on the evolver_model config string."""
model = config.evolver_model
from ...llm.model_resolver import is_atlascloud_model, strip_atlascloud_prefix

if is_atlascloud_model(model):
from ...llm.atlascloud import AtlasCloudProvider

return AtlasCloudProvider(
model=strip_atlascloud_prefix(model),
api_key=config.extra.get("atlascloud_api_key"),
base_url=config.extra.get("atlascloud_api_base"),
)

if "." in model and ("anthropic" in model or "amazon" in model or "meta" in model):
from ...llm.bedrock import BedrockProvider
Expand Down
10 changes: 10 additions & 0 deletions agent_evolve/algorithms/skillforge/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ def bash(command: str) -> str:
def create_default_llm(config: EvolveConfig) -> LLMProvider:
"""Create the default LLM provider based on the evolver_model config string."""
model = config.evolver_model
from ...llm.model_resolver import is_atlascloud_model, strip_atlascloud_prefix

if is_atlascloud_model(model):
from ...llm.atlascloud import AtlasCloudProvider

return AtlasCloudProvider(
model=strip_atlascloud_prefix(model),
api_key=config.extra.get("atlascloud_api_key"),
base_url=config.extra.get("atlascloud_api_base"),
)

if "." in model and ("anthropic" in model or "amazon" in model or "meta" in model):
from ...llm.bedrock import BedrockProvider
Expand Down
15 changes: 15 additions & 0 deletions agent_evolve/algorithms/unified/operators/llm_bash_evolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,21 @@ def bash(command: str) -> str:


def _resolve_llm(model: str, region: str):
if model.startswith(("atlascloud:", "atlascloud/")):
from ..openai_compat import OpenAICompatProvider

model_id = model.removeprefix("atlascloud:").removeprefix("atlascloud/")
return (
OpenAICompatProvider(
model=model_id or "qwen/qwen3.5-flash",
api_key=os.environ.get("ATLASCLOUD_API_KEY")
or os.environ.get("ATLAS_CLOUD_API_KEY"),
base_url=os.environ.get("ATLASCLOUD_API_BASE")
or os.environ.get("ATLAS_CLOUD_API_BASE")
or "https://api.atlascloud.ai/v1",
),
"atlascloud",
)
if (
model.startswith("openai:")
or model.startswith("/")
Expand Down
37 changes: 37 additions & 0 deletions agent_evolve/llm/atlascloud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Atlas Cloud OpenAI-compatible LLM provider."""

from __future__ import annotations

import os

from .openai import OpenAIProvider

ATLAS_CLOUD_API_BASE = "https://api.atlascloud.ai/v1"
ATLAS_CLOUD_DEFAULT_MODEL = "qwen/qwen3.5-flash"


class AtlasCloudProvider(OpenAIProvider):
"""LLM provider using Atlas Cloud's OpenAI-compatible chat API."""

def __init__(
self,
model: str = ATLAS_CLOUD_DEFAULT_MODEL,
api_key: str | None = None,
base_url: str | None = None,
):
resolved_api_key = (
api_key
or os.environ.get("ATLASCLOUD_API_KEY")
or os.environ.get("ATLAS_CLOUD_API_KEY")
)
resolved_base_url = (
base_url
or os.environ.get("ATLASCLOUD_API_BASE")
or os.environ.get("ATLAS_CLOUD_API_BASE")
or ATLAS_CLOUD_API_BASE
)
super().__init__(
model=model,
api_key=resolved_api_key,
base_url=resolved_base_url,
)
19 changes: 19 additions & 0 deletions agent_evolve/llm/model_resolver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Helpers for resolving configured LLM model strings."""

from __future__ import annotations

ATLAS_CLOUD_PREFIXES = ("atlascloud:", "atlascloud/")
ATLAS_CLOUD_DEFAULT_MODEL = "qwen/qwen3.5-flash"


def is_atlascloud_model(model: str) -> bool:
"""Return whether a model string requests Atlas Cloud explicitly."""
return model.startswith(ATLAS_CLOUD_PREFIXES)


def strip_atlascloud_prefix(model: str) -> str:
"""Return the Atlas Cloud model id after removing the provider prefix."""
for prefix in ATLAS_CLOUD_PREFIXES:
if model.startswith(prefix):
return model.removeprefix(prefix) or ATLAS_CLOUD_DEFAULT_MODEL
return model
15 changes: 13 additions & 2 deletions agent_evolve/llm/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,25 @@
class OpenAIProvider(LLMProvider):
"""LLM provider using the OpenAI API (GPT / o-series models)."""

def __init__(self, model: str = "gpt-4o", api_key: str | None = None):
def __init__(
self,
model: str = "gpt-4o",
api_key: str | None = None,
base_url: str | None = None,
):
try:
import openai
except ImportError:
raise ImportError("pip install openai (or: pip install agent-evolve[openai])")

self.model = model
self.client = openai.OpenAI(api_key=api_key) if api_key else openai.OpenAI()
kwargs: dict[str, str] = {}
if api_key:
kwargs["api_key"] = api_key
if base_url:
kwargs["base_url"] = base_url
kwargs.setdefault("api_key", "EMPTY")
self.client = openai.OpenAI(**kwargs)

def complete(
self,
Expand Down
58 changes: 58 additions & 0 deletions tests/test_atlascloud_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Tests for Atlas Cloud provider resolution."""

from __future__ import annotations

import sys
import types

from agent_evolve.algorithms.adaptive_skill.tools import create_default_llm as create_skill_llm
from agent_evolve.config import EvolveConfig
from agent_evolve.llm.model_resolver import is_atlascloud_model, strip_atlascloud_prefix


class FakeOpenAI:
def __init__(self, **kwargs):
self.kwargs = kwargs
self.chat = types.SimpleNamespace(
completions=types.SimpleNamespace(create=lambda **_: None)
)


def test_atlascloud_model_prefix_helpers():
assert is_atlascloud_model("atlascloud:qwen/qwen3.5-flash")
assert is_atlascloud_model("atlascloud/deepseek-ai/deepseek-v4-pro")
assert not is_atlascloud_model("gpt-4o")
assert strip_atlascloud_prefix("atlascloud:qwen/qwen3.5-flash") == "qwen/qwen3.5-flash"
assert (
strip_atlascloud_prefix("atlascloud/deepseek-ai/deepseek-v4-pro")
== "deepseek-ai/deepseek-v4-pro"
)


def test_atlascloud_provider_uses_openai_compatible_endpoint(monkeypatch):
fake_module = types.SimpleNamespace(OpenAI=FakeOpenAI)
monkeypatch.setitem(sys.modules, "openai", fake_module)

from agent_evolve.llm.atlascloud import AtlasCloudProvider

provider = AtlasCloudProvider(model="qwen/qwen3.5-flash", api_key="ac-test")

assert provider.model == "qwen/qwen3.5-flash"
assert provider.client.kwargs == {
"api_key": "ac-test",
"base_url": "https://api.atlascloud.ai/v1",
}


def test_default_llm_factory_resolves_atlascloud(monkeypatch):
fake_module = types.SimpleNamespace(OpenAI=FakeOpenAI)
monkeypatch.setitem(sys.modules, "openai", fake_module)
monkeypatch.setenv("ATLASCLOUD_API_KEY", "ac-env")

provider = create_skill_llm(
EvolveConfig(evolver_model="atlascloud:deepseek-ai/deepseek-v4-pro")
)

assert provider.model == "deepseek-ai/deepseek-v4-pro"
assert provider.client.kwargs["api_key"] == "ac-env"
assert provider.client.kwargs["base_url"] == "https://api.atlascloud.ai/v1"