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 @@ -102,6 +102,7 @@ for fast retrieval and self-evolving reuse.
| Capability | Provider | Used for | Fill these `.env` slots |
| --- | --- | --- | --- |
| Chat + multimodal | [OpenRouter](https://openrouter.ai/) | `LLM` / `MULTIMODAL` | `EVEROS_LLM__API_KEY`, `EVEROS_MULTIMODAL__API_KEY` |
| Chat | Atlas Cloud OpenAI-compatible endpoint | `LLM` | `EVEROS_LLM__PROVIDER=atlascloud`, `EVEROS_LLM__API_KEY` |
| Embedding + rerank | [DeepInfra](https://deepinfra.com/) | `EMBEDDING` / `RERANK` | `EVEROS_EMBEDDING__API_KEY`, `EVEROS_RERANK__API_KEY` |

You can use other OpenAI-compatible providers by changing the matching
Expand Down
7 changes: 7 additions & 0 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,17 @@
# ── LLM ───────────────────────────────────────────────
# OpenAI-protocol chat-completions endpoint used by the algo extractors.
[llm]
provider = "openai"
model = "gpt-4.1-mini"
api_key = "sk-..."
base_url = "https://api.openai.com/v1"

# Atlas Cloud is also supported through its OpenAI-compatible endpoint:
# provider = "atlascloud"
# model = "qwen/qwen3.5-flash"
# api_key = "..."
# base_url = "https://api.atlascloud.ai/v1"

# ── Embedding ─────────────────────────────────────────
[embedding]
model = "Qwen/Qwen3-Embedding-4B"
Expand Down
5 changes: 4 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ everos init --root /data/everos

| Field | Type | Default | Required | Description |
|---|---|---|---|---|
| `provider` | string | `"openrouter"` | No | Provider preset. Use `"atlascloud"` to apply Atlas Cloud's OpenAI-compatible defaults. |
| `model` | string | `"gpt-4.1-mini"` | No | LLM model identifier. |
| `api_key` | string | — | **Yes** | API key for the LLM provider. |
| `base_url` | string | — | No | Custom endpoint URL (OpenAI-compatible). |
Expand Down Expand Up @@ -202,8 +203,9 @@ variables directly:

```dockerfile
ENV EVEROS_ROOT=/data/everos
ENV EVEROS_LLM__PROVIDER=atlascloud
ENV EVEROS_LLM__API_KEY=sk-...
ENV EVEROS_LLM__MODEL=gpt-4o
ENV EVEROS_LLM__MODEL=qwen/qwen3.5-flash
ENV EVEROS_EMBEDDING__MODEL=text-embedding-3-large
ENV EVEROS_EMBEDDING__API_KEY=sk-...
ENV EVEROS_EMBEDDING__BASE_URL=https://api.openai.com/v1
Expand All @@ -225,6 +227,7 @@ Examples:

| TOML | Environment Variable |
|---|---|
| `[llm] provider = "atlascloud"` | `EVEROS_LLM__PROVIDER=atlascloud` |
| `[llm] api_key = "sk-..."` | `EVEROS_LLM__API_KEY=sk-...` |
| `[sqlite] busy_timeout_ms = 10000` | `EVEROS_SQLITE__BUSY_TIMEOUT_MS=10000` |
| `[memory] timezone = "Asia/Tokyo"` | `EVEROS_MEMORY__TIMEZONE=Asia/Tokyo` |
4 changes: 3 additions & 1 deletion src/everos/config/default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@ cache_size_kb = 2048

[llm]
# Provider-agnostic OpenAI-protocol client config. Override via env:
# EVEROS_LLM__MODEL, EVEROS_LLM__API_KEY, EVEROS_LLM__BASE_URL
# EVEROS_LLM__PROVIDER, EVEROS_LLM__MODEL, EVEROS_LLM__API_KEY,
# EVEROS_LLM__BASE_URL
# Or set the field directly in this file (<root>/everos.toml).
provider = "openrouter"
model = "openai/gpt-4.1-mini"
api_key = ""
base_url = "https://openrouter.ai/api/v1"
Expand Down
35 changes: 34 additions & 1 deletion src/everos/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,14 @@
from typing import Literal
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError

from pydantic import BaseModel, ConfigDict, Field, SecretStr, field_validator
from pydantic import (
BaseModel,
ConfigDict,
Field,
SecretStr,
field_validator,
model_validator,
)
from pydantic_settings import (
BaseSettings,
PydanticBaseSettingsSource,
Expand All @@ -38,6 +45,9 @@

_DEFAULT_TOML_PATH = Path(__file__).parent / "default.toml"
_DEFAULT_ROOT = Path("~/.everos")
_ATLASCLOUD_BASE_URL = "https://api.atlascloud.ai/v1"
_ATLASCLOUD_DEFAULT_MODEL = "qwen/qwen3.5-flash"
_OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1"


def resolve_root(explicit: str | None = None) -> Path:
Expand Down Expand Up @@ -124,15 +134,38 @@ class LLMSettings(BaseModel):
endpoint plugs in via ``base_url``.

Env binding (via parent ``Settings``):
EVEROS_LLM__PROVIDER
EVEROS_LLM__MODEL
EVEROS_LLM__API_KEY
EVEROS_LLM__BASE_URL
"""

provider: Literal["openrouter", "openai", "atlascloud", "custom"] = "openrouter"
model: str = "gpt-4.1-mini"
api_key: SecretStr | None = None
base_url: str | None = None

@model_validator(mode="after")
def _apply_provider_defaults(self) -> LLMSettings:
"""Apply provider-specific OpenAI-compatible defaults."""
if self.provider != "atlascloud":
return self

if not self.base_url or self.base_url == _OPENROUTER_BASE_URL:
self.base_url = _ATLASCLOUD_BASE_URL

if self.model in {"openai/gpt-4.1-mini", "gpt-4.1-mini"}:
self.model = _ATLASCLOUD_DEFAULT_MODEL

if not self.api_key or not self.api_key.get_secret_value():
atlas_key = os.environ.get("ATLASCLOUD_API_KEY") or os.environ.get(
"ATLAS_CLOUD_API_KEY"
)
if atlas_key:
self.api_key = SecretStr(atlas_key)

return self


class MultimodalSettings(BaseModel):
"""Multimodal parsing LLM config (everalgo-parser).
Expand Down
7 changes: 4 additions & 3 deletions src/everos/templates/env.template
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@

# ─── LLM (OpenAI-protocol compatible) ────────────────
# Any OpenAI-API-compatible endpoint plugs in via base_url. Defaults
# below target OpenRouter (one key, broad model catalogue); switch to
# OpenAI, vLLM, Ollama (OpenAI bridge), or any other compatible endpoint
# by changing model + base_url + api_key.
# below target OpenRouter (one key, broad model catalogue). Set
# EVEROS_LLM__PROVIDER=atlascloud to use Atlas Cloud's preset endpoint
# and model defaults.

EVEROS_LLM__PROVIDER=openrouter
EVEROS_LLM__MODEL=openai/gpt-4.1-mini
EVEROS_LLM__API_KEY=
EVEROS_LLM__BASE_URL=https://openrouter.ai/api/v1
Expand Down
28 changes: 28 additions & 0 deletions tests/unit/test_config/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,34 @@ def test_embedding_rerank_defaults() -> None:
assert s.rerank.api_key.get_secret_value() == ""
assert s.rerank.timeout_seconds == 30.0
assert s.llm.api_key.get_secret_value() == ""
assert s.llm.provider == "openrouter"


def test_atlascloud_llm_provider_defaults(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("ATLASCLOUD_API_KEY", "atlas-key")
s = Settings(llm={"provider": "atlascloud"})

assert s.llm.provider == "atlascloud"
assert s.llm.model == "qwen/qwen3.5-flash"
assert s.llm.base_url == "https://api.atlascloud.ai/v1"
assert s.llm.api_key is not None
assert s.llm.api_key.get_secret_value() == "atlas-key"


def test_atlascloud_llm_provider_preserves_explicit_values() -> None:
s = Settings(
llm={
"provider": "atlascloud",
"model": "deepseek-ai/deepseek-v4-pro",
"api_key": "custom-key",
"base_url": "https://proxy.example.test/v1",
}
)

assert s.llm.model == "deepseek-ai/deepseek-v4-pro"
assert s.llm.base_url == "https://proxy.example.test/v1"
assert s.llm.api_key is not None
assert s.llm.api_key.get_secret_value() == "custom-key"


def test_resolve_root_default(monkeypatch: pytest.MonkeyPatch) -> None:
Expand Down