Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def unmarshall_attributes(
marshalled: Dict[str, Any],
) -> Dict[str, Any]:
"""
Unmarshals a dictionary of marshalled attributes into a nested dictionary
Unmarshalls a dictionary of marshalled attributes into a nested dictionary

Example:
marshalled = {
Expand Down
58 changes: 25 additions & 33 deletions api/oss/src/core/tracing/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def unmarshall_attributes(
marshalled: OTelAttributes,
) -> OTelAttributes:
"""
Unmarshals a dictionary of marshalled attributes into a nested dictionary
Unmarshalls a dictionary of marshalled attributes into a nested dictionary

Example:
marshalled = {
Expand Down Expand Up @@ -89,42 +89,34 @@ def unmarshall_attributes(

for key, value in marshalled.items():
keys = key.split(".")
current = unmarshalled

level = unmarshalled

for i, part in enumerate(keys[:-1]):
if part.isdigit():
part = int(part)

if not isinstance(level, list):
level = []

while len(level) <= part:
level.append({})

level = level[part]
for i, key in enumerate(keys):
is_last = i == len(keys) - 1
next_key = keys[i + 1] if not is_last else None
is_index = key.isdigit()
key = int(key) if is_index else key

if is_last:
if isinstance(current, list) and isinstance(key, int):
while len(current) <= key:
current.append(None)
current[key] = value
elif isinstance(current, dict):
current[key] = value
else:
if part not in level:
level[part] = {} if not keys[i + 1].isdigit() else []

level = level[part]

last_key = keys[-1]

if last_key.isdigit():
last_key = int(last_key)

if not isinstance(level, list):
level = []

while len(level) <= last_key:
level.append(None)

level[last_key] = value
next_is_index = next_key.isdigit() if next_key else False

else:
level[last_key] = value
if isinstance(current, list) and isinstance(key, int):
while len(current) <= key:
current.append([] if next_is_index else {})
if current[key] is None:
current[key] = [] if next_is_index else {}
current = current[key]
elif isinstance(current, dict):
if key not in current:
current[key] = [] if next_is_index else {}
current = current[key]

return unmarshalled

Expand Down
4 changes: 4 additions & 0 deletions api/oss/src/models/api/api_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ class UpdateAppOutput(CreateAppOutput):
pass


class ReadAppOutput(CreateAppOutput):
pass


class AppOutput(CreateAppOutput):
pass

Expand Down
44 changes: 43 additions & 1 deletion api/oss/src/routers/app_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
from oss.src.models.api.api_models import (
App,
UpdateApp,
UpdateAppOutput,
CreateAppOutput,
ReadAppOutput,
UpdateAppOutput,
AddVariantFromURLPayload,
AddVariantFromKeyPayload,
)
Expand Down Expand Up @@ -260,6 +261,47 @@ async def create_app(
return CreateAppOutput(app_id=str(app_db.id), app_name=str(app_db.app_name))


@router.get("/{app_id}/", response_model=ReadAppOutput, operation_id="create_app")
async def read_app(
request: Request,
app_id: str,
) -> ReadAppOutput:
"""
Retrieve an app by its ID.

Args:
app_id (str): The ID of the app to retrieve.

Returns:
ReadAppOutput: The output containing the app's ID and name.

Raises:
HTTPException: If there is an error retrieving the app or the user does not have permission to access the app.
"""

try:
app = await db_manager.fetch_app_by_id(app_id)
except db_manager.NoResultFound:
raise HTTPException(
status_code=404, detail=f"No application with ID '{app_id}' found"
)

if is_ee():
has_permission = await check_action_access(
user_uid=request.state.user_id,
project_id=str(app.project_id),
permission=Permission.VIEW_APPLICATIONS,
)
if not has_permission:
error_msg = "You do not have access to perform this action. Please contact your organization admin."
return JSONResponse(
{"detail": error_msg},
status_code=403,
)

return ReadAppOutput(app_id=str(app.id), app_name=str(app.app_name))


@router.patch("/{app_id}/", response_model=UpdateAppOutput, operation_id="update_app")
async def update_app(
app_id: str,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

openai = OpenAI()


OpenAIInstrumentor().instrument()


Expand Down
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.56.2"
version = "0.56.3"
description = "Agenta API"
authors = [
{ name = "Mahmoud Mabrouk", email = "[email protected]" },
Expand Down
36 changes: 27 additions & 9 deletions sdk/agenta/sdk/middleware/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,18 @@ async def _get_config(self, request: Request) -> Optional[Tuple[Dict, Dict]]:

return parameters, references

config = {}
config = dict()
app_ref = None

is_test_path = request.url.path.endswith("/test")
are_refs_missing = not variant_ref and not environment_ref
should_fetch = not is_test_path or not are_refs_missing
should_fetch_application_revision = not is_test_path or not are_refs_missing
is_app_ref_incomplete = (
application_ref and application_ref.id and not application_ref.slug
)
should_fetch_application = is_app_ref_incomplete

if should_fetch:
if should_fetch_application_revision:
async with httpx.AsyncClient() as client:
response = await client.post(
f"{self.host}/api/variants/configs/fetch",
Expand All @@ -110,13 +115,26 @@ async def _get_config(self, request: Request) -> Optional[Tuple[Dict, Dict]]:
if response.status_code == 200:
config = response.json()

if not config:
config["application_ref"] = refs[
"application_ref"
] # by default, application_ref will always have an id
parameters = None
else:
elif should_fetch_application:
async with httpx.AsyncClient() as client:
response = await client.get(
f"{self.host}/api/apps/{application_ref.id}",
headers=headers,
)

if response.status_code == 200:
app = response.json()
app_ref = {
"id": app.get("app_id"),
"slug": app.get("app_name"),
}

if config:
parameters = config.get("params")
else:
# by default, application_ref will always have an id
config["application_ref"] = app_ref or refs["application_ref"]
parameters = None

references = {}

Expand Down
8 changes: 4 additions & 4 deletions sdk/agenta/sdk/tracing/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Attribute = Union[Primitive, PrimitivesSequence]


def _marshal(
def _marshall(
unmarshalled: Dict[str, Any],
*,
parent_key: Optional[str] = "",
Expand Down Expand Up @@ -59,7 +59,7 @@ def _marshal(
dict_key = child_key

marshalled.update(
_marshal(
_marshall(
value,
parent_key=dict_key,
depth=depth + 1,
Expand All @@ -76,7 +76,7 @@ def _marshal(

if isinstance(item, (dict, list)):
marshalled.update(
_marshal(
_marshall(
item,
parent_key=list_key,
depth=depth + 1,
Expand Down Expand Up @@ -177,7 +177,7 @@ def serialize(
k: v
for k, v in {
_encode_key(namespace, key): _encode_value(value)
for key, value in _marshal(attributes, max_depth=max_depth).items()
for key, value in _marshall(attributes, max_depth=max_depth).items()
}.items()
if v is not None
}
Expand Down
Loading