Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Stateless Service #2344

Merged
merged 16 commits into from
Dec 26, 2024
Merged
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
16 changes: 13 additions & 3 deletions agenta-backend/agenta_backend/routers/permissions_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ async def verify_permissions(
if isOss():
return Allow(None)

if not action or not resource_type or not resource_id:
if not action or not resource_type:
raise Deny()

if isCloudEE():
Expand All @@ -70,8 +70,8 @@ async def verify_permissions(
# CHECK PERMISSION 2/2: RESOURCE
allow_resource = await check_resource_access(
project_id=UUID(request.state.project_id),
resource_id=resource_id,
resource_type=resource_type,
resource_id=resource_id,
)

if not allow_resource:
Expand All @@ -80,13 +80,14 @@ async def verify_permissions(
return Allow(request.state.credentials)

except Exception as exc: # pylint: disable=bare-except
print(exc)
raise Deny() from exc


async def check_resource_access(
project_id: UUID,
resource_id: UUID,
resource_type: str,
resource_id: Optional[UUID] = None,
) -> bool:
resource_project_id = None

Expand All @@ -95,6 +96,15 @@ async def check_resource_access(

resource_project_id = app.project_id

if resource_type == "service":
if resource_id is None:
resource_project_id = project_id

else:
base = await db_manager.fetch_base_by_id(base_id=str(resource_id))

resource_project_id = base.project_id

allow_resource = resource_project_id == project_id

return allow_resource
1 change: 1 addition & 0 deletions agenta-cli/agenta/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from .sdk.utils.costs import calculate_token_usage
from .sdk.client import Agenta
from .sdk.litellm import litellm as callbacks
from .sdk.managers.secrets import SecretsManager
from .sdk.managers.config import ConfigManager
from .sdk.managers.variant import VariantManager
from .sdk.managers.deployment import DeploymentManager
Expand Down
2 changes: 1 addition & 1 deletion agenta-cli/agenta/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,5 +559,5 @@ def run_evaluation(app_name: str, host: str, api_key: str = None) -> str:
raise APIRequestError(
f"Request to run evaluations failed with status code {response.status_code} and error message: {error_message}."
)
print(response.json())

return response.json()
1 change: 1 addition & 0 deletions agenta-cli/agenta/sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from .decorators.routing import entrypoint, app, route
from .agenta_init import Config, AgentaSingleton, init as _init
from .utils.costs import calculate_token_usage
from .managers.secrets import SecretsManager
from .managers.config import ConfigManager
from .managers.variant import VariantManager
from .managers.deployment import DeploymentManager
Expand Down
165 changes: 47 additions & 118 deletions agenta-cli/agenta/sdk/agenta_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
from agenta.sdk.utils.logging import log
from agenta.sdk.utils.globals import set_global
from agenta.client.backend.client import AgentaApi, AsyncAgentaApi

from agenta.sdk.tracing import Tracing
from agenta.client.exceptions import APIRequestError
from agenta.sdk.context.routing import routing_context


class AgentaSingleton:
Expand Down Expand Up @@ -59,9 +60,7 @@ def init(
ValueError: If `app_id` is not specified either as an argument, in the config file, or in the environment variables.
"""

log.info("---------------------------")
log.info("Agenta SDK - using version: %s", version("agenta"))
log.info("---------------------------")
log.info("Agenta - SDK version: %s", version("agenta"))

config = {}
if config_fname:
Expand All @@ -86,6 +85,13 @@ def init(

self.api_key = api_key or getenv("AGENTA_API_KEY") or config.get("api_key")

self.base_id = getenv("AGENTA_BASE_ID")

self.service_id = getenv("AGENTA_SERVICE_ID") or self.base_id

log.info("Agenta - Service ID: %s", self.service_id)
log.info("Agenta - Application ID: %s", self.app_id)

self.tracing = Tracing(
url=f"{self.host}/api/observability/v1/otlp/traces", # type: ignore
redact=redact,
Expand All @@ -94,6 +100,7 @@ def init(

self.tracing.configure(
api_key=self.api_key,
service_id=self.service_id,
# DEPRECATING
app_id=self.app_id,
)
Expand All @@ -108,8 +115,6 @@ def init(
api_key=self.api_key if self.api_key else "",
)

self.base_id = getenv("AGENTA_BASE_ID")

self.config = Config(
host=self.host,
base_id=self.base_id,
Expand All @@ -120,28 +125,43 @@ def init(
class Config:
def __init__(
self,
host: str,
# LEGACY
host: Optional[str] = None,
base_id: Optional[str] = None,
api_key: Optional[str] = "",
api_key: Optional[str] = None,
# LEGACY
**kwargs,
):
self.host = host
self.default_parameters = {**kwargs}

def set_default(self, **kwargs):
self.default_parameters.update(kwargs)

def get_default(self):
return self.default_parameters

def __getattr__(self, key):
context = routing_context.get()

parameters = context.parameters

if not parameters:
return None

if key in parameters:
value = parameters[key]

if isinstance(value, dict):
nested_config = Config()
nested_config.set_default(**value)

self.base_id = base_id
return nested_config

if self.base_id is None:
# print(
# "Warning: Your configuration will not be saved permanently since base_id is not provided.\n"
# )
pass
return value

if base_id is None or host is None:
self.persist = False
else:
self.persist = True
self.client = AgentaApi(
base_url=self.host + "/api",
api_key=api_key if api_key else "",
)
return None

### --- LEGACY --- ###

def register_default(self, overwrite=False, **kwargs):
"""alias for default"""
Expand All @@ -153,104 +173,13 @@ def default(self, overwrite=False, **kwargs):
overwrite: Whether to overwrite the existing configuration or not
**kwargs: A dict containing the parameters
"""
self.set(
**kwargs
) # In case there is no connectivity, we still can use the default values
try:
self.push(config_name="default", overwrite=overwrite, **kwargs)
except Exception as ex:
log.warning(
"Unable to push the default configuration to the server. %s", str(ex)
)

def push(self, config_name: str, overwrite=True, **kwargs):
"""Pushes the parameters for the app variant to the server
Args:
config_name: Name of the configuration to push to
overwrite: Whether to overwrite the existing configuration or not
**kwargs: A dict containing the parameters
"""
if not self.persist:
return
try:
self.client.configs.save_config(
base_id=self.base_id,
config_name=config_name,
parameters=kwargs,
overwrite=overwrite,
)
except Exception as ex:
log.warning(
"Failed to push the configuration to the server with error: %s", ex
)

def pull(
self, config_name: str = "default", environment_name: Optional[str] = None
):
"""Pulls the parameters for the app variant from the server and sets them to the config"""
if not self.persist and (
config_name != "default" or environment_name is not None
):
raise ValueError(
"Cannot pull the configuration from the server since the app_name and base_name are not provided."
)
if self.persist:
try:
if environment_name:
config = self.client.configs.get_config(
base_id=self.base_id, environment_name=environment_name
)

else:
config = self.client.configs.get_config(
base_id=self.base_id,
config_name=config_name,
)
except Exception as ex:
log.warning(
"Failed to pull the configuration from the server with error: %s",
str(ex),
)
try:
self.set(**{"current_version": config.current_version, **config.parameters})
except Exception as ex:
log.warning("Failed to set the configuration with error: %s", str(ex))
self.set(**kwargs)

def all(self):
"""Returns all the parameters for the app variant"""
return {
k: v
for k, v in self.__dict__.items()
if k
not in [
"app_name",
"base_name",
"host",
"base_id",
"api_key",
"persist",
"client",
]
}

# function to set the parameters for the app variant
def set(self, **kwargs):
"""Sets the parameters for the app variant

Args:
**kwargs: A dict containing the parameters
"""
for key, value in kwargs.items():
setattr(self, key, value)

def dump(self):
"""Returns all the information about the current version in the configuration.
self.set_default(**kwargs)

Raises:
NotImplementedError: _description_
"""

raise NotImplementedError()
def all(self):
return self.default_parameters


def init(
Expand Down
Loading
Loading