From 0d6aa369ccc5e1d975d266a16ceb4a638fbcc2b8 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 9 Sep 2026 18:45:30 +0300 Subject: [PATCH 01/13] feat(llm): add origin-scoped aimlapi.com attribution helper --- src/everos/component/utils/attribution.py | 94 +++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/everos/component/utils/attribution.py diff --git a/src/everos/component/utils/attribution.py b/src/everos/component/utils/attribution.py new file mode 100644 index 00000000..3021b27a --- /dev/null +++ b/src/everos/component/utils/attribution.py @@ -0,0 +1,94 @@ +"""Partner attribution headers for aimlapi.com endpoints. + +aimlapi.com credits the projects that send it traffic, but only when the +request carries the partner headers below. EverOS reaches every model +provider through the same OpenAI-protocol clients, so the headers cannot +be attached at the SDK layer without leaking to whichever endpoint the +user happens to configure. Instead every helper here is *origin-scoped*: +it inspects the configured ``base_url`` and returns an empty mapping for +anything that is not an aimlapi.com host, so the headers can never ride a +request to OpenRouter, DeepInfra, OpenAI or a proxy in front of them. + +The returned mapping is always a fresh ``dict`` — callers merge it into +their own header set, and no shared constant is ever handed out for +mutation. +""" + +from __future__ import annotations + +from typing import Any +from urllib.parse import urlsplit + +AIMLAPI_BASE_URL = "https://api.aimlapi.com/v1" +"""Chat-completions / embeddings base URL for aimlapi.com.""" + +AIMLAPI_DISPLAY_NAME = "aimlapi.com" +"""Human-facing provider label, as the provider spells it.""" + +_AIMLAPI_DOMAIN = "aimlapi.com" + +# Identifies EverOS to aimlapi.com. Must match ``^part_[A-Za-z0-9]{1,64}$`` +# — a malformed id is accepted by the API and then silently unattributed. +_PARTNER_ID = "part_VxTyAUvoIVbl30dPrB7kbRZk" +_SOURCE = "agent/everos" + +# ``HTTP-Referer`` / ``X-Title`` name the *host* project (EverOS), the +# same convention OpenRouter uses for app attribution. +_REFERER = "https://github.com/EverMind-AI/EverOS" +_TITLE = "EverOS" + + +def is_aimlapi_base_url(base_url: str | None) -> bool: + """Return whether ``base_url`` points at an aimlapi.com host. + + Matches on the parsed hostname only, on a dot boundary, so lookalike + hosts such as ``api.aimlapi.com.example.net`` do not match. + + Args: + base_url: Configured OpenAI-protocol endpoint, or ``None``. + + Returns: + ``True`` when the host is ``aimlapi.com`` or a subdomain of it. + """ + if not base_url: + return False + host = (urlsplit(base_url).hostname or "").lower() + return host == _AIMLAPI_DOMAIN or host.endswith(f".{_AIMLAPI_DOMAIN}") + + +def aimlapi_headers(base_url: str | None) -> dict[str, str]: + """Return the partner attribution headers for an aimlapi.com endpoint. + + Args: + base_url: Configured OpenAI-protocol endpoint, or ``None``. + + Returns: + A new ``dict`` of headers when ``base_url`` is an aimlapi.com + host, otherwise an empty ``dict``. Never returns a shared object. + """ + if not is_aimlapi_base_url(base_url): + return {} + return { + "X-AIMLAPI-Partner-ID": _PARTNER_ID, + "X-AIMLAPI-Source": _SOURCE, + "HTTP-Referer": _REFERER, + "X-Title": _TITLE, + } + + +def aimlapi_request_extra(base_url: str | None) -> dict[str, Any]: + """Return per-request kwargs carrying the attribution headers. + + Shaped for clients that only accept extra *request* options (the + everalgo ``LLMConfig.extra`` passthrough), where ``extra_headers`` is + forwarded by the openai SDK as headers rather than as body fields. + + Args: + base_url: Configured OpenAI-protocol endpoint, or ``None``. + + Returns: + ``{"extra_headers": {...}}`` for an aimlapi.com host, otherwise an + empty ``dict`` so no key is added to the request at all. + """ + headers = aimlapi_headers(base_url) + return {"extra_headers": headers} if headers else {} From e1305e9a3474429d7f344dc981a7910da20a308d Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 9 Sep 2026 18:50:22 +0300 Subject: [PATCH 02/13] docs(utils): list attribution helpers in the public API docstring --- src/everos/component/utils/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/everos/component/utils/__init__.py b/src/everos/component/utils/__init__.py index e9cada50..9b522817 100644 --- a/src/everos/component/utils/__init__.py +++ b/src/everos/component/utils/__init__.py @@ -19,4 +19,11 @@ tokens_for_query, join_tokens, ) + from everos.component.utils.attribution import ( + AIMLAPI_BASE_URL, + AIMLAPI_DISPLAY_NAME, + aimlapi_headers, + aimlapi_request_extra, + is_aimlapi_base_url, + ) """ From b7adcad9baa372fcbd20ddf7aaf012f917ca74fe Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 9 Sep 2026 18:54:02 +0300 Subject: [PATCH 03/13] feat(llm): attach aimlapi.com attribution in the client factories --- src/everos/component/llm/client.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/everos/component/llm/client.py b/src/everos/component/llm/client.py index 8defa6d8..04021e69 100644 --- a/src/everos/component/llm/client.py +++ b/src/everos/component/llm/client.py @@ -18,6 +18,7 @@ from everalgo.llm.types import ChatMessage, ChatResponse from pydantic import BaseModel +from everos.component.utils.attribution import aimlapi_request_extra from everos.component.utils.config_hints import missing_config_error from everos.config import Settings, load_settings from everos.core.observability.logging import get_logger @@ -105,7 +106,12 @@ def get_llm_client() -> LLMClient: api_key=api_key, base_url=llm_cfg.base_url, timeout=llm_cfg.timeout_seconds, - extra=dict(llm_cfg.extra), + # Attribution first, ``[llm].extra`` second, so a configured + # key keeps winning on collision. For every non-aimlapi + # endpoint the attribution mapping is empty, so no key is + # added to the request and no header can reach a foreign + # provider. + extra={**aimlapi_request_extra(llm_cfg.base_url), **llm_cfg.extra}, ) ) # Wrap for OTel token capture only when tracing is on — keeps the @@ -251,6 +257,7 @@ def get_multimodal_llm_client() -> LLMClient: model=cfg.model, api_key=api_key, base_url=cfg.base_url, + extra=aimlapi_request_extra(cfg.base_url), ) ) logger.info("multimodal_llm_client_built", model=cfg.model) From acce775b962061d56d1e7a6b20aec78db79a4470 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 9 Sep 2026 18:55:32 +0300 Subject: [PATCH 04/13] feat(llm): attach aimlapi.com attribution in OpenAIProvider --- src/everos/component/llm/openai_provider.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/everos/component/llm/openai_provider.py b/src/everos/component/llm/openai_provider.py index a68c3ba4..c22e6267 100644 --- a/src/everos/component/llm/openai_provider.py +++ b/src/everos/component/llm/openai_provider.py @@ -18,6 +18,8 @@ import openai +from everos.component.utils.attribution import aimlapi_headers + from .protocol import ChatMessage, ChatResponse, LLMError, Usage @@ -75,6 +77,10 @@ def __init__( } if max_retries is not None: client_kwargs["max_retries"] = max_retries + # Partner attribution, merged into (not over) the SDK's own + # defaults and absent unless ``base_url`` is an aimlapi.com host. + if attribution := aimlapi_headers(base_url): + client_kwargs["default_headers"] = attribution self._client = openai.AsyncOpenAI(**client_kwargs) # type: ignore[arg-type] async def chat( From 9780d4c37c7a4933a4c46883052adffd3ef51139 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 9 Sep 2026 18:57:04 +0300 Subject: [PATCH 05/13] feat(embedding): attach aimlapi.com attribution in the embedding client --- src/everos/component/embedding/openai_provider.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/everos/component/embedding/openai_provider.py b/src/everos/component/embedding/openai_provider.py index b62ec5d8..73dc3222 100644 --- a/src/everos/component/embedding/openai_provider.py +++ b/src/everos/component/embedding/openai_provider.py @@ -24,6 +24,7 @@ import openai +from everos.component.utils.attribution import aimlapi_headers from everos.core.errors import EmbeddingInputError from everos.core.observability.tracing import memory_span, set_generation_usage @@ -87,11 +88,14 @@ def __init__( self._model = model self._batch_size = batch_size self._semaphore = asyncio.Semaphore(max_concurrent) + # Partner attribution, merged into (not over) the SDK's own + # defaults and empty unless ``base_url`` is an aimlapi.com host. self._client = openai.AsyncOpenAI( api_key=api_key, base_url=base_url, timeout=timeout, max_retries=max_retries, + default_headers=aimlapi_headers(base_url) or None, ) async def embed(self, text: str) -> list[float]: From 548432dddeea37962f3dfbe29592eea96f493731 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 9 Sep 2026 18:59:49 +0300 Subject: [PATCH 06/13] test(llm): pin attribution header contracts --- .../test_utils/test_attribution.py | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 tests/unit/test_component/test_utils/test_attribution.py diff --git a/tests/unit/test_component/test_utils/test_attribution.py b/tests/unit/test_component/test_utils/test_attribution.py new file mode 100644 index 00000000..a80a479f --- /dev/null +++ b/tests/unit/test_component/test_utils/test_attribution.py @@ -0,0 +1,99 @@ +"""Attribution headers are well-formed and scoped to aimlapi.com only. + +Pins three contracts that fail silently in production if broken: + +1. A malformed partner id is accepted by the API and then earns nothing, + so the id is asserted against the documented pattern. +2. The headers must never be attached to a request bound for another + provider, so every non-aimlapi host must yield an empty mapping. +3. Callers merge the result into their own header set, so a fresh dict + must be returned each call and never a shared constant. +""" + +from __future__ import annotations + +import re + +import pytest + +from everos.component.utils.attribution import ( + AIMLAPI_BASE_URL, + AIMLAPI_DISPLAY_NAME, + aimlapi_headers, + aimlapi_request_extra, + is_aimlapi_base_url, +) + +_PARTNER_ID_PATTERN = re.compile(r"^part_[A-Za-z0-9]{1,64}$") + +_EXPECTED_KEYS = { + "X-AIMLAPI-Partner-ID", + "X-AIMLAPI-Source", + "HTTP-Referer", + "X-Title", +} + + +def test_partner_id_matches_documented_pattern() -> None: + headers = aimlapi_headers(AIMLAPI_BASE_URL) + assert _PARTNER_ID_PATTERN.match(headers["X-AIMLAPI-Partner-ID"]) + + +def test_all_four_attribution_headers_are_present() -> None: + assert set(aimlapi_headers(AIMLAPI_BASE_URL)) == _EXPECTED_KEYS + + +def test_referer_and_title_name_the_host_project_not_the_provider() -> None: + headers = aimlapi_headers(AIMLAPI_BASE_URL) + assert headers["HTTP-Referer"] == "https://github.com/EverMind-AI/EverOS" + assert headers["X-Title"] == "EverOS" + + +def test_display_name_is_the_provider_spelling() -> None: + assert AIMLAPI_DISPLAY_NAME == "aimlapi.com" + + +@pytest.mark.parametrize( + "base_url", + [ + "https://api.aimlapi.com/v1", + "https://api.aimlapi.com/v1/", + "https://AIMLAPI.com/v1", + "http://api.aimlapi.com/v1", + ], +) +def test_aimlapi_hosts_are_recognised(base_url: str) -> None: + assert is_aimlapi_base_url(base_url) + assert set(aimlapi_headers(base_url)) == _EXPECTED_KEYS + + +@pytest.mark.parametrize( + "base_url", + [ + None, + "", + "https://openrouter.ai/api/v1", + "https://api.openai.com/v1", + "https://api.deepinfra.com/v1/openai", + # Lookalike hosts: a proxy fronting us, or an outright imposter. + "https://api.aimlapi.com.example.net/v1", + "https://not-aimlapi.com/v1", + "https://proxy.example.net/?upstream=api.aimlapi.com", + ], +) +def test_no_headers_leak_to_other_origins(base_url: str | None) -> None: + assert not is_aimlapi_base_url(base_url) + assert aimlapi_headers(base_url) == {} + assert aimlapi_request_extra(base_url) == {} + + +def test_request_extra_wraps_headers_for_the_sdk() -> None: + extra = aimlapi_request_extra(AIMLAPI_BASE_URL) + assert set(extra) == {"extra_headers"} + assert set(extra["extra_headers"]) == _EXPECTED_KEYS + + +def test_each_call_returns_a_fresh_mapping() -> None: + first = aimlapi_headers(AIMLAPI_BASE_URL) + first["X-Title"] = "mutated" + assert aimlapi_headers(AIMLAPI_BASE_URL)["X-Title"] == "EverOS" From 531afc67e9e582caf902e9e0c78a770956935a30 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 9 Sep 2026 19:02:53 +0300 Subject: [PATCH 07/13] test(llm): pin attribution wiring at each client construction site --- .../test_llm/test_attribution_wiring.py | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 tests/unit/test_component/test_llm/test_attribution_wiring.py diff --git a/tests/unit/test_component/test_llm/test_attribution_wiring.py b/tests/unit/test_component/test_llm/test_attribution_wiring.py new file mode 100644 index 00000000..3d664f66 --- /dev/null +++ b/tests/unit/test_component/test_llm/test_attribution_wiring.py @@ -0,0 +1,137 @@ +"""Attribution headers reach the wire, and only for aimlapi.com. + +The header helper is unit-tested separately; what breaks silently is the +*wiring* — a client built without the headers still works, just +unattributed, so nothing fails loudly. These tests pin that each client +construction site actually forwards them, and that a non-aimlapi +``base_url`` adds no request key at all (a stray ``extra_headers`` or a +``None`` valued key is a 400 on some upstreams). +""" + +from __future__ import annotations + +import importlib +from typing import Any + +import pytest +from pydantic import SecretStr + +from everos.component.llm.openai_provider import OpenAIProvider +from everos.config import Settings +from everos.config.settings import LLMSettings, MultimodalSettings + +_client_mod = importlib.import_module("everos.component.llm.client") + +_AIMLAPI = "https://api.aimlapi.com/v1" +_OPENROUTER = "https://openrouter.ai/api/v1" +_PARTNER_ID = "part_VxTyAUvoIVbl30dPrB7kbRZk" + + +def _recorder(captured: dict[str, Any]) -> Any: + """Return a ``build_client`` stub that records the config it is given.""" + + def _build(cfg: Any) -> object: + captured["cfg"] = cfg + return object() + + return _build + + +def _capture_llm_config( + monkeypatch: pytest.MonkeyPatch, + *, + base_url: str, + config_extra: dict[str, Any] | None = None, +) -> dict[str, Any]: + """Build the LLM singleton against ``base_url`` and return its config.""" + captured: dict[str, Any] = {} + monkeypatch.setattr(_client_mod, "_llm_client", None, raising=False) + monkeypatch.setattr( + _client_mod, + "load_settings", + lambda: Settings( + llm=LLMSettings( + model="openai/gpt-4.1-mini", + api_key=SecretStr("sk-test"), + base_url=base_url, + extra=config_extra or {}, + ) + ), + ) + monkeypatch.setattr(_client_mod, "build_client", _recorder(captured)) + _client_mod.get_llm_client() + return captured["cfg"].extra + + +def _capture_multimodal_config( + monkeypatch: pytest.MonkeyPatch, *, base_url: str +) -> dict[str, Any]: + """Build the multimodal singleton and return its config ``extra``.""" + captured: dict[str, Any] = {} + monkeypatch.setattr(_client_mod, "_multimodal_client", None, raising=False) + monkeypatch.setattr( + _client_mod, + "load_settings", + lambda: Settings( + multimodal=MultimodalSettings( + model="google/gemini-3.8-flash", + api_key=SecretStr("sk-test"), + base_url=base_url, + ) + ), + ) + monkeypatch.setattr(_client_mod, "build_client", _recorder(captured)) + _client_mod.get_multimodal_llm_client() + return captured["cfg"].extra + + +def test_llm_client_sends_attribution_to_aimlapi( + monkeypatch: pytest.MonkeyPatch, +) -> None: + extra = _capture_llm_config(monkeypatch, base_url=_AIMLAPI) + assert extra["extra_headers"]["X-AIMLAPI-Partner-ID"] == _PARTNER_ID + assert extra["extra_headers"]["X-AIMLAPI-Source"] == "agent/everos" + + +def test_llm_client_adds_no_request_key_for_other_providers( + monkeypatch: pytest.MonkeyPatch, +) -> None: + assert _capture_llm_config(monkeypatch, base_url=_OPENROUTER) == {} + + +def test_llm_client_config_extra_wins_over_attribution( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A key set in ``[llm].extra`` overrides the attribution value.""" + extra = _capture_llm_config( + monkeypatch, + base_url=_AIMLAPI, + config_extra={"extra_headers": {"X-Custom": "user"}}, + ) + assert extra["extra_headers"] == {"X-Custom": "user"} + + +def test_multimodal_client_sends_attribution_to_aimlapi( + monkeypatch: pytest.MonkeyPatch, +) -> None: + extra = _capture_multimodal_config(monkeypatch, base_url=_AIMLAPI) + assert extra["extra_headers"]["X-AIMLAPI-Partner-ID"] == _PARTNER_ID + + +def test_multimodal_client_adds_no_request_key_for_other_providers( + monkeypatch: pytest.MonkeyPatch, +) -> None: + assert _capture_multimodal_config(monkeypatch, base_url=_OPENROUTER) == {} + + +def test_openai_provider_sets_default_headers_for_aimlapi() -> None: + provider = OpenAIProvider(model="m", api_key="sk-test", base_url=_AIMLAPI) + sent = provider._client.default_headers + assert sent["X-AIMLAPI-Partner-ID"] == _PARTNER_ID + # The SDK's own defaults survive — the partner headers merge in. + assert "Content-Type" in sent + + +def test_openai_provider_sends_no_partner_headers_elsewhere() -> None: + provider = OpenAIProvider(model="m", api_key="sk-test", base_url=_OPENROUTER) + assert "X-AIMLAPI-Partner-ID" not in provider._client.default_headers From 993dde93fd8608b4be15c0d9e536044fffc0d3e4 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 9 Sep 2026 19:04:32 +0300 Subject: [PATCH 08/13] test(embedding): cover attribution headers on the embedding client --- .../test_embedding/test_openai_provider.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/unit/test_component/test_embedding/test_openai_provider.py b/tests/unit/test_component/test_embedding/test_openai_provider.py index abc6c423..d64b9396 100644 --- a/tests/unit/test_component/test_embedding/test_openai_provider.py +++ b/tests/unit/test_component/test_embedding/test_openai_provider.py @@ -35,3 +35,13 @@ async def test_empty_response_data_raises_embedding_error() -> None: with pytest.raises(EmbeddingServiceError, match="empty data"): await provider.embed("hello") + + +def test_attribution_headers_sent_only_to_aimlapi() -> None: + """Partner headers ride aimlapi.com requests and no others.""" + ours = _make_provider(base_url="https://api.aimlapi.com/v1") + sent = ours._client.default_headers["X-AIMLAPI-Partner-ID"] + assert sent == "part_VxTyAUvoIVbl30dPrB7kbRZk" + + theirs = _make_provider(base_url="https://api.deepinfra.com/v1/openai") + assert "X-AIMLAPI-Partner-ID" not in theirs._client.default_headers From e8f82e3e7ab503bea7c7a2f94b4295a44bd92036 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 9 Sep 2026 19:06:16 +0300 Subject: [PATCH 09/13] docs(config): document aimlapi.com as an LLM endpoint --- docs/configuration.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/configuration.md b/docs/configuration.md index 5cf85a19..ae211b71 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -130,6 +130,27 @@ Zilliz Cloud endpoint; a Milvus Lite filesystem path is rejected. | `api_key` | string | — | **Yes** | API key for the LLM provider. | | `base_url` | string | — | No | Custom endpoint URL (OpenAI-compatible). | +#### Provider endpoints + +Any OpenAI-protocol chat-completions endpoint works. Two aggregators cover +the shipped model slugs without rewriting them: + +| Provider | `base_url` | Notes | +|---|---|---| +| aimlapi.com | `https://api.aimlapi.com/v1` | Same `vendor/model` slug convention, so the shipped `[llm]` default works unchanged. | +| OpenRouter | `https://openrouter.ai/api/v1` | Historical default. | + +The shipped `[llm]` default `openai/gpt-4.1-mini` was called live against +aimlapi.com and answered, including the structured-output +(`response_format`) path the extractors use. + +> **Known limitation — multimodal against aimlapi.com.** The image content +> parts EverOS sends carry `image_url.detail = null`, which aimlapi.com +> rejects with HTTP 400 (`messages.0.content` / `invalid_union`) where +> OpenAI and OpenRouter accept it. Text-only calls to `[multimodal]` are +> fine; keep `[multimodal]` on a provider that tolerates the null field +> until either side changes. + ### `[multimodal]` | Field | Type | Default | Required | Description | From cc9cbfd2766ffa033423cc94874524262712a848 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 9 Sep 2026 19:07:32 +0300 Subject: [PATCH 10/13] docs(config): note aimlapi.com endpoint in config.example.toml --- config.example.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config.example.toml b/config.example.toml index 48cff25f..616731bd 100644 --- a/config.example.toml +++ b/config.example.toml @@ -20,6 +20,9 @@ # ── LLM ─────────────────────────────────────────────── # OpenAI-protocol chat-completions endpoint used by the algo extractors. +# Alternatives, same protocol — swap all three fields together: +# aimlapi.com model = "openai/gpt-4.1-mini", base_url = "https://api.aimlapi.com/v1" +# OpenRouter model = "openai/gpt-4.1-mini", base_url = "https://openrouter.ai/api/v1" [llm] model = "gpt-4.1-mini" api_key = "sk-..." From d4ece3a7644fb7bc9cfa2de92afd4d2bc20f3480 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 9 Sep 2026 19:08:20 +0300 Subject: [PATCH 11/13] docs(config): note aimlapi.com endpoint in default.toml --- src/everos/config/default.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/everos/config/default.toml b/src/everos/config/default.toml index 3dceee2f..f99345de 100644 --- a/src/everos/config/default.toml +++ b/src/everos/config/default.toml @@ -66,6 +66,10 @@ collection_prefix = "everos" # Provider-agnostic OpenAI-protocol client config. Override via env: # EVEROS_LLM__MODEL, EVEROS_LLM__API_KEY, EVEROS_LLM__BASE_URL # Or set the field directly in this file (/everos.toml). +# The model slug below is spelled the same way by both aggregators, so +# only base_url + api_key change: +# aimlapi.com -> https://api.aimlapi.com/v1 +# OpenRouter -> https://openrouter.ai/api/v1 model = "openai/gpt-4.1-mini" api_key = "" base_url = "https://openrouter.ai/api/v1" From d267f763df87c6ecf83dcc180aeecf60b6363045 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 9 Sep 2026 19:09:33 +0300 Subject: [PATCH 12/13] docs(config): note aimlapi.com endpoint in the env template --- src/everos/templates/env.template | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/everos/templates/env.template b/src/everos/templates/env.template index 45f9b7ab..97004cd0 100755 --- a/src/everos/templates/env.template +++ b/src/everos/templates/env.template @@ -23,8 +23,9 @@ # ─── 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. +# aimlapi.com (https://api.aimlapi.com/v1 — same slug spelling, so the +# model below is unchanged), OpenAI, vLLM, Ollama (OpenAI bridge), or any +# other compatible endpoint by changing model + base_url + api_key. EVEROS_LLM__MODEL=openai/gpt-4.1-mini EVEROS_LLM__API_KEY= From 20ddee8d242fac6c615eea41d21b48ea2a07a81b Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 9 Sep 2026 19:10:47 +0300 Subject: [PATCH 13/13] docs(config): regenerate .env.example from the template --- .env.example | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 45f9b7ab..97004cd0 100644 --- a/.env.example +++ b/.env.example @@ -23,8 +23,9 @@ # ─── 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. +# aimlapi.com (https://api.aimlapi.com/v1 — same slug spelling, so the +# model below is unchanged), OpenAI, vLLM, Ollama (OpenAI bridge), or any +# other compatible endpoint by changing model + base_url + api_key. EVEROS_LLM__MODEL=openai/gpt-4.1-mini EVEROS_LLM__API_KEY=