Skip to content

Commit

Permalink
Merge branch 'main' into enhancement/n+1_query-resolve
Browse files Browse the repository at this point in the history
  • Loading branch information
aybruhm committed Aug 23, 2024
2 parents 377f773 + 8efcea1 commit fe4424c
Show file tree
Hide file tree
Showing 398 changed files with 77,110 additions and 884 deletions.
11 changes: 11 additions & 0 deletions agenta-backend/agenta_backend/models/api/api_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ class CreateAppOutput(BaseModel):
app_name: str


class UpdateApp(BaseModel):
app_name: str


class UpdateAppOutput(CreateAppOutput):
pass


class AppOutput(CreateAppOutput):
pass

Expand Down Expand Up @@ -99,6 +107,9 @@ class AppVariantResponse(BaseModel):
config_name: str
uri: Optional[str]
revision: int
created_at: Optional[str] = None
updated_at: Optional[str] = None
modified_by_id: Optional[str] = None


class AppVariantRevision(BaseModel):
Expand Down
3 changes: 2 additions & 1 deletion agenta-backend/agenta_backend/models/api/user_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ class TimestampModel(BaseModel):
class User(TimestampModel):
id: Optional[str] = None
uid: str
username: str
email: str
username: str
profile_picture: Optional[str] = None
organizations: Optional[List[str]] = None


Expand Down
3 changes: 3 additions & 0 deletions agenta-backend/agenta_backend/models/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,9 @@ async def app_variant_db_to_output(app_variant_db: AppVariantDB) -> AppVariantRe
config_name=app_variant_db.config_name, # type: ignore
uri=uri, # type: ignore
revision=app_variant_db.revision, # type: ignore
created_at=str(app_variant_db.updated_at),
updated_at=str(app_variant_db.created_at),
modified_by_id=str(app_variant_db.modified_by_id),
)

if isCloudEE():
Expand Down
47 changes: 47 additions & 0 deletions agenta-backend/agenta_backend/routers/app_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
)
from agenta_backend.models.api.api_models import (
App,
UpdateApp,
UpdateAppOutput,
CreateAppOutput,
AddVariantFromImagePayload,
)
Expand Down Expand Up @@ -284,6 +286,51 @@ async def create_app(
raise HTTPException(status_code=500, detail=str(e))


@router.patch("/{app_id}/", response_model=UpdateAppOutput, operation_id="update_app")
async def update_app(
app_id: str,
payload: UpdateApp,
request: Request,
) -> UpdateAppOutput:
"""
Update an app for a user or organization.
Args:
app_id (str): The ID of the app.
payload (UpdateApp): The payload containing the app name.
stoken_session (SessionContainer): The session container containing the user's session token.
Returns:
UpdateAppOuput: The output containing the newly created app's ID and name.
Raises:
HTTPException: If there is an error creating the app or the user does not have permission to access the app.
"""

try:
app = await db_manager.fetch_app_by_id(app_id)
if isCloudEE():
has_permission = await check_action_access(
user_uid=request.state.user_id,
object=app,
permission=Permission.UPDATE_APPLICATION,
)
logger.debug(f"User has Permission to update app: {has_permission}")
if not has_permission:
error_msg = f"You do not have access to perform this action. Please contact your organization admin."
return JSONResponse(
{"detail": error_msg},
status_code=403,
)
await db_manager.update_app(
app_id=app_id, values_to_update=payload.model_dump()
)
return UpdateAppOutput(app_id=app_id, app_name=payload.app_name)
except Exception as e:
logger.exception(f"An error occurred: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))


@router.get("/", response_model=List[App], operation_id="list_apps")
async def list_apps(
request: Request,
Expand Down
23 changes: 15 additions & 8 deletions agenta-backend/agenta_backend/routers/user_profile.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
import os
from typing import Optional

from fastapi import HTTPException, Request

from agenta_backend.services import db_manager
from agenta_backend.utils.common import APIRouter
from agenta_backend.models.api.user_models import User

router = APIRouter()


@router.get("/", operation_id="user_profile")
async def user_profile(
request: Request,
):
@router.get("/", operation_id="fetch_user_profile")
async def user_profile(request: Request, user_id: Optional[str] = None):
try:
user = await db_manager.get_user(request.state.user_id)
if user_id is not None:
user = await db_manager.get_user_with_id(user_id=user_id)
else:
user = await db_manager.get_user(request.state.user_id)

assert (
user is not None
), "User not found. Please ensure that the user_id is specified correctly."
return User(
id=str(user.id),
uid=str(user.uid),
username=str(user.username),
email=str(user.email),
username=str(user.username),
created_at=str(user.created_at),
updated_at=str(user.updated_at),
).dict(exclude_unset=True)
).model_dump(exclude_unset=True)

except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
34 changes: 32 additions & 2 deletions agenta-backend/agenta_backend/services/db_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,27 @@ async def create_app_and_envs(
return app


async def update_app(app_id: str, values_to_update: dict) -> None:
"""Update the app in the database.
Arguments:
app_id (str): The app id
values_to_update (dict): The values to update in the app
"""

async with db_engine.get_session() as session:
result = await session.execute(select(AppDB).filter_by(id=uuid.UUID(app_id)))
app = result.scalars().first()
if not app:
raise NoResultFound(f"App with {app_id} not found")

for key, value in values_to_update.items():
if hasattr(app, key):
setattr(app, key, value)

await session.commit()


async def get_deployment_by_id(
deployment_id: str,
) -> DeploymentDB:
Expand Down Expand Up @@ -832,7 +853,7 @@ async def get_user_with_id(user_id: str):
user = result.scalars().first()
if user is None:
logger.error("Failed to get user with id")
raise Exception("Error while getting user")
raise NoResultFound(f"User with id {user_id} not found")
return user


Expand Down Expand Up @@ -2580,9 +2601,18 @@ async def update_app_variant(
if hasattr(app_variant, key):
setattr(app_variant, key, value)

relationships_to_load_in_session = [
"user",
"app",
"image",
"base",
]
if isCloudEE():
relationships_to_load_in_session.append("organization")

await session.commit()
await session.refresh(
app_variant, attribute_names=["user", "app", "image", "base"]
app_variant, attribute_names=relationships_to_load_in_session
)

return app_variant
Expand Down
5 changes: 5 additions & 0 deletions agenta-backend/agenta_backend/services/deployment_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,15 @@ async def validate_image(image: Image) -> bool:
msg = "Image tags cannot be empty"
logger.error(msg)
raise ValueError(msg)

if isCloudEE():
image = Image(**image.model_dump(exclude={"workspace", "organization"}))

if not image.tags.startswith(agenta_registry_repo):
raise ValueError(
f"Image should have a tag starting with the registry name ({agenta_registry_repo})\n Image Tags: {image.tags}"
)

if image not in docker_utils.list_images():
raise DockerException(
f"Image {image.docker_id} with tags {image.tags} not found"
Expand Down
18 changes: 17 additions & 1 deletion agenta-backend/agenta_backend/services/evaluators_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -621,9 +621,25 @@ def auto_json_diff(
lm_providers_keys: Dict[str, Any], # pylint: disable=unused-argument
) -> Result:
try:
output = output.get("data", "") if isinstance(output, dict) else output

if isinstance(output, dict):
output = json.dumps(output)
elif isinstance(output, str):
try:
json.loads(output)
except:
raise Exception(
f"Evaluator 'auto_json_diff' requires string outputs to be JSON strings."
)
else:
raise Exception(
f"Evaluator 'auto_json_diff' requires the output to be either a JSON string or a JSON object, but received {type(output).__name__} instead."
)

correct_answer = get_correct_answer(data_point, settings_values)
average_score = compare_jsons(
ground_truth=correct_answer,
ground_truth=json.loads(correct_answer),
app_output=json.loads(output),
settings_values=settings_values,
)
Expand Down
21 changes: 3 additions & 18 deletions agenta-backend/agenta_backend/tests/unit/test_evaluators.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,7 @@ def test_auto_contains_json(output, expected):
[
(
{
"correct_answer": {
"user": {
"name": "John",
"details": {"age": 30, "location": "New York"},
}
}
"correct_answer": '{"user": {"name": "John", "details": {"age": 30, "location": "New York"}}}'
},
'{"user": {"name": "John", "details": {"age": 30, "location": "New York"}}}',
{
Expand All @@ -205,12 +200,7 @@ def test_auto_contains_json(output, expected):
),
(
{
"correct_answer": {
"user": {
"name": "John",
"details": {"age": 30, "location": "New York"},
}
}
"correct_answer": '{"user": {"name": "John", "details": {"age": "30", "location": "New York"}}}'
},
'{"user": {"name": "John", "details": {"age": "30", "location": "New York"}}}',
{
Expand All @@ -224,12 +214,7 @@ def test_auto_contains_json(output, expected):
),
(
{
"correct_answer": {
"user": {
"name": "John",
"details": {"age": 30, "location": "New York"},
}
}
"correct_answer": '{"user": {"name": "John", "details": {"age": 30, "location": "New York"}}}'
},
'{"USER": {"NAME": "John", "DETAILS": {"AGE": 30, "LOCATION": "New York"}}}',
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,57 @@ async def test_create_app():
assert response.json()["app_name"] == "app_variant_test"


@pytest.mark.asyncio
async def test_create_app_for_renaming():
response = await test_client.post(
f"{BACKEND_API_HOST}/apps/",
json={
"app_name": "app_test",
},
timeout=timeout,
)
assert response.status_code == 200
assert response.json()["app_name"] == "app_test"


@pytest.mark.asyncio
async def test_update_app():
async with db_engine.get_session() as session:
result = await session.execute(select(AppDB).filter_by(app_name="app_test"))
app = result.scalars().first()

response = await test_client.patch(
f"{BACKEND_API_HOST}/apps/{str(app.id)}/",
json={
"app_name": "test_app",
},
timeout=timeout,
)
assert response.status_code == 200
assert response.json()["app_id"] == str(app.id)
assert response.json()["app_name"] == "test_app"


@pytest.mark.asyncio
async def test_update_app_does_not_exist():
APP_ID_NOT_FOUND = "5da3bfe7-bc4b-4713-928e-48275126a1c2"
response = await test_client.patch(
f"{BACKEND_API_HOST}/apps/{APP_ID_NOT_FOUND}/",
json={
"app_name": "test_app",
},
timeout=timeout,
)
assert response.status_code == 500
assert response.json()["detail"] == f"App with {APP_ID_NOT_FOUND} not found"


@pytest.mark.asyncio
async def test_list_apps():
response = await test_client.get(f"{BACKEND_API_HOST}/apps/")

assert response.status_code == 200
assert len(response.json()) == 1
assert len(response.json()) == 2


@pytest.mark.asyncio
Expand Down
Loading

0 comments on commit fe4424c

Please sign in to comment.