diff --git a/agenta-backend/agenta_backend/services/app_manager.py b/agenta-backend/agenta_backend/services/app_manager.py index f88e474b8a..ed7eacc63f 100644 --- a/agenta-backend/agenta_backend/services/app_manager.py +++ b/agenta-backend/agenta_backend/services/app_manager.py @@ -101,6 +101,7 @@ async def start_variant( "AGENTA_BASE_ID": str(db_app_variant.base_id), "AGENTA_APP_ID": str(db_app_variant.app_id), "AGENTA_HOST": domain_name, + "AGENTA_RUNTIME": "true", } ) if isCloudEE(): diff --git a/agenta-backend/agenta_backend/tests/conftest.py b/agenta-backend/agenta_backend/tests/conftest.py index 36866f2d45..22f08588aa 100644 --- a/agenta-backend/agenta_backend/tests/conftest.py +++ b/agenta-backend/agenta_backend/tests/conftest.py @@ -1,7 +1,10 @@ import pytest import asyncio -from agenta_backend.tests.engine import test_db_engine as db_engine +from agenta_backend.utils.common import isOss + +if isOss(): + from agenta_backend.tests.engine import test_db_engine as db_engine @pytest.fixture(scope="session", autouse=True) @@ -17,6 +20,8 @@ def event_loop(): yield res - res.run_until_complete(db_engine.remove_db()) # drop database - res.run_until_complete(db_engine.close_db()) # close connections to database + if isOss(): + res.run_until_complete(db_engine.remove_db()) # drop database + res.run_until_complete(db_engine.close_db()) # close connections to database + res._close() # close event loop # type: ignore diff --git a/agenta-cli/agenta/client/backend/__init__.py b/agenta-cli/agenta/client/backend/__init__.py index 8907b11d29..ad2226148b 100644 --- a/agenta-cli/agenta/client/backend/__init__.py +++ b/agenta-cli/agenta/client/backend/__init__.py @@ -10,11 +10,13 @@ AgentaTreesResponse, AggregatedResult, AggregatedResultEvaluatorConfig, + AnalyticsResponse, App, AppVariantResponse, AppVariantRevision, BaseOutput, BodyImportTestset, + BucketDto, CollectStatusResponse, ConfigDb, ConfigDto, @@ -42,6 +44,7 @@ EvaluatorOutputInterface, ExceptionDto, GetConfigResponse, + HeaderDto, HttpValidationError, HumanEvaluation, HumanEvaluationScenario, @@ -51,12 +54,14 @@ HumanEvaluationUpdate, Image, InviteRequest, + LegacyAnalyticsResponse, + LegacyDataPoint, LifecycleDto, LinkDto, ListApiKeysResponse, LlmRunRateLimit, LlmTokens, - LmProvidersEnum, + MetricsDto, NewHumanEvaluation, NewTestset, NodeDto, @@ -74,11 +79,17 @@ Outputs, ParentDto, Permission, + ProjectsResponse, + ProviderKeyDto, + ProviderKind, ReferenceDto, ReferenceRequestModel, Result, RootDto, Score, + SecretDto, + SecretKind, + SecretResponseDto, SimpleEvaluationOutput, Span, SpanDetail, @@ -111,6 +122,7 @@ ) from .errors import UnprocessableEntityError from . import ( + access_control, apps, bases, configs, @@ -120,12 +132,14 @@ evaluators, observability, observability_v_1, + scopes, testsets, variants, + vault, ) from .client import AgentaApi, AsyncAgentaApi from .containers import ContainerTemplatesResponse -from .observability_v_1 import Format, QueryTracesResponse +from .observability_v_1 import Format, QueryAnalyticsResponse, QueryTracesResponse from .variants import AddVariantFromBaseAndConfigResponse __all__ = [ @@ -140,12 +154,14 @@ "AgentaTreesResponse", "AggregatedResult", "AggregatedResultEvaluatorConfig", + "AnalyticsResponse", "App", "AppVariantResponse", "AppVariantRevision", "AsyncAgentaApi", "BaseOutput", "BodyImportTestset", + "BucketDto", "CollectStatusResponse", "ConfigDb", "ConfigDto", @@ -175,6 +191,7 @@ "ExceptionDto", "Format", "GetConfigResponse", + "HeaderDto", "HttpValidationError", "HumanEvaluation", "HumanEvaluationScenario", @@ -184,12 +201,14 @@ "HumanEvaluationUpdate", "Image", "InviteRequest", + "LegacyAnalyticsResponse", + "LegacyDataPoint", "LifecycleDto", "LinkDto", "ListApiKeysResponse", "LlmRunRateLimit", "LlmTokens", - "LmProvidersEnum", + "MetricsDto", "NewHumanEvaluation", "NewTestset", "NodeDto", @@ -207,12 +226,19 @@ "Outputs", "ParentDto", "Permission", + "ProjectsResponse", + "ProviderKeyDto", + "ProviderKind", + "QueryAnalyticsResponse", "QueryTracesResponse", "ReferenceDto", "ReferenceRequestModel", "Result", "RootDto", "Score", + "SecretDto", + "SecretKind", + "SecretResponseDto", "SimpleEvaluationOutput", "Span", "SpanDetail", @@ -243,6 +269,7 @@ "WorkspaceResponse", "WorkspaceRole", "WorkspaceRoleResponse", + "access_control", "apps", "bases", "configs", @@ -252,6 +279,8 @@ "evaluators", "observability", "observability_v_1", + "scopes", "testsets", "variants", + "vault", ] diff --git a/agenta-cli/agenta/client/backend/access_control/__init__.py b/agenta-cli/agenta/client/backend/access_control/__init__.py new file mode 100644 index 0000000000..67a41b2742 --- /dev/null +++ b/agenta-cli/agenta/client/backend/access_control/__init__.py @@ -0,0 +1 @@ +# This file was auto-generated by Fern from our API Definition. diff --git a/agenta-cli/agenta/client/backend/access_control/client.py b/agenta-cli/agenta/client/backend/access_control/client.py new file mode 100644 index 0000000000..0a23474e41 --- /dev/null +++ b/agenta-cli/agenta/client/backend/access_control/client.py @@ -0,0 +1,167 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.client_wrapper import SyncClientWrapper +import typing +from ..core.request_options import RequestOptions +from ..core.pydantic_utilities import parse_obj_as +from ..errors.unprocessable_entity_error import UnprocessableEntityError +from ..types.http_validation_error import HttpValidationError +from json.decoder import JSONDecodeError +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper + + +class AccessControlClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def verify_permissions( + self, + *, + action: typing.Optional[str] = None, + resource_type: typing.Optional[str] = None, + resource_id: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[typing.Any]: + """ + Parameters + ---------- + action : typing.Optional[str] + + resource_type : typing.Optional[str] + + resource_id : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[typing.Any] + Successful Response + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.access_control.verify_permissions() + """ + _response = self._client_wrapper.httpx_client.request( + "permissions/verify", + method="GET", + params={ + "action": action, + "resource_type": resource_type, + "resource_id": resource_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + +class AsyncAccessControlClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def verify_permissions( + self, + *, + action: typing.Optional[str] = None, + resource_type: typing.Optional[str] = None, + resource_id: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[typing.Any]: + """ + Parameters + ---------- + action : typing.Optional[str] + + resource_type : typing.Optional[str] + + resource_id : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[typing.Any] + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.access_control.verify_permissions() + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "permissions/verify", + method="GET", + params={ + "action": action, + "resource_type": resource_type, + "resource_id": resource_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) diff --git a/agenta-cli/agenta/client/backend/apps/client.py b/agenta-cli/agenta/client/backend/apps/client.py index b709a8538e..b85b34d6cf 100644 --- a/agenta-cli/agenta/client/backend/apps/client.py +++ b/agenta-cli/agenta/client/backend/apps/client.py @@ -216,7 +216,9 @@ def list_apps( _response = self._client_wrapper.httpx_client.request( "apps", method="GET", - params={"app_name": app_name}, + params={ + "app_name": app_name, + }, request_options=request_options, ) try: @@ -248,13 +250,15 @@ def create_app( *, app_name: str, project_id: typing.Optional[str] = OMIT, + workspace_id: typing.Optional[str] = OMIT, + organization_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CreateAppOutput: """ - Create a new app for a user. + Create a new app for a user or organization. Args: - payload (CreateApp): The payload containing the app name. + payload (CreateApp): The payload containing the app name and organization ID (optional). stoken_session (SessionContainer): The session container containing the user's session token. Returns: @@ -269,6 +273,10 @@ def create_app( project_id : typing.Optional[str] + workspace_id : typing.Optional[str] + + organization_id : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -295,6 +303,11 @@ def create_app( json={ "app_name": app_name, "project_id": project_id, + "workspace_id": workspace_id, + "organization_id": organization_id, + }, + headers={ + "content-type": "application/json", }, request_options=request_options, omit=OMIT, @@ -393,7 +406,7 @@ def update_app( request_options: typing.Optional[RequestOptions] = None, ) -> UpdateAppOutput: """ - Update an app for a user. + Update an app for a user or organization. Args: app_id (str): The ID of the app. @@ -439,6 +452,9 @@ def update_app( json={ "app_name": app_name, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -538,6 +554,9 @@ def add_variant_from_image( "base_name": base_name, "config_name": config_name, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -572,6 +591,8 @@ def create_app_and_variant_from_template( template_id: str, env_vars: typing.Dict[str, str], project_id: typing.Optional[str] = OMIT, + workspace_id: typing.Optional[str] = OMIT, + organization_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> AppVariantResponse: """ @@ -597,6 +618,10 @@ def create_app_and_variant_from_template( project_id : typing.Optional[str] + workspace_id : typing.Optional[str] + + organization_id : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -626,7 +651,12 @@ def create_app_and_variant_from_template( "app_name": app_name, "template_id": template_id, "project_id": project_id, + "workspace_id": workspace_id, "env_vars": env_vars, + "organization_id": organization_id, + }, + headers={ + "content-type": "application/json", }, request_options=request_options, omit=OMIT, @@ -752,7 +782,7 @@ def environment_revisions( base_url="https://yourhost.com/path/to/api", ) client.apps.environment_revisions( - app_id="string", + app_id="app_id", environment_name={"key": "value"}, ) """ @@ -1005,7 +1035,9 @@ async def main() -> None: _response = await self._client_wrapper.httpx_client.request( "apps", method="GET", - params={"app_name": app_name}, + params={ + "app_name": app_name, + }, request_options=request_options, ) try: @@ -1037,13 +1069,15 @@ async def create_app( *, app_name: str, project_id: typing.Optional[str] = OMIT, + workspace_id: typing.Optional[str] = OMIT, + organization_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CreateAppOutput: """ - Create a new app for a user. + Create a new app for a user or organization. Args: - payload (CreateApp): The payload containing the app name. + payload (CreateApp): The payload containing the app name and organization ID (optional). stoken_session (SessionContainer): The session container containing the user's session token. Returns: @@ -1058,6 +1092,10 @@ async def create_app( project_id : typing.Optional[str] + workspace_id : typing.Optional[str] + + organization_id : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1092,6 +1130,11 @@ async def main() -> None: json={ "app_name": app_name, "project_id": project_id, + "workspace_id": workspace_id, + "organization_id": organization_id, + }, + headers={ + "content-type": "application/json", }, request_options=request_options, omit=OMIT, @@ -1198,7 +1241,7 @@ async def update_app( request_options: typing.Optional[RequestOptions] = None, ) -> UpdateAppOutput: """ - Update an app for a user. + Update an app for a user or organization. Args: app_id (str): The ID of the app. @@ -1252,6 +1295,9 @@ async def main() -> None: json={ "app_name": app_name, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1359,6 +1405,9 @@ async def main() -> None: "base_name": base_name, "config_name": config_name, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1393,6 +1442,8 @@ async def create_app_and_variant_from_template( template_id: str, env_vars: typing.Dict[str, str], project_id: typing.Optional[str] = OMIT, + workspace_id: typing.Optional[str] = OMIT, + organization_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> AppVariantResponse: """ @@ -1418,6 +1469,10 @@ async def create_app_and_variant_from_template( project_id : typing.Optional[str] + workspace_id : typing.Optional[str] + + organization_id : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1455,7 +1510,12 @@ async def main() -> None: "app_name": app_name, "template_id": template_id, "project_id": project_id, + "workspace_id": workspace_id, "env_vars": env_vars, + "organization_id": organization_id, + }, + headers={ + "content-type": "application/json", }, request_options=request_options, omit=OMIT, @@ -1594,7 +1654,7 @@ async def environment_revisions( async def main() -> None: await client.apps.environment_revisions( - app_id="string", + app_id="app_id", environment_name={"key": "value"}, ) diff --git a/agenta-cli/agenta/client/backend/client.py b/agenta-cli/agenta/client/backend/client.py index 9fc9784d8a..2f022a7f4b 100644 --- a/agenta-cli/agenta/client/backend/client.py +++ b/agenta-cli/agenta/client/backend/client.py @@ -4,6 +4,9 @@ import httpx from .core.client_wrapper import SyncClientWrapper from .observability.client import ObservabilityClient +from .vault.client import VaultClient +from .access_control.client import AccessControlClient +from .scopes.client import ScopesClient from .apps.client import AppsClient from .variants.client import VariantsClient from .evaluations.client import EvaluationsClient @@ -19,9 +22,9 @@ from .core.pydantic_utilities import parse_obj_as from json.decoder import JSONDecodeError from .core.api_error import ApiError +from .core.jsonable_encoder import jsonable_encoder from .errors.unprocessable_entity_error import UnprocessableEntityError from .types.http_validation_error import HttpValidationError -from .core.jsonable_encoder import jsonable_encoder from .types.organization import Organization from .types.organization_output import OrganizationOutput from .types.invite_request import InviteRequest @@ -32,6 +35,9 @@ from .types.permission import Permission from .core.client_wrapper import AsyncClientWrapper from .observability.client import AsyncObservabilityClient +from .vault.client import AsyncVaultClient +from .access_control.client import AsyncAccessControlClient +from .scopes.client import AsyncScopesClient from .apps.client import AsyncAppsClient from .variants.client import AsyncVariantsClient from .evaluations.client import AsyncEvaluationsClient @@ -101,6 +107,9 @@ def __init__( timeout=_defaulted_timeout, ) self.observability = ObservabilityClient(client_wrapper=self._client_wrapper) + self.vault = VaultClient(client_wrapper=self._client_wrapper) + self.access_control = AccessControlClient(client_wrapper=self._client_wrapper) + self.scopes = ScopesClient(client_wrapper=self._client_wrapper) self.apps = AppsClient(client_wrapper=self._client_wrapper) self.variants = VariantsClient(client_wrapper=self._client_wrapper) self.evaluations = EvaluationsClient(client_wrapper=self._client_wrapper) @@ -166,10 +175,7 @@ def list_api_keys( raise ApiError(status_code=_response.status_code, body=_response_json) def create_api_key( - self, - *, - workspace_id: str, - request_options: typing.Optional[RequestOptions] = None, + self, *, request_options: typing.Optional[RequestOptions] = None ) -> str: """ Creates an API key for a user. @@ -182,8 +188,6 @@ def create_api_key( Parameters ---------- - workspace_id : str - request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -200,16 +204,11 @@ def create_api_key( api_key="YOUR_API_KEY", base_url="https://yourhost.com/path/to/api", ) - client.create_api_key( - workspace_id="workspace_id", - ) + client.create_api_key() """ _response = self._client_wrapper.httpx_client.request( "keys", method="POST", - params={ - "workspace_id": workspace_id, - }, request_options=request_options, ) try: @@ -221,16 +220,6 @@ def create_api_key( object_=_response.json(), ), ) - if _response.status_code == 422: - raise UnprocessableEntityError( - typing.cast( - HttpValidationError, - parse_obj_as( - type_=HttpValidationError, # type: ignore - object_=_response.json(), - ), - ) - ) _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) @@ -476,6 +465,9 @@ def create_organization( "description": description, "type": type, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -660,6 +652,9 @@ def update_organization( "description": description, "updated_at": updated_at, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -821,6 +816,9 @@ def resend_invitation( json={ "email": email, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -910,6 +908,9 @@ def accept_invitation( json={ "token": token, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -986,6 +987,9 @@ def create_workspace( "description": description, "type": type, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1065,6 +1069,9 @@ def update_workspace( "description": description, "updated_at": updated_at, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1262,6 +1269,9 @@ def assign_role_to_user( "organization_id": organization_id, "role": role, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1632,6 +1642,11 @@ def __init__( self.observability = AsyncObservabilityClient( client_wrapper=self._client_wrapper ) + self.vault = AsyncVaultClient(client_wrapper=self._client_wrapper) + self.access_control = AsyncAccessControlClient( + client_wrapper=self._client_wrapper + ) + self.scopes = AsyncScopesClient(client_wrapper=self._client_wrapper) self.apps = AsyncAppsClient(client_wrapper=self._client_wrapper) self.variants = AsyncVariantsClient(client_wrapper=self._client_wrapper) self.evaluations = AsyncEvaluationsClient(client_wrapper=self._client_wrapper) @@ -1705,10 +1720,7 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response_json) async def create_api_key( - self, - *, - workspace_id: str, - request_options: typing.Optional[RequestOptions] = None, + self, *, request_options: typing.Optional[RequestOptions] = None ) -> str: """ Creates an API key for a user. @@ -1721,8 +1733,6 @@ async def create_api_key( Parameters ---------- - workspace_id : str - request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1744,9 +1754,7 @@ async def create_api_key( async def main() -> None: - await client.create_api_key( - workspace_id="workspace_id", - ) + await client.create_api_key() asyncio.run(main()) @@ -1754,9 +1762,6 @@ async def main() -> None: _response = await self._client_wrapper.httpx_client.request( "keys", method="POST", - params={ - "workspace_id": workspace_id, - }, request_options=request_options, ) try: @@ -1768,16 +1773,6 @@ async def main() -> None: object_=_response.json(), ), ) - if _response.status_code == 422: - raise UnprocessableEntityError( - typing.cast( - HttpValidationError, - parse_obj_as( - type_=HttpValidationError, # type: ignore - object_=_response.json(), - ), - ) - ) _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) @@ -2055,6 +2050,9 @@ async def main() -> None: "description": description, "type": type, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2263,6 +2261,9 @@ async def main() -> None: "description": description, "updated_at": updated_at, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2440,6 +2441,9 @@ async def main() -> None: json={ "email": email, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2537,6 +2541,9 @@ async def main() -> None: json={ "token": token, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2621,6 +2628,9 @@ async def main() -> None: "description": description, "type": type, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2708,6 +2718,9 @@ async def main() -> None: "description": description, "updated_at": updated_at, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2929,6 +2942,9 @@ async def main() -> None: "organization_id": organization_id, "role": role, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) diff --git a/agenta-cli/agenta/client/backend/configs/client.py b/agenta-cli/agenta/client/backend/configs/client.py index e034ff37b0..ab54844eb9 100644 --- a/agenta-cli/agenta/client/backend/configs/client.py +++ b/agenta-cli/agenta/client/backend/configs/client.py @@ -143,6 +143,9 @@ def save_config( "parameters": parameters, "overwrite": overwrite, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -434,6 +437,9 @@ async def main() -> None: "parameters": parameters, "overwrite": overwrite, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) diff --git a/agenta-cli/agenta/client/backend/containers/client.py b/agenta-cli/agenta/client/backend/containers/client.py index c7180c0a4b..0b62a90e86 100644 --- a/agenta-cli/agenta/client/backend/containers/client.py +++ b/agenta-cli/agenta/client/backend/containers/client.py @@ -156,6 +156,9 @@ def restart_container( json={ "variant_id": variant_id, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -468,6 +471,9 @@ async def main() -> None: json={ "variant_id": variant_id, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) diff --git a/agenta-cli/agenta/client/backend/core/file.py b/agenta-cli/agenta/client/backend/core/file.py index a9623d336a..3467175cb7 100644 --- a/agenta-cli/agenta/client/backend/core/file.py +++ b/agenta-cli/agenta/client/backend/core/file.py @@ -43,23 +43,28 @@ def convert_file_dict_to_httpx_tuples( return httpx_tuples -def with_content_type(*, file: File, content_type: str) -> File: - """ """ +def with_content_type(*, file: File, default_content_type: str) -> File: + """ + This function resolves to the file's content type, if provided, and defaults + to the default_content_type value if not. + """ if isinstance(file, tuple): if len(file) == 2: filename, content = cast(Tuple[Optional[str], FileContent], file) # type: ignore - return (filename, content, content_type) + return (filename, content, default_content_type) elif len(file) == 3: - filename, content, _ = cast( + filename, content, file_content_type = cast( Tuple[Optional[str], FileContent, Optional[str]], file ) # type: ignore - return (filename, content, content_type) + out_content_type = file_content_type or default_content_type + return (filename, content, out_content_type) elif len(file) == 4: - filename, content, _, headers = cast( # type: ignore + filename, content, file_content_type, headers = cast( # type: ignore Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], file, ) - return (filename, content, content_type, headers) + out_content_type = file_content_type or default_content_type + return (filename, content, out_content_type, headers) else: raise ValueError(f"Unexpected tuple length: {len(file)}") - return (None, file, content_type) + return (None, file, default_content_type) diff --git a/agenta-cli/agenta/client/backend/environments/client.py b/agenta-cli/agenta/client/backend/environments/client.py index 1cfbb07010..f9cc94afa7 100644 --- a/agenta-cli/agenta/client/backend/environments/client.py +++ b/agenta-cli/agenta/client/backend/environments/client.py @@ -70,6 +70,9 @@ def deploy_to_environment( "environment_name": environment_name, "variant_id": variant_id, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -162,6 +165,9 @@ async def main() -> None: "environment_name": environment_name, "variant_id": variant_id, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) diff --git a/agenta-cli/agenta/client/backend/evaluations/client.py b/agenta-cli/agenta/client/backend/evaluations/client.py index a8190dd1f2..c8f39d56f2 100644 --- a/agenta-cli/agenta/client/backend/evaluations/client.py +++ b/agenta-cli/agenta/client/backend/evaluations/client.py @@ -34,6 +34,7 @@ def fetch_evaluation_ids( Fetches evaluation ids for a given resource type and id. Arguments: + app_id (str): The ID of the app for which to fetch evaluations. resource_type (str): The type of resource for which to fetch evaluations. resource_ids List[ObjectId]: The IDs of resource for which to fetch evaluations. @@ -250,6 +251,9 @@ def create_evaluation( "lm_providers_keys": lm_providers_keys, "correct_answer_column": correct_answer_column, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -322,6 +326,9 @@ def delete_evaluations( json={ "evaluations_ids": evaluations_ids, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -716,6 +723,7 @@ async def fetch_evaluation_ids( Fetches evaluation ids for a given resource type and id. Arguments: + app_id (str): The ID of the app for which to fetch evaluations. resource_type (str): The type of resource for which to fetch evaluations. resource_ids List[ObjectId]: The IDs of resource for which to fetch evaluations. @@ -727,7 +735,6 @@ async def fetch_evaluation_ids( Parameters ---------- - resource_type : str resource_ids : typing.Optional[typing.Union[str, typing.Sequence[str]]] @@ -957,6 +964,9 @@ async def main() -> None: "lm_providers_keys": lm_providers_keys, "correct_answer_column": correct_answer_column, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1037,6 +1047,9 @@ async def main() -> None: json={ "evaluations_ids": evaluations_ids, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) diff --git a/agenta-cli/agenta/client/backend/evaluators/client.py b/agenta-cli/agenta/client/backend/evaluators/client.py index 29bafb0305..bad5cab677 100644 --- a/agenta-cli/agenta/client/backend/evaluators/client.py +++ b/agenta-cli/agenta/client/backend/evaluators/client.py @@ -122,6 +122,9 @@ def evaluator_data_map( "inputs": inputs, "mapping": mapping, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -209,6 +212,9 @@ def evaluator_run( "settings": settings, "credentials": credentials, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -364,6 +370,9 @@ def create_new_evaluator_config( "evaluator_key": evaluator_key, "settings_values": settings_values, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -511,6 +520,9 @@ def update_evaluator_config( "evaluator_key": evaluator_key, "settings_values": settings_values, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -726,6 +738,9 @@ async def main() -> None: "inputs": inputs, "mapping": mapping, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -821,6 +836,9 @@ async def main() -> None: "settings": settings, "credentials": credentials, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -992,6 +1010,9 @@ async def main() -> None: "evaluator_key": evaluator_key, "settings_values": settings_values, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1155,6 +1176,9 @@ async def main() -> None: "evaluator_key": evaluator_key, "settings_values": settings_values, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) diff --git a/agenta-cli/agenta/client/backend/observability/client.py b/agenta-cli/agenta/client/backend/observability/client.py index aebe134924..dbce982f6b 100644 --- a/agenta-cli/agenta/client/backend/observability/client.py +++ b/agenta-cli/agenta/client/backend/observability/client.py @@ -66,7 +66,7 @@ def dashboard( ) """ _response = self._client_wrapper.httpx_client.request( - "observability/dashboard/", + "observability/dashboard", method="GET", params={ "app_id": app_id, @@ -152,7 +152,7 @@ def create_traces( ) """ _response = self._client_wrapper.httpx_client.request( - "observability/trace/", + "observability/trace", method="POST", json={ "trace": trace, @@ -162,6 +162,9 @@ def create_traces( direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -242,7 +245,7 @@ def get_traces( ) """ _response = self._client_wrapper.httpx_client.request( - "observability/traces/", + "observability/traces", method="GET", params={ "app_id": app_id, @@ -312,7 +315,7 @@ def delete_traces_legacy( ) """ _response = self._client_wrapper.httpx_client.request( - "observability/traces/", + "observability/traces", method="DELETE", json=request, request_options=request_options, @@ -371,7 +374,7 @@ def get_trace_detail( ) """ _response = self._client_wrapper.httpx_client.request( - f"observability/traces/{jsonable_encoder(trace_id)}/", + f"observability/traces/{jsonable_encoder(trace_id)}", method="GET", request_options=request_options, ) @@ -452,7 +455,7 @@ def get_spans_of_generation( ) """ _response = self._client_wrapper.httpx_client.request( - "observability/spans/", + "observability/spans", method="GET", params={ "app_id": app_id, @@ -522,7 +525,7 @@ def delete_spans_of_trace( ) """ _response = self._client_wrapper.httpx_client.request( - "observability/spans/", + "observability/spans", method="DELETE", json=request, request_options=request_options, @@ -587,7 +590,7 @@ def get_span_of_generation( ) """ _response = self._client_wrapper.httpx_client.request( - f"observability/spans/{jsonable_encoder(span_id)}/", + f"observability/spans/{jsonable_encoder(span_id)}", method="GET", params={ "type": type, @@ -672,7 +675,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "observability/dashboard/", + "observability/dashboard", method="GET", params={ "app_id": app_id, @@ -765,7 +768,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "observability/trace/", + "observability/trace", method="POST", json={ "trace": trace, @@ -775,6 +778,9 @@ async def main() -> None: direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -863,7 +869,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "observability/traces/", + "observability/traces", method="GET", params={ "app_id": app_id, @@ -941,7 +947,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "observability/traces/", + "observability/traces", method="DELETE", json=request, request_options=request_options, @@ -1008,7 +1014,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - f"observability/traces/{jsonable_encoder(trace_id)}/", + f"observability/traces/{jsonable_encoder(trace_id)}", method="GET", request_options=request_options, ) @@ -1097,7 +1103,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "observability/spans/", + "observability/spans", method="GET", params={ "app_id": app_id, @@ -1175,7 +1181,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "observability/spans/", + "observability/spans", method="DELETE", json=request, request_options=request_options, @@ -1248,7 +1254,7 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - f"observability/spans/{jsonable_encoder(span_id)}/", + f"observability/spans/{jsonable_encoder(span_id)}", method="GET", params={ "type": type, diff --git a/agenta-cli/agenta/client/backend/observability_v_1/__init__.py b/agenta-cli/agenta/client/backend/observability_v_1/__init__.py index aceeca0c75..780ddec649 100644 --- a/agenta-cli/agenta/client/backend/observability_v_1/__init__.py +++ b/agenta-cli/agenta/client/backend/observability_v_1/__init__.py @@ -1,5 +1,5 @@ # This file was auto-generated by Fern from our API Definition. -from .types import Format, QueryTracesResponse +from .types import Format, QueryAnalyticsResponse, QueryTracesResponse -__all__ = ["Format", "QueryTracesResponse"] +__all__ = ["Format", "QueryAnalyticsResponse", "QueryTracesResponse"] diff --git a/agenta-cli/agenta/client/backend/observability_v_1/client.py b/agenta-cli/agenta/client/backend/observability_v_1/client.py index 5fa38f8c3c..ce252ea18b 100644 --- a/agenta-cli/agenta/client/backend/observability_v_1/client.py +++ b/agenta-cli/agenta/client/backend/observability_v_1/client.py @@ -11,6 +11,7 @@ from .types.query_traces_response import QueryTracesResponse from ..errors.unprocessable_entity_error import UnprocessableEntityError from ..types.http_validation_error import HttpValidationError +from .types.query_analytics_response import QueryAnalyticsResponse from ..core.client_wrapper import AsyncClientWrapper @@ -270,6 +271,103 @@ def delete_traces( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def query_analytics( + self, + *, + format: typing.Optional[Format] = None, + focus: typing.Optional[str] = None, + oldest: typing.Optional[str] = None, + newest: typing.Optional[str] = None, + window: typing.Optional[int] = None, + filtering: typing.Optional[str] = None, + time_range: typing.Optional[str] = None, + app_id: typing.Optional[str] = None, + environment: typing.Optional[str] = None, + variant: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> QueryAnalyticsResponse: + """ + Parameters + ---------- + format : typing.Optional[Format] + + focus : typing.Optional[str] + + oldest : typing.Optional[str] + + newest : typing.Optional[str] + + window : typing.Optional[int] + + filtering : typing.Optional[str] + + time_range : typing.Optional[str] + + app_id : typing.Optional[str] + + environment : typing.Optional[str] + + variant : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + QueryAnalyticsResponse + Successful Response + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.observability_v_1.query_analytics() + """ + _response = self._client_wrapper.httpx_client.request( + "observability/v1/analytics", + method="GET", + params={ + "format": format, + "focus": focus, + "oldest": oldest, + "newest": newest, + "window": window, + "filtering": filtering, + "timeRange": time_range, + "app_id": app_id, + "environment": environment, + "variant": variant, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + QueryAnalyticsResponse, + parse_obj_as( + type_=QueryAnalyticsResponse, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + class AsyncObservabilityV1Client: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -558,3 +656,108 @@ async def main() -> None: except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + + async def query_analytics( + self, + *, + format: typing.Optional[Format] = None, + focus: typing.Optional[str] = None, + oldest: typing.Optional[str] = None, + newest: typing.Optional[str] = None, + window: typing.Optional[int] = None, + filtering: typing.Optional[str] = None, + time_range: typing.Optional[str] = None, + app_id: typing.Optional[str] = None, + environment: typing.Optional[str] = None, + variant: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> QueryAnalyticsResponse: + """ + Parameters + ---------- + format : typing.Optional[Format] + + focus : typing.Optional[str] + + oldest : typing.Optional[str] + + newest : typing.Optional[str] + + window : typing.Optional[int] + + filtering : typing.Optional[str] + + time_range : typing.Optional[str] + + app_id : typing.Optional[str] + + environment : typing.Optional[str] + + variant : typing.Optional[str] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + QueryAnalyticsResponse + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.observability_v_1.query_analytics() + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "observability/v1/analytics", + method="GET", + params={ + "format": format, + "focus": focus, + "oldest": oldest, + "newest": newest, + "window": window, + "filtering": filtering, + "timeRange": time_range, + "app_id": app_id, + "environment": environment, + "variant": variant, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + QueryAnalyticsResponse, + parse_obj_as( + type_=QueryAnalyticsResponse, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) diff --git a/agenta-cli/agenta/client/backend/observability_v_1/types/__init__.py b/agenta-cli/agenta/client/backend/observability_v_1/types/__init__.py index 7303a90f08..bf6cbe689d 100644 --- a/agenta-cli/agenta/client/backend/observability_v_1/types/__init__.py +++ b/agenta-cli/agenta/client/backend/observability_v_1/types/__init__.py @@ -1,6 +1,7 @@ # This file was auto-generated by Fern from our API Definition. from .format import Format +from .query_analytics_response import QueryAnalyticsResponse from .query_traces_response import QueryTracesResponse -__all__ = ["Format", "QueryTracesResponse"] +__all__ = ["Format", "QueryAnalyticsResponse", "QueryTracesResponse"] diff --git a/agenta-cli/agenta/client/backend/observability_v_1/types/format.py b/agenta-cli/agenta/client/backend/observability_v_1/types/format.py index ed6f7db216..b3b01bea42 100644 --- a/agenta-cli/agenta/client/backend/observability_v_1/types/format.py +++ b/agenta-cli/agenta/client/backend/observability_v_1/types/format.py @@ -2,4 +2,4 @@ import typing -Format = typing.Union[typing.Literal["opentelemetry", "agenta"], typing.Any] +Format = typing.Union[typing.Literal["legacy", "agenta"], typing.Any] diff --git a/agenta-cli/agenta/client/backend/observability_v_1/types/query_analytics_response.py b/agenta-cli/agenta/client/backend/observability_v_1/types/query_analytics_response.py new file mode 100644 index 0000000000..ada2858ab8 --- /dev/null +++ b/agenta-cli/agenta/client/backend/observability_v_1/types/query_analytics_response.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from ...types.legacy_analytics_response import LegacyAnalyticsResponse +from ...types.analytics_response import AnalyticsResponse + +QueryAnalyticsResponse = typing.Union[LegacyAnalyticsResponse, AnalyticsResponse] diff --git a/agenta-cli/agenta/client/backend/scopes/__init__.py b/agenta-cli/agenta/client/backend/scopes/__init__.py new file mode 100644 index 0000000000..67a41b2742 --- /dev/null +++ b/agenta-cli/agenta/client/backend/scopes/__init__.py @@ -0,0 +1 @@ +# This file was auto-generated by Fern from our API Definition. diff --git a/agenta-cli/agenta/client/backend/scopes/client.py b/agenta-cli/agenta/client/backend/scopes/client.py new file mode 100644 index 0000000000..7c437da103 --- /dev/null +++ b/agenta-cli/agenta/client/backend/scopes/client.py @@ -0,0 +1,114 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.client_wrapper import SyncClientWrapper +import typing +from ..core.request_options import RequestOptions +from ..types.projects_response import ProjectsResponse +from ..core.pydantic_utilities import parse_obj_as +from json.decoder import JSONDecodeError +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper + + +class ScopesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_projects( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[ProjectsResponse]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[ProjectsResponse] + Successful Response + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.scopes.get_projects() + """ + _response = self._client_wrapper.httpx_client.request( + "projects", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.List[ProjectsResponse], + parse_obj_as( + type_=typing.List[ProjectsResponse], # type: ignore + object_=_response.json(), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + +class AsyncScopesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_projects( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[ProjectsResponse]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[ProjectsResponse] + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.scopes.get_projects() + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "projects", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.List[ProjectsResponse], + parse_obj_as( + type_=typing.List[ProjectsResponse], # type: ignore + object_=_response.json(), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) diff --git a/agenta-cli/agenta/client/backend/testsets/client.py b/agenta-cli/agenta/client/backend/testsets/client.py index fc10205700..091705f1ef 100644 --- a/agenta-cli/agenta/client/backend/testsets/client.py +++ b/agenta-cli/agenta/client/backend/testsets/client.py @@ -10,8 +10,8 @@ from ..types.http_validation_error import HttpValidationError from json.decoder import JSONDecodeError from ..core.api_error import ApiError -from ..core.jsonable_encoder import jsonable_encoder from ..types.test_set_output_response import TestSetOutputResponse +from ..core.jsonable_encoder import jsonable_encoder from ..core.client_wrapper import AsyncClientWrapper # this is used as the default value for optional parameters @@ -106,7 +106,11 @@ def upload_file( raise ApiError(status_code=_response.status_code, body=_response_json) def import_testset( - self, *, request_options: typing.Optional[RequestOptions] = None + self, + *, + endpoint: typing.Optional[str] = OMIT, + testset_name: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, ) -> TestSetSimpleResponse: """ Import JSON testset data from an endpoint and save it to MongoDB. @@ -120,6 +124,10 @@ def import_testset( Parameters ---------- + endpoint : typing.Optional[str] + + testset_name : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -141,7 +149,12 @@ def import_testset( _response = self._client_wrapper.httpx_client.request( "testsets/endpoint", method="POST", + json={ + "endpoint": endpoint, + "testset_name": testset_name, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: @@ -167,6 +180,59 @@ def import_testset( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def get_testsets( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[TestSetOutputResponse]: + """ + Get all testsets. + + Returns: + + - A list of testset objects. + + Raises: + + - `HTTPException` with status code 404 if no testsets are found. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[TestSetOutputResponse] + Successful Response + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.testsets.get_testsets() + """ + _response = self._client_wrapper.httpx_client.request( + "testsets", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.List[TestSetOutputResponse], + parse_obj_as( + type_=typing.List[TestSetOutputResponse], # type: ignore + object_=_response.json(), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + def create_testset( self, *, @@ -245,31 +311,31 @@ def create_testset( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - def get_single_testset( + def delete_testsets( self, - testset_id: str, *, + testset_ids: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None, - ) -> typing.Optional[typing.Any]: + ) -> typing.List[str]: """ - Fetch a specific testset in a MongoDB collection using its \_id. + Delete specific testsets based on their unique IDs. Args: - testset_id (str): The \_id of the testset to fetch. + testset_ids (List[str]): The unique identifiers of the testsets to delete. Returns: - The requested testset if found, else an HTTPException. + A list of the deleted testsets' IDs. Parameters ---------- - testset_id : str + testset_ids : typing.Sequence[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.Optional[typing.Any] + typing.List[str] Successful Response Examples @@ -280,21 +346,28 @@ def get_single_testset( api_key="YOUR_API_KEY", base_url="https://yourhost.com/path/to/api", ) - client.testsets.get_single_testset( - testset_id="testset_id", + client.testsets.delete_testsets( + testset_ids=["testset_ids"], ) """ _response = self._client_wrapper.httpx_client.request( - f"testsets/{jsonable_encoder(testset_id)}", - method="GET", + "testsets", + method="DELETE", + json={ + "testset_ids": testset_ids, + }, + headers={ + "content-type": "application/json", + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: return typing.cast( - typing.Optional[typing.Any], + typing.List[str], parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.List[str], # type: ignore object_=_response.json(), ), ) @@ -313,27 +386,27 @@ def get_single_testset( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - def update_testset( + def deprecating_create_testset( self, - testset_id: str, + app_id: str, *, name: str, csvdata: typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]], request_options: typing.Optional[RequestOptions] = None, - ) -> typing.Optional[typing.Any]: + ) -> TestSetSimpleResponse: """ - Update a testset with given id, update the testset in MongoDB. + Create a testset with given name, save the testset to MongoDB. Args: - testset_id (str): id of the test set to be updated. - csvdata (NewTestset): New data to replace the old testset. + name (str): name of the test set. + testset (Dict[str, str]): test set data. Returns: - str: The id of the test set updated. + str: The id of the test set created. Parameters ---------- - testset_id : str + app_id : str name : str @@ -344,7 +417,7 @@ def update_testset( Returns ------- - typing.Optional[typing.Any] + TestSetSimpleResponse Successful Response Examples @@ -355,15 +428,15 @@ def update_testset( api_key="YOUR_API_KEY", base_url="https://yourhost.com/path/to/api", ) - client.testsets.update_testset( - testset_id="testset_id", + client.testsets.deprecating_create_testset( + app_id="app_id", name="name", csvdata=[{"key": "value"}], ) """ _response = self._client_wrapper.httpx_client.request( - f"testsets/{jsonable_encoder(testset_id)}", - method="PUT", + f"testsets/{jsonable_encoder(app_id)}", + method="POST", json={ "name": name, "csvdata": csvdata, @@ -374,9 +447,9 @@ def update_testset( try: if 200 <= _response.status_code < 300: return typing.cast( - typing.Optional[typing.Any], + TestSetSimpleResponse, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=TestSetSimpleResponse, # type: ignore object_=_response.json(), ), ) @@ -395,28 +468,31 @@ def update_testset( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - def get_testsets( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> typing.List[TestSetOutputResponse]: + def get_single_testset( + self, + testset_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[typing.Any]: """ - Get all testsets. - - Returns: - - - A list of testset objects. + Fetch a specific testset in a MongoDB collection using its \_id. - Raises: + Args: + testset_id (str): The \_id of the testset to fetch. - - `HTTPException` with status code 404 if no testsets are found. + Returns: + The requested testset if found, else an HTTPException. Parameters ---------- + testset_id : str + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[TestSetOutputResponse] + typing.Optional[typing.Any] Successful Response Examples @@ -427,19 +503,21 @@ def get_testsets( api_key="YOUR_API_KEY", base_url="https://yourhost.com/path/to/api", ) - client.testsets.get_testsets() + client.testsets.get_single_testset( + testset_id="testset_id", + ) """ _response = self._client_wrapper.httpx_client.request( - "testsets", + f"testsets/{jsonable_encoder(testset_id)}", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: return typing.cast( - typing.List[TestSetOutputResponse], + typing.Optional[typing.Any], parse_obj_as( - type_=typing.List[TestSetOutputResponse], # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -458,31 +536,38 @@ def get_testsets( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - def delete_testsets( + def update_testset( self, + testset_id: str, *, - testset_ids: typing.Sequence[str], + name: str, + csvdata: typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]], request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[str]: + ) -> typing.Optional[typing.Any]: """ - Delete specific testsets based on their unique IDs. + Update a testset with given id, update the testset in MongoDB. Args: - testset_ids (List[str]): The unique identifiers of the testsets to delete. + testset_id (str): id of the test set to be updated. + csvdata (NewTestset): New data to replace the old testset. Returns: - A list of the deleted testsets' IDs. + str: The id of the test set updated. Parameters ---------- - testset_ids : typing.Sequence[str] + testset_id : str + + name : str + + csvdata : typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[str] + typing.Optional[typing.Any] Successful Response Examples @@ -493,15 +578,18 @@ def delete_testsets( api_key="YOUR_API_KEY", base_url="https://yourhost.com/path/to/api", ) - client.testsets.delete_testsets( - testset_ids=["testset_ids"], + client.testsets.update_testset( + testset_id="testset_id", + name="name", + csvdata=[{"key": "value"}], ) """ _response = self._client_wrapper.httpx_client.request( - "testsets", - method="DELETE", + f"testsets/{jsonable_encoder(testset_id)}", + method="PUT", json={ - "testset_ids": testset_ids, + "name": name, + "csvdata": csvdata, }, request_options=request_options, omit=OMIT, @@ -509,9 +597,9 @@ def delete_testsets( try: if 200 <= _response.status_code < 300: return typing.cast( - typing.List[str], + typing.Optional[typing.Any], parse_obj_as( - type_=typing.List[str], # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -627,7 +715,11 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response_json) async def import_testset( - self, *, request_options: typing.Optional[RequestOptions] = None + self, + *, + endpoint: typing.Optional[str] = OMIT, + testset_name: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, ) -> TestSetSimpleResponse: """ Import JSON testset data from an endpoint and save it to MongoDB. @@ -641,6 +733,10 @@ async def import_testset( Parameters ---------- + endpoint : typing.Optional[str] + + testset_name : typing.Optional[str] + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -670,7 +766,12 @@ async def main() -> None: _response = await self._client_wrapper.httpx_client.request( "testsets/endpoint", method="POST", + json={ + "endpoint": endpoint, + "testset_name": testset_name, + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: @@ -696,6 +797,67 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + async def get_testsets( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[TestSetOutputResponse]: + """ + Get all testsets. + + Returns: + + - A list of testset objects. + + Raises: + + - `HTTPException` with status code 404 if no testsets are found. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[TestSetOutputResponse] + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.testsets.get_testsets() + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "testsets", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.List[TestSetOutputResponse], + parse_obj_as( + type_=typing.List[TestSetOutputResponse], # type: ignore + object_=_response.json(), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + async def create_testset( self, *, @@ -782,31 +944,31 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - async def get_single_testset( + async def delete_testsets( self, - testset_id: str, *, + testset_ids: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None, - ) -> typing.Optional[typing.Any]: + ) -> typing.List[str]: """ - Fetch a specific testset in a MongoDB collection using its \_id. + Delete specific testsets based on their unique IDs. Args: - testset_id (str): The \_id of the testset to fetch. + testset_ids (List[str]): The unique identifiers of the testsets to delete. Returns: - The requested testset if found, else an HTTPException. + A list of the deleted testsets' IDs. Parameters ---------- - testset_id : str + testset_ids : typing.Sequence[str] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.Optional[typing.Any] + typing.List[str] Successful Response Examples @@ -822,24 +984,31 @@ async def get_single_testset( async def main() -> None: - await client.testsets.get_single_testset( - testset_id="testset_id", + await client.testsets.delete_testsets( + testset_ids=["testset_ids"], ) asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - f"testsets/{jsonable_encoder(testset_id)}", - method="GET", + "testsets", + method="DELETE", + json={ + "testset_ids": testset_ids, + }, + headers={ + "content-type": "application/json", + }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: return typing.cast( - typing.Optional[typing.Any], + typing.List[str], parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=typing.List[str], # type: ignore object_=_response.json(), ), ) @@ -858,27 +1027,27 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - async def update_testset( + async def deprecating_create_testset( self, - testset_id: str, + app_id: str, *, name: str, csvdata: typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]], request_options: typing.Optional[RequestOptions] = None, - ) -> typing.Optional[typing.Any]: + ) -> TestSetSimpleResponse: """ - Update a testset with given id, update the testset in MongoDB. + Create a testset with given name, save the testset to MongoDB. Args: - testset_id (str): id of the test set to be updated. - csvdata (NewTestset): New data to replace the old testset. + name (str): name of the test set. + testset (Dict[str, str]): test set data. Returns: - str: The id of the test set updated. + str: The id of the test set created. Parameters ---------- - testset_id : str + app_id : str name : str @@ -889,7 +1058,7 @@ async def update_testset( Returns ------- - typing.Optional[typing.Any] + TestSetSimpleResponse Successful Response Examples @@ -905,8 +1074,8 @@ async def update_testset( async def main() -> None: - await client.testsets.update_testset( - testset_id="testset_id", + await client.testsets.deprecating_create_testset( + app_id="app_id", name="name", csvdata=[{"key": "value"}], ) @@ -915,8 +1084,8 @@ async def main() -> None: asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - f"testsets/{jsonable_encoder(testset_id)}", - method="PUT", + f"testsets/{jsonable_encoder(app_id)}", + method="POST", json={ "name": name, "csvdata": csvdata, @@ -927,9 +1096,9 @@ async def main() -> None: try: if 200 <= _response.status_code < 300: return typing.cast( - typing.Optional[typing.Any], + TestSetSimpleResponse, parse_obj_as( - type_=typing.Optional[typing.Any], # type: ignore + type_=TestSetSimpleResponse, # type: ignore object_=_response.json(), ), ) @@ -948,28 +1117,31 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - async def get_testsets( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> typing.List[TestSetOutputResponse]: + async def get_single_testset( + self, + testset_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[typing.Any]: """ - Get all testsets. - - Returns: - - - A list of testset objects. + Fetch a specific testset in a MongoDB collection using its \_id. - Raises: + Args: + testset_id (str): The \_id of the testset to fetch. - - `HTTPException` with status code 404 if no testsets are found. + Returns: + The requested testset if found, else an HTTPException. Parameters ---------- + testset_id : str + request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[TestSetOutputResponse] + typing.Optional[typing.Any] Successful Response Examples @@ -985,22 +1157,24 @@ async def get_testsets( async def main() -> None: - await client.testsets.get_testsets() + await client.testsets.get_single_testset( + testset_id="testset_id", + ) asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "testsets", + f"testsets/{jsonable_encoder(testset_id)}", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: return typing.cast( - typing.List[TestSetOutputResponse], + typing.Optional[typing.Any], parse_obj_as( - type_=typing.List[TestSetOutputResponse], # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1019,31 +1193,38 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - async def delete_testsets( + async def update_testset( self, + testset_id: str, *, - testset_ids: typing.Sequence[str], + name: str, + csvdata: typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]], request_options: typing.Optional[RequestOptions] = None, - ) -> typing.List[str]: + ) -> typing.Optional[typing.Any]: """ - Delete specific testsets based on their unique IDs. + Update a testset with given id, update the testset in MongoDB. Args: - testset_ids (List[str]): The unique identifiers of the testsets to delete. + testset_id (str): id of the test set to be updated. + csvdata (NewTestset): New data to replace the old testset. Returns: - A list of the deleted testsets' IDs. + str: The id of the test set updated. Parameters ---------- - testset_ids : typing.Sequence[str] + testset_id : str + + name : str + + csvdata : typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - typing.List[str] + typing.Optional[typing.Any] Successful Response Examples @@ -1059,18 +1240,21 @@ async def delete_testsets( async def main() -> None: - await client.testsets.delete_testsets( - testset_ids=["testset_ids"], + await client.testsets.update_testset( + testset_id="testset_id", + name="name", + csvdata=[{"key": "value"}], ) asyncio.run(main()) """ _response = await self._client_wrapper.httpx_client.request( - "testsets", - method="DELETE", + f"testsets/{jsonable_encoder(testset_id)}", + method="PUT", json={ - "testset_ids": testset_ids, + "name": name, + "csvdata": csvdata, }, request_options=request_options, omit=OMIT, @@ -1078,9 +1262,9 @@ async def main() -> None: try: if 200 <= _response.status_code < 300: return typing.cast( - typing.List[str], + typing.Optional[typing.Any], parse_obj_as( - type_=typing.List[str], # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) diff --git a/agenta-cli/agenta/client/backend/types/__init__.py b/agenta-cli/agenta/client/backend/types/__init__.py index b10c09c61b..6ef4a84f0a 100644 --- a/agenta-cli/agenta/client/backend/types/__init__.py +++ b/agenta-cli/agenta/client/backend/types/__init__.py @@ -9,11 +9,13 @@ from .agenta_trees_response import AgentaTreesResponse from .aggregated_result import AggregatedResult from .aggregated_result_evaluator_config import AggregatedResultEvaluatorConfig +from .analytics_response import AnalyticsResponse from .app import App from .app_variant_response import AppVariantResponse from .app_variant_revision import AppVariantRevision from .base_output import BaseOutput from .body_import_testset import BodyImportTestset +from .bucket_dto import BucketDto from .collect_status_response import CollectStatusResponse from .config_db import ConfigDb from .config_dto import ConfigDto @@ -41,6 +43,7 @@ from .evaluator_output_interface import EvaluatorOutputInterface from .exception_dto import ExceptionDto from .get_config_response import GetConfigResponse +from .header_dto import HeaderDto from .http_validation_error import HttpValidationError from .human_evaluation import HumanEvaluation from .human_evaluation_scenario import HumanEvaluationScenario @@ -50,12 +53,14 @@ from .human_evaluation_update import HumanEvaluationUpdate from .image import Image from .invite_request import InviteRequest +from .legacy_analytics_response import LegacyAnalyticsResponse +from .legacy_data_point import LegacyDataPoint from .lifecycle_dto import LifecycleDto from .link_dto import LinkDto from .list_api_keys_response import ListApiKeysResponse from .llm_run_rate_limit import LlmRunRateLimit from .llm_tokens import LlmTokens -from .lm_providers_enum import LmProvidersEnum +from .metrics_dto import MetricsDto from .new_human_evaluation import NewHumanEvaluation from .new_testset import NewTestset from .node_dto import NodeDto @@ -73,11 +78,17 @@ from .outputs import Outputs from .parent_dto import ParentDto from .permission import Permission +from .projects_response import ProjectsResponse +from .provider_key_dto import ProviderKeyDto +from .provider_kind import ProviderKind from .reference_dto import ReferenceDto from .reference_request_model import ReferenceRequestModel from .result import Result from .root_dto import RootDto from .score import Score +from .secret_dto import SecretDto +from .secret_kind import SecretKind +from .secret_response_dto import SecretResponseDto from .simple_evaluation_output import SimpleEvaluationOutput from .span import Span from .span_detail import SpanDetail @@ -118,11 +129,13 @@ "AgentaTreesResponse", "AggregatedResult", "AggregatedResultEvaluatorConfig", + "AnalyticsResponse", "App", "AppVariantResponse", "AppVariantRevision", "BaseOutput", "BodyImportTestset", + "BucketDto", "CollectStatusResponse", "ConfigDb", "ConfigDto", @@ -150,6 +163,7 @@ "EvaluatorOutputInterface", "ExceptionDto", "GetConfigResponse", + "HeaderDto", "HttpValidationError", "HumanEvaluation", "HumanEvaluationScenario", @@ -159,12 +173,14 @@ "HumanEvaluationUpdate", "Image", "InviteRequest", + "LegacyAnalyticsResponse", + "LegacyDataPoint", "LifecycleDto", "LinkDto", "ListApiKeysResponse", "LlmRunRateLimit", "LlmTokens", - "LmProvidersEnum", + "MetricsDto", "NewHumanEvaluation", "NewTestset", "NodeDto", @@ -182,11 +198,17 @@ "Outputs", "ParentDto", "Permission", + "ProjectsResponse", + "ProviderKeyDto", + "ProviderKind", "ReferenceDto", "ReferenceRequestModel", "Result", "RootDto", "Score", + "SecretDto", + "SecretKind", + "SecretResponseDto", "SimpleEvaluationOutput", "Span", "SpanDetail", diff --git a/agenta-cli/agenta/client/backend/types/analytics_response.py b/agenta-cli/agenta/client/backend/types/analytics_response.py new file mode 100644 index 0000000000..87195da026 --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/analytics_response.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +from .bucket_dto import BucketDto +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import pydantic + + +class AnalyticsResponse(UniversalBaseModel): + version: str + count: typing.Optional[int] = None + buckets: typing.List[BucketDto] + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/app.py b/agenta-cli/agenta/client/backend/types/app.py index 1a16548531..c98d4e9faf 100644 --- a/agenta-cli/agenta/client/backend/types/app.py +++ b/agenta-cli/agenta/client/backend/types/app.py @@ -1,14 +1,15 @@ # This file was auto-generated by Fern from our API Definition. from ..core.pydantic_utilities import UniversalBaseModel -from ..core.pydantic_utilities import IS_PYDANTIC_V2 import typing +from ..core.pydantic_utilities import IS_PYDANTIC_V2 import pydantic class App(UniversalBaseModel): app_id: str app_name: str + app_type: typing.Optional[str] = None updated_at: str if IS_PYDANTIC_V2: diff --git a/agenta-cli/agenta/client/backend/types/body_import_testset.py b/agenta-cli/agenta/client/backend/types/body_import_testset.py index 12f7ad7453..9243142a3e 100644 --- a/agenta-cli/agenta/client/backend/types/body_import_testset.py +++ b/agenta-cli/agenta/client/backend/types/body_import_testset.py @@ -9,7 +9,6 @@ class BodyImportTestset(UniversalBaseModel): endpoint: typing.Optional[str] = None testset_name: typing.Optional[str] = None - app_id: typing.Optional[str] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( diff --git a/agenta-cli/agenta/client/backend/types/bucket_dto.py b/agenta-cli/agenta/client/backend/types/bucket_dto.py new file mode 100644 index 0000000000..fe02e3e5bd --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/bucket_dto.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import datetime as dt +from .metrics_dto import MetricsDto +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import typing +import pydantic + + +class BucketDto(UniversalBaseModel): + timestamp: dt.datetime + window: int + total: MetricsDto + error: MetricsDto + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/header_dto.py b/agenta-cli/agenta/client/backend/types/header_dto.py new file mode 100644 index 0000000000..31f6050a21 --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/header_dto.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import pydantic + + +class HeaderDto(UniversalBaseModel): + name: typing.Optional[str] = None + description: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/legacy_analytics_response.py b/agenta-cli/agenta/client/backend/types/legacy_analytics_response.py new file mode 100644 index 0000000000..8201c64567 --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/legacy_analytics_response.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +from .legacy_data_point import LegacyDataPoint +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import pydantic + + +class LegacyAnalyticsResponse(UniversalBaseModel): + total_count: int + failure_rate: float + total_cost: float + avg_cost: float + avg_latency: float + total_tokens: int + avg_tokens: float + data: typing.List[LegacyDataPoint] + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/legacy_data_point.py b/agenta-cli/agenta/client/backend/types/legacy_data_point.py new file mode 100644 index 0000000000..26c987726a --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/legacy_data_point.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import datetime as dt +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import typing +import pydantic + + +class LegacyDataPoint(UniversalBaseModel): + timestamp: dt.datetime + success_count: int + failure_count: int + cost: float + latency: float + total_tokens: int + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/lm_providers_enum.py b/agenta-cli/agenta/client/backend/types/lm_providers_enum.py deleted file mode 100644 index 6aa756ba0e..0000000000 --- a/agenta-cli/agenta/client/backend/types/lm_providers_enum.py +++ /dev/null @@ -1,21 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -LmProvidersEnum = typing.Union[ - typing.Literal[ - "OPENAI_API_KEY", - "MISTRAL_API_KEY", - "COHERE_API_KEY", - "ANTHROPIC_API_KEY", - "ANYSCALE_API_KEY", - "PERPLEXITYAI_API_KEY", - "DEEPINFRA_API_KEY", - "TOGETHERAI_API_KEY", - "ALEPHALPHA_API_KEY", - "OPENROUTER_API_KEY", - "GROQ_API_KEY", - "GEMINI_API_KEY", - ], - typing.Any, -] diff --git a/agenta-cli/agenta/client/backend/types/metrics_dto.py b/agenta-cli/agenta/client/backend/types/metrics_dto.py new file mode 100644 index 0000000000..34be8ae995 --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/metrics_dto.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import pydantic + + +class MetricsDto(UniversalBaseModel): + count: typing.Optional[int] = None + duration: typing.Optional[float] = None + cost: typing.Optional[float] = None + tokens: typing.Optional[int] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/permission.py b/agenta-cli/agenta/client/backend/types/permission.py index a32616dd16..59f87e22b4 100644 --- a/agenta-cli/agenta/client/backend/types/permission.py +++ b/agenta-cli/agenta/client/backend/types/permission.py @@ -13,6 +13,7 @@ "delete_app_variant", "modify_variant_configurations", "delete_application_variant", + "run_service", "view_app_environment_deployment", "edit_app_environment_deployment", "create_app_environment_deployment", diff --git a/agenta-cli/agenta/client/backend/types/projects_response.py b/agenta-cli/agenta/client/backend/types/projects_response.py new file mode 100644 index 0000000000..31fd82d4ac --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/projects_response.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import pydantic + + +class ProjectsResponse(UniversalBaseModel): + organization_id: typing.Optional[str] = None + organization_name: typing.Optional[str] = None + workspace_id: typing.Optional[str] = None + workspace_name: typing.Optional[str] = None + project_id: str + project_name: str + user_role: typing.Optional[str] = None + is_demo: typing.Optional[bool] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/provider_key_dto.py b/agenta-cli/agenta/client/backend/types/provider_key_dto.py new file mode 100644 index 0000000000..d22e947f2f --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/provider_key_dto.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +from .provider_kind import ProviderKind +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import typing +import pydantic + + +class ProviderKeyDto(UniversalBaseModel): + provider: ProviderKind + key: str + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/provider_kind.py b/agenta-cli/agenta/client/backend/types/provider_kind.py new file mode 100644 index 0000000000..d469462030 --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/provider_kind.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ProviderKind = typing.Union[ + typing.Literal[ + "openai", + "cohere", + "anyscale", + "deepinfra", + "alephalpha", + "groq", + "mistralai", + "anthropic", + "perplexityai", + "togetherai", + "openrouter", + "gemini", + ], + typing.Any, +] diff --git a/agenta-cli/agenta/client/backend/types/secret_dto.py b/agenta-cli/agenta/client/backend/types/secret_dto.py new file mode 100644 index 0000000000..a980b9073d --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/secret_dto.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +from .secret_kind import SecretKind +from .provider_key_dto import ProviderKeyDto +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import typing +import pydantic + + +class SecretDto(UniversalBaseModel): + kind: SecretKind = "provider_key" + data: ProviderKeyDto + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/types/secret_kind.py b/agenta-cli/agenta/client/backend/types/secret_kind.py new file mode 100644 index 0000000000..8d44867226 --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/secret_kind.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SecretKind = typing.Literal["provider_key"] diff --git a/agenta-cli/agenta/client/backend/types/secret_response_dto.py b/agenta-cli/agenta/client/backend/types/secret_response_dto.py new file mode 100644 index 0000000000..1c0a850295 --- /dev/null +++ b/agenta-cli/agenta/client/backend/types/secret_response_dto.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +from .header_dto import HeaderDto +from .secret_dto import SecretDto +from .lifecycle_dto import LifecycleDto +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import pydantic + + +class SecretResponseDto(UniversalBaseModel): + header: typing.Optional[HeaderDto] = None + secret: SecretDto + id: str + lifecycle: typing.Optional[LifecycleDto] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( + extra="allow", frozen=True + ) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/agenta-cli/agenta/client/backend/variants/client.py b/agenta-cli/agenta/client/backend/variants/client.py index 389a77f778..c5fa02fa8e 100644 --- a/agenta-cli/agenta/client/backend/variants/client.py +++ b/agenta-cli/agenta/client/backend/variants/client.py @@ -97,6 +97,9 @@ def add_variant_from_base_and_config( "new_config_name": new_config_name, "parameters": parameters, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -249,6 +252,9 @@ def start_variant( object_=env_vars, annotation=DockerEnvVars, direction="write" ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -399,6 +405,9 @@ def update_variant_parameters( json={ "parameters": parameters, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -757,6 +766,9 @@ def configs_add( direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -839,6 +851,9 @@ def configs_fetch( direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -921,6 +936,9 @@ def configs_fork( direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -989,6 +1007,9 @@ def configs_commit( object_=config, annotation=ConfigDto, direction="write" ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1074,6 +1095,9 @@ def configs_deploy( direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1150,6 +1174,9 @@ def configs_delete( direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1216,6 +1243,9 @@ def configs_list( object_=application_ref, annotation=ReferenceDto, direction="write" ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1292,6 +1322,9 @@ def configs_history( direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1397,6 +1430,9 @@ async def main() -> None: "new_config_name": new_config_name, "parameters": parameters, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1565,6 +1601,9 @@ async def main() -> None: object_=env_vars, annotation=DockerEnvVars, direction="write" ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -1731,6 +1770,9 @@ async def main() -> None: json={ "parameters": parameters, }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2129,6 +2171,9 @@ async def main() -> None: direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2219,6 +2264,9 @@ async def main() -> None: direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2309,6 +2357,9 @@ async def main() -> None: direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2385,6 +2436,9 @@ async def main() -> None: object_=config, annotation=ConfigDto, direction="write" ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2478,6 +2532,9 @@ async def main() -> None: direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2562,6 +2619,9 @@ async def main() -> None: direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2636,6 +2696,9 @@ async def main() -> None: object_=application_ref, annotation=ReferenceDto, direction="write" ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) @@ -2720,6 +2783,9 @@ async def main() -> None: direction="write", ), }, + headers={ + "content-type": "application/json", + }, request_options=request_options, omit=OMIT, ) diff --git a/agenta-cli/agenta/client/backend/vault/__init__.py b/agenta-cli/agenta/client/backend/vault/__init__.py new file mode 100644 index 0000000000..67a41b2742 --- /dev/null +++ b/agenta-cli/agenta/client/backend/vault/__init__.py @@ -0,0 +1 @@ +# This file was auto-generated by Fern from our API Definition. diff --git a/agenta-cli/agenta/client/backend/vault/client.py b/agenta-cli/agenta/client/backend/vault/client.py new file mode 100644 index 0000000000..0c2a31100e --- /dev/null +++ b/agenta-cli/agenta/client/backend/vault/client.py @@ -0,0 +1,685 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from ..core.client_wrapper import SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.secret_response_dto import SecretResponseDto +from ..core.pydantic_utilities import parse_obj_as +from json.decoder import JSONDecodeError +from ..core.api_error import ApiError +from ..types.secret_dto import SecretDto +from ..types.header_dto import HeaderDto +from ..core.serialization import convert_and_respect_annotation_metadata +from ..errors.unprocessable_entity_error import UnprocessableEntityError +from ..types.http_validation_error import HttpValidationError +from ..core.jsonable_encoder import jsonable_encoder +from ..core.client_wrapper import AsyncClientWrapper + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class VaultClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_secrets( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[SecretResponseDto]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[SecretResponseDto] + Successful Response + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.vault.list_secrets() + """ + _response = self._client_wrapper.httpx_client.request( + "vault/v1/secrets", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.List[SecretResponseDto], + parse_obj_as( + type_=typing.List[SecretResponseDto], # type: ignore + object_=_response.json(), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + def create_secret( + self, + *, + secret: SecretDto, + header: typing.Optional[HeaderDto] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SecretResponseDto: + """ + Parameters + ---------- + secret : SecretDto + + header : typing.Optional[HeaderDto] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SecretResponseDto + Successful Response + + Examples + -------- + from agenta import AgentaApi, ProviderKeyDto, SecretDto + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.vault.create_secret( + secret=SecretDto( + data=ProviderKeyDto( + provider="openai", + key="key", + ), + ), + ) + """ + _response = self._client_wrapper.httpx_client.request( + "vault/v1/secrets", + method="POST", + json={ + "header": convert_and_respect_annotation_metadata( + object_=header, annotation=HeaderDto, direction="write" + ), + "secret": convert_and_respect_annotation_metadata( + object_=secret, annotation=SecretDto, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + SecretResponseDto, + parse_obj_as( + type_=SecretResponseDto, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + def read_secret( + self, secret_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> SecretResponseDto: + """ + Parameters + ---------- + secret_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SecretResponseDto + Successful Response + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.vault.read_secret( + secret_id="secret_id", + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"vault/v1/secrets/{jsonable_encoder(secret_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + SecretResponseDto, + parse_obj_as( + type_=SecretResponseDto, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + def update_secret( + self, + secret_id: str, + *, + header: typing.Optional[HeaderDto] = OMIT, + secret: typing.Optional[SecretDto] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SecretResponseDto: + """ + Parameters + ---------- + secret_id : str + + header : typing.Optional[HeaderDto] + + secret : typing.Optional[SecretDto] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SecretResponseDto + Successful Response + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.vault.update_secret( + secret_id="secret_id", + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"vault/v1/secrets/{jsonable_encoder(secret_id)}", + method="PUT", + json={ + "header": convert_and_respect_annotation_metadata( + object_=header, annotation=HeaderDto, direction="write" + ), + "secret": convert_and_respect_annotation_metadata( + object_=secret, annotation=SecretDto, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + SecretResponseDto, + parse_obj_as( + type_=SecretResponseDto, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + def delete_secret( + self, secret_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + Parameters + ---------- + secret_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from agenta import AgentaApi + + client = AgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + client.vault.delete_secret( + secret_id="secret_id", + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"vault/v1/secrets/{jsonable_encoder(secret_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + +class AsyncVaultClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_secrets( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[SecretResponseDto]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[SecretResponseDto] + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.vault.list_secrets() + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "vault/v1/secrets", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + typing.List[SecretResponseDto], + parse_obj_as( + type_=typing.List[SecretResponseDto], # type: ignore + object_=_response.json(), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + async def create_secret( + self, + *, + secret: SecretDto, + header: typing.Optional[HeaderDto] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SecretResponseDto: + """ + Parameters + ---------- + secret : SecretDto + + header : typing.Optional[HeaderDto] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SecretResponseDto + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi, ProviderKeyDto, SecretDto + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.vault.create_secret( + secret=SecretDto( + data=ProviderKeyDto( + provider="openai", + key="key", + ), + ), + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "vault/v1/secrets", + method="POST", + json={ + "header": convert_and_respect_annotation_metadata( + object_=header, annotation=HeaderDto, direction="write" + ), + "secret": convert_and_respect_annotation_metadata( + object_=secret, annotation=SecretDto, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + SecretResponseDto, + parse_obj_as( + type_=SecretResponseDto, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + async def read_secret( + self, secret_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> SecretResponseDto: + """ + Parameters + ---------- + secret_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SecretResponseDto + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.vault.read_secret( + secret_id="secret_id", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"vault/v1/secrets/{jsonable_encoder(secret_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + SecretResponseDto, + parse_obj_as( + type_=SecretResponseDto, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + async def update_secret( + self, + secret_id: str, + *, + header: typing.Optional[HeaderDto] = OMIT, + secret: typing.Optional[SecretDto] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SecretResponseDto: + """ + Parameters + ---------- + secret_id : str + + header : typing.Optional[HeaderDto] + + secret : typing.Optional[SecretDto] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SecretResponseDto + Successful Response + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.vault.update_secret( + secret_id="secret_id", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"vault/v1/secrets/{jsonable_encoder(secret_id)}", + method="PUT", + json={ + "header": convert_and_respect_annotation_metadata( + object_=header, annotation=HeaderDto, direction="write" + ), + "secret": convert_and_respect_annotation_metadata( + object_=secret, annotation=SecretDto, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + SecretResponseDto, + parse_obj_as( + type_=SecretResponseDto, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + + async def delete_secret( + self, secret_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + Parameters + ---------- + secret_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from agenta import AsyncAgentaApi + + client = AsyncAgentaApi( + api_key="YOUR_API_KEY", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.vault.delete_secret( + secret_id="secret_id", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"vault/v1/secrets/{jsonable_encoder(secret_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + HttpValidationError, + parse_obj_as( + type_=HttpValidationError, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) diff --git a/agenta-web/package-lock.json b/agenta-web/package-lock.json index 517a90d851..5b5a627212 100644 --- a/agenta-web/package-lock.json +++ b/agenta-web/package-lock.json @@ -8,6 +8,10 @@ "name": "agenta", "version": "0.29.0", "dependencies": { + "@ag-grid-community/client-side-row-model": "^31.3.4", + "@ag-grid-community/core": "^31.3.4", + "@ag-grid-community/react": "^31.3.4", + "@ag-grid-community/styles": "^31.3.4", "@ant-design/colors": "^7.0.0", "@ant-design/icons": "^5.3.7", "@dnd-kit/core": "^6.1.0", @@ -39,8 +43,6 @@ "@types/react-resizable": "^3.0.7", "@types/react-syntax-highlighter": "^15.5.7", "@types/uuid": "^9.0.7", - "ag-grid-community": "^31.3.4", - "ag-grid-react": "^31.3.4", "antd": "^5.20.6", "autoprefixer": "10.4.14", "axios": "^1.7.7", @@ -93,6 +95,45 @@ "node": ">=18" } }, + "node_modules/@ag-grid-community/client-side-row-model": { + "version": "31.3.4", + "resolved": "https://registry.npmjs.org/@ag-grid-community/client-side-row-model/-/client-side-row-model-31.3.4.tgz", + "integrity": "sha512-lbrbhwSoY1/N2CiOQBa322gVnrhyrbzyG7cvbOef3itAiaLUzQ03PWBw09d9mj+/0hnqMs386ZuGX15FWJaTqQ==", + "license": "MIT", + "dependencies": { + "@ag-grid-community/core": "31.3.4", + "tslib": "^2.3.0" + } + }, + "node_modules/@ag-grid-community/core": { + "version": "31.3.4", + "resolved": "https://registry.npmjs.org/@ag-grid-community/core/-/core-31.3.4.tgz", + "integrity": "sha512-Fz7P4lMAYDo9rG6jOA8OXlAXta/4E+PiCdjT7M9OloaXAJtU47oO4f3VKV7rdbVCC8SGSo0cBCateddQTAFCNQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + } + }, + "node_modules/@ag-grid-community/react": { + "version": "31.3.4", + "resolved": "https://registry.npmjs.org/@ag-grid-community/react/-/react-31.3.4.tgz", + "integrity": "sha512-mNkVEDiatxny6AeNIxkJe/oNsakV0RaP6qqyDla/bijKFX1cVR7t13SFYMujxdM3lF7j1s+HHJv1Z4dg9xRqSw==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@ag-grid-community/core": "31.3.4", + "react": "^16.3.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.3.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@ag-grid-community/styles": { + "version": "31.3.4", + "resolved": "https://registry.npmjs.org/@ag-grid-community/styles/-/styles-31.3.4.tgz", + "integrity": "sha512-5pgt/Qq/GxiJi59UA17ltG5U4r0J+GB3S/QCysJFi6kmgmCDsbCfisekTwSh0xxOGO+OIhejoqsOuEnTcw78kg==", + "license": "MIT" + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -5566,26 +5607,6 @@ "node": ">=0.4.0" } }, - "node_modules/ag-grid-community": { - "version": "31.3.4", - "resolved": "https://registry.npmjs.org/ag-grid-community/-/ag-grid-community-31.3.4.tgz", - "integrity": "sha512-jOxQO86C6eLnk1GdP24HB6aqaouFzMWizgfUwNY5MnetiWzz9ZaAmOGSnW/XBvdjXvC5Fpk3gSbvVKKQ7h9kBw==", - "license": "MIT" - }, - "node_modules/ag-grid-react": { - "version": "31.3.4", - "resolved": "https://registry.npmjs.org/ag-grid-react/-/ag-grid-react-31.3.4.tgz", - "integrity": "sha512-WmPASHRFGSTxCMRStWG5bRtln0Ugsdqbb3+Y8sEyGHeLw4hXqfpqie3lT9kqCOl7wPWUjCpwmFdXzRnWPmyyeg==", - "license": "MIT", - "dependencies": { - "ag-grid-community": "31.3.4", - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "react": "^16.3.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.3.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", diff --git a/agenta-web/package.json b/agenta-web/package.json index 75a559faae..deb7c42abb 100644 --- a/agenta-web/package.json +++ b/agenta-web/package.json @@ -22,6 +22,10 @@ "prepare": "cd .. && husky && if [ \"$FEATURE_FLAG\" = \"cloud-dev\" ]; then cd .. && husky; fi || true" }, "dependencies": { + "@ag-grid-community/core": "^31.3.4", + "@ag-grid-community/react": "^31.3.4", + "@ag-grid-community/client-side-row-model": "^31.3.4", + "@ag-grid-community/styles": "^31.3.4", "@ant-design/colors": "^7.0.0", "@ant-design/icons": "^5.3.7", "@dnd-kit/core": "^6.1.0", @@ -53,8 +57,6 @@ "@types/react-resizable": "^3.0.7", "@types/react-syntax-highlighter": "^15.5.7", "@types/uuid": "^9.0.7", - "ag-grid-community": "^31.3.4", - "ag-grid-react": "^31.3.4", "antd": "^5.20.6", "autoprefixer": "10.4.14", "axios": "^1.7.7", diff --git a/agenta-web/src/components/HumanEvaluationModal/HumanEvaluationModal.tsx b/agenta-web/src/components/HumanEvaluationModal/HumanEvaluationModal.tsx index 2383cac732..65ad58c868 100644 --- a/agenta-web/src/components/HumanEvaluationModal/HumanEvaluationModal.tsx +++ b/agenta-web/src/components/HumanEvaluationModal/HumanEvaluationModal.tsx @@ -6,7 +6,7 @@ import {isDemo} from "@/lib/helpers/utils" import {Button, Col, Dropdown, MenuProps, Modal, ModalProps, Row, Spin, message} from "antd" import {getErrorMessage} from "@/lib/helpers/errorHandler" import {EvaluationType} from "@/lib/enums" -import {PERMISSION_ERR_MSG} from "@/lib/helpers/axiosConfig" +import {PERMISSION_ERR_MSG} from "@/lib/api/assets/axiosConfig" import {getAllVariantParameters} from "@/lib/helpers/variantHelper" import {useRouter} from "next/router" import {useAppTheme} from "../Layout/ThemeContextProvider" diff --git a/agenta-web/src/components/Playground/Views/ParametersView.tsx b/agenta-web/src/components/Playground/Views/ParametersView.tsx index 1cc74ab20e..72da9eda90 100644 --- a/agenta-web/src/components/Playground/Views/ParametersView.tsx +++ b/agenta-web/src/components/Playground/Views/ParametersView.tsx @@ -7,7 +7,7 @@ import {ModelParameters, ObjectParameters, StringParameters} from "./ParametersC import PublishVariantModal from "./PublishVariantModal" import {deleteSingleVariant} from "@/services/playground/api" import {CloudUploadOutlined, DeleteOutlined, HistoryOutlined, SaveOutlined} from "@ant-design/icons" -import {usePostHogAg} from "@/hooks/usePostHogAg" +import {usePostHogAg} from "@/lib/helpers/analytics/hooks/usePostHogAg" import {isDemo} from "@/lib/helpers/utils" import {useQueryParam} from "@/hooks/useQuery" import {dynamicComponent, dynamicService} from "@/lib/helpers/dynamic" @@ -128,7 +128,7 @@ const ParametersView: React.FC = ({ onStateChange(false) res(true) }) - posthog.capture("variant_saved", {variant_id: variant.variantId}) + posthog?.capture?.("variant_saved", {variant_id: variant.variantId}) }) } diff --git a/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx b/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx index f4f03608a8..ab4221e04c 100644 --- a/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx +++ b/agenta-web/src/components/Playground/Views/PublishVariantModal.tsx @@ -1,4 +1,4 @@ -import {usePostHogAg} from "@/hooks/usePostHogAg" +import {usePostHogAg} from "@/lib/helpers/analytics/hooks/usePostHogAg" import {Environment, Variant} from "@/lib/Types" import {variantNameWithRev} from "@/lib/helpers/variantHelper" import {fetchEnvironments, createPublishVariant} from "@/services/deployment/api" @@ -58,7 +58,7 @@ const PublishVariantModal: React.FC = ({ closeModal() await loadEnvironments() message.success(`Published ${variant.variantName} to ${envName}`) - posthog.capture("app_deployed", {app_id: appId, environment: envName}) + posthog?.capture?.("app_deployed", {app_id: appId, environment: envName}) }) } diff --git a/agenta-web/src/components/TestSetTable/EditRowModal.tsx b/agenta-web/src/components/TestSetTable/EditRowModal.tsx index 2e593f265c..73f646473b 100644 --- a/agenta-web/src/components/TestSetTable/EditRowModal.tsx +++ b/agenta-web/src/components/TestSetTable/EditRowModal.tsx @@ -1,11 +1,12 @@ import {capitalize} from "@/lib/helpers/utils" -import {AgGridReact} from "ag-grid-react" import {Input, Modal} from "antd" import React, {forwardRef, useCallback, useImperativeHandle, useMemo, useState} from "react" import {useAppTheme} from "../Layout/ThemeContextProvider" import useResizeObserver from "@/hooks/useResizeObserver" import {createUseStyles} from "react-jss" +import AgGridReact from "@/lib/helpers/agGrid" + const useStyles = createUseStyles({ cellContainer: { lineHeight: "22px", diff --git a/agenta-web/src/components/TestSetTable/TableCellsRenderer.tsx b/agenta-web/src/components/TestSetTable/TableCellsRenderer.tsx index 9c7461329c..6e20673a67 100644 --- a/agenta-web/src/components/TestSetTable/TableCellsRenderer.tsx +++ b/agenta-web/src/components/TestSetTable/TableCellsRenderer.tsx @@ -1,7 +1,7 @@ import {Tooltip} from "antd" import {createUseStyles} from "react-jss" import {EditOutlined} from "@ant-design/icons" -import {ICellRendererParams} from "ag-grid-community" +import {type ICellRendererParams} from "@ag-grid-community/core" const useStylesCell = createUseStyles({ cellContainer: { diff --git a/agenta-web/src/components/TestSetTable/TestsetTable.tsx b/agenta-web/src/components/TestSetTable/TestsetTable.tsx index 42c216760f..7047529804 100644 --- a/agenta-web/src/components/TestSetTable/TestsetTable.tsx +++ b/agenta-web/src/components/TestSetTable/TestsetTable.tsx @@ -1,6 +1,6 @@ import React, {useState, useRef, useEffect, ReactNode} from "react" -import {AgGridReact} from "ag-grid-react" -import {IHeaderParams} from "ag-grid-community" + +import {type IHeaderParams} from "@ag-grid-community/core" import {createUseStyles} from "react-jss" import {Button, Input, Typography, message} from "antd" import TestsetMusHaveNameModal from "./InsertTestsetNameModal" @@ -17,6 +17,7 @@ import {NoticeType} from "antd/es/message/interface" import {GenericObject, KeyValuePair} from "@/lib/Types" import TableCellsRenderer from "./TableCellsRenderer" import TableHeaderComponent from "./TableHeaderComponent" +import AgGridReact, {type AgGridReactType} from "@/lib/helpers/agGrid" type TestsetTableProps = { mode: "edit" @@ -83,7 +84,7 @@ const TestsetTable: React.FC = ({mode}) => { const [inputValues, setInputValues] = useStateCallback(columnDefs.map((col) => col.field)) const [focusedRowData, setFocusedRowData] = useState() const [writeMode, setWriteMode] = useState(mode) - const gridRef = useRef(null) + const [gridRef, setGridRef] = useState() const [selectedRow, setSelectedRow] = useState([]) @@ -180,8 +181,8 @@ const TestsetTable: React.FC = ({mode}) => { setColumnDefs(newColumnDefs) setRowData(newRowData) - if (gridRef.current) { - gridRef.current.setColumnDefs(newColumnDefs) + if (gridRef) { + gridRef.setColumnDefs(newColumnDefs) } } @@ -224,12 +225,14 @@ const TestsetTable: React.FC = ({mode}) => { } const onRowSelectedOrDeselected = () => { - if (!gridRef?.current) return - setSelectedRow(gridRef?.current?.getSelectedNodes()) + if (!gridRef) return + const selectedNodes = gridRef?.getSelectedNodes() + setSelectedRow(selectedNodes as any) } const onDeleteRow = () => { - const selectedNodes = gridRef.current.getSelectedNodes() + if (!gridRef) return + const selectedNodes = gridRef.getSelectedNodes() const selectedData = selectedNodes.map((node: GenericObject) => node.data) const newrowData = rowData.filter((row) => !selectedData.includes(row)) setRowData(newrowData) @@ -256,8 +259,8 @@ const TestsetTable: React.FC = ({mode}) => { setColumnDefs(newColumnDefs) setRowData(newRowData) setIsDataChanged(true) - if (gridRef.current) { - gridRef.current.setColumnDefs(newColumnDefs) + if (gridRef) { + gridRef.setColumnDefs(newColumnDefs) } } @@ -342,7 +345,7 @@ const TestsetTable: React.FC = ({mode}) => { style={{height: 500}} > (gridRef.current = params.api)} + onGridReady={(params) => setGridRef(params.api)} rowData={rowData} columnDefs={columnDefs} defaultColDef={defaultColDef} diff --git a/agenta-web/src/components/pages/app-management/index.tsx b/agenta-web/src/components/pages/app-management/index.tsx index 72b760b8ac..6a69ef89a7 100644 --- a/agenta-web/src/components/pages/app-management/index.tsx +++ b/agenta-web/src/components/pages/app-management/index.tsx @@ -7,7 +7,7 @@ import {waitForAppToStart} from "@/services/api" import {createUseStyles} from "react-jss" import {useAppsData} from "@/contexts/app.context" import {useProfileData} from "@/contexts/profile.context" -import {usePostHogAg} from "@/hooks/usePostHogAg" +import {usePostHogAg} from "@/lib/helpers/analytics/hooks/usePostHogAg" import {LlmProvider, getAllProviderLlmKeys} from "@/lib/helpers/llmProviders" import {dynamicComponent, dynamicContext} from "@/lib/helpers/dynamic" import dayjs from "dayjs" @@ -130,7 +130,7 @@ const AppManagement: React.FC = () => { setFetchingTemplate(false) if (status === "success") { mutate() - posthog.capture("app_deployment", { + posthog?.capture?.("app_deployment", { properties: { app_id: appId, environment: "UI", diff --git a/agenta-web/src/components/pages/evaluations/FilterColumns/FilterColumns.tsx b/agenta-web/src/components/pages/evaluations/FilterColumns/FilterColumns.tsx index f008293c0e..ff7be0ca1f 100644 --- a/agenta-web/src/components/pages/evaluations/FilterColumns/FilterColumns.tsx +++ b/agenta-web/src/components/pages/evaluations/FilterColumns/FilterColumns.tsx @@ -4,7 +4,7 @@ import {Button, Dropdown, Space} from "antd" import {ItemType} from "antd/es/menu/interface" import React from "react" import {createUseStyles} from "react-jss" -import {ColDef} from "ag-grid-community" +import {type ColDef} from "@ag-grid-community/core" const useStyles = createUseStyles((theme: JSSTheme) => ({ dropdownMenu: { diff --git a/agenta-web/src/components/pages/evaluations/cellRenderers/cellRenderers.tsx b/agenta-web/src/components/pages/evaluations/cellRenderers/cellRenderers.tsx index 9d7e3bb75a..48ef1e6173 100644 --- a/agenta-web/src/components/pages/evaluations/cellRenderers/cellRenderers.tsx +++ b/agenta-web/src/components/pages/evaluations/cellRenderers/cellRenderers.tsx @@ -12,7 +12,7 @@ import { FullscreenOutlined, InfoCircleOutlined, } from "@ant-design/icons" -import {ICellRendererParams} from "ag-grid-community" +import {type ICellRendererParams} from "@ag-grid-community/core" import {GlobalToken, Space, Tooltip, Typography, message, theme} from "antd" import dayjs from "dayjs" import relativeTime from "dayjs/plugin/relativeTime" diff --git a/agenta-web/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx b/agenta-web/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx index c8d7de665f..5da1fd9f24 100644 --- a/agenta-web/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx +++ b/agenta-web/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx @@ -8,9 +8,8 @@ import { _Evaluation, _EvaluationScenario, } from "@/lib/Types" -import {ColDef, ICellRendererParams} from "ag-grid-community" +import {type ColDef, type ICellRendererParams} from "@ag-grid-community/core" import {fetchAllComparisonResults} from "@/services/evaluations/api" -import {AgGridReact} from "ag-grid-react" import {Button, DropdownProps, Space, Spin, Tag, Tooltip, Typography} from "antd" import React, {useEffect, useMemo, useRef, useState} from "react" import {createUseStyles} from "react-jss" @@ -33,6 +32,7 @@ import {escapeNewlines} from "@/lib/helpers/fileManipulations" import EvaluationErrorModal from "../EvaluationErrorProps/EvaluationErrorModal" import EvaluationErrorText from "../EvaluationErrorProps/EvaluationErrorText" import {getStringOrJson} from "@/lib/helpers/utils" +import AgGridReact, {type AgGridReactType} from "@/lib/helpers/agGrid" const useStyles = createUseStyles((theme: JSSTheme) => ({ table: { @@ -87,7 +87,7 @@ const EvaluationCompareMode: React.FC = () => { const [rows, setRows] = useState([]) const [testset, setTestset] = useState() const [evaluators] = useAtom(evaluatorsAtom) - const gridRef = useRef>() + const [gridRef, setGridRef] = useState>() const [isFilterColsDropdownOpen, setIsFilterColsDropdownOpen] = useState(false) const [isDiffDropdownOpen, setIsDiffDropdownOpen] = useState(false) const [selectedCorrectAnswer, setSelectedCorrectAnswer] = useState(["noDiffColumnIsSelected"]) @@ -375,14 +375,14 @@ const EvaluationCompareMode: React.FC = () => { setRows(rows) setTestset(testset) setTimeout(() => { - if (!gridRef.current) return + if (!gridRef) return const ids: string[] = - gridRef.current.api + gridRef.api .getColumns() ?.filter((column) => column.getColDef().field?.endsWith("result")) ?.map((item) => item.getColId()) || [] - gridRef.current.api.autoSizeColumns(ids, false) + gridRef.api.autoSizeColumns(ids, false) setFetching(false) }, 100) }) @@ -390,8 +390,9 @@ const EvaluationCompareMode: React.FC = () => { } useEffect(() => { + if (!gridRef) return fetcher() - }, [appId, evaluationIdsStr]) + }, [appId, evaluationIdsStr, gridRef]) const handleToggleVariantVisibility = (evalId: string) => { if (!hiddenVariants.includes(evalId)) { @@ -437,7 +438,7 @@ const EvaluationCompareMode: React.FC = () => { } const onExport = (): void => { - const gridApi = gridRef.current?.api + const gridApi = gridRef?.api if (!gridApi) return const {currentApp} = getAppValues() @@ -576,7 +577,7 @@ const EvaluationCompareMode: React.FC = () => { data-cy="evaluation-compare-table" > - ref={gridRef as any} + gridRef={setGridRef} rowData={rows} columnDefs={colDefs} getRowId={(params) => params.data.rowId} diff --git a/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx b/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx index 0fe867e6d2..a7f3c60826 100644 --- a/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx +++ b/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx @@ -13,8 +13,7 @@ import { fetchAllEvaluators, } from "@/services/evaluations/api" import {CheckOutlined, DeleteOutlined, DownloadOutlined} from "@ant-design/icons" -import {ColDef, ICellRendererParams} from "ag-grid-community" -import {AgGridReact} from "ag-grid-react" +import {type ColDef, type ICellRendererParams} from "@ag-grid-community/core" import {DropdownProps, Space, Spin, Tag, Tooltip, Typography} from "antd" import {useRouter} from "next/router" import React, {useEffect, useMemo, useRef, useState} from "react" @@ -36,6 +35,7 @@ import FilterColumns, {generateFilterItems} from "../FilterColumns/FilterColumns import {variantNameWithRev} from "@/lib/helpers/variantHelper" import {escapeNewlines} from "@/lib/helpers/fileManipulations" import {getStringOrJson} from "@/lib/helpers/utils" +import AgGridReact, {type AgGridReactType} from "@/lib/helpers/agGrid" const useStyles = createUseStyles((theme: JSSTheme) => ({ infoRow: { @@ -66,7 +66,7 @@ const EvaluationScenarios: React.FC = () => { const [scenarios, setScenarios] = useState<_EvaluationScenario[]>([]) const [fetching, setFetching] = useState(false) const [evaluators, setEvaluators] = useAtom(evaluatorsAtom) - const gridRef = useRef>() + const [gridRef, setGridRef] = useState>() const evalaution = scenarios[0]?.evaluation const [selectedCorrectAnswer, setSelectedCorrectAnswer] = useState(["noDiffColumnIsSelected"]) const [isFilterColsDropdownOpen, setIsFilterColsDropdownOpen] = useState(false) @@ -287,14 +287,14 @@ const EvaluationScenarios: React.FC = () => { setScenarios(scenarios) setEvaluators(evaluators) setTimeout(() => { - if (!gridRef.current) return + if (!gridRef) return const ids: string[] = - gridRef.current.api + gridRef.api .getColumns() ?.filter((column) => column.getColDef().field === "results") ?.map((item) => item.getColId()) || [] - gridRef.current.api.autoSizeColumns(ids, false) + gridRef.api.autoSizeColumns(ids, false) setFetching(false) }, 100) }) @@ -303,13 +303,14 @@ const EvaluationScenarios: React.FC = () => { } useEffect(() => { + if (!gridRef) return fetcher() - }, [appId, evaluationId]) + }, [appId, gridRef, evaluationId]) const onExport = () => { - if (!gridRef.current) return + if (!gridRef) return const {currentApp} = getAppValues() - gridRef.current.api.exportDataAsCsv({ + gridRef.api.exportDataAsCsv({ fileName: `${currentApp?.app_name}_${evalaution.variants[0].variantName}.csv`, processHeaderCallback: (params) => { if (params.column.getColDef().headerName === "Output") { @@ -422,7 +423,7 @@ const EvaluationScenarios: React.FC = () => { data-cy="evalaution-scenarios-table" > - ref={gridRef as any} + gridRef={setGridRef} rowData={scenarios} columnDefs={colDefs} getRowId={(params) => params.data.id} diff --git a/agenta-web/src/contexts/app.context.tsx b/agenta-web/src/contexts/app.context.tsx index 548aa34f39..e0e6768dc7 100644 --- a/agenta-web/src/contexts/app.context.tsx +++ b/agenta-web/src/contexts/app.context.tsx @@ -45,12 +45,9 @@ const useApps = () => { const isMockProjectId = projectId === DEFAULT_UUID const {selectedOrg, loading} = useOrgData() + const shouldFetch = !!user && (!isDemo() || !!selectedOrg?.id) const {data, error, isLoading, mutate} = useSWR( - !!user - ? `${getAgentaApiUrl()}/api/apps?` + - (!isMockProjectId ? `project_id=${projectId}&` : "") - : null, - !!user ? (isDemo() ? (selectedOrg?.id ? axiosFetcher : () => {}) : axiosFetcher) : null, + shouldFetch ? `/api/apps?` + (!isMockProjectId ? `project_id=${projectId}&` : "") : null, { shouldRetryOnError: false, }, diff --git a/agenta-web/src/contexts/profile.context.tsx b/agenta-web/src/contexts/profile.context.tsx index a59ffa4229..815b616fcf 100644 --- a/agenta-web/src/contexts/profile.context.tsx +++ b/agenta-web/src/contexts/profile.context.tsx @@ -1,4 +1,4 @@ -import {usePostHogAg} from "@/hooks/usePostHogAg" +import {usePostHogAg} from "@/lib/helpers/analytics/hooks/usePostHogAg" import {useSession} from "@/hooks/useSession" import useStateCallback from "@/hooks/useStateCallback" import {isDemo} from "@/lib/helpers/utils" @@ -38,7 +38,7 @@ const ProfileContextProvider: React.FC = ({children}) => { setLoading(true) fetchProfile() .then((profile) => { - posthog.identify() + posthog?.identify?.() setUser(profile.data, onSuccess) }) .catch((error) => { diff --git a/agenta-web/src/hooks/useAgGridCustomHeaders.tsx b/agenta-web/src/hooks/useAgGridCustomHeaders.tsx deleted file mode 100644 index 221ef33edf..0000000000 --- a/agenta-web/src/hooks/useAgGridCustomHeaders.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import {ColDef} from "ag-grid-community" -import {useEffect, useMemo, useState} from "react" -import {createCache, extractStyle, StyleProvider} from "@ant-design/cssinjs" -import type Entity from "@ant-design/cssinjs/es/Cache" -import {renderToString} from "react-dom/server" -import {AgGridReact} from "ag-grid-react" - -export const useAgGridCustomHeaders = (gridApi?: AgGridReact["api"]) => { - const [gridRendered, setGridRendered] = useState(false) - const cache = useMemo(() => createCache(), []) - - const colDefs: ColDef[] = gridApi?.getColumnDefs() || [] - - useEffect(() => { - gridApi?.addEventListener("firstDataRendered", () => { - setGridRendered(true) - }) - }, [gridApi]) - - useEffect(() => { - if (!gridRendered) return - - const colDefsMap: Record = {} - - colDefs - .filter((colDef) => !!colDef.headerComponentParams?.headerNode) - .forEach((colDef) => { - const field = colDef.field! - if (!colDefsMap[field]) colDefsMap[field] = [] - colDefsMap[field].push(colDef) - }) - - Object.entries(colDefsMap).forEach(([field, colDefs]) => { - document.querySelectorAll(`.ag-header-cell[col-id^="${field}"]`).forEach((el, ix) => { - const html = renderToString( - - - {colDefs[ix].headerComponentParams.headerNode} - - , - ) - const headerNode = el.querySelector(".ag-header-cell-text") - if (headerNode) { - headerNode.innerHTML = html - const agHeader = document.querySelector(".ag-header-row") - - if (agHeader) { - // re insert html if it gets removed - const observer = new MutationObserver(() => { - document - .querySelectorAll(`.ag-header-cell[col-id^="${field}"]`) - .forEach((el) => { - const hasCustomHeader = !!el.querySelector( - ".agenta-custom-header-ag-grid", - ) - if (!hasCustomHeader) headerNode.innerHTML = html - }) - }) - observer.observe(agHeader, { - characterData: false, - childList: true, - attributes: false, - }) - } - } - }) - }) - - const styleText = extractStyle(cache) - let styleNode = document.querySelector("#antd-custom-style-node") - if (!styleNode) { - styleNode = document.createElement("div") - styleNode.id = "antd-custom-style-node" - document.body.appendChild(styleNode) - } - styleNode.innerHTML = styleText - }, [colDefs, gridRendered]) -} diff --git a/agenta-web/src/hooks/usePostHogAg.ts b/agenta-web/src/hooks/usePostHogAg.ts deleted file mode 100644 index 3fbac630cd..0000000000 --- a/agenta-web/src/hooks/usePostHogAg.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {useLayoutEffect} from "react" -import {isDemo, generateOrRetrieveDistinctId} from "@/lib/helpers/utils" -import {usePostHog} from "posthog-js/react" -import {useProfileData} from "@/contexts/profile.context" -import useIsomorphicLayoutEffect from "./useIsomorphicLayoutEffect" - -export const usePostHogAg = () => { - const trackingEnabled = process.env.NEXT_PUBLIC_TELEMETRY_TRACKING_ENABLED === "true" - const {user} = useProfileData() - const posthog = usePostHog() - - const _id: string | undefined = isDemo() ? user?.email : generateOrRetrieveDistinctId() - - const capture: typeof posthog.capture = (...args) => { - if (trackingEnabled && user?.id) { - return posthog.capture(...args) - } - return undefined - } - - const identify: typeof posthog.identify = (id, ...args) => { - if (trackingEnabled && user?.id) { - posthog.identify(_id !== undefined ? _id : id, ...args) - } - } - - useIsomorphicLayoutEffect(() => { - if (!trackingEnabled) posthog.opt_out_capturing() - }, [trackingEnabled]) - - useIsomorphicLayoutEffect(() => { - if (posthog.get_distinct_id() !== _id) identify() - }, [user?.id]) - - return {...posthog, identify, capture} -} diff --git a/agenta-web/src/hooks/useSession.ts b/agenta-web/src/hooks/useSession.ts index df7fb53c03..8312247920 100644 --- a/agenta-web/src/hooks/useSession.ts +++ b/agenta-web/src/hooks/useSession.ts @@ -1,7 +1,6 @@ import {useProfileData} from "@/contexts/profile.context" import {isDemo} from "@/lib/helpers/utils" import {useRouter} from "next/router" -import posthog from "posthog-js" import {useSessionContext} from "supertokens-auth-react/recipe/session" import {signOut} from "supertokens-auth-react/recipe/session" @@ -17,7 +16,8 @@ export const useSession: () => {loading: boolean; doesSessionExist: boolean; log doesSessionExist: (res as any).doesSessionExist, logout: () => { signOut() - .then(() => { + .then(async () => { + const posthog = (await import("posthog-js")).default posthog.reset() reset() router.push("/auth") diff --git a/agenta-web/src/lib/api/SWRConfig.tsx b/agenta-web/src/lib/api/SWRConfig.tsx new file mode 100644 index 0000000000..b393aedbdf --- /dev/null +++ b/agenta-web/src/lib/api/SWRConfig.tsx @@ -0,0 +1,19 @@ +import {SWRConfig, type SWRConfiguration} from "swr" +import axios from "@/lib/api/assets/axiosConfig" + +const config: SWRConfiguration = { + fetcher: (url: string) => axios.get(url).then((res) => res.data), +} + +const AgSWRConfig = ({ + children, + config: passedConfig = {}, +}: { + children: React.ReactNode + config?: Partial +}) => { + const mergedConfig = {...config, ...passedConfig} + return {children} +} + +export default AgSWRConfig diff --git a/agenta-web/src/lib/helpers/axiosConfig.ts b/agenta-web/src/lib/api/assets/axiosConfig.ts similarity index 94% rename from agenta-web/src/lib/helpers/axiosConfig.ts rename to agenta-web/src/lib/api/assets/axiosConfig.ts index 0961ebe87c..3da6bbe2a5 100644 --- a/agenta-web/src/lib/helpers/axiosConfig.ts +++ b/agenta-web/src/lib/api/assets/axiosConfig.ts @@ -1,8 +1,8 @@ import axiosApi from "axios" -import {getErrorMessage, globalErrorHandler} from "./errorHandler" +import {getErrorMessage, globalErrorHandler} from "../../helpers/errorHandler" import {signOut} from "supertokens-auth-react/recipe/session" import router from "next/router" -import {getAgentaApiUrl} from "./utils" +import {getAgentaApiUrl} from "../../helpers/utils" import isObject from "lodash/isObject" import AlertPopup from "@/components/AlertPopup/AlertPopup" diff --git a/agenta-web/src/lib/helpers/agGrid.tsx b/agenta-web/src/lib/helpers/agGrid.tsx new file mode 100644 index 0000000000..e6e5565da7 --- /dev/null +++ b/agenta-web/src/lib/helpers/agGrid.tsx @@ -0,0 +1,38 @@ +import type {SetStateAction, ComponentProps, LegacyRef} from "react" +import dynamic from "next/dynamic" +import {type AgGridReact as AgGridReactType, AgGridReactProps} from "@ag-grid-community/react" + +type ComponentType = ( + props: AgGridReactProps & { + gridRef?: LegacyRef> | ((ref: AgGridReactType) => void) + }, +) => JSX.Element + +const AgGridReact = dynamic( + async (): Promise => { + const ClientSideRowModelModule = await import( + "@ag-grid-community/client-side-row-model" + ).then((module) => module.ClientSideRowModelModule) + const ModuleRegistry = await import("@ag-grid-community/core").then( + (module) => module.ModuleRegistry, + ) + ModuleRegistry.registerModules([ClientSideRowModelModule]) + + const AgGridReact = await import("@ag-grid-community/react").then((mod) => mod.AgGridReact) + + const GridWrapper = ( + props: AgGridReactProps & {gridRef?: LegacyRef>}, + ) => { + return + } + + return GridWrapper as ComponentType + }, + { + ssr: false, + }, +) + +export type {AgGridReactType} + +export default AgGridReact as unknown as ComponentType diff --git a/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx b/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx new file mode 100644 index 0000000000..f4d0b3f7f3 --- /dev/null +++ b/agenta-web/src/lib/helpers/analytics/AgPosthogProvider.tsx @@ -0,0 +1,55 @@ +import {useCallback, useEffect, useRef, useState} from "react" +import {useRouter} from "next/router" +import {useAtom} from "jotai" +import {posthogAtom, type PostHogConfig} from "./store/atoms" +import {CustomPosthogProviderType} from "./types" + +const CustomPosthogProvider: CustomPosthogProviderType = ({children, config}) => { + const router = useRouter() + const [loadingPosthog, setLoadingPosthog] = useState(false) + const [posthogClient, setPosthogClient] = useAtom(posthogAtom) + + const initPosthog = useCallback(async () => { + if (!!posthogClient) return + if (loadingPosthog) return + + setLoadingPosthog(true) + + try { + const posthog = (await import("posthog-js")).default + + posthog.init(process.env.NEXT_PUBLIC_POSTHOG_API_KEY!, { + api_host: "https://app.posthog.com", + // Enable debug mode in development + loaded: (posthog) => { + setPosthogClient(posthog) + if (process.env.NODE_ENV === "development") posthog.debug() + }, + capture_pageview: false, + ...config, + }) + } finally { + setLoadingPosthog(false) + } + }, [loadingPosthog, config, posthogClient, setPosthogClient]) + + useEffect(() => { + initPosthog() + }, [initPosthog]) + + const handleRouteChange = useCallback(() => { + posthogClient?.capture("$pageview", {$current_url: window.location.href}) + }, [posthogClient]) + + useEffect(() => { + router.events.on("routeChangeComplete", handleRouteChange) + + return () => { + router.events.off("routeChangeComplete", handleRouteChange) + } + }, [handleRouteChange, router.events]) + + return <>{children} +} + +export default CustomPosthogProvider diff --git a/agenta-web/src/lib/helpers/analytics/hooks/usePostHogAg.ts b/agenta-web/src/lib/helpers/analytics/hooks/usePostHogAg.ts new file mode 100644 index 0000000000..ab3bb2a08f --- /dev/null +++ b/agenta-web/src/lib/helpers/analytics/hooks/usePostHogAg.ts @@ -0,0 +1,51 @@ +import {isDemo, generateOrRetrieveDistinctId} from "@/lib/helpers/utils" +import {useProfileData} from "@/contexts/profile.context" +import {useAtom} from "jotai" +import {posthogAtom} from "../store/atoms" +import {type PostHog} from "posthog-js" +import useIsomorphicLayoutEffect from "@/hooks/useIsomorphicLayoutEffect" + +interface ExtendedPostHog extends PostHog { + identify: PostHog["identify"] + capture: PostHog["capture"] +} + +export const usePostHogAg = (): ExtendedPostHog | null => { + const trackingEnabled = process.env.NEXT_PUBLIC_TELEMETRY_TRACKING_ENABLED === "true" + const {user} = useProfileData() + const [posthog] = useAtom(posthogAtom) + + const _id: string | undefined = isDemo() ? user?.email : generateOrRetrieveDistinctId() + const capture: PostHog["capture"] = (...args) => { + if (trackingEnabled && user?.id) { + return posthog?.capture?.(...args) + } + return undefined + } + const identify: PostHog["identify"] = (id, ...args) => { + if (trackingEnabled && user?.id) { + posthog?.identify?.(_id !== undefined ? _id : id, ...args) + } + } + useIsomorphicLayoutEffect(() => { + if (!posthog) return + + if (!trackingEnabled) { + console.log("POSTHOG: opt_out_capturing") + posthog.opt_out_capturing() + } + }, [posthog, trackingEnabled]) + + useIsomorphicLayoutEffect(() => { + if (!posthog) return + if (posthog.get_distinct_id() !== _id) identify() + }, [posthog, _id]) + + return posthog + ? ({ + ...posthog, + identify, + capture, + } as ExtendedPostHog) + : null +} diff --git a/agenta-web/src/lib/helpers/analytics/store/atoms.ts b/agenta-web/src/lib/helpers/analytics/store/atoms.ts new file mode 100644 index 0000000000..55acac9d8c --- /dev/null +++ b/agenta-web/src/lib/helpers/analytics/store/atoms.ts @@ -0,0 +1,5 @@ +import {atom} from "jotai" +import {type PostHog, type PostHogConfig} from "posthog-js" + +export type {PostHogConfig} +export const posthogAtom = atom(null) diff --git a/agenta-web/src/lib/helpers/analytics/types.d.ts b/agenta-web/src/lib/helpers/analytics/types.d.ts new file mode 100644 index 0000000000..d55cc0d99c --- /dev/null +++ b/agenta-web/src/lib/helpers/analytics/types.d.ts @@ -0,0 +1,7 @@ +import {type PostHogConfig} from "./store/atoms" + +export interface CustomPosthogProviderType + extends React.FC<{ + children: React.ReactNode + config: Partial + }> {} diff --git a/agenta-web/src/lib/hooks/useVariant.ts b/agenta-web/src/lib/hooks/useVariant.ts index 676fa64899..525f54d6bd 100644 --- a/agenta-web/src/lib/hooks/useVariant.ts +++ b/agenta-web/src/lib/hooks/useVariant.ts @@ -1,7 +1,7 @@ import {useState, useEffect, useRef} from "react" import {Variant, Parameter} from "@/lib/Types" import {getAllVariantParameters, updateInputParams} from "@/lib/helpers/variantHelper" -import {PERMISSION_ERR_MSG} from "../helpers/axiosConfig" +import {PERMISSION_ERR_MSG} from "../api/assets/axiosConfig" import {createNewVariant, fetchVariantLogs, updateVariantParams} from "@/services/playground/api" /** diff --git a/agenta-web/src/pages/_app.tsx b/agenta-web/src/pages/_app.tsx index c84846caf9..d47ae0bf67 100644 --- a/agenta-web/src/pages/_app.tsx +++ b/agenta-web/src/pages/_app.tsx @@ -1,12 +1,7 @@ -import {useEffect} from "react" import type {AppProps} from "next/app" -import {useRouter} from "next/router" import Head from "next/head" import dynamic from "next/dynamic" -import posthog from "posthog-js" -import {PostHogProvider} from "posthog-js/react" - import "@/styles/globals.css" import Layout from "@/components/Layout/Layout" import {dynamicComponent} from "@/lib/helpers/dynamic" @@ -14,42 +9,18 @@ import ThemeContextProvider from "@/components/Layout/ThemeContextProvider" import AppContextProvider from "@/contexts/app.context" import ProfileContextProvider from "@/contexts/profile.context" import ProjectContextProvider from "@/contexts/project.context" -import "ag-grid-community/styles/ag-grid.css" -import "ag-grid-community/styles/ag-theme-alpine.css" import {Inter} from "next/font/google" +import AgSWRConfig from "@/lib/api/SWRConfig" const NoMobilePageWrapper = dynamicComponent("NoMobilePageWrapper/NoMobilePageWrapper") +const CustomPosthogProvider = dynamic(() => import("@/lib/helpers/analytics/AgPosthogProvider")) const inter = Inter({ subsets: ["latin"], variable: "--font-inter", }) -// Initialize the Posthog client -if (typeof window !== "undefined") { - posthog.init(process.env.NEXT_PUBLIC_POSTHOG_API_KEY!, { - api_host: "https://app.posthog.com", - // Enable debug mode in development - loaded: (posthog) => { - if (process.env.NODE_ENV === "development") posthog.debug() - }, - capture_pageview: false, - persistence: "localStorage+cookie", - }) -} - export default function App({Component, pageProps}: AppProps) { - const router = useRouter() - - useEffect(() => { - const handleRouteChange = () => - posthog.capture("$pageview", {$current_url: window.location.href}) - router.events.on("routeChangeComplete", handleRouteChange) - - return () => { - router.events.off("routeChangeComplete", handleRouteChange) - } - }, []) return ( <> @@ -57,20 +28,26 @@ export default function App({Component, pageProps}: AppProps) {
- - - - - - - - - - - - - - + + + + + + + + + + + + + + + +
) diff --git a/agenta-web/src/pages/apps/[app_id]/evaluations/human_a_b_testing/[evaluation_id]/index.tsx b/agenta-web/src/pages/apps/[app_id]/evaluations/human_a_b_testing/[evaluation_id]/index.tsx index 33f8a33a68..f2f75cdfbb 100644 --- a/agenta-web/src/pages/apps/[app_id]/evaluations/human_a_b_testing/[evaluation_id]/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/evaluations/human_a_b_testing/[evaluation_id]/index.tsx @@ -12,6 +12,9 @@ import {useAtom} from "jotai" import {evaluationAtom, evaluationScenariosAtom} from "@/lib/atoms/evaluation" import {getTestsetChatColumn} from "@/lib/helpers/testset" +import "@ag-grid-community/styles/ag-grid.css" +import "@ag-grid-community/styles/ag-theme-alpine.css" + export default function Evaluation() { const router = useRouter() const evaluationTableId = router.query.evaluation_id diff --git a/agenta-web/src/pages/apps/[app_id]/evaluations/index.tsx b/agenta-web/src/pages/apps/[app_id]/evaluations/index.tsx index aa69f6987c..b7888273c3 100644 --- a/agenta-web/src/pages/apps/[app_id]/evaluations/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/evaluations/index.tsx @@ -7,6 +7,9 @@ import {ChartDonut, ListChecks, TestTube} from "@phosphor-icons/react" import {Tabs, TabsProps, Typography} from "antd" import {createUseStyles} from "react-jss" +import "@ag-grid-community/styles/ag-grid.css" +import "@ag-grid-community/styles/ag-theme-alpine.css" + const useStyles = createUseStyles((theme: JSSTheme) => ({ container: { display: "flex", diff --git a/agenta-web/src/pages/apps/[app_id]/evaluations/results/[evaluation_id]/index.tsx b/agenta-web/src/pages/apps/[app_id]/evaluations/results/[evaluation_id]/index.tsx index fb2d997f15..5faa93de6b 100644 --- a/agenta-web/src/pages/apps/[app_id]/evaluations/results/[evaluation_id]/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/evaluations/results/[evaluation_id]/index.tsx @@ -1,6 +1,9 @@ import React from "react" import EvaluationScenarios from "@/components/pages/evaluations/evaluationScenarios/EvaluationScenarios" +import "@ag-grid-community/styles/ag-grid.css" +import "@ag-grid-community/styles/ag-theme-alpine.css" + const EvaluationDetail = () => { return } diff --git a/agenta-web/src/pages/apps/[app_id]/evaluations/results/compare/index.tsx b/agenta-web/src/pages/apps/[app_id]/evaluations/results/compare/index.tsx index 0e346a3b7b..aae01c02ec 100644 --- a/agenta-web/src/pages/apps/[app_id]/evaluations/results/compare/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/evaluations/results/compare/index.tsx @@ -1,6 +1,9 @@ import React from "react" import EvaluationCompare from "@/components/pages/evaluations/evaluationCompare/EvaluationCompare" +import "@ag-grid-community/styles/ag-grid.css" +import "@ag-grid-community/styles/ag-theme-alpine.css" + const EvaluationCompareDetails = () => { return } diff --git a/agenta-web/src/pages/apps/[app_id]/evaluations/single_model_test/[evaluation_id]/index.tsx b/agenta-web/src/pages/apps/[app_id]/evaluations/single_model_test/[evaluation_id]/index.tsx index 9c0573d2eb..ed9dc5e25f 100644 --- a/agenta-web/src/pages/apps/[app_id]/evaluations/single_model_test/[evaluation_id]/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/evaluations/single_model_test/[evaluation_id]/index.tsx @@ -10,6 +10,9 @@ import {fetchVariants} from "@/services/api" import {getTestsetChatColumn} from "@/lib/helpers/testset" import SingleModelEvaluationTable from "@/components/EvaluationTable/SingleModelEvaluationTable" +import "@ag-grid-community/styles/ag-grid.css" +import "@ag-grid-community/styles/ag-theme-alpine.css" + export default function Evaluation() { const router = useRouter() const evaluationTableId = router.query.evaluation_id diff --git a/agenta-web/src/pages/testsets/[testset_id]/index.tsx b/agenta-web/src/pages/testsets/[testset_id]/index.tsx index 9e9a4a47bf..8e983cde67 100644 --- a/agenta-web/src/pages/testsets/[testset_id]/index.tsx +++ b/agenta-web/src/pages/testsets/[testset_id]/index.tsx @@ -1,5 +1,7 @@ import React from "react" import TestsetTable from "@/components/TestSetTable/TestsetTable" +import "@ag-grid-community/styles/ag-grid.css" +import "@ag-grid-community/styles/ag-theme-alpine.css" const testsetDisplay = () => { return diff --git a/agenta-web/src/pages/testsets/index.tsx b/agenta-web/src/pages/testsets/index.tsx index 6ccc96f6ad..ace37fedcd 100644 --- a/agenta-web/src/pages/testsets/index.tsx +++ b/agenta-web/src/pages/testsets/index.tsx @@ -1,5 +1,5 @@ -import React, {useMemo, useState} from "react" -import TestsetModal from "@/components/pages/testset/modals" +import {type Key, useMemo, useState} from "react" +import dynamic from "next/dynamic" import NoResultsFound from "@/components/NoResultsFound/NoResultsFound" import {formatDate} from "@/lib/helpers/dateTimeHelper" import {checkIfResourceValidForDeletion} from "@/lib/helpers/evaluate" @@ -14,6 +14,10 @@ import {createUseStyles} from "react-jss" import dayjs from "dayjs" import {useAppsData} from "@/contexts/app.context" +const TestsetModal = dynamic(() => import("@/components/pages/testset/modals"), { + ssr: false, +}) + const useStyles = createUseStyles((theme: JSSTheme) => ({ modal: { transition: "width 0.3s ease", @@ -55,7 +59,7 @@ const Testset = () => { const classes = useStyles() const router = useRouter() const {apps, isLoading: isAppsLoading} = useAppsData() - const [selectedRowKeys, setSelectedRowKeys] = useState([]) + const [selectedRowKeys, setSelectedRowKeys] = useState([]) const {testsets, isTestsetsLoading, mutate} = useLoadTestsetsList() const [isCreateTestsetModalOpen, setIsCreateTestsetModalOpen] = useState(false) const [searchTerm, setSearchTerm] = useState("") @@ -64,7 +68,7 @@ const Testset = () => { const [current, setCurrent] = useState(0) const rowSelection = { - onChange: (selectedRowKeys: React.Key[]) => { + onChange: (selectedRowKeys: Key[]) => { setSelectedRowKeys(selectedRowKeys) }, } diff --git a/agenta-web/src/services/api.ts b/agenta-web/src/services/api.ts index 4031e76103..acbf35af51 100644 --- a/agenta-web/src/services/api.ts +++ b/agenta-web/src/services/api.ts @@ -1,5 +1,5 @@ import {getCurrentProject} from "@/contexts/project.context" -import axios from "@/lib//helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import Session from "supertokens-auth-react/recipe/session" import {formatDay} from "@/lib/helpers/dateTimeHelper" import { diff --git a/agenta-web/src/services/app-selector/api/index.ts b/agenta-web/src/services/app-selector/api/index.ts index f7aa7b63d2..fe28fd4550 100644 --- a/agenta-web/src/services/app-selector/api/index.ts +++ b/agenta-web/src/services/app-selector/api/index.ts @@ -1,6 +1,6 @@ import {getCurrentProject} from "@/contexts/project.context" import {AppTemplate} from "@/lib/Types" -import axios from "@/lib/helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import {dynamicContext} from "@/lib/helpers/dynamic" import {LlmProvider} from "@/lib/helpers/llmProviders" import {getAgentaApiUrl} from "@/lib/helpers/utils" diff --git a/agenta-web/src/services/deployment/api/index.ts b/agenta-web/src/services/deployment/api/index.ts index 6a7153c883..97ac167543 100644 --- a/agenta-web/src/services/deployment/api/index.ts +++ b/agenta-web/src/services/deployment/api/index.ts @@ -1,6 +1,6 @@ import {getCurrentProject} from "@/contexts/project.context" import {Environment} from "@/lib/Types" -import axios from "@/lib/helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import {getAgentaApiUrl} from "@/lib/helpers/utils" //Prefix convention: diff --git a/agenta-web/src/services/evaluations/api/index.ts b/agenta-web/src/services/evaluations/api/index.ts index e747856832..13981ae6bb 100644 --- a/agenta-web/src/services/evaluations/api/index.ts +++ b/agenta-web/src/services/evaluations/api/index.ts @@ -1,4 +1,4 @@ -import axios from "@/lib//helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import { ComparisonResultRow, Evaluator, diff --git a/agenta-web/src/services/human-evaluations/api/index.ts b/agenta-web/src/services/human-evaluations/api/index.ts index c4a8cb0e03..1c3e647ed8 100644 --- a/agenta-web/src/services/human-evaluations/api/index.ts +++ b/agenta-web/src/services/human-evaluations/api/index.ts @@ -1,4 +1,4 @@ -import axios from "@/lib//helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import { EvaluationResponseType, Evaluation, diff --git a/agenta-web/src/services/observability/core/index.ts b/agenta-web/src/services/observability/core/index.ts index 40bd3f77c5..714fed4c16 100644 --- a/agenta-web/src/services/observability/core/index.ts +++ b/agenta-web/src/services/observability/core/index.ts @@ -1,6 +1,6 @@ import {getAgentaApiUrl} from "@/lib/helpers/utils" import {_AgentaRootsResponse} from "../types" -import axios from "@/lib/helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import {getCurrentProject} from "@/contexts/project.context" //Prefix convention: diff --git a/agenta-web/src/services/playground/api/index.ts b/agenta-web/src/services/playground/api/index.ts index f04fc7aefb..404392eb4b 100644 --- a/agenta-web/src/services/playground/api/index.ts +++ b/agenta-web/src/services/playground/api/index.ts @@ -1,6 +1,6 @@ import {getCurrentProject} from "@/contexts/project.context" import {Parameter} from "@/lib/Types" -import axios from "@/lib/helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import {getAgentaApiUrl} from "@/lib/helpers/utils" //Prefix convention: diff --git a/agenta-web/src/services/project/index.ts b/agenta-web/src/services/project/index.ts index 95d629d118..cad28a54be 100644 --- a/agenta-web/src/services/project/index.ts +++ b/agenta-web/src/services/project/index.ts @@ -1,4 +1,4 @@ -import axios from "@/lib/helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import {getAgentaApiUrl} from "@/lib/helpers/utils" import {ProjectsResponse} from "./types" diff --git a/agenta-web/src/services/testsets/api/index.ts b/agenta-web/src/services/testsets/api/index.ts index e6fb42f774..8527bd46a3 100644 --- a/agenta-web/src/services/testsets/api/index.ts +++ b/agenta-web/src/services/testsets/api/index.ts @@ -1,5 +1,5 @@ import useSWR from "swr" -import axios from "@/lib/helpers/axiosConfig" +import axios from "@/lib/api/assets/axiosConfig" import {getAgentaApiUrl} from "@/lib/helpers/utils" import {axiosFetcher} from "@/services/api" import {getCurrentProject} from "@/contexts/project.context" @@ -14,11 +14,10 @@ import {getCurrentProject} from "@/contexts/project.context" export const useLoadTestsetsList = () => { const {projectId} = getCurrentProject() - const {data, error, mutate, isLoading} = useSWR( - `${getAgentaApiUrl()}/api/testsets?project_id=${projectId}`, - axiosFetcher, - {revalidateOnFocus: false, shouldRetryOnError: false}, - ) + const {data, error, mutate, isLoading} = useSWR(`/api/testsets?project_id=${projectId}`, { + revalidateOnFocus: false, + shouldRetryOnError: false, + }) return { testsets: data || [],