Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c1d3dc8
fixed the testset json related issues and removed the testcase_dedup_…
ashrafchowdury Mar 11, 2026
9b97197
v0.93.1
jp-agenta Mar 11, 2026
a705d3f
removed the data column from parent
ashrafchowdury Mar 11, 2026
db2320b
cleanup
ashrafchowdury Mar 11, 2026
fff0f64
filter out version 0 from the ui
ashrafchowdury Mar 11, 2026
b2e2d9f
added modal for disconnect testset changes
ashrafchowdury Mar 11, 2026
730e604
added testset change warning modal
ashrafchowdury Mar 11, 2026
c12ae2b
fix mistral
jp-agenta Mar 12, 2026
70a6c9d
Merge pull request #3959 from Agenta-AI/frontend/enhance-playground-t…
mmabrouk Mar 12, 2026
7ed1034
direct mistral fixes
jp-agenta Mar 12, 2026
66acbfa
format/lint
jp-agenta Mar 12, 2026
8ab3ed2
chore: remove unused workspace dependencies from web package
ardaerzin Mar 12, 2026
3000b71
Merge branch 'release/v0.93.1' into forntend/fix-stateless-playground…
mmabrouk Mar 12, 2026
db4b264
Merge branch 'release/v0.94.3' into frontend-fix/turbo-cache-hits
jp-agenta Mar 12, 2026
999b150
fix mistral legacy
jp-agenta Mar 12, 2026
909cdbc
format / lint
jp-agenta Mar 12, 2026
2b99622
fix str>uuid
jp-agenta Mar 12, 2026
bbcde07
fixed the all the reported issues
ashrafchowdury Mar 13, 2026
b3134ed
fix
ashrafchowdury Mar 13, 2026
2d1f215
fix the sync issues after save
ashrafchowdury Mar 13, 2026
1dd39a4
fix drift
jp-agenta Mar 13, 2026
24fc637
v0.94.4
mmabrouk Mar 13, 2026
ec83cb2
fix fails
ashrafchowdury Mar 13, 2026
18a60bc
Merge branch 'release/v0.94.4' into forntend/fix-stateless-playground…
bekossy Mar 13, 2026
a9c99d7
Potential fix for pull request finding
bekossy Mar 13, 2026
de83074
Merge branch 'release/v0.94.4' into fix/mistral-api-key-issue-in-web
bekossy Mar 13, 2026
62649b1
Merge branch 'fix/mistral-api-key-issue-in-web' of github.com:Agenta-…
bekossy Mar 13, 2026
148110d
fix lint
bekossy Mar 13, 2026
fe78c6a
Merge pull request #3958 from Agenta-AI/forntend/fix-stateless-playgr…
bekossy Mar 13, 2026
d9de6ba
Merge pull request #3969 from Agenta-AI/fix/mistral-api-key-issue-in-web
bekossy Mar 13, 2026
bb52653
Merge pull request #3982 from Agenta-AI/fix/sso-and-domains-failing
bekossy Mar 13, 2026
8248b1e
Merge pull request #3970 from Agenta-AI/frontend-fix/turbo-cache-hits
bekossy Mar 13, 2026
230f324
Revert "[chore] cleanup workspace dependencies to fix turbo "
bekossy Mar 13, 2026
0116318
Merge pull request #3990 from Agenta-AI/revert-3970-frontend-fix/turb…
bekossy Mar 13, 2026
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
9 changes: 5 additions & 4 deletions api/ee/src/apis/fastapi/organizations/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Optional
from datetime import datetime
from uuid import UUID

from pydantic import BaseModel, Field

Expand All @@ -25,7 +26,7 @@ class OrganizationDomainVerify(BaseModel):
class OrganizationDomainResponse(BaseModel):
"""Response model for a domain."""

id: str
id: UUID
slug: str

name: Optional[str]
Expand All @@ -38,7 +39,7 @@ class OrganizationDomainResponse(BaseModel):
created_at: datetime
updated_at: Optional[datetime]

organization_id: str
organization_id: UUID

class Config:
from_attributes = True
Expand Down Expand Up @@ -76,7 +77,7 @@ class OrganizationProviderUpdate(BaseModel):
class OrganizationProviderResponse(BaseModel):
"""Response model for an SSO provider."""

id: str
id: UUID
slug: str

name: Optional[str]
Expand All @@ -89,7 +90,7 @@ class OrganizationProviderResponse(BaseModel):
created_at: datetime
updated_at: Optional[datetime]

organization_id: str
organization_id: UUID

class Config:
from_attributes = True
48 changes: 48 additions & 0 deletions api/ee/tests/pytest/unit/test_organization_fastapi_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from datetime import datetime, timezone
from uuid import uuid4

from ee.src.apis.fastapi.organizations.models import (
OrganizationDomainResponse,
OrganizationProviderResponse,
)
from ee.src.core.organizations.types import OrganizationDomain, OrganizationProvider


def test_domain_response_accepts_uuid_backed_domain_dto():
domain = OrganizationDomain(
id=uuid4(),
organization_id=uuid4(),
slug="example.com",
name="Example",
description="Example domain",
token="verify-me",
flags={"is_verified": False},
created_at=datetime.now(timezone.utc),
updated_at=None,
)

response = OrganizationDomainResponse.model_validate(domain)

dumped = response.model_dump(mode="json")
assert isinstance(dumped["id"], str)
assert isinstance(dumped["organization_id"], str)


def test_provider_response_accepts_uuid_backed_provider_dto():
provider = OrganizationProvider(
id=uuid4(),
organization_id=uuid4(),
slug="oidc",
name="OIDC",
description="OIDC provider",
settings={"issuer_url": "https://issuer.example.com"},
flags={"is_active": True, "is_valid": True},
created_at=datetime.now(timezone.utc),
updated_at=None,
)

response = OrganizationProviderResponse.model_validate(provider)

dumped = response.model_dump(mode="json")
assert isinstance(dumped["id"], str)
assert isinstance(dumped["organization_id"], str)
21 changes: 14 additions & 7 deletions api/oss/src/core/secrets/dtos.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,31 +84,38 @@ def validate_secret_data_based_on_kind(cls, values: Dict[str, Any]):
data = data.model_dump()
values["data"] = data

standard_provider_kinds = {provider.value for provider in StandardProviderKind}
custom_provider_kinds = {provider.value for provider in CustomProviderKind}

if kind == SecretKind.PROVIDER_KEY.value:
if not isinstance(data, dict):
raise ValueError(
"The provided request secret dto is not a valid type for StandardProviderDTO"
)
if not isinstance(data["provider"], dict) or "key" not in data["provider"]:
provider = data.get("provider")
if not isinstance(provider, dict) or "key" not in provider:
raise ValueError(
"The provided request secret dto is missing required fields for StandardProviderSettingsDTO"
)
if data["kind"] not in StandardProviderKind.__members__.values():
# Accept the legacy provider slug on input, but persist the canonical value.
if data.get("kind") == StandardProviderKind.MISTRALAI.value:
data["kind"] = StandardProviderKind.MISTRAL.value
if data.get("kind") not in standard_provider_kinds:
raise ValueError(
"The provided kind in data is not a valid StandardProviderKind enum"
)

elif kind == SecretKind.CUSTOM_PROVIDER.value:
if not isinstance(data, dict):
raise ValueError(
"The provided request secret dto is not a valid type for CustomProviderDTO"
)
# Fix inconsistent API naming - Users might enter 'togetherai' but the API requires 'together_ai'
# This ensures compatibility with LiteLLM which requires the provider in "together_ai" format
if data.get("kind", "") == "togetherai":
data["kind"] = "together_ai"

if not isinstance(data, dict):
raise ValueError(
"The provided request secret dto is not a valid type for CustomProviderDTO"
)
if data["kind"] not in CustomProviderKind.__members__.values():
if data.get("kind") not in custom_provider_kinds:
raise ValueError(
"The provided kind in data is not a valid CustomProviderKind enum"
)
Expand Down
33 changes: 31 additions & 2 deletions api/oss/src/core/secrets/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,35 @@
from oss.src.models.api.evaluation_model import LMProvidersEnum


_LEGACY_SYSTEM_ENV_NAMES = {
LMProvidersEnum.mistral.value: ("MISTRALAI_API_KEY",),
}

_PROVIDER_ENV_ALIASES = {
"mistralai": LMProvidersEnum.mistral.value,
}


def _get_system_env_secret(secret_name: str) -> str | None:
for env_name in (secret_name, *_LEGACY_SYSTEM_ENV_NAMES.get(secret_name, ())):
env_var = os.getenv(env_name)
if env_var:
return env_var

return None


def _provider_slug_to_env_var(provider_slug: str) -> str:
if not provider_slug:
return ""

canonical_provider = LMProvidersEnum.__members__.get(provider_slug.replace("_", ""))
if canonical_provider:
return canonical_provider.value

return _PROVIDER_ENV_ALIASES.get(provider_slug, f"{provider_slug.upper()}_API_KEY")


async def get_system_llm_providers_secrets() -> Dict[str, Any]:
"""
Fetches LLM providers secrets from system environment variables.
Expand All @@ -15,7 +44,7 @@ async def get_system_llm_providers_secrets() -> Dict[str, Any]:
secrets = {}
for llm_provider in LMProvidersEnum:
secret_name = llm_provider.value
env_var = os.getenv(secret_name)
env_var = _get_system_env_secret(secret_name)
if env_var:
secrets[secret_name] = env_var

Expand Down Expand Up @@ -46,7 +75,7 @@ async def get_user_llm_providers_secrets(project_id: str) -> Dict[str, Any]:
for secret in secrets:
kind = secret["data"].get("kind")
provider_slug = kind.value if kind else ""
secret_name = f"{provider_slug.upper()}_API_KEY"
secret_name = _provider_slug_to_env_var(provider_slug)
if provider_slug:
provider = secret["data"].get("provider")
readable_secrets[secret_name] = provider.get("key") if provider else None
Expand Down
1 change: 0 additions & 1 deletion api/oss/src/models/api/evaluation_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ class LLMRunRateLimit(BaseModel):
class LMProvidersEnum(str, Enum):
openai = "OPENAI_API_KEY"
mistral = "MISTRAL_API_KEY"
mistralai = "MISTRALAI_API_KEY"
cohere = "COHERE_API_KEY"
anthropic = "ANTHROPIC_API_KEY"
anyscale = "ANYSCALE_API_KEY"
Expand Down
1 change: 0 additions & 1 deletion api/oss/tests/legacy/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ def sample_testset_endpoint_json():
API_KEYS_MAPPING = {
"OPENAI_API_KEY": "openai",
"MISTRAL_API_KEY": "mistral",
"MISTRALAI_API_KEY": "mistralai",
"COHERE_API_KEY": "cohere",
"ANTHROPIC_API_KEY": "anthropic",
"ANYSCALE_API_KEY": "anyscale",
Expand Down
60 changes: 60 additions & 0 deletions api/oss/tests/pytest/unit/secrets/test_dtos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import pytest
from pydantic import ValidationError

from oss.src.core.secrets.dtos import CreateSecretDTO, UpdateSecretDTO


def test_create_secret_normalizes_mistralai_standard_provider_payload():
payload = {
"header": {"name": "Mistral AI", "description": ""},
"secret": {
"kind": "provider_key",
"data": {
"kind": "mistralai",
"provider": {
"key": "TEST_KEY",
},
},
},
}

secret = CreateSecretDTO.model_validate(payload)

assert secret.secret.data.kind == "mistral"
assert secret.secret.data.provider.key == "TEST_KEY"


def test_update_secret_normalizes_mistralai_standard_provider_payload():
payload = {
"secret": {
"kind": "provider_key",
"data": {
"kind": "mistralai",
"provider": {
"key": "TEST_KEY",
},
},
},
}

secret = UpdateSecretDTO.model_validate(payload)

assert secret.secret.data.kind == "mistral"
assert secret.secret.data.provider.key == "TEST_KEY"


def test_create_secret_rejects_missing_standard_provider_kind():
payload = {
"header": {"name": "Mistral AI", "description": ""},
"secret": {
"kind": "provider_key",
"data": {
"provider": {
"key": "TEST_KEY",
},
},
},
}

with pytest.raises(ValidationError, match="StandardProviderKind"):
CreateSecretDTO.model_validate(payload)
64 changes: 64 additions & 0 deletions api/oss/tests/pytest/unit/secrets/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from types import SimpleNamespace

import pytest

from oss.src.core.secrets.enums import StandardProviderKind
from oss.src.core.secrets.utils import (
get_system_llm_providers_secrets,
get_user_llm_providers_secrets,
)


class _FakeVaultService:
def __init__(self, *_args, **_kwargs):
pass

async def list_secrets(self, project_id):
del project_id
return [
SimpleNamespace(
kind="provider_key",
model_dump=lambda include=None: {
"data": {
"kind": StandardProviderKind.MISTRALAI,
"provider": {"key": "mistral-key"},
}
},
),
SimpleNamespace(
kind="provider_key",
model_dump=lambda include=None: {
"data": {
"kind": StandardProviderKind.TOGETHERAI,
"provider": {"key": "together-key"},
}
},
),
]


@pytest.mark.asyncio
async def test_get_user_llm_providers_secrets_normalizes_legacy_provider_slugs(
monkeypatch,
):
monkeypatch.setattr("oss.src.core.secrets.utils.VaultService", _FakeVaultService)

secrets = await get_user_llm_providers_secrets(
"00000000-0000-0000-0000-000000000000"
)

assert secrets["MISTRAL_API_KEY"] == "mistral-key"
assert "MISTRALAI_API_KEY" not in secrets
assert secrets["TOGETHERAI_API_KEY"] == "together-key"
assert "TOGETHER_AI_API_KEY" not in secrets


@pytest.mark.asyncio
async def test_get_system_llm_providers_secrets_reads_legacy_mistralai_env(monkeypatch):
monkeypatch.delenv("MISTRAL_API_KEY", raising=False)
monkeypatch.setenv("MISTRALAI_API_KEY", "legacy-mistral-key")

secrets = await get_system_llm_providers_secrets()

assert secrets["MISTRAL_API_KEY"] == "legacy-mistral-key"
assert "MISTRALAI_API_KEY" not in secrets
2 changes: 1 addition & 1 deletion api/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "api"
version = "0.94.3"
version = "0.94.4"
description = "Agenta API"
authors = [
{ name = "Mahmoud Mabrouk", email = "mahmoud@agenta.ai" },
Expand Down
Loading
Loading