From df07bdd6f2dce1338e9ea2f26b9573d84dc2d35b Mon Sep 17 00:00:00 2001 From: Dan Constantini Date: Wed, 21 Aug 2024 21:50:13 +0200 Subject: [PATCH 1/5] update doc string --- literalai/api/__init__.py | 75 +----------------------------- literalai/client.py | 98 ++++----------------------------------- 2 files changed, 10 insertions(+), 163 deletions(-) diff --git a/literalai/api/__init__.py b/literalai/api/__init__.py index 480a185..df96959 100644 --- a/literalai/api/__init__.py +++ b/literalai/api/__init__.py @@ -190,21 +190,7 @@ class LiteralAPI(BaseLiteralAPI): def make_gql_call( self, description: str, query: str, variables: Dict[str, Any] ) -> Dict: - """ - Executes a GraphQL call with the provided query and variables. - - Args: - description (str): Description of the GraphQL operation for logging purposes. - query (str): The GraphQL query to be executed. - variables (Dict[str, Any]): Variables required for the GraphQL query. - - Returns: - Dict: The JSON response from the GraphQL endpoint. - - Raises: - Exception: If the GraphQL call fails or returns errors. - """ - + def raise_error(error): logger.error(f"Failed to {description}: {error}") raise Exception(error) @@ -250,16 +236,6 @@ def raise_error(error): raise Exception("Unknown error") def make_rest_call(self, subpath: str, body: Dict[str, Any]) -> Dict: - """ - Executes a REST API call to the specified subpath with the given body. - - Args: - subpath (str): The subpath of the REST API endpoint. - body (Dict[str, Any]): The JSON body to send with the POST request. - - Returns: - Dict: The JSON response from the REST API endpoint. - """ with httpx.Client(follow_redirects=True) as client: response = client.post( self.rest_endpoint + subpath, @@ -290,18 +266,6 @@ def gql_helper( variables: Dict, process_response: Callable[..., R], ) -> R: - """ - Helper function to make a GraphQL call and process the response. - - Args: - query (str): The GraphQL query to execute. - description (str): Description of the GraphQL operation for logging purposes. - variables (Dict): Variables required for the GraphQL query. - process_response (Callable[..., R]): A function to process the response. - - Returns: - R: The result of processing the response. - """ response = self.make_gql_call(description, query, variables) return process_response(response) @@ -1416,21 +1380,6 @@ class AsyncLiteralAPI(BaseLiteralAPI): async def make_gql_call( self, description: str, query: str, variables: Dict[str, Any] ) -> Dict: - """ - Asynchronously makes a GraphQL call using the provided query and variables. - - Args: - description (str): Description of the GraphQL operation for logging purposes. - query (str): The GraphQL query to be executed. - variables (Dict[str, Any]): Variables required for the GraphQL query. - - Returns: - Dict: The JSON response from the GraphQL endpoint. - - Raises: - Exception: If the GraphQL call fails or returns errors. - """ - def raise_error(error): logger.error(f"Failed to {description}: {error}") raise Exception(error) @@ -1477,16 +1426,6 @@ def raise_error(error): raise Exception("Unkown error") async def make_rest_call(self, subpath: str, body: Dict[str, Any]) -> Dict: - """ - Asynchronously makes a REST API call to a specified subpath with the provided body. - - Args: - subpath (str): The endpoint subpath to which the POST request is made. - body (Dict[str, Any]): The JSON body of the POST request. - - Returns: - Dict: The JSON response from the REST API endpoint. - """ async with httpx.AsyncClient(follow_redirects=True) as client: response = await client.post( self.rest_endpoint + subpath, @@ -1517,18 +1456,6 @@ async def gql_helper( variables: Dict, process_response: Callable[..., R], ) -> R: - """ - Helper function to process a GraphQL query by making an asynchronous call and processing the response. - - Args: - query (str): The GraphQL query to be executed. - description (str): Description of the GraphQL operation for logging purposes. - variables (Dict): Variables required for the GraphQL query. - process_response (Callable[..., R]): The function to process the response. - - Returns: - R: The result of processing the response. - """ response = await self.make_gql_call(description, query, variables) return process_response(response) diff --git a/literalai/client.py b/literalai/client.py index 468dcb5..1b8eb02 100644 --- a/literalai/client.py +++ b/literalai/client.py @@ -28,6 +28,9 @@ class BaseLiteralClient: + """ + Base class for LiteralClient and AsyncLiteralClient. + """ api: Union[LiteralAPI, AsyncLiteralAPI] def __init__( @@ -61,12 +64,6 @@ def __init__( ) def to_sync(self) -> "LiteralClient": - """ - Converts the current client to its synchronous version. - - Returns: - LiteralClient: The current client's synchronous version. - """ if isinstance(self.api, AsyncLiteralAPI): return LiteralClient( batch_size=self.event_processor.batch_size, @@ -136,18 +133,6 @@ def thread( name: Optional[str] = None, **kwargs, ): - """ - Creates a thread where all the subsequents steps will be logged. - Works as a decorator or a ContextManager. - - Args: - original_function: The function to execute in the thread's context. - thread_id (Optional[str]): The id of the thread to create. - name (Optional[str]): The name of the thread to create. - - Returns: - The wrapper for the thread's context. - """ if original_function: return thread_decorator( self, func=original_function, thread_id=thread_id, name=name, **kwargs @@ -167,23 +152,6 @@ def step( root_run_id: Optional[str] = None, **kwargs, ): - """ - Creates a step where all the subsequents steps will be logged. Works as a decorator or a ContextManager. - This is used to create Agent steps. For conversational messages use `message` instead. - - Args: - original_function: The function to execute in the step's context. - name (Optional[str]): The name of the step to create. - type (TrueStepType): The type of the step. Must be one of the following : - "run", "tool", "llm", "embedding", "retrieval","rerank", "undefined". - id (Optional[str]): The id of the step to create. - parent_id (Optional[str]): The id of the parent step. - thread_id (Optional[str]): The id of the parent thread. - root_run_id (Optional[str]): The id of the root run. - - Returns: - The wrapper for the step's context. - """ if original_function: return step_decorator( self, @@ -218,20 +186,6 @@ def run( thread_id: Optional[str] = None, root_run_id: Optional[str] = None, ): - """ - Creates a run where all the subsequents steps will be logged. Works as a decorator or a ContextManager. - - Args: - original_function: The function to execute in the step's context. - name (Optional[str]): The name of the step to create. - id (Optional[str]): The id of the step to create. - parent_id (Optional[str]): The id of the parent step. - thread_id (Optional[str]): The id of the parent thread. - root_run_id (Optional[str]): The id of the root run. - - Returns: - The wrapper for the step's context. - """ return self.step( original_function=original_function, name=name, @@ -255,26 +209,6 @@ def message( metadata: Dict = {}, root_run_id: Optional[str] = None, ): - """ - Creates a conversational message step and sends it to Literal AI. - For agentic steps or runs use `step` or `run` respectively instead. - - Args: - content (str): The text content of the message. - id (Optional[str]): The id of the step to create. - parent_id (Optional[str]): The id of the parent step. - type (TrueStepType): The type of the step. Must be one of the following : - "user_message", "assistant_message", "system_message". - name (Optional[str]): The name of the step to create. - thread_id (Optional[str]): The id of the parent thread. - attachments (List[Attachment]): A list of attachments to append to the message. - tags (Optional[List[str]]): A list of tags to add to the message. - metadata (Dict): Metadata to add to the message, in key-value pairs. - root_run_id (Optional[str]): The id of the root run. - - Returns: - Message: the created message. - """ step = Message( name=name, id=id, @@ -298,17 +232,6 @@ def environment( env: Environment = "prod", **kwargs, ): - """ - Sets the environment to add to all subsequent threads and steps. Works as a decorator or a ContextManager. - Entities logged in the "experiment" environment are filtered out of the Literal AI UI. - - Args: - original_function: The function to execute in the step's context. - env (Environment): The environment to add to logged entities. - - Returns: - The wrapper for the context. - """ if original_function: return env_decorator( self, @@ -328,15 +251,6 @@ def experiment_item_run( original_function=None, **kwargs, ): - """ - Creates an experiment run. Works as a decorator or a ContextManager. - - Args: - original_function: The function to execute in the step's context. - - Returns: - The wrapper for the context. - """ if original_function: return experiment_item_run_decorator( self, @@ -426,6 +340,9 @@ def flush_and_stop(self): class LiteralClient(BaseLiteralClient): + """ + Synchronous client for interacting with the Literal AI API. + """ api: LiteralAPI def __init__( @@ -450,6 +367,9 @@ def flush(self): class AsyncLiteralClient(BaseLiteralClient): + """ + Asynchronous client for interacting with the Literal AI API. + """ api: AsyncLiteralAPI def __init__( From 798156635d22c1b88397b0ef6b2df7d4ec648bad Mon Sep 17 00:00:00 2001 From: Dan Constantini Date: Sat, 24 Aug 2024 17:17:40 +0200 Subject: [PATCH 2/5] update examples --- literalai/api/__init__.py | 19 +++++++++++++++++++ literalai/client.py | 25 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/literalai/api/__init__.py b/literalai/api/__init__.py index df96959..0164431 100644 --- a/literalai/api/__init__.py +++ b/literalai/api/__init__.py @@ -144,6 +144,7 @@ def handle_bytes(item): class BaseLiteralAPI: + def __init__( self, api_key: Optional[str] = None, @@ -185,6 +186,15 @@ def headers(self): class LiteralAPI(BaseLiteralAPI): + """ + ```python + from literalai import LiteralClient + # Initialize the client + client = LiteralClient(api_key="your_api_key_here") + # Access the API's methods + print(client.api) + ``` + """ R = TypeVar("R") def make_gql_call( @@ -1375,6 +1385,15 @@ def get_my_project_id(self): class AsyncLiteralAPI(BaseLiteralAPI): + """ + ```python + from literalai import AsyncLiteralClient + # Initialize the client + async_client = AsyncLiteralClient(api_key="your_api_key_here") + # Access the API's methods + print(async_client.api) + ``` + """ R = TypeVar("R") async def make_gql_call( diff --git a/literalai/client.py b/literalai/client.py index 1b8eb02..16ca22e 100644 --- a/literalai/client.py +++ b/literalai/client.py @@ -30,6 +30,19 @@ class BaseLiteralClient: """ Base class for LiteralClient and AsyncLiteralClient. + Example: + ```python + from literalai import LiteralClient, AsyncLiteralClient + + # Initialize the client + client = LiteralClient(api_key="your_api_key_here") + async_client = AsyncLiteralClient(api_key="your_api_key_here") + ``` + Attributes: + api (Union[LiteralAPI, AsyncLiteralAPI]): The API client used for communication with Literal AI. + disabled (bool): Flag indicating whether the client is disabled. + event_processor (EventProcessor): Processor for handling events. + """ api: Union[LiteralAPI, AsyncLiteralAPI] @@ -342,6 +355,12 @@ def flush_and_stop(self): class LiteralClient(BaseLiteralClient): """ Synchronous client for interacting with the Literal AI API. + Example: + ```python + from literalai import LiteralClient + # Initialize the client + client = LiteralClient(api_key="your_api_key_here") + ``` """ api: LiteralAPI @@ -369,6 +388,12 @@ def flush(self): class AsyncLiteralClient(BaseLiteralClient): """ Asynchronous client for interacting with the Literal AI API. + Example: + ```python + from literalai import AsyncLiteralClient + # Initialize the client + async_client = AsyncLiteralClient(api_key="your_api_key_here") + ``` """ api: AsyncLiteralAPI From f2065cf93ce3d2320c4d8776e8d0654a8bf6911f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugues=20de=20Saxc=C3=A9?= Date: Mon, 26 Aug 2024 10:33:05 +0200 Subject: [PATCH 3/5] fix: linting --- literalai/api/__init__.py | 25 +++++++++++-------------- literalai/client.py | 5 ++++- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/literalai/api/__init__.py b/literalai/api/__init__.py index 0164431..d0698cb 100644 --- a/literalai/api/__init__.py +++ b/literalai/api/__init__.py @@ -144,7 +144,7 @@ def handle_bytes(item): class BaseLiteralAPI: - + def __init__( self, api_key: Optional[str] = None, @@ -195,12 +195,13 @@ class LiteralAPI(BaseLiteralAPI): print(client.api) ``` """ + R = TypeVar("R") def make_gql_call( self, description: str, query: str, variables: Dict[str, Any] ) -> Dict: - + def raise_error(error): logger.error(f"Failed to {description}: {error}") raise Exception(error) @@ -672,15 +673,14 @@ def upload_file( fields: Dict = request_dict.get("fields", {}) object_key: Optional[str] = fields.get("key") upload_type: Literal["raw", "multipart"] = cast( - Literal["raw", "multipart"], request_dict.get( - "uploadType", "multipart") + Literal["raw", "multipart"], request_dict.get("uploadType", "multipart") ) signed_url: Optional[str] = json_res.get("signedUrl") # Prepare form data form_data = ( {} - ) # type: Dict[str, Union[Tuple[Union[str, None], Any], Tuple[Union[str, None], Any, Any]]] + ) # type: Dict[str, Union[Tuple[Union[str, None], Any], Tuple[Union[str, None], Any, Any]]] for field_name, field_value in fields.items(): form_data[field_name] = (None, field_value) @@ -750,8 +750,7 @@ def create_attachment( if active_steps := active_steps_var.get([]): step_id = active_steps[-1].id else: - raise Exception( - "No step_id provided and no active step found.") + raise Exception("No step_id provided and no active step found.") ( query, @@ -773,8 +772,7 @@ def create_attachment( ) if content: - uploaded = self.upload_file( - content=content, thread_id=thread_id, mime=mime) + uploaded = self.upload_file(content=content, thread_id=thread_id, mime=mime) if uploaded["object_key"] is None or uploaded["url"] is None: raise Exception("Failed to upload file") @@ -1372,7 +1370,6 @@ def update_prompt_ab_testing( ) # Misc API - def get_my_project_id(self): """ Retrieves the projectId associated to the API key. @@ -1394,6 +1391,7 @@ class AsyncLiteralAPI(BaseLiteralAPI): print(async_client.api) ``` """ + R = TypeVar("R") async def make_gql_call( @@ -1898,15 +1896,14 @@ async def upload_file( fields: Dict = request_dict.get("fields", {}) object_key: Optional[str] = fields.get("key") upload_type: Literal["raw", "multipart"] = cast( - Literal["raw", "multipart"], request_dict.get( - "uploadType", "multipart") + Literal["raw", "multipart"], request_dict.get("uploadType", "multipart") ) signed_url: Optional[str] = json_res.get("signedUrl") # Prepare form data form_data = ( - {} - ) # type: Dict[str, Union[Tuple[Union[str, None], Any], Tuple[Union[str, None], Any, Any]]] + {} + ) # type: Dict[str, Union[Tuple[Union[str, None], Any], Tuple[Union[str, None], Any, Any]]] for field_name, field_value in fields.items(): form_data[field_name] = (None, field_value) diff --git a/literalai/client.py b/literalai/client.py index 16ca22e..7628739 100644 --- a/literalai/client.py +++ b/literalai/client.py @@ -29,7 +29,7 @@ class BaseLiteralClient: """ - Base class for LiteralClient and AsyncLiteralClient. + Base class for LiteralClient and AsyncLiteralClient. Example: ```python from literalai import LiteralClient, AsyncLiteralClient @@ -44,6 +44,7 @@ class BaseLiteralClient: event_processor (EventProcessor): Processor for handling events. """ + api: Union[LiteralAPI, AsyncLiteralAPI] def __init__( @@ -362,6 +363,7 @@ class LiteralClient(BaseLiteralClient): client = LiteralClient(api_key="your_api_key_here") ``` """ + api: LiteralAPI def __init__( @@ -395,6 +397,7 @@ class AsyncLiteralClient(BaseLiteralClient): async_client = AsyncLiteralClient(api_key="your_api_key_here") ``` """ + api: AsyncLiteralAPI def __init__( From 079c6fa07b41c7213ff60092779729d7a6a197a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugues=20de=20Saxc=C3=A9?= Date: Mon, 26 Aug 2024 11:24:32 +0200 Subject: [PATCH 4/5] fix: lint --- literalai/api/__init__.py | 8 ++++---- literalai/instrumentation/mistralai.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/literalai/api/__init__.py b/literalai/api/__init__.py index d0698cb..d271ba4 100644 --- a/literalai/api/__init__.py +++ b/literalai/api/__init__.py @@ -190,9 +190,9 @@ class LiteralAPI(BaseLiteralAPI): ```python from literalai import LiteralClient # Initialize the client - client = LiteralClient(api_key="your_api_key_here") + literalai_client = LiteralClient(api_key="your_api_key_here") # Access the API's methods - print(client.api) + print(literalai_client.api) ``` """ @@ -1386,9 +1386,9 @@ class AsyncLiteralAPI(BaseLiteralAPI): ```python from literalai import AsyncLiteralClient # Initialize the client - async_client = AsyncLiteralClient(api_key="your_api_key_here") + async_literalai_client = AsyncLiteralClient(api_key="your_api_key_here") # Access the API's methods - print(async_client.api) + print(async_literalai_client.api) ``` """ diff --git a/literalai/instrumentation/mistralai.py b/literalai/instrumentation/mistralai.py index ea905a6..8ceccb5 100644 --- a/literalai/instrumentation/mistralai.py +++ b/literalai/instrumentation/mistralai.py @@ -311,7 +311,7 @@ def streaming_response( time.time() - context["start"] ) * 1000 token_count += 1 - completion += chunk.data.choices[0].delta.content + completion += str(chunk.data.choices[0].delta.content) if ( generation From 18ae60b7518aa2801277b8d770bf9ead2d521606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugues=20de=20Saxc=C3=A9?= Date: Tue, 27 Aug 2024 15:35:14 +0200 Subject: [PATCH 5/5] fix: sort a/b rollouts asc by version in tests --- tests/e2e/test_e2e.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e/test_e2e.py b/tests/e2e/test_e2e.py index 1063352..1137878 100644 --- a/tests/e2e/test_e2e.py +++ b/tests/e2e/test_e2e.py @@ -658,6 +658,7 @@ async def test_prompt_ab_testing(self, client: LiteralClient): ) ab_testing = client.api.get_prompt_ab_testing(name=prompt_v1.name) + ab_testing = sorted(ab_testing, key=lambda x: x["version"]) assert len(ab_testing) == 2 assert ab_testing[0]["version"] == 0