From 15db2d308095cb027726e0ef0db9c743aa2629f3 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 3 Sep 2024 23:03:13 +0200 Subject: [PATCH 001/305] feat(sdk) migrate to opentelemetry (WIP) --- agenta-cli/agenta/__init__.py | 12 +- agenta-cli/agenta/sdk/__init__.py | 8 +- agenta-cli/agenta/sdk/agenta_init.py | 107 +-- agenta-cli/agenta/sdk/config_manager.py | 2 +- agenta-cli/agenta/sdk/context.py | 41 -- agenta-cli/agenta/sdk/decorators/__init__.py | 0 agenta-cli/agenta/sdk/decorators/base.py | 10 - .../{llm_entrypoint.py => routing.py} | 174 +++-- agenta-cli/agenta/sdk/decorators/tracing.py | 177 ++--- agenta-cli/agenta/sdk/router.py | 7 - agenta-cli/agenta/sdk/tracing/callbacks.py | 187 ------ agenta-cli/agenta/sdk/tracing/litellm.py | 266 ++++++++ agenta-cli/agenta/sdk/tracing/llm_tracing.py | 617 ------------------ .../agenta/sdk/tracing/opentelemetry.py | 459 +++++++++++++ .../agenta/sdk/tracing/tasks_manager.py | 129 ---- .../agenta/sdk/tracing/tracing_context.py | 27 - agenta-cli/agenta/sdk/types.py | 12 - .../utils/{helper/openai_cost.py => costs.py} | 0 agenta-cli/agenta/sdk/utils/debug.py | 10 +- agenta-cli/agenta/sdk/utils/globals.py | 8 +- .../{tracing/logger.py => utils/logging.py} | 8 +- 21 files changed, 985 insertions(+), 1276 deletions(-) delete mode 100644 agenta-cli/agenta/sdk/context.py create mode 100644 agenta-cli/agenta/sdk/decorators/__init__.py delete mode 100644 agenta-cli/agenta/sdk/decorators/base.py rename agenta-cli/agenta/sdk/decorators/{llm_entrypoint.py => routing.py} (88%) delete mode 100644 agenta-cli/agenta/sdk/tracing/callbacks.py create mode 100644 agenta-cli/agenta/sdk/tracing/litellm.py delete mode 100644 agenta-cli/agenta/sdk/tracing/llm_tracing.py create mode 100644 agenta-cli/agenta/sdk/tracing/opentelemetry.py delete mode 100644 agenta-cli/agenta/sdk/tracing/tasks_manager.py delete mode 100644 agenta-cli/agenta/sdk/tracing/tracing_context.py rename agenta-cli/agenta/sdk/utils/{helper/openai_cost.py => costs.py} (100%) rename agenta-cli/agenta/sdk/{tracing/logger.py => utils/logging.py} (58%) diff --git a/agenta-cli/agenta/__init__.py b/agenta-cli/agenta/__init__.py index a6d7047439..a4cf8bdadb 100644 --- a/agenta-cli/agenta/__init__.py +++ b/agenta-cli/agenta/__init__.py @@ -1,7 +1,5 @@ from .sdk.utils.preinit import PreInitObject -from .sdk.context import get_contexts, save_context from .sdk.types import ( - Context, DictInput, MultipleChoice, FloatParam, @@ -15,14 +13,14 @@ BinaryParam, ) -from .sdk.tracing.logger import llm_logger as logging -from .sdk.tracing.llm_tracing import Tracing +from .sdk.utils.logging import log as logging +from .sdk.tracing.opentelemetry import Tracing from .sdk.decorators.tracing import instrument -from .sdk.decorators.llm_entrypoint import entrypoint, app, route +from .sdk.decorators.routing import entrypoint, app, route from .sdk.agenta_init import Config, AgentaSingleton, init -from .sdk.utils.helper.openai_cost import calculate_token_usage +from .sdk.utils.costs import calculate_token_usage from .sdk.client import Agenta -from .sdk.tracing import callbacks +from .sdk.tracing import litellm as callbacks from .sdk.config_manager import ConfigManager from .sdk import assets as assets diff --git a/agenta-cli/agenta/sdk/__init__.py b/agenta-cli/agenta/sdk/__init__.py index 982e3e8faf..02e3794795 100644 --- a/agenta-cli/agenta/sdk/__init__.py +++ b/agenta-cli/agenta/sdk/__init__.py @@ -1,7 +1,5 @@ from .utils.preinit import PreInitObject # always the first import! -from .context import get_contexts, save_context from .types import ( - Context, DictInput, MultipleChoice, FloatParam, @@ -15,11 +13,11 @@ BinaryParam, ) -from .tracing.llm_tracing import Tracing +from .tracing.opentelemetry import Tracing from .decorators.tracing import instrument -from .decorators.llm_entrypoint import entrypoint, app, route +from .decorators.routing import entrypoint, app, route from .agenta_init import Config, AgentaSingleton, init -from .utils.helper.openai_cost import calculate_token_usage +from .utils.costs import calculate_token_usage from .config_manager import ConfigManager config = PreInitObject("agenta.config", Config) diff --git a/agenta-cli/agenta/sdk/agenta_init.py b/agenta-cli/agenta/sdk/agenta_init.py index dd8386bb79..6199dd3122 100644 --- a/agenta-cli/agenta/sdk/agenta_init.py +++ b/agenta-cli/agenta/sdk/agenta_init.py @@ -2,10 +2,12 @@ import logging import toml from typing import Optional +from importlib.metadata import version +from agenta.sdk.utils.logging import log from agenta.sdk.utils.globals import set_global from agenta.client.backend.client import AgentaApi -from agenta.sdk.tracing.llm_tracing import Tracing +from agenta.sdk.tracing.opentelemetry import Tracing from agenta.client.exceptions import APIRequestError @@ -17,9 +19,8 @@ class AgentaSingleton: """Singleton class to save all the "global variables" for the sdk.""" _instance = None - setup = None config = None - tracing: Optional[Tracing] = None + tracing = None def __new__(cls): if not cls._instance: @@ -28,73 +29,104 @@ def __new__(cls): def init( self, - app_id: Optional[str] = None, + project_id: Optional[str] = None, host: Optional[str] = None, api_key: Optional[str] = None, config_fname: Optional[str] = None, ) -> None: + log.info(f"\n--------------------------------") + log.info(f"Using Agenta Python SDK version: {version('agenta')}") + log.info(f"--------------------------------\n") + """Main function to initialize the singleton. - Initializes the singleton with the given `app_id`, `host`, and `api_key`. The order of precedence for these variables is: + Initializes the singleton with the given `project_id`, `host`, and `api_key`. The order of precedence for these variables is: 1. Explicit argument provided in the function call. 2. Value from the configuration file specified by `config_fname`. 3. Environment variables. Examples: - ag.init(app_id="xxxx", api_key="xxx") + ag.init(project_id="xxxx", api_key="xxx") ag.init(config_fname="config.toml") ag.init() #assuming env vars are set Args: - app_id (Optional[str]): ID of the Agenta application. Defaults to None. If not provided, will look for "app_id" in the config file, then "AGENTA_APP_ID" in environment variables. + project_id (Optional[str]): ID of the Agenta application. Defaults to None. If not provided, will look for "project_id" in the config file, then "AGENTA_PROJECT_ID" in environment variables. host (Optional[str]): Host name of the backend server. Defaults to None. If not provided, will look for "backend_host" in the config file, then "AGENTA_HOST" in environment variables. api_key (Optional[str]): API Key to use with the host of the backend server. Defaults to None. If not provided, will look for "api_key" in the config file, then "AGENTA_API_KEY" in environment variables. config_fname (Optional[str]): Path to the configuration file (relative or absolute). Defaults to None. Raises: - ValueError: If `app_id` is not specified either as an argument, in the config file, or in the environment variables. + ValueError: If `project_id` is not specified either as an argument, in the config file, or in the environment variables. """ + config = {} if config_fname: config = toml.load(config_fname) - self.app_id = app_id or config.get("app_id") or os.environ.get("AGENTA_APP_ID") self.host = ( host or config.get("backend_host") + or config.get("host") or os.environ.get("AGENTA_HOST", "https://cloud.agenta.ai") ) - self.api_key = ( - api_key or config.get("api_key") or os.environ.get("AGENTA_API_KEY") - ) - if not self.app_id: + self.project_id = ( + project_id + or config.get("project_id") + or os.environ.get("AGENTA_PROJECT_ID") + ) + if not self.project_id: raise ValueError( - "App ID must be specified. You can provide it in one of the following ways:\n" - "1. As an argument when calling ag.init(app_id='your_app_id').\n" + "Project ID must be specified. You can provide it in one of the following ways:\n" + "1. As an argument when calling ag.init(project_id='your_project_id').\n" "2. In the configuration file specified by config_fname.\n" - "3. As an environment variable 'AGENTA_APP_ID'." + "3. As an environment variable 'AGENTA_PROJECT_ID'." ) + + self.api_key = ( + api_key or config.get("api_key") or os.environ.get("AGENTA_API_KEY") + ) + + self.tracing = Tracing( + url=f"{self.host}/api/observability/v2/traces", # type: ignore + project_id=self.project_id, + api_key=self.api_key, + ) + self.base_id = os.environ.get("AGENTA_BASE_ID") - if self.base_id is None: - print( - "Warning: Your configuration will not be saved permanently since base_id is not provided." - ) - self.config = Config(base_id=self.base_id, host=self.host, api_key=self.api_key) # type: ignore + self.config = Config( + host=self.host, + base_id=self.base_id, + api_key=self.api_key, + ) class Config: - def __init__(self, base_id: str, host: str, api_key: Optional[str] = ""): - self.base_id = base_id + + def __init__( + self, + host: str, + base_id: Optional[str] = None, + api_key: Optional[str] = "", + ): self.host = host + self.base_id = base_id + + if self.base_id is None: + print( + "Warning: Your configuration will not be saved permanently since base_id is not provided.\n" + ) + if base_id is None or host is None: self.persist = False else: self.persist = True self.client = AgentaApi( - base_url=self.host + "/api", api_key=api_key if api_key else "" + base_url=self.host + "/api", + api_key=api_key if api_key else "", ) def register_default(self, overwrite=False, **kwargs): @@ -208,42 +240,43 @@ def dump(self): def init( - app_id: Optional[str] = None, host: Optional[str] = None, + project_id: Optional[str] = None, api_key: Optional[str] = None, config_fname: Optional[str] = None, - max_workers: Optional[int] = None, ): """Main function to initialize the agenta sdk. - Initializes agenta with the given `app_id`, `host`, and `api_key`. The order of precedence for these variables is: + Initializes agenta with the given `project_id`, `host`, and `api_key`. The order of precedence for these variables is: 1. Explicit argument provided in the function call. 2. Value from the configuration file specified by `config_fname`. 3. Environment variables. - - `app_id` is a required parameter (to be specified in one of the above ways) + - `project_id` is a required parameter (to be specified in one of the above ways) - `host` is optional and defaults to "https://cloud.agenta.ai" - `api_key` is optional and defaults to "". It is required only when using cloud or enterprise version of agenta. Args: - app_id (Optional[str]): ID of the Agenta application. Defaults to None. If not provided, will look for "app_id" in the config file, then "AGENTA_APP_ID" in environment variables. + project_id (Optional[str]): ID of the Agenta application. Defaults to None. If not provided, will look for "project_id" in the config file, then "AGENTA_PROJECT_ID" in environment variables. host (Optional[str]): Host name of the backend server. Defaults to None. If not provided, will look for "backend_host" in the config file, then "AGENTA_HOST" in environment variables. api_key (Optional[str]): API Key to use with the host of the backend server. Defaults to None. If not provided, will look for "api_key" in the config file, then "AGENTA_API_KEY" in environment variables. config_fname (Optional[str]): Path to the configuration file. Defaults to None. Raises: - ValueError: If `app_id` is not specified either as an argument, in the config file, or in the environment variables. + ValueError: If `project_id` is not specified either as an argument, in the config file, or in the environment variables. """ singleton = AgentaSingleton() - singleton.init(app_id=app_id, host=host, api_key=api_key, config_fname=config_fname) + singleton.init( + host=host, + project_id=project_id, + api_key=api_key, + config_fname=config_fname, + ) - tracing = Tracing( - host=singleton.host, # type: ignore - app_id=singleton.app_id, # type: ignore - api_key=singleton.api_key, - max_workers=max_workers, + set_global( + config=singleton.config, + tracing=singleton.tracing, ) - set_global(setup=singleton.setup, config=singleton.config, tracing=tracing) diff --git a/agenta-cli/agenta/sdk/config_manager.py b/agenta-cli/agenta/sdk/config_manager.py index e6824dd21a..893339cf54 100644 --- a/agenta-cli/agenta/sdk/config_manager.py +++ b/agenta-cli/agenta/sdk/config_manager.py @@ -7,7 +7,7 @@ from pydantic import BaseModel, ValidationError from agenta.client.backend.client import AgentaApi -from agenta.sdk.decorators.llm_entrypoint import route_context +from agenta.sdk.decorators.routing import route_context from . import AgentaSingleton diff --git a/agenta-cli/agenta/sdk/context.py b/agenta-cli/agenta/sdk/context.py deleted file mode 100644 index 6960a17d26..0000000000 --- a/agenta-cli/agenta/sdk/context.py +++ /dev/null @@ -1,41 +0,0 @@ -import json -import sqlite3 -from typing import List -from .types import Context - - -def setup_db(): - conn = sqlite3.connect("context.db") - c = conn.cursor() - c.execute( - """ - CREATE TABLE IF NOT EXISTS contexts - (id INTEGER PRIMARY KEY AUTOINCREMENT, context TEXT) - """ - ) - conn.commit() - conn.close() - - -def get_contexts() -> List[Context]: - contexts = [] - conn = sqlite3.connect("context.db") - c = conn.cursor() - for row in c.execute("SELECT * FROM contexts"): - context_data = json.loads(row[1]) - contexts.append(Context.parse_obj(context_data)) - conn.close() - return contexts - - -def save_context(result: Context): - conn = sqlite3.connect("context.db") - c = conn.cursor() - c.execute( - """ - INSERT INTO contexts (context) VALUES (?) - """, - (json.dumps(result.dict()),), - ) - conn.commit() - conn.close() diff --git a/agenta-cli/agenta/sdk/decorators/__init__.py b/agenta-cli/agenta/sdk/decorators/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/agenta-cli/agenta/sdk/decorators/base.py b/agenta-cli/agenta/sdk/decorators/base.py deleted file mode 100644 index ae831fbba1..0000000000 --- a/agenta-cli/agenta/sdk/decorators/base.py +++ /dev/null @@ -1,10 +0,0 @@ -# Stdlib Imports -from typing import Any, Callable - - -class BaseDecorator: - def __init__(self): - pass - - def __call__(self, func: Callable[..., Any]) -> Callable[..., Any]: - raise NotImplementedError diff --git a/agenta-cli/agenta/sdk/decorators/llm_entrypoint.py b/agenta-cli/agenta/sdk/decorators/routing.py similarity index 88% rename from agenta-cli/agenta/sdk/decorators/llm_entrypoint.py rename to agenta-cli/agenta/sdk/decorators/routing.py index 025b55a9b0..600b0dddb6 100644 --- a/agenta-cli/agenta/sdk/decorators/llm_entrypoint.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -1,6 +1,5 @@ """The code for the Agenta SDK""" -from agenta.sdk.utils.debug import debug, DEBUG, SHIFT import os import sys import time @@ -19,13 +18,9 @@ from fastapi import Body, FastAPI, UploadFile, HTTPException import agenta as ag -from agenta.sdk.context import save_context from agenta.sdk.router import router as router -from agenta.sdk.tracing.logger import llm_logger as logging -from agenta.sdk.tracing.tracing_context import tracing_context, TracingContext -from agenta.sdk.decorators.base import BaseDecorator +from agenta.sdk.utils.logging import log from agenta.sdk.types import ( - Context, DictInput, FloatParam, InFile, @@ -67,7 +62,7 @@ app.include_router(router, prefix="") -logging.setLevel("DEBUG") +log.setLevel("DEBUG") route_context = contextvars.ContextVar("route_context", default={}) @@ -96,7 +91,7 @@ class PathValidator(BaseModel): url: HttpUrl -class route(BaseDecorator): +class route: # This decorator is used to expose specific stages of a workflow (embedding, retrieval, summarization, etc.) # as independent endpoints. It is designed for backward compatibility with existing code that uses # the @entrypoint decorator, which has certain limitations. By using @route(), we can create new @@ -118,7 +113,7 @@ def __call__(self, f): return f -class entrypoint(BaseDecorator): +class entrypoint: """ Decorator class to wrap a function for HTTP POST, terminal exposure and enable tracing. @@ -154,8 +149,6 @@ async def chain_of_prompts_llm(prompt: str): def __init__( self, func: Callable[..., Any], route_path="", config_schema: BaseModel = None ): - logging.info(f"Using Agenta Python SDK version {version('agenta')}") - DEFAULT_PATH = "generate" PLAYGROUND_PATH = "/playground" RUN_PATH = "/run" @@ -176,8 +169,9 @@ def __init__( config_params = config.dict() if config else ag.config.all() ingestible_files = self.extract_ingestible_files(func_signature) + self.route_path = route_path + ### --- Playground --- # - @debug() @functools.wraps(func) async def wrapper(*args, **kwargs) -> Any: func_params, api_config_params = self.split_kwargs(kwargs, config_params) @@ -185,10 +179,6 @@ async def wrapper(*args, **kwargs) -> Any: if not config_schema: ag.config.set(**api_config_params) - # Set the configuration and environment of the LLM app parent span at run-time - ag.tracing.update_baggage( - {"config": config_params, "environment": "playground"} - ) with route_context_manager(config=api_config_params): entrypoint_result = await self.execute_function( func, @@ -216,9 +206,11 @@ async def wrapper(*args, **kwargs) -> Any: { "func": func.__name__, "endpoint": route, - "params": {**config_params, **func_signature.parameters} - if not config - else func_signature.parameters, + "params": ( + {**config_params, **func_signature.parameters} + if not config + else func_signature.parameters + ), "config": config, } ) @@ -229,16 +221,17 @@ async def wrapper(*args, **kwargs) -> Any: { "func": func.__name__, "endpoint": route, - "params": {**config_params, **func_signature.parameters} - if not config - else func_signature.parameters, + "params": ( + {**config_params, **func_signature.parameters} + if not config + else func_signature.parameters + ), "config": config, } ) ### ---------------------------- # ### --- Deployed / Published --- # - @debug() @functools.wraps(func) async def wrapper_deployed(*args, **kwargs) -> Any: func_params = { @@ -252,10 +245,6 @@ async def wrapper_deployed(*args, **kwargs) -> Any: else: ag.config.pull(config_name="default") - # Set the configuration and environment of the LLM app parent span at run-time - ag.tracing.update_baggage( - {"config": config_params, "environment": kwargs["environment"]} - ) with route_context_manager( variant=kwargs["config"], environment=kwargs["environment"] ): @@ -347,84 +336,86 @@ def ingest_files( if name in func_params and func_params[name] is not None: func_params[name] = self.ingest_file(func_params[name]) - async def execute_function( - self, func: Callable[..., Any], inline_trace, *args, **func_params - ): - """Execute the function and handle any exceptions.""" + def patch_result(self, result): + data = ( + result["message"] + if isinstance(result, dict) + and all(key in result for key in ["message", "cost", "usage"]) + else result + ) - try: - """Note: The following block is for backward compatibility. - It allows functions to work seamlessly whether they are synchronous or asynchronous. - For synchronous functions, it calls them directly, while for asynchronous functions, - it awaits their execution. - """ - logging.info(f"Using Agenta Python SDK version {version('agenta')}") + if data is None: + data = ( + "Function executed successfully, but did return None. \n Are you sure you did not forget to return a value?", + ) - WAIT_FOR_SPANS = True - TIMEOUT = 1 - TIMESTEP = 0.1 - NOFSTEPS = TIMEOUT / TIMESTEP + if not isinstance(result, dict): + data = str(data) - data = None - trace = None + return data - token = None - if tracing_context.get() is None: - token = tracing_context.set(TracingContext()) + async def execute_function( + self, + func: Callable[..., Any], + inline_trace, + *args, + **func_params, + ): + log.info(f"\n--------------------------") + log.info( + f"Running application route: {repr(self.route_path if self.route_path != '' else '/')}" + ) + log.info(f"--------------------------\n") - is_coroutine_function = inspect.iscoroutinefunction(func) + WAIT_FOR_SPANS = True + TIMEOUT = 1 + TIMESTEP = 0.1 + FINALSTEP = 0.001 + NOFSTEPS = TIMEOUT / TIMESTEP - if is_coroutine_function: - result = await func(*args, **func_params["params"]) - else: - result = func(*args, **func_params["params"]) + try: + result = ( + await func(*args, **func_params["params"]) + if inspect.iscoroutinefunction(func) + else func(*args, **func_params["params"]) + ) - if token is not None: - if WAIT_FOR_SPANS: - remaining_steps = NOFSTEPS + data = self.patch_result(result) - while not ag.tracing.is_trace_ready() and remaining_steps > 0: - await asyncio.sleep(TIMESTEP) - remaining_steps -= 1 + if WAIT_FOR_SPANS: + remaining_steps = NOFSTEPS - trace = ag.tracing.dump_trace() + while ag.tracing.is_processing() and remaining_steps > 0: + await asyncio.sleep(TIMESTEP) + remaining_steps -= 1 - if not inline_trace: - trace = {"trace_id": trace["trace_id"]} + await asyncio.sleep(FINALSTEP) - ag.tracing.flush_spans() - tracing_context.reset(token) + trace = ag.tracing.get_inline_trace(trace_id_only=(not inline_trace)) - if isinstance(result, Context): - save_context(result) + response = BaseResponse(data=data, trace=trace) - data = result + return response - # PATCH : if result is not a dict, make it a dict - if not isinstance(result, dict): - data = result - else: - # PATCH : if result is a legacy dict, clean it up - if ( - "message" in result.keys() - and "cost" in result.keys() - and "usage" in result.keys() - ): - data = result["message"] - # END OF PATH + except Exception as e: + if WAIT_FOR_SPANS: + remaining_steps = NOFSTEPS - if data is None: - data = ( - "Function executed successfully, but did return None. \n Are you sure you did not forget to return a value?", - ) + while ag.tracing.is_processing() and remaining_steps > 0: + await asyncio.sleep(TIMESTEP) + remaining_steps -= 1 - response = BaseResponse(data=data, trace=trace) + await asyncio.sleep(FINALSTEP) - # logging.debug(response) + trace = ag.tracing.get_inline_trace(trace_id_only=(not inline_trace)) - return response + log.info("========= Error ==========") + log.info("") + + print("-> trace") + log.info(trace) + print("-> exception") - except Exception as e: self.handle_exception(e) def handle_exception(self, e: Exception): @@ -639,9 +630,6 @@ def handle_terminal_run( } ) - # Set the configuration and environment of the LLM app parent span at run-time - ag.tracing.update_baggage({"config": args_config_params, "environment": "bash"}) - loop = asyncio.get_event_loop() result = loop.run_until_complete( @@ -652,15 +640,15 @@ def handle_terminal_run( ) ) - print("\n========== Result ==========\n") + log.info("========= Result =========") + log.info("") print("-> data") print(json.dumps(result.data, indent=2)) print("-> trace") print(json.dumps(result.trace, indent=2)) - - with open("trace.json", "w") as trace_file: - json.dump(result.trace, trace_file, indent=4) + log.info("") + log.info("==========================") def override_config_in_schema( self, diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 7382fbb76d..f6c78c2ce1 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -1,130 +1,131 @@ -# Stdlib Imports import inspect -import traceback from functools import wraps -from typing import Any, Callable, Optional, List, Union +from itertools import chain +from contextlib import suppress +from typing import Callable, Optional, Union, Any, Dict, List -# Own Imports import agenta as ag -from agenta.sdk.decorators.base import BaseDecorator -from agenta.sdk.tracing.logger import llm_logger as logging -from agenta.sdk.utils.debug import debug, DEBUG, SHIFT -logging.setLevel("DEBUG") - - -class instrument(BaseDecorator): - """Decorator class for monitoring llm apps functions. - - Args: - BaseDecorator (object): base decorator class - - Example: - ```python - import agenta as ag - - prompt_config = {"system_prompt": ..., "temperature": 0.5, "max_tokens": ...} - - @ag.instrument(spankind="llm") - async def litellm_openai_call(prompt:str) -> str: - return "do something" - - @ag.instrument(config=prompt_config) # spankind for parent span defaults to workflow - async def generate(prompt: str): - return ... - ``` - """ +class instrument: + DEFAULT_KEY = "__default__" def __init__( self, - config: Optional[dict] = None, - spankind: str = "workflow", - ignore_inputs: Union[List[str], bool] = False, - ignore_outputs: Union[List[str], bool] = False, + kind: str = "UNKNOWN", + config: Optional[Dict[str, Any]] = None, + ignore: Union[List[str], bool] = False, ) -> None: + self.kind = kind self.config = config - self.spankind = spankind - self.ignore_inputs = ignore_inputs - self.ignore_outputs = ignore_outputs + self.ignore = ignore def __call__(self, func: Callable[..., Any]): is_coroutine_function = inspect.iscoroutinefunction(func) - def get_inputs(*args, **kwargs): - func_args = inspect.getfullargspec(func).args - input_dict = {name: value for name, value in zip(func_args, args)} - input_dict.update(kwargs) + def parse(*args, **kwargs) -> Dict[str, Any]: + inputs = { + key: value + for key, value in chain( + zip(inspect.getfullargspec(func).args, args), + kwargs.items(), + ) + } - return input_dict + return inputs - def redact(io, blacklist): - return { - key: io[key] - for key in io.keys() + def redact( + io: Dict[str, Any], ignore: List[str] | bool = False + ) -> Dict[str, Any]: + io = { + key: value + for key, value in io.items() if key not in ( - blacklist - if isinstance(blacklist, list) - else [] - if blacklist is False - else io.keys() + ignore + if isinstance(ignore, list) + else io.keys() if ignore is True else [] ) } - def patch(result): - TRACE_DEFAULT_KEY = "__default__" - - outputs = result + return io - # PATCH : if result is not a dict, make it a dict - if not isinstance(result, dict): - outputs = {TRACE_DEFAULT_KEY: result} - else: - # PATCH : if result is a legacy dict, clean it up - if ( - "message" in result.keys() - and "cost" in result.keys() - and "usage" in result.keys() - ): - outputs = {TRACE_DEFAULT_KEY: result["message"]} - - ag.tracing.store_cost(result["cost"]) - ag.tracing.store_usage(result["usage"]) + def patch(result): + outputs = ( + {instrument.DEFAULT_KEY: result} + if not isinstance(result, dict) + else ( + {instrument.DEFAULT_KEY: result["message"]} + if all(key in result for key in ["message", "cost", "usage"]) + else result + ) + ) return outputs @wraps(func) async def async_wrapper(*args, **kwargs): async def wrapped_func(*args, **kwargs): - with ag.tracing.Context( - name=func.__name__, - input=redact(get_inputs(*args, **kwargs), self.ignore_inputs), - spankind=self.spankind, - config=self.config, - ): - result = await func(*args, **kwargs) + with ag.tracing.start_as_current_span(func.__name__, self.kind): + try: + ag.tracing.set_attributes( + "metadata.config", + self.config, + ) + ag.tracing.set_attributes( + "data.inputs", + redact(parse(*args, **kwargs), self.ignore), + ) + + result = await func(*args, **kwargs) + + ag.tracing.set_attributes( + "data.outputs", + redact(patch(result), self.ignore), + ) + ag.tracing.set_status("OK") + + return result - ag.tracing.store_outputs(redact(patch(result), self.ignore_outputs)) + except Exception as e: + ag.tracing.record_exception(e) - return result + ag.tracing.set_status("ERROR") + + raise e return await wrapped_func(*args, **kwargs) @wraps(func) def sync_wrapper(*args, **kwargs): def wrapped_func(*args, **kwargs): - with ag.tracing.Context( - name=func.__name__, - input=redact(get_inputs(*args, **kwargs), self.ignore_inputs), - spankind=self.spankind, - config=self.config, - ): - result = func(*args, **kwargs) + with ag.tracing.start_as_current_span(func.__name__, self.kind): + try: + ag.tracing.set_attributes( + "metadata.config", + self.config, + ) + ag.tracing.set_attributes( + "data.inputs", + redact(parse(*args, **kwargs), self.ignore), + ) + + result = func(*args, **kwargs) + + ag.tracing.set_attributes( + "data.outputs", + redact(patch(result), self.ignore), + ) + ag.tracing.set_status("OK") + + return result + + except Exception as e: + ag.tracing.record_exception(e) - ag.tracing.store_outputs(redact(patch(result), self.ignore_outputs)) + ag.tracing.set_status("ERROR") - return result + raise e return wrapped_func(*args, **kwargs) diff --git a/agenta-cli/agenta/sdk/router.py b/agenta-cli/agenta/sdk/router.py index 42c766717b..b4cb63b59b 100644 --- a/agenta-cli/agenta/sdk/router.py +++ b/agenta-cli/agenta/sdk/router.py @@ -1,15 +1,8 @@ from fastapi import APIRouter -from .context import get_contexts router = APIRouter() -@router.get("/contexts/") -def get_all_contexts(): - contexts = get_contexts() - return {"contexts": contexts} - - @router.get("/health") def health(): return {"status": "ok"} diff --git a/agenta-cli/agenta/sdk/tracing/callbacks.py b/agenta-cli/agenta/sdk/tracing/callbacks.py deleted file mode 100644 index 2b1f7eb9a2..0000000000 --- a/agenta-cli/agenta/sdk/tracing/callbacks.py +++ /dev/null @@ -1,187 +0,0 @@ -import agenta as ag - -from agenta.sdk.tracing.tracing_context import tracing_context, TracingContext -from agenta.sdk.tracing.logger import llm_logger as logging - -from agenta.sdk.utils.debug import debug - -TRACE_DEFAULT_KEY = "__default__" - - -def litellm_handler(): - try: - from litellm.utils import ModelResponse - from litellm.integrations.custom_logger import ( - CustomLogger as LitellmCustomLogger, - ) - except ImportError as exc: - raise ImportError( - "The litellm SDK is not installed. Please install it using `pip install litellm`." - ) from exc - except Exception as exc: - raise Exception( - "Unexpected error occurred when importing litellm: {}".format(exc) - ) from exc - - class LitellmHandler(LitellmCustomLogger): - """This handler is responsible for instrumenting certain events when using litellm to call LLMs. - - Args: - LitellmCustomLogger (object): custom logger that allows us to override the events to capture. - """ - - def __init__(self): - self.span = None - - @property - def _trace(self): - return ag.tracing - - @debug() - def log_pre_api_call(self, model, messages, kwargs): - call_type = kwargs.get("call_type") - span_kind = ( - "llm" if call_type in ["completion", "acompletion"] else "embedding" - ) - - self.span = ag.tracing.open_span( - name=f"{span_kind}_call", - input={"messages": kwargs["messages"]}, - spankind=span_kind, - active=False, - ) - logging.info(f"log_pre_api_call({self.span.id})") - ag.tracing.set_attributes( - { - "model_config": { - "model": kwargs.get("model"), - **kwargs.get( - "optional_params" - ), # model-specific params passed in - }, - } - ) - - @debug() - def log_stream_event(self, kwargs, response_obj, start_time, end_time): - logging.info(f"log_stream_event({self.span.id})") - ag.tracing.set_status(status="OK", span_id=self.span.id) - ag.tracing.store_cost(kwargs.get("response_cost"), span_id=self.span.id) - ag.tracing.store_usage( - response_obj.usage.dict() if hasattr(response_obj, "usage") else None, - span_id=self.span.id, - ) - ag.tracing.store_outputs( - # the complete streamed response (only set if `completion(..stream=True)` - outputs={TRACE_DEFAULT_KEY: kwargs.get("complete_streaming_response")}, - span_id=self.span.id, - ) - ag.tracing.close_span(span_id=self.span.id) - - @debug() - def log_success_event(self, kwargs, response_obj, start_time, end_time): - logging.info(f"log_success_event({self.span.id})") - ag.tracing.set_status(status="OK", span_id=self.span.id) - ag.tracing.store_cost(kwargs.get("response_cost"), span_id=self.span.id) - ag.tracing.store_usage( - response_obj.usage.dict() if hasattr(response_obj, "usage") else None, - span_id=self.span.id, - ) - ag.tracing.store_outputs( - outputs={TRACE_DEFAULT_KEY: response_obj.choices[0].message.content}, - span_id=self.span.id, - ) - ag.tracing.close_span(span_id=self.span.id) - - @debug() - def log_failure_event(self, kwargs, response_obj, start_time, end_time): - logging.info("log_failure_event()", self.span) - ag.tracing.set_status(status="ERROR", span_id=self.span.id) - ag.tracing.set_attributes( - { - "traceback_exception": repr( - kwargs["traceback_exception"] - ), # the traceback generated via `traceback.format_exc()` - "call_end_time": kwargs[ - "end_time" - ], # datetime object of when call was completed - }, - span_id=self.span.id, - ) - ag.tracing.store_cost(kwargs.get("response_cost"), span_id=self.span.id) - ag.tracing.store_usage( - response_obj.usage.dict() if hasattr(response_obj, "usage") else None, - span_id=self.span.id, - ) - ag.tracing.store_outputs( - # the Exception raised - outputs={TRACE_DEFAULT_KEY: repr(kwargs["exception"])}, - span_id=self.span.id, - ) - ag.tracing.close_span(span_id=self.span.id) - - @debug() - async def async_log_stream_event( - self, kwargs, response_obj, start_time, end_time - ): - logging.info(f"async_log_stream_event({self.span.id})") - ag.tracing.set_status(status="OK", span_id=self.span.id) - ag.tracing.store_cost(kwargs.get("response_cost"), span_id=self.span.id) - ag.tracing.store_usage( - response_obj.usage.dict() if hasattr(response_obj, "usage") else None, - span_id=self.span.id, - ) - ag.tracing.store_outputs( - # the complete streamed response (only set if `completion(..stream=True)`) - outputs={TRACE_DEFAULT_KEY: kwargs.get("complete_streaming_response")}, - span_id=self.span.id, - ) - ag.tracing.close_span(span_id=self.span.id) - - @debug() - async def async_log_success_event( - self, kwargs, response_obj, start_time, end_time - ): - logging.info(f"async_log_success_event({self.span.id})") - ag.tracing.set_status(status="OK", span_id=self.span.id) - ag.tracing.store_cost(kwargs.get("response_cost"), span_id=self.span.id) - ag.tracing.store_usage( - response_obj.usage.dict() if hasattr(response_obj, "usage") else None, - span_id=self.span.id, - ) - ag.tracing.store_outputs( - outputs={TRACE_DEFAULT_KEY: response_obj.choices[0].message.content}, - span_id=self.span.id, - ) - ag.tracing.close_span(span_id=self.span.id) - - @debug() - async def async_log_failure_event( - self, kwargs, response_obj, start_time, end_time - ): - logging.info(f"async_log_failure_event({self.span.id})") - ag.tracing.set_status(status="ERROR", span_id=self.span.id) - ag.tracing.set_attributes( - { - "traceback_exception": kwargs[ - "traceback_exception" - ], # the traceback generated via `traceback.format_exc()` - "call_end_time": kwargs[ - "end_time" - ], # datetime object of when call was completed - }, - span_id=self.span.id, - ) - ag.tracing.store_cost(kwargs.get("response_cost"), span_id=self.span.id) - ag.tracing.store_usage( - response_obj.usage.dict() if hasattr(response_obj, "usage") else None, - span_id=self.span.id, - ) - ag.tracing.store_outputs( - # the Exception raised - outputs={TRACE_DEFAULT_KEY: repr(kwargs["exception"])}, - span_id=self.span.id, - ) - ag.tracing.close_span(span_id=self.span.id) - - return LitellmHandler() diff --git a/agenta-cli/agenta/sdk/tracing/litellm.py b/agenta-cli/agenta/sdk/tracing/litellm.py new file mode 100644 index 0000000000..54146126a6 --- /dev/null +++ b/agenta-cli/agenta/sdk/tracing/litellm.py @@ -0,0 +1,266 @@ +import agenta as ag + + +def litellm_handler(): + try: + from litellm.utils import ModelResponse + from litellm.integrations.custom_logger import ( + CustomLogger as LitellmCustomLogger, + ) + except ImportError as exc: + raise ImportError( + "The litellm SDK is not installed. Please install it using `pip install litellm`." + ) from exc + except Exception as exc: + raise Exception( + "Unexpected error occurred when importing litellm: {}".format(exc) + ) from exc + + class LitellmHandler(LitellmCustomLogger): + """This handler is responsible for instrumenting certain events when using litellm to call LLMs. + + Args: + LitellmCustomLogger (object): custom logger that allows us to override the events to capture. + """ + + def __init__(self): + self.span = None + + def log_pre_api_call( + self, + model, + messages, + kwargs, + ): + kind = ( + "GENERATION" + if kwargs.get("call_type") in ["completion", "acompletion"] + else "EMBEDDING" + ) + + self.span = ag.tracing.start_span(name=f"litellm_{kind.lower()}", kind=kind) + + if not self.span: + ag.logging.error("LiteLLM callback error: span not found.") + return + + # ag.logging.info(f"log_pre_api_call({hex(self.span.context.span_id)[2:]})") + + ag.tracing.set_attributes( + namespace="data.inputs", + attributes={"messages": kwargs["messages"]}, + span=self.span, + ) + + ag.tracing.set_attributes( + namespace="metadata.config", + attributes={ + "model": kwargs.get("model"), + **kwargs.get("optional_params"), + }, + span=self.span, + ) + + def log_stream_event( + self, + kwargs, + response_obj, + start_time, + end_time, + ): + if not self.span: + ag.logging.error("LiteLLM callback error: span not found.") + return + + # ag.logging.info(f"log_stream({hex(self.span.context.span_id)[2:]})") + + ag.tracing.set_attributes( + namespace="data.outputs", + attributes={"__default__": kwargs.get("complete_streaming_response")}, + span=self.span, + ) + + ag.tracing.set_attributes( + namespace="metrics.costs", + attributes={"marginal": kwargs.get("response_cost")}, + span=self.span, + ) + + ag.tracing.set_attributes( + namespace="metrics.tokens", + attributes=( + { + "prompt": response_obj.usage.prompt_tokens, + "completion": response_obj.usage.completion_tokens, + "total": response_obj.usage.total_tokens, + } + ), + span=self.span, + ) + + ag.tracing.set_status(status="OK", span=self.span) + + self.span.end() + + def log_success_event( + self, + kwargs, + response_obj, + start_time, + end_time, + ): + if not self.span: + ag.logging.error("LiteLLM callback error: span not found.") + return + + # ag.logging.info(f"log_success({hex(self.span.context.span_id)[2:]})") + + ag.tracing.set_attributes( + namespace="data.outputs", + attributes={"__default__": response_obj.choices[0].message.content}, + span=self.span, + ) + + ag.tracing.set_attributes( + namespace="metrics.costs", + attributes={"marginal": kwargs.get("response_cost")}, + span=self.span, + ) + + ag.tracing.set_attributes( + namespace="metrics.tokens", + attributes=( + { + "prompt": response_obj.usage.prompt_tokens, + "completion": response_obj.usage.completion_tokens, + "total": response_obj.usage.total_tokens, + } + ), + span=self.span, + ) + + ag.tracing.set_status(status="OK", span=self.span) + + self.span.end() + + def log_failure_event( + self, + kwargs, + response_obj, + start_time, + end_time, + ): + if not self.span: + ag.logging.error("LiteLLM callback error: span not found.") + return + + # ag.logging.info(f"log_failure({hex(self.span.context.span_id)[2:]})") + + ag.tracing.record_exception(kwargs["exception"], span=self.span) + + ag.tracing.set_status(status="ERROR", span=self.span) + + self.span.end() + + async def async_log_stream_event( + self, + kwargs, + response_obj, + start_time, + end_time, + ): + if not self.span: + ag.logging.error("LiteLLM callback error: span not found.") + return + + # ag.logging.info(f"async_log_stream({hex(self.span.context.span_id)[2:]})") + + ag.tracing.set_attributes( + namespace="data.outputs", + attributes={"__default__": kwargs.get("complete_streaming_response")}, + span=self.span, + ) + + ag.tracing.set_attributes( + namespace="metrics.costs", + attributes={"marginal": kwargs.get("response_cost")}, + span=self.span, + ) + + ag.tracing.set_attributes( + namespace="metrics.tokens", + attributes=( + { + "prompt": response_obj.usage.prompt_tokens, + "completion": response_obj.usage.completion_tokens, + "total": response_obj.usage.total_tokens, + } + ), + span=self.span, + ) + + ag.tracing.set_status(status="OK", span=self.span) + + self.span.end() + + async def async_log_success_event( + self, + kwargs, + response_obj, + start_time, + end_time, + ): + if not self.span: + ag.logging.error("LiteLLM callback error: span not found.") + return + + # ag.logging.info(f"async_log_success({hex(self.span.context.span_id)[2:]})") + + ag.tracing.set_attributes( + namespace="data.outputs", + attributes={"__default__": response_obj.choices[0].message.content}, + span=self.span, + ) + + ag.tracing.set_attributes( + namespace="metrics.costs", + attributes={"marginal": kwargs.get("response_cost")}, + span=self.span, + ) + + ag.tracing.set_attributes( + namespace="metrics.tokens", + attributes=( + { + "prompt": response_obj.usage.prompt_tokens, + "completion": response_obj.usage.completion_tokens, + "total": response_obj.usage.total_tokens, + } + ), + span=self.span, + ) + + ag.tracing.set_status(status="OK", span=self.span) + + self.span.end() + + async def async_log_failure_event( + self, + kwargs, + response_obj, + start_time, + end_time, + ): + if not self.span: + ag.logging.error("LiteLLM callback error: span not found.") + return + + # ag.logging.info(f"async_log_failure({hex(self.span.context.span_id)[2:]})") + + ag.tracing.record_exception(kwargs["exception"], span=self.span) + + ag.tracing.set_status(status="ERROR", span=self.span) + + self.span.end() + + return LitellmHandler() diff --git a/agenta-cli/agenta/sdk/tracing/llm_tracing.py b/agenta-cli/agenta/sdk/tracing/llm_tracing.py deleted file mode 100644 index 009da6a302..0000000000 --- a/agenta-cli/agenta/sdk/tracing/llm_tracing.py +++ /dev/null @@ -1,617 +0,0 @@ -import os -import copy -import json -from uuid import uuid4 - -import traceback -from threading import Lock -from datetime import datetime, timezone -from typing import Optional, Dict, Any, List - -from contextlib import contextmanager - -from agenta.sdk.tracing.tracing_context import tracing_context, TracingContext -from agenta.sdk.tracing.logger import llm_logger as logging -from agenta.sdk.tracing.tasks_manager import TaskQueue -from agenta.client.backend.client import AsyncAgentaApi -from agenta.client.backend.client import AsyncObservabilityClient -from agenta.client.backend.types.create_span import ( - CreateSpan, - LlmTokens, -) -from agenta.client.backend.types.span_status_code import SpanStatusCode - -from bson.objectid import ObjectId - -VARIANT_TRACKING_FEATURE_FLAG = False - -from agenta.sdk.utils.debug import debug - - -logging.setLevel("DEBUG") - - -class SingletonMeta(type): - """ - Thread-safe implementation of Singleton. - """ - - _instances = {} # type: ignore - - # We need the lock mechanism to synchronize threads \ - # during the initial access to the Singleton object. - _lock: Lock = Lock() - - def __call__(cls, *args, **kwargs): - """ - Ensures that changes to the `__init__` arguments do not affect the - returned instance. - - Uses a lock to make this method thread-safe. If an instance of the class - does not already exist, it creates one. Otherwise, it returns the - existing instance. - """ - - with cls._lock: - if cls not in cls._instances: - instance = super().__call__(*args, **kwargs) - cls._instances[cls] = instance - return cls._instances[cls] - - -class Tracing(metaclass=SingletonMeta): - """The `Tracing` class is an agent for LLM tracing with specific initialization arguments. - - __init__ args: - host (str): The URL of the backend host - api_key (str): The API Key of the backend host - tasks_manager (TaskQueue): The tasks manager dedicated to handling asynchronous tasks - max_workers (int): The maximum number of workers to run tracing - """ - - def __init__( - self, - host: str, - app_id: str, - api_key: Optional[str] = None, - max_workers: Optional[int] = None, - ): - self.host = host + "/api" - self.api_key = api_key if api_key is not None else "" - self.app_id = app_id - self.tasks_manager = TaskQueue( - max_workers if max_workers else 4, logger=logging - ) - self.baggage = None - - @property - def client(self) -> AsyncObservabilityClient: - """Initialize observability async client - - Returns: - AsyncObservabilityClient: async client - """ - - return AsyncAgentaApi( - base_url=self.host, api_key=self.api_key, timeout=120 # type: ignore - ).observability - - ### --- Context Manager --- ### - - @contextmanager - def Context(self, **kwargs): - # This will evolve as be work towards OTel compliance - - token = None - - try: - if tracing_context.get() is None: - token = tracing_context.set(TracingContext()) - - self.open_span(**kwargs) - - yield - - self.set_status(status="OK") - - except Exception as e: - logging.error(e) - - result = { - "message": str(e), - "stacktrace": traceback.format_exc(), - } - - self.set_status(status="ERROR") - self.set_attributes({"traceback_exception": traceback.format_exc()}) - self.store_outputs(result) - - raise - - finally: - self.close_span() - - if token is not None: - self.flush_spans() - - tracing_context.reset(token) - - ### --- API --- ### - - @debug() - def get_context(self): - tracing = tracing_context.get() - - return tracing - - @debug() - def update_baggage( - self, - attributes: Dict[str, Any] = {}, - ): - if self.baggage is None: - self.baggage = {} - - for key, value in attributes.items(): - self.baggage[key] = value - - @debug() - def open_trace( - self, - span: Optional[CreateSpan], - config: Optional[Dict[str, Any]] = None, - **kwargs, - ) -> None: - tracing = tracing_context.get() - - tracing.trace_id = self._create_trace_id() - - logging.info(f"Opening trace {tracing.trace_id}") - - if span is not None: - ### --- TO BE CLEANED --- >>> - span.environment = ( - self.baggage.get("environment") - if self.baggage is not None - else os.environ.get("environment", "unset") - ) - - span.config = ( - self.baggage.get("config") - if not config and self.baggage is not None - else None - ) - if VARIANT_TRACKING_FEATURE_FLAG: - # TODO: we should get the variant_id and variant_name (and environment) from the config object - span.variant_id = config.variant_id # type: ignore - span.variant_name = (config.variant_name,) # type: ignore - ### --- TO BE CLEANED --- <<< - - logging.info(f"Opened trace {tracing.trace_id}") - - @debug() - def set_trace_tags(self, tags: List[str]) -> None: - tracing = tracing_context.get() - - tracing.trace_tags.extend(tags) - - @debug() - def is_trace_ready(self): - tracing = tracing_context.get() - - are_spans_ready = [ - (span.end_time == span.start_time) for span in tracing.spans.values() - ] - - return all(are_spans_ready) - - @debug() - def close_trace(self) -> None: - """ - Ends the active trace and sends the recorded spans for processing. - - Args: - parent_span (CreateSpan): The parent span of the trace. - - Raises: - RuntimeError: If there is no active trace to end. - - Returns: - None - """ - tracing = tracing_context.get() - - logging.info(f"Closing trace {tracing.trace_id}") - - trace_id = tracing.trace_id - - if tracing.trace_id is None: - logging.error("Cannot close trace, no trace to close") - return - - if not self.api_key: - logging.error("No API key") - else: - self._process_spans() - - self._clear_trace_tags() - - logging.info(f"Closed trace {trace_id}") - - @debug() - def open_span( - self, - name: str, - spankind: str, - input: Dict[str, Any], - active: bool = True, - config: Optional[Dict[str, Any]] = None, - **kwargs, - ) -> CreateSpan: - tracing = tracing_context.get() - - span_id = self._create_span_id() - - logging.info(f"Opening span {span_id} {spankind.upper()}") - - ### --- TO BE CLEANED --- >>> - now = datetime.now(timezone.utc) - - span = CreateSpan( - id=span_id, - inputs=input, - name=name, - app_id=self.app_id, - config=config, - spankind=spankind.upper(), - attributes={}, - status="UNSET", - start_time=now, - internals=None, - outputs=None, - tags=None, - user=None, - end_time=now, - tokens=None, - cost=None, - token_consumption=None, - parent_span_id=None, - ) - - if tracing.trace_id is None: - self.open_trace(span, config) - else: - span.parent_span_id = tracing.active_span.id # type: ignore - - tracing.spans[span.id] = span - - if active: - tracing.active_span = span - else: - # DETACHED SPAN - pass - - ### --- TO BE CLEANED --- <<< - - logging.info(f"Opened span {span_id} {spankind.upper()}") - - return span - - @debug(req=True) - def set_attributes( - self, attributes: Dict[str, Any] = {}, span_id: Optional[str] = None - ) -> None: - """ - Set attributes for the active span. - - Args: - attributes (Dict[str, Any], optional): A dictionary of attributes to set. Defaults to {}. - """ - - span = self._get_target_span(span_id) - - logging.info( - f"Setting span {span.id} {span.spankind.upper()} attributes={attributes}" - ) - - for key, value in attributes.items(): - span.attributes[key] = value # type: ignore - - @debug() - def set_status(self, status: str, span_id: Optional[str] = None) -> None: - """ - Set status for the active span. - - Args: - status: Enum ( UNSET, OK, ERROR ) - """ - span = self._get_target_span(span_id) - - logging.info(f"Setting span {span.id} {span.spankind.upper()} status={status}") - - span.status = status - - @debug() - def close_span(self, span_id: Optional[str] = None) -> None: - """ - Ends the active span, if it is a parent span, ends the trace too. - - Args: - outputs (Dict[str, Any]): A dictionary containing the outputs of the span. - It should have the following keys: - - "message" (str): The message output of the span. - - "cost" (Optional[Any]): The cost of the span. - - "usage" (Optional[Any]): The number of tokens used in the span. - - Raises: - ValueError: If there is no active span to end. - - Returns: - None - """ - span = self._get_target_span(span_id) - - spankind = span.spankind - - logging.info(f"Closing span {span.id} {spankind}") - - ### --- TO BE CLEANED --- >>> - span.end_time = datetime.now(timezone.utc) - - # TODO: Remove this whole part. Setting the cost should be done through set_span_attribute - if isinstance(span.outputs, dict): - self._update_span_cost(span, span.outputs.get("cost", None)) - self._update_span_tokens(span, span.outputs.get("usage", None)) - - span_parent_id = span.parent_span_id - - if span_parent_id is not None: - tracing = tracing_context.get() - - parent_span = tracing.spans[span_parent_id] - self._update_span_cost(parent_span, span.cost) - self._update_span_tokens(parent_span, span.tokens) - - if span_id is None: - tracing.active_span = parent_span - ### --- TO BE CLEANED --- <<< - - logging.info(f"Closed span {span.id} {spankind}") - - @debug() - def store_internals( - self, internals: Dict[str, Any] = {}, span_id: Optional[str] = None - ) -> None: - """ - Set internals for the active span. - - Args: - internals (Dict[str, Any], optional): A dictionary of local variables to set. Defaults to {}. - """ - span = self._get_target_span(span_id) - - logging.info( - f"Setting span {span.id} {span.spankind.upper()} internals={internals}" - ) - - if span.internals is None: - span.internals = dict() - - for key, value in internals.items(): - span.internals[key] = value # type: ignore - - @debug() - def store_outputs( - self, outputs: Dict[str, Any] = {}, span_id: Optional[str] = None - ) -> None: - """ - Set outputs for the active span. - - Args: - outputs (Dict[str, Any], optional): A dictionary of output variables to set. Defaults to {}. - """ - span = self._get_target_span(span_id) - - logging.info( - f"Setting span {span.id} {span.spankind.upper()} outputs={outputs}" - ) - - span.outputs = outputs - - @debug() - def store_cost(self, cost: float = 0.0, span_id: Optional[str] = None) -> None: - """ - ... - """ - span = self._get_target_span(span_id) - - logging.info(f"Setting span {span.id} {span.spankind.upper()} cost={cost}") - - self._update_span_cost(span, cost) - - @debug() - def store_usage(self, tokens: dict = {}, span_id: Optional[str] = None) -> None: - """ - ... - """ - span = self._get_target_span(span_id) - - logging.info( - f"Setting span {span.id} {span.spankind.upper()} tokens={repr(tokens)}" - ) - - self._update_span_tokens(span, tokens) - - @debug() - def dump_trace(self): - """ - Collects and organizes tracing information into a dictionary. - This function retrieves the current tracing context and extracts relevant data such as `trace_id`, `cost`, `tokens`, and `latency` for the whole trace. - It also dumps detailed span information using the `dump_spans` method and includes it in the trace dictionary. - If an error occurs during the process, it logs the error message and stack trace. - - Returns: - dict: A dictionary containing the trace information. - """ - try: - trace = dict() - - tracing = tracing_context.get() - - trace["trace_id"] = tracing.trace_id - - for span in tracing.spans.values(): - if span.parent_span_id is None: - trace["cost"] = span.cost - trace["usage"] = ( - None if span.tokens is None else json.loads(span.tokens.json()) - ) - trace["latency"] = (span.end_time - span.start_time).total_seconds() - - spans = ( - [] - if len(tracing.spans) == 0 - else [json.loads(span.json()) for span in tracing.spans.values()] - ) - - if spans is not None: - trace["spans"] = spans - - except Exception as e: - logging.error(e) - logging.error(traceback.format_exc()) - - return trace - - def flush_spans(self) -> None: - self.close_trace() - - ### --- Legacy API --- ### - - def start_trace( - self, - span: CreateSpan, - config: Optional[Dict[str, Any]] = None, - **kwargs, - ) -> None: # Legacy - self.open_trace(span, config, **kwargs) - - def end_trace(self, _: CreateSpan) -> None: # Legacy - self.close_trace() - - def start_span( - self, - name: str, - spankind: str, - input: Dict[str, Any], - config: Optional[Dict[str, Any]] = None, - **kwargs, - ) -> CreateSpan: # Legacy - return self.open_span(name, spankind, input, config, **kwargs) - - def update_span_status(self, _: CreateSpan, status: str) -> None: # Legacy - self.update_span_status(status) - - def set_span_attribute( - self, - attributes: Dict[str, Any] = {}, - ) -> None: # Legacy - self.set_attributes(attributes) - - def end_span(self, outputs: Dict[str, Any]) -> None: # Legacy - self.store_outputs(outputs) - self.close_span() - - ### --- Helper Functions --- ### - - def _create_trace_id(self) -> str: - """Creates a 32HEXDIGL / ObjectId ID for the trace object. - - Returns: - str: stringify oid of the trace - """ - - # return uuid4().hex - return str(ObjectId()) - - def _clear_trace_tags(self) -> None: - tracing = tracing_context.get() - - tracing.trace_tags.clear() - - def _create_span_id(self) -> str: - """Creates a 16HEXDIGL / ObjectId ID for the span object. - - Returns: - str: stringify oid of the span - """ - - # return uuid4().hex[:16] - return str(ObjectId()) - - def _get_target_span(self, span_id) -> CreateSpan: - tracing = tracing_context.get() - - span = None - - if span_id is None: - if tracing.active_span is None: - logging.error(f"Cannot set attributes, no active span") - return - - span = tracing.active_span - else: - if span_id not in tracing.spans.keys(): - logging.error(f"Cannot set attributes, span ({span_id}) not found") - return - - span = tracing.spans[span_id] - - return span - - def _process_spans(self) -> None: - tracing = tracing_context.get() - - spans = list(tracing.spans.values()) - - logging.info(f"Sending trace {tracing.trace_id} spans={len(spans)} ") - - self.tasks_manager.add_task( - tracing.trace_id, - "send-trace", - # mock_create_traces( - self.client.create_traces( - trace=tracing.trace_id, spans=spans # type: ignore - ), - self.client, - ) - - logging.info(f"Sent trace {tracing.trace_id}") - - def _update_span_cost(self, span: CreateSpan, cost: Optional[float]) -> None: - if span is not None and cost is not None and isinstance(cost, float): - if span.cost is None: - span.cost = cost - else: - span.cost += cost - - def _update_span_tokens(self, span: CreateSpan, tokens: Optional[dict]) -> None: - if isinstance(tokens, LlmTokens): - tokens = tokens.dict() - if span is not None and tokens is not None and isinstance(tokens, dict): - if span.tokens is None: - span.tokens = LlmTokens(**tokens) - else: - span.tokens.prompt_tokens += ( - tokens["prompt_tokens"] - if tokens["prompt_tokens"] is not None - else 0 - ) - span.tokens.completion_tokens += ( - tokens["completion_tokens"] - if tokens["completion_tokens"] is not None - else 0 - ) - span.tokens.total_tokens += ( - tokens["total_tokens"] if tokens["total_tokens"] is not None else 0 - ) diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py new file mode 100644 index 0000000000..daa81ebcd4 --- /dev/null +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -0,0 +1,459 @@ +import os +import copy +import json +from uuid import uuid4 + +import traceback +from threading import Lock +from datetime import datetime, timezone +from typing import Optional, Dict, Any, List, Literal + +from contextlib import contextmanager, suppress + +import agenta as ag + +from importlib.metadata import version + +from agenta.sdk.utils.logging import log +from agenta.client.backend.client import AsyncAgentaApi +from agenta.client.backend.client import AsyncObservabilityClient +from agenta.client.backend.types.create_span import ( + CreateSpan, + LlmTokens, +) + +from agenta.client.backend.types.span_status_code import SpanStatusCode + +from bson.objectid import ObjectId + +VARIANT_TRACKING_FEATURE_FLAG = False + +from agenta.sdk.utils.debug import debug + +from typing import Sequence +from opentelemetry.context import Context +from opentelemetry.sdk.trace import ( + Span, + TracerProvider, + ConcurrentMultiSpanProcessor, + SynchronousMultiSpanProcessor, + Status as FullStatus, + StatusCode, +) +from opentelemetry.exporter.otlp.proto.http.trace_exporter import ( + OTLPSpanExporter, +) +from opentelemetry.trace.propagation import get_current_span +from opentelemetry.sdk.trace.export import ( + SpanExportResult, + SpanExporter, + ReadableSpan, + BatchSpanProcessor, + _DEFAULT_EXPORT_TIMEOUT_MILLIS, + _DEFAULT_MAX_QUEUE_SIZE, +) + +_AGENTA_ROOT_SPAN_ID = "f" * 16 + +_AGENTA_PROJECT_ID_HEADER = "project-id" +_AGENTA_API_KEY_HEADER = "api-key" + +_AGENTA_EXPERIMENT_ID_HEADER = "experiment-id" + + +log.setLevel("DEBUG") + + +class SingletonMeta(type): + """ + Thread-safe implementation of Singleton. + """ + + _instances = {} # type: ignore + + # We need the lock mechanism to synchronize threads \ + # during the initial access to the Singleton object. + _lock: Lock = Lock() + + def __call__(cls, *args, **kwargs): + """ + Ensures that changes to the `__init__` arguments do not affect the + returned instance. + + Uses a lock to make this method thread-safe. If an instance of the class + does not already exist, it creates one. Otherwise, it returns the + existing instance. + """ + + with cls._lock: + if cls not in cls._instances: + instance = super().__call__(*args, **kwargs) + cls._instances[cls] = instance + return cls._instances[cls] + + +class TraceProcessor(BatchSpanProcessor): + + def __init__( + self, + span_exporter: SpanExporter, + max_queue_size: int = None, + schedule_delay_millis: float = None, + max_export_batch_size: int = None, + export_timeout_millis: float = None, + ): + super().__init__( + span_exporter, + _DEFAULT_MAX_QUEUE_SIZE, + 60 * 60 * 1000, # 1 hour + _DEFAULT_MAX_QUEUE_SIZE, + _DEFAULT_EXPORT_TIMEOUT_MILLIS, + ) + + self.registry = dict() + + def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None: + super().on_start(span, parent_context=parent_context) + + if span.context.trace_id not in self.registry: + self.registry[span.context.trace_id] = dict() + + self.registry[span.context.trace_id][span.context.span_id] = True + + def on_end(self, span: ReadableSpan): + super().on_end(span) + + del self.registry[span.context.trace_id][span.context.span_id] + + if self.is_done(): + self.force_flush() + + def is_done(self): + return all( + not len(self.registry[trace_id]) for trace_id in self.registry.keys() + ) + + +class InlineTraceExporter(SpanExporter): + + def __init__(self, registry): + self._shutdown = False + self._registry = registry + + def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: + if self._shutdown: + return + + for span in spans: + self._registry.update(**{span.context.span_id.to_bytes(8).hex(): span}) + + def shutdown(self) -> None: + self._shutdown = True + + def force_flush(self, timeout_millis: int = 30000) -> bool: + return True + + +Namespace = Literal[ + "data.inputs", + "data.internals", + "data.outputs", + "metrics.scores", + "metrics.costs", + "metrics.tokens", + "metadata.config", + "metadata.version", + "tags", + "resource.project", + "resource.experiment", + "resource.application", + "resource.configuration", + "resource.service", + "extra", +] + +Status = Literal[ + "OK", + "ERROR", +] + + +class Tracing: + VERSION = "2.0" + + # @suppress(Exception) + def __init__( + self, + url: str, + project_id: Optional[str] = None, + api_key: Optional[str] = None, + experiment_id: Optional[str] = None, + ) -> None: + # ENDPOINT + self.url = url + # AUTHENTICATION + self.project_id = project_id + # AUTHORIZATION + self.api_key = api_key + # EXPERIMENT + self.experiment_id = experiment_id + + # HEADERS + self.headers = {} + if self.project_id: + self.headers.update(**{_AGENTA_PROJECT_ID_HEADER: self.project_id}) + if api_key: + self.headers.update(**{_AGENTA_API_KEY_HEADER: self.api_key}) + if experiment_id: + self.headers.update(**{_AGENTA_EXPERIMENT_ID_HEADER: self.experiment_id}) + + # SPANS (INLINE TRACE) + self.spans: Dict[str:ReadableSpan] = dict() + + # TRACER PROVIDER + self.tracer_provider = TracerProvider( + active_span_processor=SynchronousMultiSpanProcessor() + # active_span_processor=ConcurrentMultiSpanProcessor( + # num_threads=2, # inline_processor + remote_processor + # ) + ) + # self.tracer_provider.add_span_processor( + # TraceProcessor( + # OTLPSpanExporter( + # endpoint=self.url, + # headers=self.headers, + # ) + # ) + # ) + self.inline_processor = TraceProcessor(InlineTraceExporter(registry=self.spans)) + self.tracer_provider.add_span_processor(self.inline_processor) + # TRACER + self.tracer = self.tracer_provider.get_tracer("ag.tracing") + + # @suppress(Exception) + @contextmanager + def start_as_current_span(self, name: str, kind: str): + with self.tracer.start_as_current_span(name) as span: + self.set_attributes( + namespace="metadata.version", + attributes={"agenta": version("agenta"), "tracing": Tracing.VERSION}, + ) + self.set_attributes( + namespace="extra", + attributes={"kind": kind}, + ) + yield span + + # @suppress(Exception) + def start_span(self, name: str, kind: str): + span = self.tracer.start_span(name) + + self.set_attributes( + namespace="metadata.version", + attributes={"agenta": version("agenta"), "tracing": Tracing.VERSION}, + span=span, + ) + self.set_attributes( + namespace="extra", + attributes={"kind": kind}, + span=span, + ) + + return span + + def set_status( + self, + status: Status, + message: Optional[str] = None, + span: Optional[Span] = None, + ): + if span is None: + span = get_current_span() + + if status == "OK": + if span.status.status_code != StatusCode.ERROR: + span.set_status( + FullStatus(status_code=StatusCode.OK, description=message), + ) + elif status == "ERROR": + span.set_status( + FullStatus(status_code=StatusCode.ERROR, description=message), + ) + + def add_event( + self, + name, + attributes=None, + timestamp=None, + span: Optional[Span] = None, + ): + if span is None: + span = get_current_span() + + span.add_event( + name=name, + attributes=attributes, + timestamp=timestamp, + ) + + def record_exception( + self, exception, attributes=None, timestamp=None, span: Optional[Span] = None + ): + if span is None: + span = get_current_span() + + span.record_exception( + exception=exception, + attributes=attributes, + timestamp=timestamp, + escaped=None, + ) + + def _key(self, namespace, key=""): + return f"ag.{namespace}.{key}" + + def _value(self, value: Any) -> str: + if value is None: + return "null" + + if not isinstance(value, (str, int, float, bool)): + return json.dumps(value) + + return value + + def set_attributes( + self, + namespace: Namespace, + attributes: Optional[Dict[str, Any]] = None, + span: Optional[Span] = None, + ) -> None: + if attributes is None: + return + + if span is None: + span = get_current_span() + + for key, value in attributes.items(): + span.set_attribute( + self._key(namespace, key), + self._value(value), + ) + + def store_inputs(self, attributes: dict, span: Optional[Span] = None) -> None: + self.set_attributes("data.inputs", attributes, span) + + def store_internals(self, attributes: dict, span: Optional[Span] = None) -> None: + self.set_attributes("data.internals", attributes, span) + + def store_outputs(self, attributes: dict, span: Optional[Span] = None) -> None: + self.set_attributes("data.outputs", attributes, span) + + def store_costs(self, attributes: dict, span: Optional[Span] = None) -> None: + self.set_attributes("metrics.costs", attributes, span) + + def store_latencies(self, attributes: dict, span: Optional[Span] = None) -> None: + self.set_attributes("metrics.latencies", attributes, span) + + def store_tokens(self, attributes: dict, span: Optional[Span] = None) -> None: + self.set_attributes("metrics.tokens", attributes, span) + + def store_config(self, attributes: dict, span: Optional[Span] = None) -> None: + self.set_attributes("metadata.config", attributes, span) + + def store_tags(self, attributes: dict, span: Optional[Span] = None) -> None: + self.set_attributes("tags", attributes, span) + + def is_processing(self) -> bool: + return not self.inline_processor.is_done() + + def get_inline_trace(self, trace_id_only: bool = False): + spans_idx: Dict[str, List[ReadableSpan]] = dict() + + for span in self.spans.values(): + span: ReadableSpan + + trace_id = hex(span.context.trace_id)[2:] + + if trace_id not in spans_idx: + spans_idx[trace_id] = list() + + if not trace_id_only: + spans_idx[trace_id].append(self._parse_to_legacy_span(span)) + + inline_traces = [ + {"trace_id": trace_id, "spans": spans} + for trace_id, spans in spans_idx.items() + ] + + if len(inline_traces) > 1: + log.error("Unexpected error while parsing inline trace: too many traces.") + + return inline_traces[0] + + def _get_attributes( + self, + namespace: str, + span: ReadableSpan, + ): + return { + k.replace(self._key(namespace), ""): v + for k, v in span.attributes.items() + if k.startswith(self._key(namespace)) + } + + def _parse_to_legacy_span( + self, + span: ReadableSpan, + ): + + attributes = dict(span.attributes) + + for event in span.events: + if event.name == "exception": + attributes.update(**event.attributes) + + legacy_span = CreateSpan( + id=hex(span.context.span_id)[2:], + spankind=self._get_attributes("extra", span).get("kind", "UNKNOWN"), + name=span.name, + status=str(span.status.status_code.name), + # + start_time=datetime.fromtimestamp( + span.start_time / 1_000_000_000, + ).isoformat(), + end_time=datetime.fromtimestamp( + span.end_time / 1_000_000_000, + ).isoformat(), + # + parent_span_id=hex(span.parent.span_id)[2:] if span.parent else None, + # + inputs=self._get_attributes("data.inputs", span), + internals=self._get_attributes("data.internals", span), + outputs=self._get_attributes("data.outputs", span), + # + config=self._get_attributes("metadata.config", span), + # + tokens=LlmTokens( + prompt_tokens=self._get_attributes("metrics.tokens", span).get( + "prompt", None + ), + completion_tokens=self._get_attributes("metrics.tokens", span).get( + "completion", None + ), + total_tokens=self._get_attributes("metrics.tokens", span).get( + "total", None + ), + ), + cost=self._get_attributes("metrics.costs", span).get("marginal", 0.0), + # + app_id="", + variant_id=None, + variant_name=None, + environment=None, + tags=None, + token_consumption=None, + attributes=attributes, + user=None, + ) + + return json.loads(legacy_span.json()) diff --git a/agenta-cli/agenta/sdk/tracing/tasks_manager.py b/agenta-cli/agenta/sdk/tracing/tasks_manager.py deleted file mode 100644 index 74015bd1b3..0000000000 --- a/agenta-cli/agenta/sdk/tracing/tasks_manager.py +++ /dev/null @@ -1,129 +0,0 @@ -# Stdlib Imports -import queue -import asyncio -from logging import Logger -from typing import Coroutine, Optional, Union -from concurrent.futures import ThreadPoolExecutor - -# Own Imports -from agenta.client.backend.types.error import Error -from agenta.client.backend.client import AsyncObservabilityClient - - -class AsyncTask(object): - """Wraps a coroutine (an asynchronous function defined with async def). - - Args: - coroutine (Coroutine): asynchronous function - """ - - def __init__( - self, - coroutine_id: str, - coroutine_type: str, - coroutine: Coroutine, - client: AsyncObservabilityClient, - ): - self.coroutine_id = coroutine_id - self.coroutine_type = coroutine_type - self.coroutine = coroutine - self.task: Optional[asyncio.Task] = None - self.client = client - - async def run(self) -> Union[asyncio.Task, Error]: - """Creates an asyncio Task from the coroutine and starts it - - Returns: - Task: asyncio task - """ - - try: - self.task = asyncio.create_task(self.coroutine) - except Exception as exc: - return Error(message="error running task", stacktrace=str(exc)) - return await self.task - - def cancel(self): - """ - Cancels running asyncio Task. - """ - - if self.task: - self.task.cancel() - - -class TaskQueue(object): - """Stores a list of AsyncTask instances. - - Args: - tasks (List[AsyncTasks]): list of async task instances - - Example Usage: - ```python - queue = TaskQueue() - queue.add_task(long_running_task(1)) - queue.add_task(long_running_task(2)) - ``` - """ - - def __init__(self, num_workers: int, logger: Logger): - self.tasks = queue.Queue() # type: ignore - self._logger = logger - self._thread_pool = ThreadPoolExecutor(max_workers=num_workers) - - def add_task( - self, - coroutine_id: str, - coroutine_type: str, - coroutine: Coroutine, - obs_client: AsyncObservabilityClient, - ) -> AsyncTask: - """Adds a new task to be executed. - - Args: - coroutine_id (str): The Id of the coroutine - coroutine_type (str): The type of coroutine - coroutine (Coroutine): async task - obs_client (AsyncObservabilityClient): The async observability client - - Returns: - AsyncTask: task to be executed - """ - - task = AsyncTask(coroutine_id, coroutine_type, coroutine, obs_client) - self.tasks.put(task) - return self._worker() - - def _worker(self): - """ - Runs task gotten from the queue in a thread pool. - """ - - while True: - task: AsyncTask = self.tasks.get() # type: ignore - try: - future = self._thread_pool.submit(asyncio.run, task.run()) - future.result() - except Exception as exc: - self._logger.error( - f"Task '{task.coroutine_type}' failed with error: {str(exc)}" - ) - break - finally: - self.tasks.task_done() - break - - def _get_size(self) -> int: - """Returns the approximate number of items in the queue.""" - - return self.tasks.qsize() - - def flush(self) -> None: - """Clears all items from the queue.""" - - q_size = self._get_size() - self._logger.info("Flushing queue...") - with self.tasks.mutex: # acts as a lock to ensure that only one thread can access the queue - self.tasks.join() - self._logger.info(f"Queue with {q_size} items flushed successfully") - return diff --git a/agenta-cli/agenta/sdk/tracing/tracing_context.py b/agenta-cli/agenta/sdk/tracing/tracing_context.py deleted file mode 100644 index 95a3238e8d..0000000000 --- a/agenta-cli/agenta/sdk/tracing/tracing_context.py +++ /dev/null @@ -1,27 +0,0 @@ -from contextvars import ContextVar - -from typing import Optional, Dict, List - -from agenta.client.backend.types.create_span import CreateSpan - -CURRENT_TRACING_CONTEXT_KEY = "current_tracing_context" - - -class TracingContext: - def __init__(self): - ### --- TRACE --- ### - self.trace_id: Optional[str] = None - self.trace_tags: List[str] = [] - - ### --- SPANS --- ### - self.active_span: Optional[CreateSpan] = None - self.spans: Dict[str, CreateSpan] = {} - - def __repr__(self) -> str: - return f"TracingContext(trace='{self.trace_id}', spans={[f'{span.id} {span.spankind}' for span in self.spans.values()]})" - - def __str__(self) -> str: - return self.__repr__() - - -tracing_context = ContextVar(CURRENT_TRACING_CONTEXT_KEY, default=None) diff --git a/agenta-cli/agenta/sdk/types.py b/agenta-cli/agenta/sdk/types.py index 55e5e2b9cd..86f362af25 100644 --- a/agenta-cli/agenta/sdk/types.py +++ b/agenta-cli/agenta/sdk/types.py @@ -186,15 +186,3 @@ def __new__(cls, url: str): @classmethod def __schema_type_properties__(cls) -> dict: return {"x-parameter": "file_url", "type": "string"} - - -class Context(BaseModel): - model_config = ConfigDict(extra="allow") - - def to_json(self): - return self.model_dump() - - @classmethod - def from_json(cls, json_str: str): - data = json.loads(json_str) - return cls(**data) diff --git a/agenta-cli/agenta/sdk/utils/helper/openai_cost.py b/agenta-cli/agenta/sdk/utils/costs.py similarity index 100% rename from agenta-cli/agenta/sdk/utils/helper/openai_cost.py rename to agenta-cli/agenta/sdk/utils/costs.py diff --git a/agenta-cli/agenta/sdk/utils/debug.py b/agenta-cli/agenta/sdk/utils/debug.py index 445fa84bfe..cd4b4c5328 100644 --- a/agenta-cli/agenta/sdk/utils/debug.py +++ b/agenta-cli/agenta/sdk/utils/debug.py @@ -1,7 +1,7 @@ from inspect import iscoroutinefunction from functools import wraps -from agenta.sdk.tracing.logger import llm_logger as logging +from agenta.sdk.utils.logging import log DEBUG = False SHIFT = 7 @@ -14,7 +14,7 @@ def log_decorator(f): @wraps(f) async def async_log_wrapper(*args, **kwargs): if DEBUG: - logging.debug( + log.debug( " ".join( [ chars[0] * shift + " " * (SHIFT - shift), @@ -26,7 +26,7 @@ async def async_log_wrapper(*args, **kwargs): ) result = await f(*args, **kwargs) if DEBUG: - logging.debug( + log.debug( " ".join( [ chars[1] * shift + " " * (SHIFT - shift), @@ -40,7 +40,7 @@ async def async_log_wrapper(*args, **kwargs): @wraps(f) def log_wrapper(*args, **kwargs): if DEBUG: - logging.debug( + log.debug( " ".join( [ chars[0] * shift + " " * (SHIFT - shift), @@ -52,7 +52,7 @@ def log_wrapper(*args, **kwargs): ) result = f(*args, **kwargs) if DEBUG: - logging.debug( + log.debug( " ".join( [ chars[1] * shift + " " * (SHIFT - shift), diff --git a/agenta-cli/agenta/sdk/utils/globals.py b/agenta-cli/agenta/sdk/utils/globals.py index 7b1bb3ff00..f05141e089 100644 --- a/agenta-cli/agenta/sdk/utils/globals.py +++ b/agenta-cli/agenta/sdk/utils/globals.py @@ -1,15 +1,13 @@ import agenta -def set_global(setup=None, config=None, tracing=None): - """Allows usage of agenta.config and agenta.setup in the user's code. +def set_global(config=None, tracing=None): + """Allows usage of agenta.config and agenta.tracing in the user's code. Args: - setup: _description_. Defaults to None. config: _description_. Defaults to None. + tracing: _description_. Defaults to None. """ - if setup is not None: - agenta.setup = setup if config is not None: agenta.config = config if tracing is not None: diff --git a/agenta-cli/agenta/sdk/tracing/logger.py b/agenta-cli/agenta/sdk/utils/logging.py similarity index 58% rename from agenta-cli/agenta/sdk/tracing/logger.py rename to agenta-cli/agenta/sdk/utils/logging.py index a2038989ae..48ce8ca619 100644 --- a/agenta-cli/agenta/sdk/tracing/logger.py +++ b/agenta-cli/agenta/sdk/utils/logging.py @@ -1,12 +1,11 @@ import logging -class LLMLogger: - def __init__(self, name="LLMLogger", level=logging.DEBUG): +class Logger: + def __init__(self, name="ag.logger", level=logging.DEBUG): self.logger = logging.getLogger(name) self.logger.setLevel(level) - # Add a stream logger to view the logs in the console console_handler = logging.StreamHandler() self.logger.addHandler(console_handler) @@ -15,5 +14,4 @@ def log(self) -> logging.Logger: return self.logger -# Initialize llm logger -llm_logger = LLMLogger().log +log = Logger().log From 00e75604f378e899cb4e77ae706e18362bbeb182 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 3 Sep 2024 23:09:40 +0200 Subject: [PATCH 002/305] clean up imports --- .../agenta/sdk/tracing/opentelemetry.py | 26 ++----------------- 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index daa81ebcd4..e2a00ac9fd 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -1,36 +1,14 @@ -import os -import copy import json -from uuid import uuid4 -import traceback from threading import Lock from datetime import datetime, timezone -from typing import Optional, Dict, Any, List, Literal - +from typing import Optional, Dict, Any, List, Literal, Sequence from contextlib import contextmanager, suppress - -import agenta as ag - from importlib.metadata import version from agenta.sdk.utils.logging import log -from agenta.client.backend.client import AsyncAgentaApi -from agenta.client.backend.client import AsyncObservabilityClient -from agenta.client.backend.types.create_span import ( - CreateSpan, - LlmTokens, -) - -from agenta.client.backend.types.span_status_code import SpanStatusCode - -from bson.objectid import ObjectId - -VARIANT_TRACKING_FEATURE_FLAG = False - -from agenta.sdk.utils.debug import debug +from agenta.client.backend.types.create_span import CreateSpan, LlmTokens -from typing import Sequence from opentelemetry.context import Context from opentelemetry.sdk.trace import ( Span, From e34df9ea362ef11ce870561be6912382736bf63c Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 4 Sep 2024 14:48:54 +0200 Subject: [PATCH 003/305] Add global tracer provider to enable instrumentation libraries to do their work --- agenta-cli/agenta/sdk/tracing/opentelemetry.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index e2a00ac9fd..57e77c0213 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -9,6 +9,7 @@ from agenta.sdk.utils.logging import log from agenta.client.backend.types.create_span import CreateSpan, LlmTokens +from opentelemetry.trace import set_tracer_provider from opentelemetry.context import Context from opentelemetry.sdk.trace import ( Span, @@ -166,6 +167,7 @@ def __init__( project_id: Optional[str] = None, api_key: Optional[str] = None, experiment_id: Optional[str] = None, + set_global_tracer_provider: Optional[bool] = True, ) -> None: # ENDPOINT self.url = url @@ -195,6 +197,7 @@ def __init__( # num_threads=2, # inline_processor + remote_processor # ) ) + # self.tracer_provider.add_span_processor( # TraceProcessor( # OTLPSpanExporter( @@ -205,6 +208,10 @@ def __init__( # ) self.inline_processor = TraceProcessor(InlineTraceExporter(registry=self.spans)) self.tracer_provider.add_span_processor(self.inline_processor) + + if set_global_tracer_provider is True: + set_tracer_provider(self.tracer_provider) + # TRACER self.tracer = self.tracer_provider.get_tracer("ag.tracing") From f1cc055ca329bf13a76f94897069bf79331fad1f Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 4 Sep 2024 16:57:31 +0200 Subject: [PATCH 004/305] Add OTLP processor --- agenta-cli/agenta/sdk/decorators/routing.py | 13 +++-- .../agenta/sdk/tracing/opentelemetry.py | 57 +++++++++++-------- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index 600b0dddb6..c601d4a191 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -640,13 +640,18 @@ def handle_terminal_run( ) ) + log.info("") log.info("========= Result =========") log.info("") - print("-> data") - print(json.dumps(result.data, indent=2)) - print("-> trace") - print(json.dumps(result.trace, indent=2)) + # print("-> data") + # print(json.dumps(result.data, indent=2)) + # print("-> trace") + # print(json.dumps(result.trace, indent=2)) + for span in result.trace["spans"]: + log.info( + f"{span['id']} {span['spankind'].ljust(10)} {span['status'].ljust(5)} {span['name']}" + ) log.info("") log.info("==========================") diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index 57e77c0213..71596aa602 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -1,7 +1,7 @@ import json from threading import Lock -from datetime import datetime, timezone +from datetime import datetime from typing import Optional, Dict, Any, List, Literal, Sequence from contextlib import contextmanager, suppress from importlib.metadata import version @@ -94,6 +94,8 @@ def __init__( def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None: super().on_start(span, parent_context=parent_context) + log.info(f"> {span.context.span_id.to_bytes(8).hex()} {span.name}") + if span.context.trace_id not in self.registry: self.registry[span.context.trace_id] = dict() @@ -102,6 +104,8 @@ def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None def on_end(self, span: ReadableSpan): super().on_end(span) + log.info(f" < {span.context.span_id.to_bytes(8).hex()} {span.name}") + del self.registry[span.context.trace_id][span.context.span_id] if self.is_done(): @@ -167,10 +171,10 @@ def __init__( project_id: Optional[str] = None, api_key: Optional[str] = None, experiment_id: Optional[str] = None, - set_global_tracer_provider: Optional[bool] = True, ) -> None: + # ENDPOINT - self.url = url + self.url = "http://localhost:4318/v1/traces" # url # AUTHENTICATION self.project_id = project_id # AUTHORIZATION @@ -190,30 +194,35 @@ def __init__( # SPANS (INLINE TRACE) self.spans: Dict[str:ReadableSpan] = dict() + # SPAN PROCESSOR + # self.processor = SynchronousMultiSpanProcessor() + self.processor = ConcurrentMultiSpanProcessor(num_threads=2) + + base_shutdown = self.processor.shutdown + + def safe_shutdown(): + with suppress(Exception): + base_shutdown() + + self.processor.shutdown = safe_shutdown + # TRACER PROVIDER - self.tracer_provider = TracerProvider( - active_span_processor=SynchronousMultiSpanProcessor() - # active_span_processor=ConcurrentMultiSpanProcessor( - # num_threads=2, # inline_processor + remote_processor - # ) - ) + self.tracer_provider = TracerProvider(active_span_processor=self.processor) - # self.tracer_provider.add_span_processor( - # TraceProcessor( - # OTLPSpanExporter( - # endpoint=self.url, - # headers=self.headers, - # ) - # ) - # ) + # TRACE PROCESSORS self.inline_processor = TraceProcessor(InlineTraceExporter(registry=self.spans)) self.tracer_provider.add_span_processor(self.inline_processor) - if set_global_tracer_provider is True: - set_tracer_provider(self.tracer_provider) + self.remote_processor = TraceProcessor( + OTLPSpanExporter(endpoint=self.url, headers=self.headers) + ) + self.tracer_provider.add_span_processor(self.remote_processor) + + # GLOBAL TRACER PROVIDER + set_tracer_provider(self.tracer_provider) # TRACER - self.tracer = self.tracer_provider.get_tracer("ag.tracing") + self.tracer = self.tracer_provider.get_tracer("agenta.tracer") # @suppress(Exception) @contextmanager @@ -357,7 +366,7 @@ def get_inline_trace(self, trace_id_only: bool = False): for span in self.spans.values(): span: ReadableSpan - trace_id = hex(span.context.trace_id)[2:] + trace_id = span.context.trace_id.to_bytes(16).hex() if trace_id not in spans_idx: spans_idx[trace_id] = list() @@ -398,7 +407,7 @@ def _parse_to_legacy_span( attributes.update(**event.attributes) legacy_span = CreateSpan( - id=hex(span.context.span_id)[2:], + id=span.context.span_id.to_bytes(8).hex(), spankind=self._get_attributes("extra", span).get("kind", "UNKNOWN"), name=span.name, status=str(span.status.status_code.name), @@ -410,7 +419,9 @@ def _parse_to_legacy_span( span.end_time / 1_000_000_000, ).isoformat(), # - parent_span_id=hex(span.parent.span_id)[2:] if span.parent else None, + parent_span_id=( + span.parent.span_id.to_bytes(8).hex() if span.parent else None + ), # inputs=self._get_attributes("data.inputs", span), internals=self._get_attributes("data.internals", span), From a4c4b1fba9d788dda15ebec92c8586b5b2729be4 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 4 Sep 2024 18:07:47 +0200 Subject: [PATCH 005/305] clean up --- .../client/backend/core/pydantic_utilities.py | 4 ++-- agenta-cli/agenta/sdk/decorators/routing.py | 18 +++++++++--------- agenta-cli/agenta/sdk/tracing/opentelemetry.py | 18 ++++++++++++------ 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/agenta-cli/agenta/client/backend/core/pydantic_utilities.py b/agenta-cli/agenta/client/backend/core/pydantic_utilities.py index 70e0c15edd..834551cb10 100644 --- a/agenta-cli/agenta/client/backend/core/pydantic_utilities.py +++ b/agenta-cli/agenta/client/backend/core/pydantic_utilities.py @@ -77,8 +77,8 @@ def to_jsonable_with_fallback( class UniversalBaseModel(pydantic.BaseModel): class Config: populate_by_name = True - smart_union = True - allow_population_by_field_name = True + # smart_union = True + # allow_population_by_field_name = True json_encoders = {dt.datetime: serialize_datetime} def json(self, **kwargs: typing.Any) -> str: diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index c601d4a191..13540670ec 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -640,20 +640,20 @@ def handle_terminal_run( ) ) - log.info("") - log.info("========= Result =========") - log.info("") + log.info("\n========= Result =========\n") + + log.info(result.trace["trace_id"]) - # print("-> data") - # print(json.dumps(result.data, indent=2)) - # print("-> trace") - # print(json.dumps(result.trace, indent=2)) + # log.info("") + # log.info(json.dumps(result.data, indent=2)) + # log.info(json.dumps(result.trace, indent=2)) + + log.info("") for span in result.trace["spans"]: log.info( f"{span['id']} {span['spankind'].ljust(10)} {span['status'].ljust(5)} {span['name']}" ) - log.info("") - log.info("==========================") + log.info("\n==========================\n") def override_config_in_schema( self, diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index 71596aa602..a0f44b6934 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -1,4 +1,5 @@ import json +import requests from threading import Lock from datetime import datetime @@ -213,10 +214,17 @@ def safe_shutdown(): self.inline_processor = TraceProcessor(InlineTraceExporter(registry=self.spans)) self.tracer_provider.add_span_processor(self.inline_processor) - self.remote_processor = TraceProcessor( - OTLPSpanExporter(endpoint=self.url, headers=self.headers) - ) - self.tracer_provider.add_span_processor(self.remote_processor) + try: + requests.post(self.url) + + self.remote_processor = TraceProcessor( + OTLPSpanExporter(endpoint=self.url, headers=self.headers) + ) + self.tracer_provider.add_span_processor(self.remote_processor) + except requests.exceptions.ConnectionError: + log.error( + f"Warning: Your traces will not be exported since {self.url} is unreachable." + ) # GLOBAL TRACER PROVIDER set_tracer_provider(self.tracer_provider) @@ -224,7 +232,6 @@ def safe_shutdown(): # TRACER self.tracer = self.tracer_provider.get_tracer("agenta.tracer") - # @suppress(Exception) @contextmanager def start_as_current_span(self, name: str, kind: str): with self.tracer.start_as_current_span(name) as span: @@ -238,7 +245,6 @@ def start_as_current_span(self, name: str, kind: str): ) yield span - # @suppress(Exception) def start_span(self, name: str, kind: str): span = self.tracer.start_span(name) From 15a2f466cb4f074fc36ce71ffa29df7cf3c7c991 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 4 Sep 2024 18:09:15 +0200 Subject: [PATCH 006/305] bump pre-release version --- agenta-cli/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index 02df5018d6..f7441e5888 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.24.2" +version = "0.24.5a1" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] From 11a8b3d05c17c56aac15637762933122661f7ccf Mon Sep 17 00:00:00 2001 From: jp-agenta Date: Wed, 4 Sep 2024 20:39:46 +0200 Subject: [PATCH 007/305] Update logging.py --- agenta-cli/agenta/sdk/utils/logging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-cli/agenta/sdk/utils/logging.py b/agenta-cli/agenta/sdk/utils/logging.py index 48ce8ca619..d04b1c42b5 100644 --- a/agenta-cli/agenta/sdk/utils/logging.py +++ b/agenta-cli/agenta/sdk/utils/logging.py @@ -2,7 +2,7 @@ class Logger: - def __init__(self, name="ag.logger", level=logging.DEBUG): + def __init__(self, name="agenta.logger", level=logging.DEBUG): self.logger = logging.getLogger(name) self.logger.setLevel(level) From 867bd098cb93a9e8e2c3170dac61e63184edd72b Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 5 Sep 2024 16:40:06 +0200 Subject: [PATCH 008/305] clean up dependencies --- agenta-cli/agenta/sdk/tracing/opentelemetry.py | 6 +++--- agenta-cli/pyproject.toml | 4 ++++ agenta-cli/requirements.txt | 0 3 files changed, 7 insertions(+), 3 deletions(-) delete mode 100644 agenta-cli/requirements.txt diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index a0f44b6934..9ab1882f09 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -1,5 +1,5 @@ import json -import requests +import httpx from threading import Lock from datetime import datetime @@ -215,13 +215,13 @@ def safe_shutdown(): self.tracer_provider.add_span_processor(self.inline_processor) try: - requests.post(self.url) + httpx.post(self.url) self.remote_processor = TraceProcessor( OTLPSpanExporter(endpoint=self.url, headers=self.headers) ) self.tracer_provider.add_span_processor(self.remote_processor) - except requests.exceptions.ConnectionError: + except: log.error( f"Warning: Your traces will not be exported since {self.url} is unreachable." ) diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index f7441e5888..02a2a6172b 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -33,6 +33,10 @@ pymongo = "^4.6.3" cachetools = "^5.3.3" pyyaml = "^6.0.2" +opentelemetry-api = "^1.27.0" +opentelemetry-sdk = "^1.27.0" +opentelemetry-exporter-otlp = "^1.27.0" + [tool.poetry.dev-dependencies] pytest = "^8.3" setuptools = "^71.1.0" diff --git a/agenta-cli/requirements.txt b/agenta-cli/requirements.txt deleted file mode 100644 index e69de29bb2..0000000000 From 56c0487e043c464363c1ff73e70fdb05bfb50778 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 5 Sep 2024 17:36:59 +0200 Subject: [PATCH 009/305] text with example --- agenta-cli/agenta/sdk/decorators/tracing.py | 18 +- agenta-cli/requirements.txt | 183 ++++++++++++++++++++ 2 files changed, 194 insertions(+), 7 deletions(-) create mode 100644 agenta-cli/requirements.txt diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index f6c78c2ce1..b9bd5e7ecb 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -14,11 +14,15 @@ def __init__( self, kind: str = "UNKNOWN", config: Optional[Dict[str, Any]] = None, - ignore: Union[List[str], bool] = False, + ignore_inputs: Optional[bool] = None, + ignore_outputs: Optional[bool] = None, + # DEPRECATED + spankind: Optional[str] = "UNKNOWN", ) -> None: - self.kind = kind + self.kind = spankind if spankind is not None else kind self.config = config - self.ignore = ignore + self.ignore_inputs = ignore_inputs + self.ignore_outputs = ignore_outputs def __call__(self, func: Callable[..., Any]): is_coroutine_function = inspect.iscoroutinefunction(func) @@ -74,14 +78,14 @@ async def wrapped_func(*args, **kwargs): ) ag.tracing.set_attributes( "data.inputs", - redact(parse(*args, **kwargs), self.ignore), + redact(parse(*args, **kwargs), self.ignore_inputs), ) result = await func(*args, **kwargs) ag.tracing.set_attributes( "data.outputs", - redact(patch(result), self.ignore), + redact(patch(result), self.ignore_outputs), ) ag.tracing.set_status("OK") @@ -107,14 +111,14 @@ def wrapped_func(*args, **kwargs): ) ag.tracing.set_attributes( "data.inputs", - redact(parse(*args, **kwargs), self.ignore), + redact(parse(*args, **kwargs), self.ignore_inputs), ) result = func(*args, **kwargs) ag.tracing.set_attributes( "data.outputs", - redact(patch(result), self.ignore), + redact(patch(result), self.ignore_outputs), ) ag.tracing.set_status("OK") diff --git a/agenta-cli/requirements.txt b/agenta-cli/requirements.txt new file mode 100644 index 0000000000..6f11f4d0a0 --- /dev/null +++ b/agenta-cli/requirements.txt @@ -0,0 +1,183 @@ +# +# This file is autogenerated by pip-compile with Python 3.12 +# by the following command: +# +# pip-compile --output-file=requirements.txt --strip-extras pyproject.toml +# +annotated-types==0.7.0 + # via pydantic +anyio==4.4.0 + # via + # httpx + # starlette +asttokens==2.4.1 + # via stack-data +backoff==2.2.1 + # via posthog +cachetools==5.5.0 + # via agenta (pyproject.toml) +certifi==2024.8.30 + # via + # httpcore + # httpx + # requests +charset-normalizer==3.3.2 + # via requests +click==8.1.7 + # via agenta (pyproject.toml) +decorator==5.1.1 + # via + # ipdb + # ipython +deprecated==1.2.14 + # via + # opentelemetry-api + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http + # opentelemetry-semantic-conventions +dnspython==2.6.1 + # via pymongo +docker==7.1.0 + # via agenta (pyproject.toml) +executing==2.1.0 + # via stack-data +fastapi==0.112.3 + # via agenta (pyproject.toml) +googleapis-common-protos==1.65.0 + # via + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http +grpcio==1.66.1 + # via opentelemetry-exporter-otlp-proto-grpc +h11==0.14.0 + # via httpcore +httpcore==1.0.5 + # via httpx +httpx==0.27.2 + # via agenta (pyproject.toml) +idna==3.8 + # via + # anyio + # httpx + # requests +importlib-metadata==8.4.0 + # via + # agenta (pyproject.toml) + # opentelemetry-api +ipdb==0.13.13 + # via agenta (pyproject.toml) +ipython==8.18.0 + # via ipdb +jedi==0.19.1 + # via ipython +matplotlib-inline==0.1.7 + # via ipython +monotonic==1.6 + # via posthog +opentelemetry-api==1.27.0 + # via + # agenta (pyproject.toml) + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http + # opentelemetry-sdk + # opentelemetry-semantic-conventions +opentelemetry-exporter-otlp==1.27.0 + # via agenta (pyproject.toml) +opentelemetry-exporter-otlp-proto-common==1.27.0 + # via + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http +opentelemetry-exporter-otlp-proto-grpc==1.27.0 + # via opentelemetry-exporter-otlp +opentelemetry-exporter-otlp-proto-http==1.27.0 + # via opentelemetry-exporter-otlp +opentelemetry-proto==1.27.0 + # via + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http +opentelemetry-sdk==1.27.0 + # via + # agenta (pyproject.toml) + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http +opentelemetry-semantic-conventions==0.48b0 + # via opentelemetry-sdk +parso==0.8.4 + # via jedi +pexpect==4.9.0 + # via ipython +posthog==3.6.3 + # via agenta (pyproject.toml) +prompt-toolkit==3.0.36 + # via + # ipython + # questionary +protobuf==4.25.4 + # via + # googleapis-common-protos + # opentelemetry-proto +ptyprocess==0.7.0 + # via pexpect +pure-eval==0.2.3 + # via stack-data +pydantic==2.8.2 + # via + # agenta (pyproject.toml) + # fastapi +pydantic-core==2.20.1 + # via pydantic +pygments==2.18.0 + # via ipython +pymongo==4.8.0 + # via agenta (pyproject.toml) +python-dateutil==2.9.0.post0 + # via posthog +python-dotenv==1.0.1 + # via agenta (pyproject.toml) +python-multipart==0.0.9 + # via agenta (pyproject.toml) +pyyaml==6.0.2 + # via agenta (pyproject.toml) +questionary==2.0.1 + # via agenta (pyproject.toml) +requests==2.32.3 + # via + # docker + # opentelemetry-exporter-otlp-proto-http + # posthog +six==1.16.0 + # via + # asttokens + # posthog + # python-dateutil +sniffio==1.3.1 + # via + # anyio + # httpx +stack-data==0.6.3 + # via ipython +starlette==0.38.4 + # via fastapi +toml==0.10.2 + # via agenta (pyproject.toml) +traitlets==5.14.3 + # via + # ipython + # matplotlib-inline +typing-extensions==4.12.2 + # via + # fastapi + # opentelemetry-sdk + # pydantic + # pydantic-core +urllib3==2.2.2 + # via + # docker + # requests +wcwidth==0.2.13 + # via prompt-toolkit +wrapt==1.16.0 + # via deprecated +zipp==3.20.1 + # via importlib-metadata From 6fc0c129185b7a11d75c44f56f3d25eb98c4c4fa Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 5 Sep 2024 17:38:05 +0200 Subject: [PATCH 010/305] Add example --- .../mflix_with_otel_instrumentation/app.py | 182 ++++++++++++++++++ .../env.example | 5 + .../mflix_with_otel_instrumentation/readme.md | 28 +++ .../requirements.txt | 6 + .../testset.csv | 11 ++ 5 files changed, 232 insertions(+) create mode 100644 examples/rag_applications/mflix_with_otel_instrumentation/app.py create mode 100644 examples/rag_applications/mflix_with_otel_instrumentation/env.example create mode 100644 examples/rag_applications/mflix_with_otel_instrumentation/readme.md create mode 100644 examples/rag_applications/mflix_with_otel_instrumentation/requirements.txt create mode 100644 examples/rag_applications/mflix_with_otel_instrumentation/testset.csv diff --git a/examples/rag_applications/mflix_with_otel_instrumentation/app.py b/examples/rag_applications/mflix_with_otel_instrumentation/app.py new file mode 100644 index 0000000000..f15c159eec --- /dev/null +++ b/examples/rag_applications/mflix_with_otel_instrumentation/app.py @@ -0,0 +1,182 @@ +import certifi +from dotenv import dotenv_values +from pymongo import MongoClient +from pymongo.server_api import ServerApi +from openai import OpenAI + +import agenta as ag + +from opentelemetry.instrumentation.openai import OpenAIInstrumentor + +config = dotenv_values(".env") + +openai = OpenAI(api_key=config["OPENAI_API_KEY"]) + +mongodb = MongoClient( + config["MONGODB_ATLAS_URI"], tlsCAFile=certifi.where(), server_api=ServerApi("1") +) +db = mongodb[config["MONGODB_DATABASE_NAME"]] + +ag.init() + +OpenAIInstrumentor().instrument() + +ag.config.default( + # RETRIEVER + retriever_prompt=ag.TextParam("Movies about {topic} in the genre of {genre}."), + retriever_multiplier=ag.FloatParam(default=3, minval=1, maxval=10), + # GENERATOR + generator_context_prompt=ag.TextParam( + "Given the following list of suggested movies:\n\n{movies}" + ), + generator_instructions_prompt=ag.TextParam( + "Provide a list of {count} movies about {topic} in the genre of {genre}." + ), + generator_model=ag.MultipleChoiceParam( + "gpt-3.5-turbo", ["gpt-4o-mini", "gpt-3.5-turbo"] + ), + generator_temperature=ag.FloatParam(default=0.8), + # SUMMARIZER + summarizer_context_prompt=ag.TextParam( + "Act as a professional cinema critic.\nBe concise and factual.\nUse one intro sentence, and one sentence per movie." + ), + summarizer_instructions_prompt=ag.TextParam( + "Summarize the following recommendations about {topic} in the genre of {genre}:\n\n{report}" + ), + summarizer_model=ag.MultipleChoiceParam( + "gpt-4o-mini", ["gpt-4o-mini", "gpt-3.5-turbo"] + ), + summarizer_temperature=ag.FloatParam(default=0.2), +) + + +@ag.instrument( + spankind="EMBEDDING", + ignore_inputs=["description"], + ignore_outputs=["embedding", "cost", "usage"], +) +def embed(description: str): + response = openai.embeddings.create( + input=description, model="text-embedding-ada-002" + ) + return { + "embedding": response.data[0].embedding, + "cost": ag.calculate_token_usage( + "text-embedding-ada-002", response.usage.dict() + ), + "usage": response.usage.dict(), + } + + +@ag.instrument(spankind="SEARCH", ignore_inputs=True, ignore_outputs=True) +def search(query: list, topk: int): + embeddings = db["embedded_movies"] + + pipeline = [ + { + "$vectorSearch": { + "index": "semantic_similarity_search_index", + "path": "plot_embedding", + "queryVector": query, + "numCandidates": 200, + "limit": topk, + } + }, + {"$project": {"_id": 0, "title": 1, "genres": 1, "plot": 1, "year": 1}}, + ] + + movies = [movie for movie in embeddings.aggregate(pipeline)] + + return movies + + +@ag.instrument(spankind="MESSAGE") +async def chat(prompts: str, opts: dict): + response = openai.chat.completions.create( + model=opts["model"], + temperature=opts["temperature"], + messages=[ + {"role": agent, "content": prompt} for (agent, prompt) in prompts.items() + ], + ) + + return { + "message": response.choices[0].message.content, + "cost": ag.calculate_token_usage(opts["model"], response.usage.dict()), + "usage": response.usage.dict(), + } + + +@ag.instrument(spankind="RETRIEVER", ignore_inputs=True) +async def retriever(topic: str, genre: str, count: int) -> dict: + prompt = ag.config.retriever_prompt.format(topic=topic, genre=genre) + topk = count * ag.config.retriever_multiplier + + ag.tracing.store_internals({"prompt": prompt}) + + query = embed(prompt) + + result = search(query["embedding"], topk) + + movies = [ + f"{movie['title']} ({movie['year']}) in {movie['genres']}: {movie['plot']}" + for movie in result + ] + + return {"movies": movies} + + +@ag.instrument(spankind="GENERATOR", ignore_inputs=True) +async def reporter(topic: str, genre: str, count: int, movies: dict) -> dict: + context = ag.config.generator_context_prompt.format(movies="\n".join(movies)) + instructions = ag.config.generator_instructions_prompt.format( + count=count, topic=topic, genre=genre + ) + + prompts = {"system": context, "user": instructions} + opts = { + "model": ag.config.generator_model, + "temperature": ag.config.generator_temperature, + } + + result = await chat(prompts, opts) + + report = result["message"] + + return {"report": report} + + +@ag.instrument(spankind="GENERATOR", ignore_inputs=True) +async def summarizer(topic: str, genre: str, report: dict) -> dict: + context = ag.config.summarizer_context_prompt + instructions = ag.config.summarizer_instructions_prompt.format( + topic=topic, genre=genre, report=report + ) + + prompts = {"system": context, "user": instructions} + opts = { + "model": ag.config.summarizer_model, + "temperature": ag.config.summarizer_temperature, + } + + result = await chat(prompts, opts) + + report = result["message"] + + return {"report": report} + + +@ag.entrypoint +@ag.instrument(spankind="WORKFLOW") +async def rag(topic: str, genre: str, count: int = 5): + count = int(count) + + result = await retriever(topic, genre, count) + + result = await reporter(topic, genre, count, result["movies"]) + + result = await summarizer(topic, genre, result["report"]) + + result = await summarizer(topic, genre, result["report"]) + + return result["report"] diff --git a/examples/rag_applications/mflix_with_otel_instrumentation/env.example b/examples/rag_applications/mflix_with_otel_instrumentation/env.example new file mode 100644 index 0000000000..b29425bd44 --- /dev/null +++ b/examples/rag_applications/mflix_with_otel_instrumentation/env.example @@ -0,0 +1,5 @@ +# Rename this file to .env +AGENTA_API_KEY=xxx +OPENAI_API_KEY=sk-test-xxx +MONGODB_ATLAS_URI=mongodb+srv://xxx:xxx@xxx.xxx.mongodb.net/?retryWrites=true&w=majority&appName=xxx +MONGODB_DATABASE_NAME=xxx \ No newline at end of file diff --git a/examples/rag_applications/mflix_with_otel_instrumentation/readme.md b/examples/rag_applications/mflix_with_otel_instrumentation/readme.md new file mode 100644 index 0000000000..c9df15017d --- /dev/null +++ b/examples/rag_applications/mflix_with_otel_instrumentation/readme.md @@ -0,0 +1,28 @@ +# Step-by-step Guide + +## Vector Database (MongoDB) + +1. Connect to [MongoDB ATLAS](https://www.mongodb.com/products/platform/atlas-database) +2. Create a Cluster +3. Configure Database Access to access data from username and password +4. Configure Network Access to allow for ingress from your IP address +5. Load [sample datasets](https://www.mongodb.com/docs/atlas/sample-data/#std-label-load-sample-data), expecially the [mflix dataset](https://www.mongodb.com/docs/atlas/sample-data/sample-mflix/) + +## LLM Provider (OpenAI) + +1. Connect to [OpenAI Platform](https://platform.openai.com/) +2. Create API keys + +## RAG Application + +1. Set environment variables + * `AGENTA_API_KEY` + * `OPENAI_API_KEY` + * `MONGODB_ATLAS_URI` + * `MONGODB_DATABASE_NAME` +2. Install dependencies (optional) + * `pip install -r requirements` +3. Create app variant + * `agenta init` +4. Serve app variant + * `agenta variant serve app.py` diff --git a/examples/rag_applications/mflix_with_otel_instrumentation/requirements.txt b/examples/rag_applications/mflix_with_otel_instrumentation/requirements.txt new file mode 100644 index 0000000000..29ad3c879d --- /dev/null +++ b/examples/rag_applications/mflix_with_otel_instrumentation/requirements.txt @@ -0,0 +1,6 @@ +agenta +python-dotenv +pymongo +openai +certifi +opentelemetry.instrumentation.openai \ No newline at end of file diff --git a/examples/rag_applications/mflix_with_otel_instrumentation/testset.csv b/examples/rag_applications/mflix_with_otel_instrumentation/testset.csv new file mode 100644 index 0000000000..4cdc7bb5d9 --- /dev/null +++ b/examples/rag_applications/mflix_with_otel_instrumentation/testset.csv @@ -0,0 +1,11 @@ +count,genre,topic,correct_answer +3,drama,family,"The Pursuit of Happyness, Little Miss Sunshine, The Blind Side" +2,science fiction,space exploration,"Interstellar, The Martian, Gravity" +1,horror,haunted house,"The Conjuring, Insidious, Poltergeist" +3,fantasy,mythical creatures,"Harry Potter, The Lord of the Rings, The Chronicles of Narnia" +1,romance,forbidden love,"Romeo and Juliet, Titanic, The Notebook" +2,mystery,detective,"Sherlock Holmes, Knives Out, The Girl with the Dragon Tattoo" +2,thriller,espionage,"Mission Impossible, Tinker Tailor Soldier Spy, Skyfall" +1,comedy,satire,"The Great Dictator, Superbad, The Grand Budapest Hotel" +3,adventure,lost civilizations,"Indiana Jones, Journey to the Center of the Earth, Lara Croft: Tomb Raider" +1,biography,historical figures,"The Imitation Game, Lincoln, Schindler's List" From c597ac59e3bf84084a845c2490db82082ccb264b Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 5 Sep 2024 18:00:06 +0200 Subject: [PATCH 011/305] revert back on 'app_id' -> 'project_id' change for now --- agenta-cli/agenta/sdk/agenta_init.py | 36 +++++++++---------- .../agenta/sdk/tracing/opentelemetry.py | 10 +++--- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/agenta-cli/agenta/sdk/agenta_init.py b/agenta-cli/agenta/sdk/agenta_init.py index 6199dd3122..054f0d4a65 100644 --- a/agenta-cli/agenta/sdk/agenta_init.py +++ b/agenta-cli/agenta/sdk/agenta_init.py @@ -29,7 +29,7 @@ def __new__(cls): def init( self, - project_id: Optional[str] = None, + app_id: Optional[str] = None, host: Optional[str] = None, api_key: Optional[str] = None, config_fname: Optional[str] = None, @@ -40,24 +40,24 @@ def init( """Main function to initialize the singleton. - Initializes the singleton with the given `project_id`, `host`, and `api_key`. The order of precedence for these variables is: + Initializes the singleton with the given `app_id`, `host`, and `api_key`. The order of precedence for these variables is: 1. Explicit argument provided in the function call. 2. Value from the configuration file specified by `config_fname`. 3. Environment variables. Examples: - ag.init(project_id="xxxx", api_key="xxx") + ag.init(app_id="xxxx", api_key="xxx") ag.init(config_fname="config.toml") ag.init() #assuming env vars are set Args: - project_id (Optional[str]): ID of the Agenta application. Defaults to None. If not provided, will look for "project_id" in the config file, then "AGENTA_PROJECT_ID" in environment variables. + app_id (Optional[str]): ID of the Agenta application. Defaults to None. If not provided, will look for "app_id" in the config file, then "AGENTA_APP_ID" in environment variables. host (Optional[str]): Host name of the backend server. Defaults to None. If not provided, will look for "backend_host" in the config file, then "AGENTA_HOST" in environment variables. api_key (Optional[str]): API Key to use with the host of the backend server. Defaults to None. If not provided, will look for "api_key" in the config file, then "AGENTA_API_KEY" in environment variables. config_fname (Optional[str]): Path to the configuration file (relative or absolute). Defaults to None. Raises: - ValueError: If `project_id` is not specified either as an argument, in the config file, or in the environment variables. + ValueError: If `app_id` is not specified either as an argument, in the config file, or in the environment variables. """ config = {} @@ -71,17 +71,13 @@ def init( or os.environ.get("AGENTA_HOST", "https://cloud.agenta.ai") ) - self.project_id = ( - project_id - or config.get("project_id") - or os.environ.get("AGENTA_PROJECT_ID") - ) - if not self.project_id: + self.app_id = app_id or config.get("app_id") or os.environ.get("AGENTA_APP_ID") + if not self.app_id: raise ValueError( "Project ID must be specified. You can provide it in one of the following ways:\n" - "1. As an argument when calling ag.init(project_id='your_project_id').\n" + "1. As an argument when calling ag.init(app_id='your_app_id').\n" "2. In the configuration file specified by config_fname.\n" - "3. As an environment variable 'AGENTA_PROJECT_ID'." + "3. As an environment variable 'AGENTA_APP_ID'." ) self.api_key = ( @@ -90,7 +86,7 @@ def init( self.tracing = Tracing( url=f"{self.host}/api/observability/v2/traces", # type: ignore - project_id=self.project_id, + app_id=self.app_id, api_key=self.api_key, ) @@ -241,37 +237,37 @@ def dump(self): def init( host: Optional[str] = None, - project_id: Optional[str] = None, + app_id: Optional[str] = None, api_key: Optional[str] = None, config_fname: Optional[str] = None, ): """Main function to initialize the agenta sdk. - Initializes agenta with the given `project_id`, `host`, and `api_key`. The order of precedence for these variables is: + Initializes agenta with the given `app_id`, `host`, and `api_key`. The order of precedence for these variables is: 1. Explicit argument provided in the function call. 2. Value from the configuration file specified by `config_fname`. 3. Environment variables. - - `project_id` is a required parameter (to be specified in one of the above ways) + - `app_id` is a required parameter (to be specified in one of the above ways) - `host` is optional and defaults to "https://cloud.agenta.ai" - `api_key` is optional and defaults to "". It is required only when using cloud or enterprise version of agenta. Args: - project_id (Optional[str]): ID of the Agenta application. Defaults to None. If not provided, will look for "project_id" in the config file, then "AGENTA_PROJECT_ID" in environment variables. + app_id (Optional[str]): ID of the Agenta application. Defaults to None. If not provided, will look for "app_id" in the config file, then "AGENTA_APP_ID" in environment variables. host (Optional[str]): Host name of the backend server. Defaults to None. If not provided, will look for "backend_host" in the config file, then "AGENTA_HOST" in environment variables. api_key (Optional[str]): API Key to use with the host of the backend server. Defaults to None. If not provided, will look for "api_key" in the config file, then "AGENTA_API_KEY" in environment variables. config_fname (Optional[str]): Path to the configuration file. Defaults to None. Raises: - ValueError: If `project_id` is not specified either as an argument, in the config file, or in the environment variables. + ValueError: If `app_id` is not specified either as an argument, in the config file, or in the environment variables. """ singleton = AgentaSingleton() singleton.init( host=host, - project_id=project_id, + app_id=app_id, api_key=api_key, config_fname=config_fname, ) diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index 9ab1882f09..90995a7be0 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -35,7 +35,7 @@ _AGENTA_ROOT_SPAN_ID = "f" * 16 -_AGENTA_PROJECT_ID_HEADER = "project-id" +_AGENTA_APP_ID_HEADER = "app-id" _AGENTA_API_KEY_HEADER = "api-key" _AGENTA_EXPERIMENT_ID_HEADER = "experiment-id" @@ -169,7 +169,7 @@ class Tracing: def __init__( self, url: str, - project_id: Optional[str] = None, + app_id: Optional[str] = None, api_key: Optional[str] = None, experiment_id: Optional[str] = None, ) -> None: @@ -177,7 +177,7 @@ def __init__( # ENDPOINT self.url = "http://localhost:4318/v1/traces" # url # AUTHENTICATION - self.project_id = project_id + self.app_id = app_id # AUTHORIZATION self.api_key = api_key # EXPERIMENT @@ -185,8 +185,8 @@ def __init__( # HEADERS self.headers = {} - if self.project_id: - self.headers.update(**{_AGENTA_PROJECT_ID_HEADER: self.project_id}) + if self.app_id: + self.headers.update(**{_AGENTA_APP_ID_HEADER: self.app_id}) if api_key: self.headers.update(**{_AGENTA_API_KEY_HEADER: self.api_key}) if experiment_id: From 76d7b57cbe712b5d6d59dc2cc22527a8fd93f3b2 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 5 Sep 2024 18:02:21 +0200 Subject: [PATCH 012/305] add app_id back to spans --- agenta-cli/agenta/sdk/tracing/opentelemetry.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index 90995a7be0..9b1a203ab4 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -35,7 +35,6 @@ _AGENTA_ROOT_SPAN_ID = "f" * 16 -_AGENTA_APP_ID_HEADER = "app-id" _AGENTA_API_KEY_HEADER = "api-key" _AGENTA_EXPERIMENT_ID_HEADER = "experiment-id" @@ -185,8 +184,6 @@ def __init__( # HEADERS self.headers = {} - if self.app_id: - self.headers.update(**{_AGENTA_APP_ID_HEADER: self.app_id}) if api_key: self.headers.update(**{_AGENTA_API_KEY_HEADER: self.api_key}) if experiment_id: @@ -448,7 +445,7 @@ def _parse_to_legacy_span( ), cost=self._get_attributes("metrics.costs", span).get("marginal", 0.0), # - app_id="", + app_id=self.app_id, variant_id=None, variant_name=None, environment=None, From aa7d84821efb4d23f64ead9cfc7f0e349c40fd02 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 5 Sep 2024 18:16:19 +0200 Subject: [PATCH 013/305] Add backend dependency --- agenta-backend/pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/agenta-backend/pyproject.toml b/agenta-backend/pyproject.toml index d60daaf09e..bbfe69e578 100644 --- a/agenta-backend/pyproject.toml +++ b/agenta-backend/pyproject.toml @@ -43,6 +43,8 @@ alembic = "^1.13.2" numpy = "1.26.3" autoevals = "^0.0.83" +protobuf = "^4.25.4" + [tool.poetry.group.dev.dependencies] pytest = "^7.3.1" httpx = "^0.24.0" From cfd8165c4474e1b62269136c1f0e86e905355abb Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 5 Sep 2024 18:19:02 +0200 Subject: [PATCH 014/305] installing protobuf --- agenta-backend/poetry.lock | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/agenta-backend/poetry.lock b/agenta-backend/poetry.lock index 5203992662..209a8f29d6 100644 --- a/agenta-backend/poetry.lock +++ b/agenta-backend/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "aioboto3" @@ -1888,6 +1888,26 @@ files = [ [package.dependencies] wcwidth = "*" +[[package]] +name = "protobuf" +version = "4.25.4" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.4-cp310-abi3-win32.whl", hash = "sha256:db9fd45183e1a67722cafa5c1da3e85c6492a5383f127c86c4c4aa4845867dc4"}, + {file = "protobuf-4.25.4-cp310-abi3-win_amd64.whl", hash = "sha256:ba3d8504116a921af46499471c63a85260c1a5fc23333154a427a310e015d26d"}, + {file = "protobuf-4.25.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:eecd41bfc0e4b1bd3fa7909ed93dd14dd5567b98c941d6c1ad08fdcab3d6884b"}, + {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4c8a70fdcb995dcf6c8966cfa3a29101916f7225e9afe3ced4395359955d3835"}, + {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:3319e073562e2515c6ddc643eb92ce20809f5d8f10fead3332f71c63be6a7040"}, + {file = "protobuf-4.25.4-cp38-cp38-win32.whl", hash = "sha256:7e372cbbda66a63ebca18f8ffaa6948455dfecc4e9c1029312f6c2edcd86c4e1"}, + {file = "protobuf-4.25.4-cp38-cp38-win_amd64.whl", hash = "sha256:051e97ce9fa6067a4546e75cb14f90cf0232dcb3e3d508c448b8d0e4265b61c1"}, + {file = "protobuf-4.25.4-cp39-cp39-win32.whl", hash = "sha256:90bf6fd378494eb698805bbbe7afe6c5d12c8e17fca817a646cd6a1818c696ca"}, + {file = "protobuf-4.25.4-cp39-cp39-win_amd64.whl", hash = "sha256:ac79a48d6b99dfed2729ccccee547b34a1d3d63289c71cef056653a846a2240f"}, + {file = "protobuf-4.25.4-py3-none-any.whl", hash = "sha256:bfbebc1c8e4793cfd58589acfb8a1026be0003e852b9da7db5a4285bde996978"}, + {file = "protobuf-4.25.4.tar.gz", hash = "sha256:0dc4a62cc4052a036ee2204d26fe4d835c62827c855c8a03f29fe6da146b380d"}, +] + [[package]] name = "psycopg2-binary" version = "2.9.9" @@ -3529,4 +3549,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "f5c8de708b6eb4ed62dc425f7cc65d42e66b615c8b42591f1fc94ab765495108" +content-hash = "8af5a2fcfe8b30f6c8b36f6ef1e10e22ddca741515d817af923f51aca6affa83" From 1e578d8daa590719587d14539df3b2fe89a250ef Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 5 Sep 2024 18:24:29 +0200 Subject: [PATCH 015/305] Adjust observability url --- agenta-cli/agenta/sdk/agenta_init.py | 2 +- agenta-cli/agenta/sdk/tracing/opentelemetry.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agenta-cli/agenta/sdk/agenta_init.py b/agenta-cli/agenta/sdk/agenta_init.py index 054f0d4a65..74ba3d457e 100644 --- a/agenta-cli/agenta/sdk/agenta_init.py +++ b/agenta-cli/agenta/sdk/agenta_init.py @@ -85,7 +85,7 @@ def init( ) self.tracing = Tracing( - url=f"{self.host}/api/observability/v2/traces", # type: ignore + url=f"{self.host}/api/observability/v1/traces", # type: ignore app_id=self.app_id, api_key=self.api_key, ) diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index 9b1a203ab4..23abc4b158 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -174,7 +174,7 @@ def __init__( ) -> None: # ENDPOINT - self.url = "http://localhost:4318/v1/traces" # url + self.url = url # "http://localhost:4318/v1/traces" # AUTHENTICATION self.app_id = app_id # AUTHORIZATION From 55dd605f163d665decb683b9fcc1c8e393a3339a Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 6 Sep 2024 13:46:27 +0200 Subject: [PATCH 016/305] add opentelemetry to poetry.lock --- agenta-cli/poetry.lock | 408 +++++++++++++++++++++++++++++++++-------- 1 file changed, 328 insertions(+), 80 deletions(-) diff --git a/agenta-cli/poetry.lock b/agenta-cli/poetry.lock index 4a23352b7c..961f36514c 100644 --- a/agenta-cli/poetry.lock +++ b/agenta-cli/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "annotated-types" version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -16,7 +15,6 @@ files = [ name = "anyio" version = "4.4.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -39,7 +37,6 @@ trio = ["trio (>=0.23)"] name = "asttokens" version = "2.4.1" description = "Annotate AST trees with source code positions" -category = "main" optional = false python-versions = "*" files = [ @@ -58,7 +55,6 @@ test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] name = "backoff" version = "2.2.1" description = "Function decoration for backoff and retry" -category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -70,7 +66,6 @@ files = [ name = "cachetools" version = "5.4.0" description = "Extensible memoizing collections and decorators" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -82,7 +77,6 @@ files = [ name = "certifi" version = "2024.7.4" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -94,7 +88,6 @@ files = [ name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -194,7 +187,6 @@ files = [ name = "click" version = "8.1.7" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -209,7 +201,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -221,7 +212,6 @@ files = [ name = "decorator" version = "5.1.1" description = "Decorators for Humans" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -229,11 +219,27 @@ files = [ {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, ] +[[package]] +name = "deprecated" +version = "1.2.14" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, + {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] + [[package]] name = "dnspython" version = "2.6.1" description = "DNS toolkit" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -254,7 +260,6 @@ wmi = ["wmi (>=1.5.1)"] name = "docker" version = "7.1.0" description = "A Python library for the Docker Engine API." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -277,7 +282,6 @@ websockets = ["websocket-client (>=1.3.0)"] name = "email-validator" version = "2.2.0" description = "A robust email address syntax and deliverability validation library." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -293,7 +297,6 @@ idna = ">=2.0.0" name = "exceptiongroup" version = "1.2.2" description = "Backport of PEP 654 (exception groups)" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -308,7 +311,6 @@ test = ["pytest (>=6)"] name = "executing" version = "2.0.1" description = "Get the currently executing AST node of a frame, and other information" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -323,7 +325,6 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth name = "fastapi" version = "0.111.1" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -349,7 +350,6 @@ all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)" name = "fastapi-cli" version = "0.0.4" description = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -363,11 +363,85 @@ typer = ">=0.12.3" [package.extras] standard = ["fastapi", "uvicorn[standard] (>=0.15.0)"] +[[package]] +name = "googleapis-common-protos" +version = "1.65.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis_common_protos-1.65.0-py2.py3-none-any.whl", hash = "sha256:2972e6c496f435b92590fd54045060867f3fe9be2c82ab148fc8885035479a63"}, + {file = "googleapis_common_protos-1.65.0.tar.gz", hash = "sha256:334a29d07cddc3aa01dee4988f9afd9b2916ee2ff49d6b757155dc0d197852c0"}, +] + +[package.dependencies] +protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + +[[package]] +name = "grpcio" +version = "1.66.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.8" +files = [ + {file = "grpcio-1.66.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:4877ba180591acdf127afe21ec1c7ff8a5ecf0fe2600f0d3c50e8c4a1cbc6492"}, + {file = "grpcio-1.66.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3750c5a00bd644c75f4507f77a804d0189d97a107eb1481945a0cf3af3e7a5ac"}, + {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a013c5fbb12bfb5f927444b477a26f1080755a931d5d362e6a9a720ca7dbae60"}, + {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1b24c23d51a1e8790b25514157d43f0a4dce1ac12b3f0b8e9f66a5e2c4c132f"}, + {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7ffb8ea674d68de4cac6f57d2498fef477cef582f1fa849e9f844863af50083"}, + {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:307b1d538140f19ccbd3aed7a93d8f71103c5d525f3c96f8616111614b14bf2a"}, + {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1c17ebcec157cfb8dd445890a03e20caf6209a5bd4ac5b040ae9dbc59eef091d"}, + {file = "grpcio-1.66.1-cp310-cp310-win32.whl", hash = "sha256:ef82d361ed5849d34cf09105d00b94b6728d289d6b9235513cb2fcc79f7c432c"}, + {file = "grpcio-1.66.1-cp310-cp310-win_amd64.whl", hash = "sha256:292a846b92cdcd40ecca46e694997dd6b9be6c4c01a94a0dfb3fcb75d20da858"}, + {file = "grpcio-1.66.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:c30aeceeaff11cd5ddbc348f37c58bcb96da8d5aa93fed78ab329de5f37a0d7a"}, + {file = "grpcio-1.66.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8a1e224ce6f740dbb6b24c58f885422deebd7eb724aff0671a847f8951857c26"}, + {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a66fe4dc35d2330c185cfbb42959f57ad36f257e0cc4557d11d9f0a3f14311df"}, + {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3ba04659e4fce609de2658fe4dbf7d6ed21987a94460f5f92df7579fd5d0e22"}, + {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4573608e23f7e091acfbe3e84ac2045680b69751d8d67685ffa193a4429fedb1"}, + {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7e06aa1f764ec8265b19d8f00140b8c4b6ca179a6dc67aa9413867c47e1fb04e"}, + {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3885f037eb11f1cacc41f207b705f38a44b69478086f40608959bf5ad85826dd"}, + {file = "grpcio-1.66.1-cp311-cp311-win32.whl", hash = "sha256:97ae7edd3f3f91480e48ede5d3e7d431ad6005bfdbd65c1b56913799ec79e791"}, + {file = "grpcio-1.66.1-cp311-cp311-win_amd64.whl", hash = "sha256:cfd349de4158d797db2bd82d2020554a121674e98fbe6b15328456b3bf2495bb"}, + {file = "grpcio-1.66.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:a92c4f58c01c77205df6ff999faa008540475c39b835277fb8883b11cada127a"}, + {file = "grpcio-1.66.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fdb14bad0835914f325349ed34a51940bc2ad965142eb3090081593c6e347be9"}, + {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f03a5884c56256e08fd9e262e11b5cfacf1af96e2ce78dc095d2c41ccae2c80d"}, + {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ca2559692d8e7e245d456877a85ee41525f3ed425aa97eb7a70fc9a79df91a0"}, + {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ca1be089fb4446490dd1135828bd42a7c7f8421e74fa581611f7afdf7ab761"}, + {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d639c939ad7c440c7b2819a28d559179a4508783f7e5b991166f8d7a34b52815"}, + {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b9feb4e5ec8dc2d15709f4d5fc367794d69277f5d680baf1910fc9915c633524"}, + {file = "grpcio-1.66.1-cp312-cp312-win32.whl", hash = "sha256:7101db1bd4cd9b880294dec41a93fcdce465bdbb602cd8dc5bd2d6362b618759"}, + {file = "grpcio-1.66.1-cp312-cp312-win_amd64.whl", hash = "sha256:b0aa03d240b5539648d996cc60438f128c7f46050989e35b25f5c18286c86734"}, + {file = "grpcio-1.66.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:ecfe735e7a59e5a98208447293ff8580e9db1e890e232b8b292dc8bd15afc0d2"}, + {file = "grpcio-1.66.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4825a3aa5648010842e1c9d35a082187746aa0cdbf1b7a2a930595a94fb10fce"}, + {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f517fd7259fe823ef3bd21e508b653d5492e706e9f0ef82c16ce3347a8a5620c"}, + {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1fe60d0772831d96d263b53d83fb9a3d050a94b0e94b6d004a5ad111faa5b5b"}, + {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31a049daa428f928f21090403e5d18ea02670e3d5d172581670be006100db9ef"}, + {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f914386e52cbdeb5d2a7ce3bf1fdfacbe9d818dd81b6099a05b741aaf3848bb"}, + {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bff2096bdba686019fb32d2dde45b95981f0d1490e054400f70fc9a8af34b49d"}, + {file = "grpcio-1.66.1-cp38-cp38-win32.whl", hash = "sha256:aa8ba945c96e73de29d25331b26f3e416e0c0f621e984a3ebdb2d0d0b596a3b3"}, + {file = "grpcio-1.66.1-cp38-cp38-win_amd64.whl", hash = "sha256:161d5c535c2bdf61b95080e7f0f017a1dfcb812bf54093e71e5562b16225b4ce"}, + {file = "grpcio-1.66.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:d0cd7050397b3609ea51727b1811e663ffda8bda39c6a5bb69525ef12414b503"}, + {file = "grpcio-1.66.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0e6c9b42ded5d02b6b1fea3a25f036a2236eeb75d0579bfd43c0018c88bf0a3e"}, + {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:c9f80f9fad93a8cf71c7f161778ba47fd730d13a343a46258065c4deb4b550c0"}, + {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dd67ed9da78e5121efc5c510f0122a972216808d6de70953a740560c572eb44"}, + {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48b0d92d45ce3be2084b92fb5bae2f64c208fea8ceed7fccf6a7b524d3c4942e"}, + {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d813316d1a752be6f5c4360c49f55b06d4fe212d7df03253dfdae90c8a402bb"}, + {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c9bebc6627873ec27a70fc800f6083a13c70b23a5564788754b9ee52c5aef6c"}, + {file = "grpcio-1.66.1-cp39-cp39-win32.whl", hash = "sha256:30a1c2cf9390c894c90bbc70147f2372130ad189cffef161f0432d0157973f45"}, + {file = "grpcio-1.66.1-cp39-cp39-win_amd64.whl", hash = "sha256:17663598aadbedc3cacd7bbde432f541c8e07d2496564e22b214b22c7523dac8"}, + {file = "grpcio-1.66.1.tar.gz", hash = "sha256:35334f9c9745add3e357e3372756fd32d925bd52c41da97f4dfdafbde0bf0ee2"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.66.1)"] + [[package]] name = "h11" version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -379,7 +453,6 @@ files = [ name = "httpcore" version = "1.0.5" description = "A minimal low-level HTTP client." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -394,14 +467,13 @@ h11 = ">=0.13,<0.15" [package.extras] asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] trio = ["trio (>=0.22.0,<0.26.0)"] [[package]] name = "httptools" version = "0.6.1" description = "A collection of framework independent HTTP protocol utils." -category = "main" optional = false python-versions = ">=3.8.0" files = [ @@ -450,7 +522,6 @@ test = ["Cython (>=0.29.24,<0.30.0)"] name = "httpx" version = "0.27.0" description = "The next generation HTTP client." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -461,21 +532,20 @@ files = [ [package.dependencies] anyio = "*" certifi = "*" -httpcore = ">=1.0.0,<2.0.0" +httpcore = "==1.*" idna = "*" sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] -cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [[package]] name = "idna" version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -487,7 +557,6 @@ files = [ name = "importlib-metadata" version = "8.2.0" description = "Read metadata from Python packages" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -507,7 +576,6 @@ test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "p name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -519,7 +587,6 @@ files = [ name = "ipdb" version = "0.13.13" description = "IPython-enabled pdb" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -536,7 +603,6 @@ tomli = {version = "*", markers = "python_version > \"3.6\" and python_version < name = "ipython" version = "8.18.0" description = "IPython: Productive Interactive Computing" -category = "main" optional = false python-versions = ">=3.9" files = [ @@ -574,7 +640,6 @@ test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pa name = "jedi" version = "0.19.1" description = "An autocompletion tool for Python that can be used for text editors." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -594,7 +659,6 @@ testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] name = "jinja2" version = "3.1.4" description = "A very fast and expressive template engine." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -612,7 +676,6 @@ i18n = ["Babel (>=2.7)"] name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -637,7 +700,6 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "markupsafe" version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -707,7 +769,6 @@ files = [ name = "matplotlib-inline" version = "0.1.7" description = "Inline Matplotlib backend for Jupyter" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -722,7 +783,6 @@ traitlets = "*" name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -734,7 +794,6 @@ files = [ name = "monotonic" version = "1.6" description = "An implementation of time.monotonic() for Python 2 & < 3.3" -category = "main" optional = false python-versions = "*" files = [ @@ -742,11 +801,139 @@ files = [ {file = "monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7"}, ] +[[package]] +name = "opentelemetry-api" +version = "1.27.0" +description = "OpenTelemetry Python API" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_api-1.27.0-py3-none-any.whl", hash = "sha256:953d5871815e7c30c81b56d910c707588000fff7a3ca1c73e6531911d53065e7"}, + {file = "opentelemetry_api-1.27.0.tar.gz", hash = "sha256:ed673583eaa5f81b5ce5e86ef7cdaf622f88ef65f0b9aab40b843dcae5bef342"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +importlib-metadata = ">=6.0,<=8.4.0" + +[[package]] +name = "opentelemetry-exporter-otlp" +version = "1.27.0" +description = "OpenTelemetry Collector Exporters" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_exporter_otlp-1.27.0-py3-none-any.whl", hash = "sha256:7688791cbdd951d71eb6445951d1cfbb7b6b2d7ee5948fac805d404802931145"}, + {file = "opentelemetry_exporter_otlp-1.27.0.tar.gz", hash = "sha256:4a599459e623868cc95d933c301199c2367e530f089750e115599fccd67cb2a1"}, +] + +[package.dependencies] +opentelemetry-exporter-otlp-proto-grpc = "1.27.0" +opentelemetry-exporter-otlp-proto-http = "1.27.0" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.27.0" +description = "OpenTelemetry Protobuf encoding" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_exporter_otlp_proto_common-1.27.0-py3-none-any.whl", hash = "sha256:675db7fffcb60946f3a5c43e17d1168a3307a94a930ecf8d2ea1f286f3d4f79a"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.27.0.tar.gz", hash = "sha256:159d27cf49f359e3798c4c3eb8da6ef4020e292571bd8c5604a2a573231dd5c8"}, +] + +[package.dependencies] +opentelemetry-proto = "1.27.0" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.27.0" +description = "OpenTelemetry Collector Protobuf over gRPC Exporter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_exporter_otlp_proto_grpc-1.27.0-py3-none-any.whl", hash = "sha256:56b5bbd5d61aab05e300d9d62a6b3c134827bbd28d0b12f2649c2da368006c9e"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.27.0.tar.gz", hash = "sha256:af6f72f76bcf425dfb5ad11c1a6d6eca2863b91e63575f89bb7b4b55099d968f"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +googleapis-common-protos = ">=1.52,<2.0" +grpcio = ">=1.0.0,<2.0.0" +opentelemetry-api = ">=1.15,<2.0" +opentelemetry-exporter-otlp-proto-common = "1.27.0" +opentelemetry-proto = "1.27.0" +opentelemetry-sdk = ">=1.27.0,<1.28.0" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-http" +version = "1.27.0" +description = "OpenTelemetry Collector Protobuf over HTTP Exporter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_exporter_otlp_proto_http-1.27.0-py3-none-any.whl", hash = "sha256:688027575c9da42e179a69fe17e2d1eba9b14d81de8d13553a21d3114f3b4d75"}, + {file = "opentelemetry_exporter_otlp_proto_http-1.27.0.tar.gz", hash = "sha256:2103479092d8eb18f61f3fbff084f67cc7f2d4a7d37e75304b8b56c1d09ebef5"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +googleapis-common-protos = ">=1.52,<2.0" +opentelemetry-api = ">=1.15,<2.0" +opentelemetry-exporter-otlp-proto-common = "1.27.0" +opentelemetry-proto = "1.27.0" +opentelemetry-sdk = ">=1.27.0,<1.28.0" +requests = ">=2.7,<3.0" + +[[package]] +name = "opentelemetry-proto" +version = "1.27.0" +description = "OpenTelemetry Python Proto" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_proto-1.27.0-py3-none-any.whl", hash = "sha256:b133873de5581a50063e1e4b29cdcf0c5e253a8c2d8dc1229add20a4c3830ace"}, + {file = "opentelemetry_proto-1.27.0.tar.gz", hash = "sha256:33c9345d91dafd8a74fc3d7576c5a38f18b7fdf8d02983ac67485386132aedd6"}, +] + +[package.dependencies] +protobuf = ">=3.19,<5.0" + +[[package]] +name = "opentelemetry-sdk" +version = "1.27.0" +description = "OpenTelemetry Python SDK" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_sdk-1.27.0-py3-none-any.whl", hash = "sha256:365f5e32f920faf0fd9e14fdfd92c086e317eaa5f860edba9cdc17a380d9197d"}, + {file = "opentelemetry_sdk-1.27.0.tar.gz", hash = "sha256:d525017dea0ccce9ba4e0245100ec46ecdc043f2d7b8315d56b19aff0904fa6f"}, +] + +[package.dependencies] +opentelemetry-api = "1.27.0" +opentelemetry-semantic-conventions = "0.48b0" +typing-extensions = ">=3.7.4" + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.48b0" +description = "OpenTelemetry Semantic Conventions" +optional = false +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_semantic_conventions-0.48b0-py3-none-any.whl", hash = "sha256:a0de9f45c413a8669788a38569c7e0a11ce6ce97861a628cca785deecdc32a1f"}, + {file = "opentelemetry_semantic_conventions-0.48b0.tar.gz", hash = "sha256:12d74983783b6878162208be57c9effcb89dc88691c64992d70bb89dc00daa1a"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +opentelemetry-api = "1.27.0" + [[package]] name = "packaging" version = "24.1" description = "Core utilities for Python packages" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -758,7 +945,6 @@ files = [ name = "parso" version = "0.8.4" description = "A Python Parser" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -774,7 +960,6 @@ testing = ["docopt", "pytest"] name = "pexpect" version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." -category = "main" optional = false python-versions = "*" files = [ @@ -789,7 +974,6 @@ ptyprocess = ">=0.5" name = "pluggy" version = "1.5.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -805,7 +989,6 @@ testing = ["pytest", "pytest-benchmark"] name = "posthog" version = "3.5.0" description = "Integrate PostHog into any python application." -category = "main" optional = false python-versions = "*" files = [ @@ -829,7 +1012,6 @@ test = ["coverage", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint" name = "prompt-toolkit" version = "3.0.36" description = "Library for building powerful interactive command lines in Python" -category = "main" optional = false python-versions = ">=3.6.2" files = [ @@ -840,11 +1022,30 @@ files = [ [package.dependencies] wcwidth = "*" +[[package]] +name = "protobuf" +version = "4.25.4" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.4-cp310-abi3-win32.whl", hash = "sha256:db9fd45183e1a67722cafa5c1da3e85c6492a5383f127c86c4c4aa4845867dc4"}, + {file = "protobuf-4.25.4-cp310-abi3-win_amd64.whl", hash = "sha256:ba3d8504116a921af46499471c63a85260c1a5fc23333154a427a310e015d26d"}, + {file = "protobuf-4.25.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:eecd41bfc0e4b1bd3fa7909ed93dd14dd5567b98c941d6c1ad08fdcab3d6884b"}, + {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4c8a70fdcb995dcf6c8966cfa3a29101916f7225e9afe3ced4395359955d3835"}, + {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:3319e073562e2515c6ddc643eb92ce20809f5d8f10fead3332f71c63be6a7040"}, + {file = "protobuf-4.25.4-cp38-cp38-win32.whl", hash = "sha256:7e372cbbda66a63ebca18f8ffaa6948455dfecc4e9c1029312f6c2edcd86c4e1"}, + {file = "protobuf-4.25.4-cp38-cp38-win_amd64.whl", hash = "sha256:051e97ce9fa6067a4546e75cb14f90cf0232dcb3e3d508c448b8d0e4265b61c1"}, + {file = "protobuf-4.25.4-cp39-cp39-win32.whl", hash = "sha256:90bf6fd378494eb698805bbbe7afe6c5d12c8e17fca817a646cd6a1818c696ca"}, + {file = "protobuf-4.25.4-cp39-cp39-win_amd64.whl", hash = "sha256:ac79a48d6b99dfed2729ccccee547b34a1d3d63289c71cef056653a846a2240f"}, + {file = "protobuf-4.25.4-py3-none-any.whl", hash = "sha256:bfbebc1c8e4793cfd58589acfb8a1026be0003e852b9da7db5a4285bde996978"}, + {file = "protobuf-4.25.4.tar.gz", hash = "sha256:0dc4a62cc4052a036ee2204d26fe4d835c62827c855c8a03f29fe6da146b380d"}, +] + [[package]] name = "ptyprocess" version = "0.7.0" description = "Run a subprocess in a pseudo terminal" -category = "main" optional = false python-versions = "*" files = [ @@ -856,7 +1057,6 @@ files = [ name = "pure-eval" version = "0.2.3" description = "Safely evaluate AST nodes without side effects" -category = "main" optional = false python-versions = "*" files = [ @@ -871,7 +1071,6 @@ tests = ["pytest"] name = "pydantic" version = "2.8.2" description = "Data validation using Python type hints" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -894,7 +1093,6 @@ email = ["email-validator (>=2.0.0)"] name = "pydantic-core" version = "2.20.1" description = "Core functionality for Pydantic validation and serialization" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -996,7 +1194,6 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" name = "pygments" version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1011,7 +1208,6 @@ windows-terminal = ["colorama (>=0.4.6)"] name = "pymongo" version = "4.8.0" description = "Python driver for MongoDB " -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1084,7 +1280,6 @@ zstd = ["zstandard"] name = "pytest" version = "8.3.1" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1107,7 +1302,6 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments name = "python-dateutil" version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -1122,7 +1316,6 @@ six = ">=1.5" name = "python-dotenv" version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1137,7 +1330,6 @@ cli = ["click (>=5.0)"] name = "python-multipart" version = "0.0.9" description = "A streaming multipart parser for Python" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1152,7 +1344,6 @@ dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatc name = "pywin32" version = "306" description = "Python for Window Extensions" -category = "main" optional = false python-versions = "*" files = [ @@ -1176,7 +1367,6 @@ files = [ name = "pyyaml" version = "6.0.2" description = "YAML parser and emitter for Python" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1239,7 +1429,6 @@ files = [ name = "questionary" version = "2.0.1" description = "Python library to build pretty command line user prompts ⭐️" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1254,7 +1443,6 @@ prompt_toolkit = ">=2.0,<=3.0.36" name = "requests" version = "2.32.3" description = "Python HTTP for Humans." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1276,7 +1464,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "rich" version = "13.7.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -1295,7 +1482,6 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] name = "setuptools" version = "71.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1306,13 +1492,12 @@ files = [ [package.extras] core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (>=1.11.0,<1.12.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "shellingham" version = "1.5.4" description = "Tool to Detect Surrounding Shell" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1324,7 +1509,6 @@ files = [ name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1336,7 +1520,6 @@ files = [ name = "sniffio" version = "1.3.1" description = "Sniff out which async library your code is running under" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1348,7 +1531,6 @@ files = [ name = "stack-data" version = "0.6.3" description = "Extract data from python stack frames and tracebacks for informative displays" -category = "main" optional = false python-versions = "*" files = [ @@ -1368,7 +1550,6 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] name = "starlette" version = "0.37.2" description = "The little ASGI library that shines." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1387,7 +1568,6 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7 name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" -category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1399,7 +1579,6 @@ files = [ name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1411,7 +1590,6 @@ files = [ name = "traitlets" version = "5.14.3" description = "Traitlets Python configuration system" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1427,7 +1605,6 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, name = "typer" version = "0.12.3" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1445,7 +1622,6 @@ typing-extensions = ">=3.7.4.3" name = "typing-extensions" version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1457,7 +1633,6 @@ files = [ name = "urllib3" version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1475,7 +1650,6 @@ zstd = ["zstandard (>=0.18.0)"] name = "uvicorn" version = "0.30.3" description = "The lightning-fast ASGI server." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1491,7 +1665,7 @@ httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standar python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} -uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} @@ -1502,7 +1676,6 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", name = "uvloop" version = "0.19.0" description = "Fast implementation of asyncio event loop on top of libuv" -category = "main" optional = false python-versions = ">=3.8.0" files = [ @@ -1547,7 +1720,6 @@ test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)" name = "watchfiles" version = "0.22.0" description = "Simple, modern and high performance file watching and code reload in python." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1635,7 +1807,6 @@ anyio = ">=3.0.0" name = "wcwidth" version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" -category = "main" optional = false python-versions = "*" files = [ @@ -1647,7 +1818,6 @@ files = [ name = "websockets" version = "12.0" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1725,11 +1895,89 @@ files = [ {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, ] +[[package]] +name = "wrapt" +version = "1.16.0" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.6" +files = [ + {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, + {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, + {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, + {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, + {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, + {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, + {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, + {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, + {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, + {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, +] + [[package]] name = "zipp" version = "3.19.2" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1744,4 +1992,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "e13b8a1057fac3640fa68fe0e6de7cfa4175cebd5aae00cfaf29d2e4af122646" +content-hash = "8d9266ec491f0f3c5661315ed214fe711c61c92c3c6133694fadd6644addec3e" From 4c742679bb3d26cdf92dd8907e5015023182a56d Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Sat, 7 Sep 2024 08:44:49 +0200 Subject: [PATCH 017/305] integration fixes --- agenta-cli/agenta/sdk/agenta_init.py | 3 +- agenta-cli/agenta/sdk/decorators/routing.py | 2 +- agenta-cli/agenta/sdk/tracing/litellm.py | 6 +- .../agenta/sdk/tracing/opentelemetry.py | 63 +++++++++++++++---- 4 files changed, 58 insertions(+), 16 deletions(-) diff --git a/agenta-cli/agenta/sdk/agenta_init.py b/agenta-cli/agenta/sdk/agenta_init.py index 74ba3d457e..537070103f 100644 --- a/agenta-cli/agenta/sdk/agenta_init.py +++ b/agenta-cli/agenta/sdk/agenta_init.py @@ -66,9 +66,10 @@ def init( self.host = ( host + or os.environ.get("AGENTA_HOST") or config.get("backend_host") or config.get("host") - or os.environ.get("AGENTA_HOST", "https://cloud.agenta.ai") + or "https://cloud.agenta.ai" ) self.app_id = app_id or config.get("app_id") or os.environ.get("AGENTA_APP_ID") diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index 13540670ec..4065951539 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -651,7 +651,7 @@ def handle_terminal_run( log.info("") for span in result.trace["spans"]: log.info( - f"{span['id']} {span['spankind'].ljust(10)} {span['status'].ljust(5)} {span['name']}" + f"{span['app_id']} {span['id']} {span['spankind'].ljust(10)} {span['status'].ljust(5)} {span['name']}" ) log.info("\n==========================\n") diff --git a/agenta-cli/agenta/sdk/tracing/litellm.py b/agenta-cli/agenta/sdk/tracing/litellm.py index 54146126a6..2756bdb1d6 100644 --- a/agenta-cli/agenta/sdk/tracing/litellm.py +++ b/agenta-cli/agenta/sdk/tracing/litellm.py @@ -40,11 +40,13 @@ def log_pre_api_call( self.span = ag.tracing.start_span(name=f"litellm_{kind.lower()}", kind=kind) + ag.logging.warning(f"------> {self.span}") + if not self.span: ag.logging.error("LiteLLM callback error: span not found.") return - # ag.logging.info(f"log_pre_api_call({hex(self.span.context.span_id)[2:]})") + ag.logging.info(f"log_pre_api_call({hex(self.span.context.span_id)[2:]})") ag.tracing.set_attributes( namespace="data.inputs", @@ -214,7 +216,7 @@ async def async_log_success_event( ag.logging.error("LiteLLM callback error: span not found.") return - # ag.logging.info(f"async_log_success({hex(self.span.context.span_id)[2:]})") + ag.logging.info(f"async_log_success({hex(self.span.context.span_id)[2:]})") ag.tracing.set_attributes( namespace="data.outputs", diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index 23abc4b158..ce6e7e70f0 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -12,6 +12,7 @@ from opentelemetry.trace import set_tracer_provider from opentelemetry.context import Context +from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace import ( Span, TracerProvider, @@ -35,7 +36,7 @@ _AGENTA_ROOT_SPAN_ID = "f" * 16 -_AGENTA_API_KEY_HEADER = "api-key" +_AGENTA_API_KEY_HEADER = "Authorization" _AGENTA_EXPERIMENT_ID_HEADER = "experiment-id" @@ -76,6 +77,7 @@ class TraceProcessor(BatchSpanProcessor): def __init__( self, span_exporter: SpanExporter, + scope: Dict[str, Any] = None, max_queue_size: int = None, schedule_delay_millis: float = None, max_export_batch_size: int = None, @@ -90,11 +92,16 @@ def __init__( ) self.registry = dict() + self.scope = scope def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None: super().on_start(span, parent_context=parent_context) - log.info(f"> {span.context.span_id.to_bytes(8).hex()} {span.name}") + span.set_attributes( + attributes={f"ag.extra.{k}": v for k, v in self.scope.items()} + ) + + log.info(f"> {span.context.span_id.to_bytes(8, 'big').hex()} {span.name}") if span.context.trace_id not in self.registry: self.registry[span.context.trace_id] = dict() @@ -104,7 +111,7 @@ def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None def on_end(self, span: ReadableSpan): super().on_end(span) - log.info(f" < {span.context.span_id.to_bytes(8).hex()} {span.name}") + log.info(f" < {span.context.span_id.to_bytes(8, 'big').hex()} {span.name}") del self.registry[span.context.trace_id][span.context.span_id] @@ -128,7 +135,9 @@ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: return for span in spans: - self._registry.update(**{span.context.span_id.to_bytes(8).hex(): span}) + self._registry.update( + **{span.context.span_id.to_bytes(8, "big").hex(): span} + ) def shutdown(self) -> None: self._shutdown = True @@ -182,6 +191,8 @@ def __init__( # EXPERIMENT self.experiment_id = experiment_id + log.info(f"---------- {self.app_id}") + # HEADERS self.headers = {} if api_key: @@ -205,20 +216,39 @@ def safe_shutdown(): self.processor.shutdown = safe_shutdown # TRACER PROVIDER - self.tracer_provider = TracerProvider(active_span_processor=self.processor) + self.tracer_provider = TracerProvider( + active_span_processor=self.processor, + resource=Resource( + attributes={ + "service.name": "agenta", + "service.version": Tracing.VERSION, + } + ), + ) # TRACE PROCESSORS - self.inline_processor = TraceProcessor(InlineTraceExporter(registry=self.spans)) + self.inline_processor = TraceProcessor( + InlineTraceExporter(registry=self.spans), + scope={"app_id": self.app_id}, + ) self.tracer_provider.add_span_processor(self.inline_processor) try: - httpx.post(self.url) + import os + + log.info(f"Connecting to the remote trace receiver at {self.url}...") + + httpx.get(self.url, headers=self.headers) + + log.info(f"Connection established.") self.remote_processor = TraceProcessor( - OTLPSpanExporter(endpoint=self.url, headers=self.headers) + OTLPSpanExporter(endpoint=self.url, headers=self.headers), + scope={"app_id": self.app_id}, ) self.tracer_provider.add_span_processor(self.remote_processor) except: + log.info(f"Connection failed.") log.error( f"Warning: Your traces will not be exported since {self.url} is unreachable." ) @@ -240,6 +270,10 @@ def start_as_current_span(self, name: str, kind: str): namespace="extra", attributes={"kind": kind}, ) + self.set_attributes( + namespace="extra", + attributes={"app_id": self.app_id}, + ) yield span def start_span(self, name: str, kind: str): @@ -255,6 +289,11 @@ def start_span(self, name: str, kind: str): attributes={"kind": kind}, span=span, ) + self.set_attributes( + namespace="extra", + attributes={"app_id": self.app_id}, + span=span, + ) return span @@ -369,7 +408,7 @@ def get_inline_trace(self, trace_id_only: bool = False): for span in self.spans.values(): span: ReadableSpan - trace_id = span.context.trace_id.to_bytes(16).hex() + trace_id = span.context.trace_id.to_bytes(16, "big").hex() if trace_id not in spans_idx: spans_idx[trace_id] = list() @@ -410,7 +449,7 @@ def _parse_to_legacy_span( attributes.update(**event.attributes) legacy_span = CreateSpan( - id=span.context.span_id.to_bytes(8).hex(), + id=span.context.span_id.to_bytes(8, "big").hex(), spankind=self._get_attributes("extra", span).get("kind", "UNKNOWN"), name=span.name, status=str(span.status.status_code.name), @@ -423,7 +462,7 @@ def _parse_to_legacy_span( ).isoformat(), # parent_span_id=( - span.parent.span_id.to_bytes(8).hex() if span.parent else None + span.parent.span_id.to_bytes(8, "big").hex() if span.parent else None ), # inputs=self._get_attributes("data.inputs", span), @@ -445,7 +484,7 @@ def _parse_to_legacy_span( ), cost=self._get_attributes("metrics.costs", span).get("marginal", 0.0), # - app_id=self.app_id, + app_id=self._get_attributes("extra", span).get("app_id", ""), variant_id=None, variant_name=None, environment=None, From 5dad0301799bc4fe9b03ccc7493d15e69659a489 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Mon, 9 Sep 2024 08:11:23 +0200 Subject: [PATCH 018/305] Fixing integrating issues --- agenta-cli/agenta/sdk/decorators/routing.py | 3 + agenta-cli/agenta/sdk/decorators/tracing.py | 44 +++++++ agenta-cli/agenta/sdk/tracing/litellm.py | 16 +-- .../agenta/sdk/tracing/opentelemetry.py | 124 +++++++++++------- agenta-cli/agenta/sdk/utils/costs.py | 3 + 5 files changed, 131 insertions(+), 59 deletions(-) diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index 4065951539..55cef0e207 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -643,6 +643,9 @@ def handle_terminal_run( log.info("\n========= Result =========\n") log.info(result.trace["trace_id"]) + log.info(result.trace["cost"]) + log.info(result.trace["latency"]) + log.info(result.trace["tokens"]) # log.info("") # log.info(json.dumps(result.data, indent=2)) diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index b9bd5e7ecb..17ea8f7ae4 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -83,10 +83,33 @@ async def wrapped_func(*args, **kwargs): result = await func(*args, **kwargs) + cost = 0.0 + usage = {} + if isinstance(result, dict): + cost = result.get("cost", 0.0) + usage = result.get("usage", {}) + print("--------", cost, usage) + + ag.tracing.set_attributes( + namespace="metrics.marginal.costs", + attributes={"total": cost}, + ) + ag.tracing.set_attributes( + namespace="metrics.marginal.tokens", + attributes=( + { + "prompt": usage.get("prompt_tokens", 0), + "completion": usage.get("completion_tokens", 0), + "total": usage.get("total_tokens", 0), + } + ), + ) + ag.tracing.set_attributes( "data.outputs", redact(patch(result), self.ignore_outputs), ) + ag.tracing.set_status("OK") return result @@ -116,6 +139,27 @@ def wrapped_func(*args, **kwargs): result = func(*args, **kwargs) + cost = 0.0 + usage = {} + if isinstance(result, dict): + cost = result.get("cost", 0.0) + usage = result.get("usage", {}) + print("--------", cost, usage) + + ag.tracing.set_attributes( + namespace="metrics.marginal.costs", + attributes={"total": cost}, + ) + ag.tracing.set_attributes( + namespace="metrics.marginal.tokens", + attributes=( + { + "prompt": usage.get("prompt_tokens", 0), + "completion": usage.get("completion_tokens", 0), + "total": usage.get("total_tokens", 0), + } + ), + ) ag.tracing.set_attributes( "data.outputs", redact(patch(result), self.ignore_outputs), diff --git a/agenta-cli/agenta/sdk/tracing/litellm.py b/agenta-cli/agenta/sdk/tracing/litellm.py index 2756bdb1d6..2839a5f1d9 100644 --- a/agenta-cli/agenta/sdk/tracing/litellm.py +++ b/agenta-cli/agenta/sdk/tracing/litellm.py @@ -83,13 +83,13 @@ def log_stream_event( ) ag.tracing.set_attributes( - namespace="metrics.costs", + namespace="metrics.marginal.costs", attributes={"marginal": kwargs.get("response_cost")}, span=self.span, ) ag.tracing.set_attributes( - namespace="metrics.tokens", + namespace="metrics.marginal.tokens", attributes=( { "prompt": response_obj.usage.prompt_tokens, @@ -124,13 +124,13 @@ def log_success_event( ) ag.tracing.set_attributes( - namespace="metrics.costs", + namespace="metrics.marginal.costs", attributes={"marginal": kwargs.get("response_cost")}, span=self.span, ) ag.tracing.set_attributes( - namespace="metrics.tokens", + namespace="metrics.marginal.tokens", attributes=( { "prompt": response_obj.usage.prompt_tokens, @@ -184,13 +184,13 @@ async def async_log_stream_event( ) ag.tracing.set_attributes( - namespace="metrics.costs", + namespace="metrics.marginal.costs", attributes={"marginal": kwargs.get("response_cost")}, span=self.span, ) ag.tracing.set_attributes( - namespace="metrics.tokens", + namespace="metrics.marginal.tokens", attributes=( { "prompt": response_obj.usage.prompt_tokens, @@ -225,13 +225,13 @@ async def async_log_success_event( ) ag.tracing.set_attributes( - namespace="metrics.costs", + namespace="metrics.marginal.costs", attributes={"marginal": kwargs.get("response_cost")}, span=self.span, ) ag.tracing.set_attributes( - namespace="metrics.tokens", + namespace="metrics.marginal.tokens", attributes=( { "prompt": response_obj.usage.prompt_tokens, diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index ce6e7e70f0..73e38983ba 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -151,8 +151,8 @@ def force_flush(self, timeout_millis: int = 30000) -> bool: "data.internals", "data.outputs", "metrics.scores", - "metrics.costs", - "metrics.tokens", + "metrics.marginal.costs", + "metrics.marginal.tokens", "metadata.config", "metadata.version", "tags", @@ -171,7 +171,7 @@ def force_flush(self, timeout_millis: int = 30000) -> bool: class Tracing: - VERSION = "2.0" + VERSION = "0.1.0" # @suppress(Exception) def __init__( @@ -262,38 +262,23 @@ def safe_shutdown(): @contextmanager def start_as_current_span(self, name: str, kind: str): with self.tracer.start_as_current_span(name) as span: - self.set_attributes( - namespace="metadata.version", - attributes={"agenta": version("agenta"), "tracing": Tracing.VERSION}, - ) + span: Span + self.set_attributes( namespace="extra", attributes={"kind": kind}, ) - self.set_attributes( - namespace="extra", - attributes={"app_id": self.app_id}, - ) + yield span def start_span(self, name: str, kind: str): span = self.tracer.start_span(name) - self.set_attributes( - namespace="metadata.version", - attributes={"agenta": version("agenta"), "tracing": Tracing.VERSION}, - span=span, - ) self.set_attributes( namespace="extra", attributes={"kind": kind}, span=span, ) - self.set_attributes( - namespace="extra", - attributes={"app_id": self.app_id}, - span=span, - ) return span @@ -352,7 +337,7 @@ def _value(self, value: Any) -> str: if value is None: return "null" - if not isinstance(value, (str, int, float, bool)): + if not isinstance(value, (str, int, float, bool, bytes)): return json.dumps(value) return value @@ -375,30 +360,9 @@ def set_attributes( self._value(value), ) - def store_inputs(self, attributes: dict, span: Optional[Span] = None) -> None: - self.set_attributes("data.inputs", attributes, span) - def store_internals(self, attributes: dict, span: Optional[Span] = None) -> None: self.set_attributes("data.internals", attributes, span) - def store_outputs(self, attributes: dict, span: Optional[Span] = None) -> None: - self.set_attributes("data.outputs", attributes, span) - - def store_costs(self, attributes: dict, span: Optional[Span] = None) -> None: - self.set_attributes("metrics.costs", attributes, span) - - def store_latencies(self, attributes: dict, span: Optional[Span] = None) -> None: - self.set_attributes("metrics.latencies", attributes, span) - - def store_tokens(self, attributes: dict, span: Optional[Span] = None) -> None: - self.set_attributes("metrics.tokens", attributes, span) - - def store_config(self, attributes: dict, span: Optional[Span] = None) -> None: - self.set_attributes("metadata.config", attributes, span) - - def store_tags(self, attributes: dict, span: Optional[Span] = None) -> None: - self.set_attributes("tags", attributes, span) - def is_processing(self) -> bool: return not self.inline_processor.is_done() @@ -417,10 +381,67 @@ def get_inline_trace(self, trace_id_only: bool = False): spans_idx[trace_id].append(self._parse_to_legacy_span(span)) inline_traces = [ - {"trace_id": trace_id, "spans": spans} + { + "trace_id": trace_id, + "cost": sum( + float( + span.attributes.get( + "ag.metrics.marginal.costs.total", + 0.0, + ) + ) + for span in self.spans.values() + ), + "tokens": { + "prompt": sum( + int( + span.attributes.get( + "ag.metrics.marginal.tokens.prompt", + 0, + ) + ) + for span in self.spans.values() + ), + "completion": sum( + int( + span.attributes.get( + "ag.metrics.marginal.tokens.completion", + 0, + ) + ) + for span in self.spans.values() + ), + "total": sum( + int( + span.attributes.get( + "ag.metrics.marginal.tokens.total", + 0, + ) + ) + for span in self.spans.values() + ), + }, + "latency": sum( + float(span.end_time - span.start_time) / 1_000_000_000 + for span in self.spans.values() + if span.parent is None + ), + "spans": spans, + } for trace_id, spans in spans_idx.items() ] + print( + "-------", + sum( + float(span.end_time - span.start_time) / 1_000_000_000 + for span in self.spans.values() + if span.parent is None + ), + ) + + self.spans.clear() + if len(inline_traces) > 1: log.error("Unexpected error while parsing inline trace: too many traces.") @@ -472,17 +493,18 @@ def _parse_to_legacy_span( config=self._get_attributes("metadata.config", span), # tokens=LlmTokens( - prompt_tokens=self._get_attributes("metrics.tokens", span).get( - "prompt", None - ), - completion_tokens=self._get_attributes("metrics.tokens", span).get( - "completion", None + prompt_tokens=self._get_attributes("metrics.marginal.tokens", span).get( + "prompt", + None, ), - total_tokens=self._get_attributes("metrics.tokens", span).get( + completion_tokens=self._get_attributes( + "metrics.marginal.tokens", span + ).get("completion", None), + total_tokens=self._get_attributes("metrics.marginal.tokens", span).get( "total", None ), ), - cost=self._get_attributes("metrics.costs", span).get("marginal", 0.0), + cost=self._get_attributes("metrics.marginal.costs", span).get("total", 0.0), # app_id=self._get_attributes("extra", span).get("app_id", ""), variant_id=None, diff --git a/agenta-cli/agenta/sdk/utils/costs.py b/agenta-cli/agenta/sdk/utils/costs.py index 8473dee57f..667ae27cda 100644 --- a/agenta-cli/agenta/sdk/utils/costs.py +++ b/agenta-cli/agenta/sdk/utils/costs.py @@ -49,6 +49,9 @@ "gpt-35-turbo-16k-completion": 0.004, "gpt-35-turbo-16k-0613-completion": 0.004, # Others + "text-embedding-ada-002": 0.1, + "text-ada-002": 0.1, + "adav2": 0.1, "text-ada-001": 0.0004, "ada": 0.0004, "text-babbage-001": 0.0005, From 5de26a24bdcf2b698432ef90ec85087a24db0ba7 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 10 Sep 2024 10:51:40 +0200 Subject: [PATCH 019/305] Fix integration issues --- agenta-cli/agenta/sdk/config_manager.py | 4 +- agenta-cli/agenta/sdk/context/__init__.py | 0 agenta-cli/agenta/sdk/context/routing.py | 25 ++ agenta-cli/agenta/sdk/context/tracing.py | 3 + agenta-cli/agenta/sdk/decorators/routing.py | 59 +++-- agenta-cli/agenta/sdk/decorators/tracing.py | 38 ++- .../agenta/sdk/tracing/opentelemetry.py | 216 +++++++++++------- 7 files changed, 224 insertions(+), 121 deletions(-) create mode 100644 agenta-cli/agenta/sdk/context/__init__.py create mode 100644 agenta-cli/agenta/sdk/context/routing.py create mode 100644 agenta-cli/agenta/sdk/context/tracing.py diff --git a/agenta-cli/agenta/sdk/config_manager.py b/agenta-cli/agenta/sdk/config_manager.py index 893339cf54..aad0187be8 100644 --- a/agenta-cli/agenta/sdk/config_manager.py +++ b/agenta-cli/agenta/sdk/config_manager.py @@ -7,7 +7,7 @@ from pydantic import BaseModel, ValidationError from agenta.client.backend.client import AgentaApi -from agenta.sdk.decorators.routing import route_context +from agenta.sdk.context.routing import routing_context from . import AgentaSingleton @@ -47,7 +47,7 @@ def get_from_route(schema: Type[T]) -> T: 3. 'variant' Only one of these should be provided. """ - context = route_context.get() + context = routing_context.get() if ("config" in context and context["config"]) and ( ("environment" in context and context["environment"]) or ("variant" in context and context["variant"]) diff --git a/agenta-cli/agenta/sdk/context/__init__.py b/agenta-cli/agenta/sdk/context/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/agenta-cli/agenta/sdk/context/routing.py b/agenta-cli/agenta/sdk/context/routing.py new file mode 100644 index 0000000000..4f69c6eee9 --- /dev/null +++ b/agenta-cli/agenta/sdk/context/routing.py @@ -0,0 +1,25 @@ +from contextlib import contextmanager +from contextvars import ContextVar +from typing import Any, Dict, Optional + +routing_context = ContextVar("routing_context", default={}) + + +@contextmanager +def routing_context_manager( + config: Optional[Dict[str, Any]] = None, + environment: Optional[str] = None, + version: Optional[str] = None, + variant: Optional[str] = None, +): + context = { + "config": config, + "environment": environment, + "version": version, + "variant": variant, + } + token = routing_context.set(context) + try: + yield + finally: + routing_context.reset(token) diff --git a/agenta-cli/agenta/sdk/context/tracing.py b/agenta-cli/agenta/sdk/context/tracing.py new file mode 100644 index 0000000000..0585a014ad --- /dev/null +++ b/agenta-cli/agenta/sdk/context/tracing.py @@ -0,0 +1,3 @@ +from contextvars import ContextVar + +tracing_context = ContextVar("tracing_context", default={}) diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index 55cef0e207..4abe1da3fd 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -18,6 +18,9 @@ from fastapi import Body, FastAPI, UploadFile, HTTPException import agenta as ag + +from agenta.sdk.context.routing import routing_context_manager, routing_context +from agenta.sdk.context.tracing import tracing_context from agenta.sdk.router import router as router from agenta.sdk.utils.logging import log from agenta.sdk.types import ( @@ -64,28 +67,6 @@ log.setLevel("DEBUG") -route_context = contextvars.ContextVar("route_context", default={}) - - -@contextmanager -def route_context_manager( - config: Optional[Dict[str, Any]] = None, - environment: Optional[str] = None, - version: Optional[str] = None, - variant: Optional[str] = None, -): - context = { - "config": config, - "environment": environment, - "version": version, - "variant": variant, - } - token = route_context.set(context) - try: - yield - finally: - route_context.reset(token) - class PathValidator(BaseModel): url: HttpUrl @@ -147,7 +128,10 @@ async def chain_of_prompts_llm(prompt: str): routes = list() def __init__( - self, func: Callable[..., Any], route_path="", config_schema: BaseModel = None + self, + func: Callable[..., Any], + route_path="", + config_schema: Optional[BaseModel] = None, ): DEFAULT_PATH = "generate" PLAYGROUND_PATH = "/playground" @@ -179,7 +163,10 @@ async def wrapper(*args, **kwargs) -> Any: if not config_schema: ag.config.set(**api_config_params) - with route_context_manager(config=api_config_params): + with routing_context_manager( + config=api_config_params, + environment="playground", + ): entrypoint_result = await self.execute_function( func, True, # inline trace: True @@ -245,8 +232,10 @@ async def wrapper_deployed(*args, **kwargs) -> Any: else: ag.config.pull(config_name="default") - with route_context_manager( - variant=kwargs["config"], environment=kwargs["environment"] + with routing_context_manager( + config=ag.config.all(), + variant=kwargs["config"], + environment=kwargs["environment"], ): entrypoint_result = await self.execute_function( func, @@ -367,6 +356,8 @@ async def execute_function( ) log.info(f"--------------------------\n") + tracing_context.set(routing_context.get()) + WAIT_FOR_SPANS = True TIMEOUT = 1 TIMESTEP = 0.1 @@ -632,13 +623,17 @@ def handle_terminal_run( loop = asyncio.get_event_loop() - result = loop.run_until_complete( - self.execute_function( - func, - True, # inline trace: True - **{"params": args_func_params, "config_params": args_config_params}, + with routing_context_manager( + config=args_config_params, + environment="terminal", + ): + result = loop.run_until_complete( + self.execute_function( + func, + True, # inline trace: True + **{"params": args_func_params, "config_params": args_config_params}, + ) ) - ) log.info("\n========= Result =========\n") diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 17ea8f7ae4..31418fab16 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -1,11 +1,13 @@ import inspect from functools import wraps from itertools import chain -from contextlib import suppress +from contextvars import ContextVar from typing import Callable, Optional, Union, Any, Dict, List import agenta as ag +from agenta.sdk.context.tracing import tracing_context + class instrument: DEFAULT_KEY = "__default__" @@ -70,12 +72,25 @@ def patch(result): @wraps(func) async def async_wrapper(*args, **kwargs): async def wrapped_func(*args, **kwargs): + rctx = tracing_context.get() + print("..", func.__name__, rctx) with ag.tracing.start_as_current_span(func.__name__, self.kind): try: + rctx = tracing_context.get() + print("...", func.__name__, rctx) + ag.tracing.set_attributes( + "metadata", {"config": rctx.get("config", {})} + ) + ag.tracing.set_attributes( + "metadata", {"environment": rctx.get("environment", {})} + ) + ag.tracing.set_attributes( + "metadata", {"version": rctx.get("version", {})} + ) ag.tracing.set_attributes( - "metadata.config", - self.config, + "metadata", {"variant": rctx.get("variant", {})} ) + ag.tracing.set_attributes( "data.inputs", redact(parse(*args, **kwargs), self.ignore_inputs), @@ -126,12 +141,25 @@ async def wrapped_func(*args, **kwargs): @wraps(func) def sync_wrapper(*args, **kwargs): def wrapped_func(*args, **kwargs): + rctx = tracing_context.get() + print("..", func.__name__, rctx) with ag.tracing.start_as_current_span(func.__name__, self.kind): try: + rctx = tracing_context.get() + print("...", func.__name__, rctx) ag.tracing.set_attributes( - "metadata.config", - self.config, + "metadata", {"config": rctx.get("config", {})} ) + ag.tracing.set_attributes( + "metadata", {"environment": rctx.get("environment", {})} + ) + ag.tracing.set_attributes( + "metadata", {"version": rctx.get("version", {})} + ) + ag.tracing.set_attributes( + "metadata", {"variant": rctx.get("variant", {})} + ) + ag.tracing.set_attributes( "data.inputs", redact(parse(*args, **kwargs), self.ignore_inputs), diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index 73e38983ba..9e38faa8cf 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -3,12 +3,14 @@ from threading import Lock from datetime import datetime +from collections import OrderedDict from typing import Optional, Dict, Any, List, Literal, Sequence from contextlib import contextmanager, suppress from importlib.metadata import version from agenta.sdk.utils.logging import log from agenta.client.backend.types.create_span import CreateSpan, LlmTokens +from agenta.sdk.context.tracing import tracing_context from opentelemetry.trace import set_tracer_provider from opentelemetry.context import Context @@ -191,8 +193,6 @@ def __init__( # EXPERIMENT self.experiment_id = experiment_id - log.info(f"---------- {self.app_id}") - # HEADERS self.headers = {} if api_key: @@ -360,6 +360,17 @@ def set_attributes( self._value(value), ) + def _get_attributes( + self, + namespace: str, + span: ReadableSpan, + ): + return { + k.replace(self._key(namespace), ""): v + for k, v in span.attributes.items() + if k.startswith(self._key(namespace)) + } + def store_internals(self, attributes: dict, span: Optional[Span] = None) -> None: self.set_attributes("data.internals", attributes, span) @@ -367,79 +378,52 @@ def is_processing(self) -> bool: return not self.inline_processor.is_done() def get_inline_trace(self, trace_id_only: bool = False): - spans_idx: Dict[str, List[ReadableSpan]] = dict() + traces_idx: Dict[str, Dict[str, ReadableSpan]] = dict() for span in self.spans.values(): span: ReadableSpan trace_id = span.context.trace_id.to_bytes(16, "big").hex() + span_id = span.context.span_id.to_bytes(8, "big").hex() - if trace_id not in spans_idx: - spans_idx[trace_id] = list() + if trace_id not in traces_idx: + traces_idx[trace_id] = dict() if not trace_id_only: - spans_idx[trace_id].append(self._parse_to_legacy_span(span)) + traces_idx[trace_id][span_id] = self._parse_to_legacy_span(span) + + if len(traces_idx) > 1: + log.error("Rrror while parsing inline trace: too many traces.") + + trace_id = list(traces_idx.keys())[0] + spans_idx = traces_idx[trace_id] + + spans_id_tree = self._make_spans_id_tree(spans_idx) + + if len(spans_id_tree) > 1: + log.error("Error while parsing inline trace: too many root spans.") + + root_span_id = list(spans_id_tree.keys())[0] + + self._cumulate_costs(spans_id_tree, spans_idx) + self._cumulate_tokens(spans_id_tree, spans_idx) inline_traces = [ { "trace_id": trace_id, - "cost": sum( - float( - span.attributes.get( - "ag.metrics.marginal.costs.total", - 0.0, - ) - ) - for span in self.spans.values() - ), - "tokens": { - "prompt": sum( - int( - span.attributes.get( - "ag.metrics.marginal.tokens.prompt", - 0, - ) - ) - for span in self.spans.values() - ), - "completion": sum( - int( - span.attributes.get( - "ag.metrics.marginal.tokens.completion", - 0, - ) - ) - for span in self.spans.values() - ), - "total": sum( - int( - span.attributes.get( - "ag.metrics.marginal.tokens.total", - 0, - ) - ) - for span in self.spans.values() - ), - }, - "latency": sum( - float(span.end_time - span.start_time) / 1_000_000_000 - for span in self.spans.values() - if span.parent is None - ), - "spans": spans, + "cost": spans_idx[root_span_id]["cost"], + "tokens": spans_idx[root_span_id]["tokens"], + "latency": datetime.fromisoformat( + spans_idx[root_span_id]["end_time"].replace("Z", "+00:00") + ).timestamp() + - datetime.fromisoformat( + spans_idx[root_span_id]["start_time"].replace("Z", "+00:00") + ).timestamp(), + "spans": list(spans.values()), } - for trace_id, spans in spans_idx.items() + for trace_id, spans in traces_idx.items() ] - print( - "-------", - sum( - float(span.end_time - span.start_time) / 1_000_000_000 - for span in self.spans.values() - if span.parent is None - ), - ) - self.spans.clear() if len(inline_traces) > 1: @@ -447,21 +431,7 @@ def get_inline_trace(self, trace_id_only: bool = False): return inline_traces[0] - def _get_attributes( - self, - namespace: str, - span: ReadableSpan, - ): - return { - k.replace(self._key(namespace), ""): v - for k, v in span.attributes.items() - if k.startswith(self._key(namespace)) - } - - def _parse_to_legacy_span( - self, - span: ReadableSpan, - ): + def _parse_to_legacy_span(self, span: ReadableSpan): attributes = dict(span.attributes) @@ -490,30 +460,112 @@ def _parse_to_legacy_span( internals=self._get_attributes("data.internals", span), outputs=self._get_attributes("data.outputs", span), # - config=self._get_attributes("metadata.config", span), + config=json.loads( + self._get_attributes("metadata", span).get("config", "{}") + ), # tokens=LlmTokens( prompt_tokens=self._get_attributes("metrics.marginal.tokens", span).get( - "prompt", - None, + "prompt", 0 ), completion_tokens=self._get_attributes( "metrics.marginal.tokens", span - ).get("completion", None), + ).get("completion", 0), total_tokens=self._get_attributes("metrics.marginal.tokens", span).get( - "total", None + "total", 0 ), ), cost=self._get_attributes("metrics.marginal.costs", span).get("total", 0.0), # app_id=self._get_attributes("extra", span).get("app_id", ""), + environment=self._get_attributes("metadata", span).get("environment"), + # variant_id=None, variant_name=None, - environment=None, tags=None, token_consumption=None, attributes=attributes, user=None, ) - return json.loads(legacy_span.json()) + return json.loads(legacy_span.model_dump_json()) + + def _make_spans_id_tree(self, spans): + tree = OrderedDict() + index = {} + + def push(span) -> None: + if span["parent_span_id"] is None: + tree[span["id"]] = OrderedDict() + index[span["id"]] = tree[span["id"]] + elif span["parent_span_id"] in index: + index[span["parent_span_id"]][span["id"]] = OrderedDict() + index[span["id"]] = index[span["parent_span_id"]][span["id"]] + else: + log.error("The parent span id should have been in the tracing tree.") + + for span in sorted(spans.values(), key=lambda span: span["start_time"]): + push(span) + + return tree + + def _visit_tree_dfs( + self, + spans_id_tree: OrderedDict, + spans_idx: Dict[str, CreateSpan], + get_metric, + accumulate_metric, + set_metric, + ): + for span_id, children_spans_id_tree in spans_id_tree.items(): + cumulated_metric = get_metric(spans_idx[span_id]) + + self._visit_tree_dfs( + children_spans_id_tree, + spans_idx, + get_metric, + accumulate_metric, + set_metric, + ) + + for child_span_id in children_spans_id_tree.keys(): + child_metric = get_metric(spans_idx[child_span_id]) + cumulated_metric = accumulate_metric(cumulated_metric, child_metric) + + set_metric(spans_idx[span_id], cumulated_metric) + + def _cumulate_costs( + self, + spans_id_tree: OrderedDict, + spans_idx: Dict[str, dict], + ): + def _get(span): + return span["cost"] + + def _acc(a, b): + return a + b + + def _set(span, cost): + span["cost"] = cost + + self._visit_tree_dfs(spans_id_tree, spans_idx, _get, _acc, _set) + + def _cumulate_tokens( + self, + spans_id_tree: OrderedDict, + spans_idx: Dict[str, dict], + ): + def _get(span): + return span["tokens"] + + def _acc(a, b): + return { + "prompt_tokens": a["prompt_tokens"] + b["prompt_tokens"], + "completion_tokens": a["completion_tokens"] + b["completion_tokens"], + "total_tokens": a["total_tokens"] + b["total_tokens"], + } + + def _set(span, tokens): + span["tokens"] = tokens + + self._visit_tree_dfs(spans_id_tree, spans_idx, _get, _acc, _set) From 9c934e5f4e7756dc0c16350de88632678d96e793 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 10 Sep 2024 15:56:40 +0200 Subject: [PATCH 020/305] refactor, clean-up, and handle non-standard types --- agenta-cli/agenta/sdk/decorators/routing.py | 41 +- agenta-cli/agenta/sdk/decorators/tracing.py | 8 - agenta-cli/agenta/sdk/tracing/conventions.py | 24 + agenta-cli/agenta/sdk/tracing/exporters.py | 40 ++ agenta-cli/agenta/sdk/tracing/inline_trace.py | 120 +++++ .../agenta/sdk/tracing/opentelemetry.py | 508 +++--------------- agenta-cli/agenta/sdk/tracing/processors.py | 68 +++ agenta-cli/agenta/sdk/tracing/spans.py | 100 ++++ agenta-cli/agenta/sdk/tracing/trace_tree.py | 88 +++ agenta-cli/agenta/sdk/tracing/tracers.py | 61 +++ agenta-cli/agenta/sdk/utils/singleton.py | 29 + 11 files changed, 621 insertions(+), 466 deletions(-) create mode 100644 agenta-cli/agenta/sdk/tracing/conventions.py create mode 100644 agenta-cli/agenta/sdk/tracing/exporters.py create mode 100644 agenta-cli/agenta/sdk/tracing/inline_trace.py create mode 100644 agenta-cli/agenta/sdk/tracing/processors.py create mode 100644 agenta-cli/agenta/sdk/tracing/spans.py create mode 100644 agenta-cli/agenta/sdk/tracing/trace_tree.py create mode 100644 agenta-cli/agenta/sdk/tracing/tracers.py create mode 100644 agenta-cli/agenta/sdk/utils/singleton.py diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index 4abe1da3fd..5ac129182c 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -635,22 +635,37 @@ def handle_terminal_run( ) ) - log.info("\n========= Result =========\n") + SHOW_DETAILS = True + SHOW_DATA = False + SHOW_SPANS = True + SHOW_SPAN_ATTRIBUTES = False - log.info(result.trace["trace_id"]) - log.info(result.trace["cost"]) - log.info(result.trace["latency"]) - log.info(result.trace["tokens"]) + log.info("\n========= Result =========\n") - # log.info("") - # log.info(json.dumps(result.data, indent=2)) - # log.info(json.dumps(result.trace, indent=2)) + log.info(f"trace_id: {result.trace['trace_id']}") + if SHOW_DETAILS: + log.info(f"latency: {result.trace['latency']}") + log.info(f"cost: {result.trace['cost']}") + log.info(f"tokens: {list(result.trace['tokens'].values())}") + + if SHOW_DATA: + log.info(" ") + log.info(f"data:") + log.info(json.dumps(result.data, indent=2)) + + if SHOW_SPANS: + log.info(" ") + log.info(f"trace:") + log.info(f"----------------") + for span in result.trace["spans"]: + log.info( + f"{span['id']} {span['spankind'].ljust(10)} {span['status'].ljust(5)} {span['name']}" + ) + if SHOW_SPAN_ATTRIBUTES: + [log.info(f" {attribute}") for attribute in span["attributes"]] + log.info(" ") + log.info(f"----------------") - log.info("") - for span in result.trace["spans"]: - log.info( - f"{span['app_id']} {span['id']} {span['spankind'].ljust(10)} {span['status'].ljust(5)} {span['name']}" - ) log.info("\n==========================\n") def override_config_in_schema( diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 31418fab16..30ea7f8239 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -72,12 +72,9 @@ def patch(result): @wraps(func) async def async_wrapper(*args, **kwargs): async def wrapped_func(*args, **kwargs): - rctx = tracing_context.get() - print("..", func.__name__, rctx) with ag.tracing.start_as_current_span(func.__name__, self.kind): try: rctx = tracing_context.get() - print("...", func.__name__, rctx) ag.tracing.set_attributes( "metadata", {"config": rctx.get("config", {})} ) @@ -103,7 +100,6 @@ async def wrapped_func(*args, **kwargs): if isinstance(result, dict): cost = result.get("cost", 0.0) usage = result.get("usage", {}) - print("--------", cost, usage) ag.tracing.set_attributes( namespace="metrics.marginal.costs", @@ -141,12 +137,9 @@ async def wrapped_func(*args, **kwargs): @wraps(func) def sync_wrapper(*args, **kwargs): def wrapped_func(*args, **kwargs): - rctx = tracing_context.get() - print("..", func.__name__, rctx) with ag.tracing.start_as_current_span(func.__name__, self.kind): try: rctx = tracing_context.get() - print("...", func.__name__, rctx) ag.tracing.set_attributes( "metadata", {"config": rctx.get("config", {})} ) @@ -172,7 +165,6 @@ def wrapped_func(*args, **kwargs): if isinstance(result, dict): cost = result.get("cost", 0.0) usage = result.get("usage", {}) - print("--------", cost, usage) ag.tracing.set_attributes( namespace="metrics.marginal.costs", diff --git a/agenta-cli/agenta/sdk/tracing/conventions.py b/agenta-cli/agenta/sdk/tracing/conventions.py new file mode 100644 index 0000000000..16ced5d08c --- /dev/null +++ b/agenta-cli/agenta/sdk/tracing/conventions.py @@ -0,0 +1,24 @@ +from typing import Literal + +Namespace = Literal[ + "data.inputs", + "data.internals", + "data.outputs", + "metrics.scores", + "metrics.marginal.costs", + "metrics.marginal.tokens", + "metadata.config", + "metadata.version", + "tags", + "resource.project", + "resource.experiment", + "resource.application", + "resource.configuration", + "resource.service", + "extra", +] + +Status = Literal[ + "OK", + "ERROR", +] diff --git a/agenta-cli/agenta/sdk/tracing/exporters.py b/agenta-cli/agenta/sdk/tracing/exporters.py new file mode 100644 index 0000000000..fc04f0a8b2 --- /dev/null +++ b/agenta-cli/agenta/sdk/tracing/exporters.py @@ -0,0 +1,40 @@ +from typing import Sequence, Dict + +from opentelemetry.sdk.trace.export import ( + SpanExportResult, + SpanExporter, + ReadableSpan, +) + + +class InlineTraceExporter(SpanExporter): + + def __init__( + self, + registry: Dict[str, ReadableSpan], + ): + self._shutdown = False + self._registry = registry + + def export( + self, + spans: Sequence[ReadableSpan], + ) -> SpanExportResult: + if self._shutdown: + return + + for span in spans: + self._registry.update( + **{span.context.span_id.to_bytes(8, "big").hex(): span} + ) + + def shutdown( + self, + ) -> None: + self._shutdown = True + + def force_flush( + self, + timeout_millis: int = 30000, + ) -> bool: + return True diff --git a/agenta-cli/agenta/sdk/tracing/inline_trace.py b/agenta-cli/agenta/sdk/tracing/inline_trace.py new file mode 100644 index 0000000000..9e1127e4cc --- /dev/null +++ b/agenta-cli/agenta/sdk/tracing/inline_trace.py @@ -0,0 +1,120 @@ +from typing import Dict +from json import loads +from datetime import datetime + +from opentelemetry.sdk.trace.export import ReadableSpan + +from agenta.client.backend.types.create_span import CreateSpan +from agenta.client.backend.types.llm_tokens import LlmTokens +from agenta.sdk.utils.logging import log +from agenta.sdk.tracing.spans import get_attributes +from agenta.sdk.tracing.trace_tree import ( + make_spans_id_tree, + cumulate_costs, + cumulate_tokens, +) + + +def get_trace(spans: Dict[str, ReadableSpan], trace_id_only: bool = False): + traces_idx: Dict[str, Dict[str, ReadableSpan]] = dict() + + for span in spans.values(): + span: ReadableSpan + + trace_id = span.context.trace_id.to_bytes(16, "big").hex() + span_id = span.context.span_id.to_bytes(8, "big").hex() + + if trace_id not in traces_idx: + traces_idx[trace_id] = dict() + + if not trace_id_only: + traces_idx[trace_id][span_id] = _parse_to_legacy_span(span) + + spans.clear() # might need to be moved to a context variable + + if len(traces_idx) > 1: + log.error("Error while parsing inline trace: too many traces.") + trace_id = list(traces_idx.keys())[0] + + spans_idx = traces_idx[trace_id] + spans_id_tree = make_spans_id_tree(spans_idx) + + if len(spans_id_tree) > 1: + log.error("Error while parsing inline trace: too many root spans.") + root_span_id = list(spans_id_tree.keys())[0] + + cumulate_costs(spans_id_tree, spans_idx) + cumulate_tokens(spans_id_tree, spans_idx) + + inline_trace = { + "trace_id": trace_id, + "cost": spans_idx[root_span_id]["cost"], + "tokens": spans_idx[root_span_id]["tokens"], + "latency": datetime.fromisoformat( + spans_idx[root_span_id]["end_time"].replace("Z", "+00:00") + ).timestamp() + - datetime.fromisoformat( + spans_idx[root_span_id]["start_time"].replace("Z", "+00:00") + ).timestamp(), + "spans": list(spans_idx.values()), + } + + return inline_trace + + +def _parse_to_legacy_span(span: ReadableSpan): + attributes = dict(span.attributes) + + for event in span.events: + if event.name == "exception": + attributes.update(**event.attributes) + + legacy_span = CreateSpan( + id=span.context.span_id.to_bytes(8, "big").hex(), + spankind=get_attributes(span, "extra").get("kind", "UNKNOWN"), + name=span.name, + status=str(span.status.status_code.name), + # + start_time=datetime.fromtimestamp( + span.start_time / 1_000_000_000, + ).isoformat(), + end_time=datetime.fromtimestamp( + span.end_time / 1_000_000_000, + ).isoformat(), + # + parent_span_id=( + span.parent.span_id.to_bytes(8, "big").hex() if span.parent else None + ), + # + inputs=get_attributes(span, "data.inputs"), + internals=get_attributes(span, "data.internals"), + outputs=get_attributes(span, "data.outputs"), + # + config=get_attributes(span, "metadata").get("config", {}), + # + tokens=LlmTokens( + prompt_tokens=get_attributes(span, "metrics.marginal.tokens").get( + "prompt", 0 + ), + completion_tokens=get_attributes(span, "metrics.marginal.tokens").get( + "completion", 0 + ), + total_tokens=get_attributes(span, "metrics.marginal.tokens").get( + "total", 0 + ), + ), + cost=get_attributes(span, "metrics.marginal.costs").get("total", 0.0), + # + app_id=get_attributes(span, "extra").get("app_id", ""), + environment=get_attributes(span, "metadata").get("environment"), + # + attributes=attributes, + # + variant_id=None, + variant_name=None, + tags=None, + token_consumption=None, + user=None, + ) + + return loads(legacy_span.model_dump_json()) diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index 9e38faa8cf..223800f371 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -1,177 +1,33 @@ -import json import httpx -from threading import Lock -from datetime import datetime -from collections import OrderedDict -from typing import Optional, Dict, Any, List, Literal, Sequence -from contextlib import contextmanager, suppress -from importlib.metadata import version +from typing import Optional, Any, Dict +from contextlib import contextmanager + +from opentelemetry.trace import set_tracer_provider +from opentelemetry.trace.propagation import get_current_span +from opentelemetry.sdk.trace import Span +from opentelemetry.sdk.trace.export import ReadableSpan from agenta.sdk.utils.logging import log -from agenta.client.backend.types.create_span import CreateSpan, LlmTokens -from agenta.sdk.context.tracing import tracing_context -from opentelemetry.trace import set_tracer_provider -from opentelemetry.context import Context -from opentelemetry.sdk.resources import Resource -from opentelemetry.sdk.trace import ( - Span, - TracerProvider, - ConcurrentMultiSpanProcessor, - SynchronousMultiSpanProcessor, - Status as FullStatus, - StatusCode, -) -from opentelemetry.exporter.otlp.proto.http.trace_exporter import ( - OTLPSpanExporter, +from agenta.sdk.tracing.conventions import Namespace, Status +from agenta.sdk.tracing.tracers import ConcurrentTracerProvider +from agenta.sdk.tracing.spans import ( + set_status as otel_set_status, + add_event as otel_add_event, + record_exception as otel_record_exception, + set_attributes as otel_set_attributes, + get_attributes as otel_get_attributes, ) -from opentelemetry.trace.propagation import get_current_span -from opentelemetry.sdk.trace.export import ( - SpanExportResult, - SpanExporter, - ReadableSpan, - BatchSpanProcessor, - _DEFAULT_EXPORT_TIMEOUT_MILLIS, - _DEFAULT_MAX_QUEUE_SIZE, +from agenta.sdk.tracing.inline_trace import ( + get_trace as inline_get_trace, ) -_AGENTA_ROOT_SPAN_ID = "f" * 16 - _AGENTA_API_KEY_HEADER = "Authorization" -_AGENTA_EXPERIMENT_ID_HEADER = "experiment-id" - - log.setLevel("DEBUG") -class SingletonMeta(type): - """ - Thread-safe implementation of Singleton. - """ - - _instances = {} # type: ignore - - # We need the lock mechanism to synchronize threads \ - # during the initial access to the Singleton object. - _lock: Lock = Lock() - - def __call__(cls, *args, **kwargs): - """ - Ensures that changes to the `__init__` arguments do not affect the - returned instance. - - Uses a lock to make this method thread-safe. If an instance of the class - does not already exist, it creates one. Otherwise, it returns the - existing instance. - """ - - with cls._lock: - if cls not in cls._instances: - instance = super().__call__(*args, **kwargs) - cls._instances[cls] = instance - return cls._instances[cls] - - -class TraceProcessor(BatchSpanProcessor): - - def __init__( - self, - span_exporter: SpanExporter, - scope: Dict[str, Any] = None, - max_queue_size: int = None, - schedule_delay_millis: float = None, - max_export_batch_size: int = None, - export_timeout_millis: float = None, - ): - super().__init__( - span_exporter, - _DEFAULT_MAX_QUEUE_SIZE, - 60 * 60 * 1000, # 1 hour - _DEFAULT_MAX_QUEUE_SIZE, - _DEFAULT_EXPORT_TIMEOUT_MILLIS, - ) - - self.registry = dict() - self.scope = scope - - def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None: - super().on_start(span, parent_context=parent_context) - - span.set_attributes( - attributes={f"ag.extra.{k}": v for k, v in self.scope.items()} - ) - - log.info(f"> {span.context.span_id.to_bytes(8, 'big').hex()} {span.name}") - - if span.context.trace_id not in self.registry: - self.registry[span.context.trace_id] = dict() - - self.registry[span.context.trace_id][span.context.span_id] = True - - def on_end(self, span: ReadableSpan): - super().on_end(span) - - log.info(f" < {span.context.span_id.to_bytes(8, 'big').hex()} {span.name}") - - del self.registry[span.context.trace_id][span.context.span_id] - - if self.is_done(): - self.force_flush() - - def is_done(self): - return all( - not len(self.registry[trace_id]) for trace_id in self.registry.keys() - ) - - -class InlineTraceExporter(SpanExporter): - - def __init__(self, registry): - self._shutdown = False - self._registry = registry - - def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: - if self._shutdown: - return - - for span in spans: - self._registry.update( - **{span.context.span_id.to_bytes(8, "big").hex(): span} - ) - - def shutdown(self) -> None: - self._shutdown = True - - def force_flush(self, timeout_millis: int = 30000) -> bool: - return True - - -Namespace = Literal[ - "data.inputs", - "data.internals", - "data.outputs", - "metrics.scores", - "metrics.marginal.costs", - "metrics.marginal.tokens", - "metadata.config", - "metadata.version", - "tags", - "resource.project", - "resource.experiment", - "resource.application", - "resource.configuration", - "resource.service", - "extra", -] - -Status = Literal[ - "OK", - "ERROR", -] - - class Tracing: VERSION = "0.1.0" @@ -181,7 +37,6 @@ def __init__( url: str, app_id: Optional[str] = None, api_key: Optional[str] = None, - experiment_id: Optional[str] = None, ) -> None: # ENDPOINT @@ -190,63 +45,36 @@ def __init__( self.app_id = app_id # AUTHORIZATION self.api_key = api_key - # EXPERIMENT - self.experiment_id = experiment_id # HEADERS self.headers = {} if api_key: self.headers.update(**{_AGENTA_API_KEY_HEADER: self.api_key}) - if experiment_id: - self.headers.update(**{_AGENTA_EXPERIMENT_ID_HEADER: self.experiment_id}) # SPANS (INLINE TRACE) - self.spans: Dict[str:ReadableSpan] = dict() - - # SPAN PROCESSOR - # self.processor = SynchronousMultiSpanProcessor() - self.processor = ConcurrentMultiSpanProcessor(num_threads=2) - - base_shutdown = self.processor.shutdown - - def safe_shutdown(): - with suppress(Exception): - base_shutdown() - - self.processor.shutdown = safe_shutdown + self.spans: Dict[str, ReadableSpan] = dict() # TRACER PROVIDER - self.tracer_provider = TracerProvider( - active_span_processor=self.processor, - resource=Resource( - attributes={ - "service.name": "agenta", - "service.version": Tracing.VERSION, - } - ), - ) + self.tracer_provider = ConcurrentTracerProvider("agenta", Tracing.VERSION) # TRACE PROCESSORS - self.inline_processor = TraceProcessor( - InlineTraceExporter(registry=self.spans), + self.inline_processor = self.tracer_provider.add_inline_processor( + registry=self.spans, scope={"app_id": self.app_id}, ) - self.tracer_provider.add_span_processor(self.inline_processor) try: - import os - log.info(f"Connecting to the remote trace receiver at {self.url}...") httpx.get(self.url, headers=self.headers) log.info(f"Connection established.") - self.remote_processor = TraceProcessor( - OTLPSpanExporter(endpoint=self.url, headers=self.headers), + self.otlp_processor = self.tracer_provider.add_otlp_processor( + endpoint=self.url, + headers=self.headers, scope={"app_id": self.app_id}, ) - self.tracer_provider.add_span_processor(self.remote_processor) except: log.info(f"Connection failed.") log.error( @@ -262,16 +90,15 @@ def safe_shutdown(): @contextmanager def start_as_current_span(self, name: str, kind: str): with self.tracer.start_as_current_span(name) as span: - span: Span - self.set_attributes( namespace="extra", attributes={"kind": kind}, + span=span, ) yield span - def start_span(self, name: str, kind: str): + def start_span(self, name: str, kind: str) -> Span: span = self.tracer.start_span(name) self.set_attributes( @@ -287,19 +114,11 @@ def set_status( status: Status, message: Optional[str] = None, span: Optional[Span] = None, - ): + ) -> None: if span is None: span = get_current_span() - if status == "OK": - if span.status.status_code != StatusCode.ERROR: - span.set_status( - FullStatus(status_code=StatusCode.OK, description=message), - ) - elif status == "ERROR": - span.set_status( - FullStatus(status_code=StatusCode.ERROR, description=message), - ) + otel_set_status(span, status, message) def add_event( self, @@ -307,265 +126,64 @@ def add_event( attributes=None, timestamp=None, span: Optional[Span] = None, - ): + ) -> None: if span is None: span = get_current_span() - span.add_event( - name=name, - attributes=attributes, - timestamp=timestamp, - ) + otel_add_event(span, name, attributes, timestamp) def record_exception( - self, exception, attributes=None, timestamp=None, span: Optional[Span] = None - ): + self, + exception, + attributes=None, + timestamp=None, + span: Optional[Span] = None, + ) -> None: if span is None: span = get_current_span() - span.record_exception( - exception=exception, - attributes=attributes, - timestamp=timestamp, - escaped=None, - ) - - def _key(self, namespace, key=""): - return f"ag.{namespace}.{key}" - - def _value(self, value: Any) -> str: - if value is None: - return "null" - - if not isinstance(value, (str, int, float, bool, bytes)): - return json.dumps(value) - - return value + otel_record_exception(span, exception, attributes, timestamp) def set_attributes( self, namespace: Namespace, - attributes: Optional[Dict[str, Any]] = None, + attributes: Dict[str, Any], span: Optional[Span] = None, ) -> None: - if attributes is None: - return - if span is None: span = get_current_span() - for key, value in attributes.items(): - span.set_attribute( - self._key(namespace, key), - self._value(value), - ) + otel_set_attributes(span, namespace, attributes) - def _get_attributes( + def get_attributes( self, - namespace: str, - span: ReadableSpan, - ): - return { - k.replace(self._key(namespace), ""): v - for k, v in span.attributes.items() - if k.startswith(self._key(namespace)) - } - - def store_internals(self, attributes: dict, span: Optional[Span] = None) -> None: - self.set_attributes("data.internals", attributes, span) + namespace: Namespace, + span: Optional[ReadableSpan | Span] = None, + ) -> Dict[str, Any]: + if span is None: + span = get_current_span() - def is_processing(self) -> bool: - return not self.inline_processor.is_done() + return otel_get_attributes(span, namespace) - def get_inline_trace(self, trace_id_only: bool = False): - traces_idx: Dict[str, Dict[str, ReadableSpan]] = dict() - - for span in self.spans.values(): - span: ReadableSpan - - trace_id = span.context.trace_id.to_bytes(16, "big").hex() - span_id = span.context.span_id.to_bytes(8, "big").hex() - - if trace_id not in traces_idx: - traces_idx[trace_id] = dict() - - if not trace_id_only: - traces_idx[trace_id][span_id] = self._parse_to_legacy_span(span) - - if len(traces_idx) > 1: - log.error("Rrror while parsing inline trace: too many traces.") - - trace_id = list(traces_idx.keys())[0] - spans_idx = traces_idx[trace_id] - - spans_id_tree = self._make_spans_id_tree(spans_idx) - - if len(spans_id_tree) > 1: - log.error("Error while parsing inline trace: too many root spans.") - - root_span_id = list(spans_id_tree.keys())[0] - - self._cumulate_costs(spans_id_tree, spans_idx) - self._cumulate_tokens(spans_id_tree, spans_idx) - - inline_traces = [ - { - "trace_id": trace_id, - "cost": spans_idx[root_span_id]["cost"], - "tokens": spans_idx[root_span_id]["tokens"], - "latency": datetime.fromisoformat( - spans_idx[root_span_id]["end_time"].replace("Z", "+00:00") - ).timestamp() - - datetime.fromisoformat( - spans_idx[root_span_id]["start_time"].replace("Z", "+00:00") - ).timestamp(), - "spans": list(spans.values()), - } - for trace_id, spans in traces_idx.items() - ] - - self.spans.clear() - - if len(inline_traces) > 1: - log.error("Unexpected error while parsing inline trace: too many traces.") - - return inline_traces[0] - - def _parse_to_legacy_span(self, span: ReadableSpan): - - attributes = dict(span.attributes) - - for event in span.events: - if event.name == "exception": - attributes.update(**event.attributes) - - legacy_span = CreateSpan( - id=span.context.span_id.to_bytes(8, "big").hex(), - spankind=self._get_attributes("extra", span).get("kind", "UNKNOWN"), - name=span.name, - status=str(span.status.status_code.name), - # - start_time=datetime.fromtimestamp( - span.start_time / 1_000_000_000, - ).isoformat(), - end_time=datetime.fromtimestamp( - span.end_time / 1_000_000_000, - ).isoformat(), - # - parent_span_id=( - span.parent.span_id.to_bytes(8, "big").hex() if span.parent else None - ), - # - inputs=self._get_attributes("data.inputs", span), - internals=self._get_attributes("data.internals", span), - outputs=self._get_attributes("data.outputs", span), - # - config=json.loads( - self._get_attributes("metadata", span).get("config", "{}") - ), - # - tokens=LlmTokens( - prompt_tokens=self._get_attributes("metrics.marginal.tokens", span).get( - "prompt", 0 - ), - completion_tokens=self._get_attributes( - "metrics.marginal.tokens", span - ).get("completion", 0), - total_tokens=self._get_attributes("metrics.marginal.tokens", span).get( - "total", 0 - ), - ), - cost=self._get_attributes("metrics.marginal.costs", span).get("total", 0.0), - # - app_id=self._get_attributes("extra", span).get("app_id", ""), - environment=self._get_attributes("metadata", span).get("environment"), - # - variant_id=None, - variant_name=None, - tags=None, - token_consumption=None, + def store_internals( + self, + attributes: Dict[str, Any], + span: Optional[Span] = None, + ) -> None: + self.set_attributes( + namespace="data.internals", attributes=attributes, - user=None, + span=span, ) - return json.loads(legacy_span.model_dump_json()) - - def _make_spans_id_tree(self, spans): - tree = OrderedDict() - index = {} - - def push(span) -> None: - if span["parent_span_id"] is None: - tree[span["id"]] = OrderedDict() - index[span["id"]] = tree[span["id"]] - elif span["parent_span_id"] in index: - index[span["parent_span_id"]][span["id"]] = OrderedDict() - index[span["id"]] = index[span["parent_span_id"]][span["id"]] - else: - log.error("The parent span id should have been in the tracing tree.") - - for span in sorted(spans.values(), key=lambda span: span["start_time"]): - push(span) - - return tree - - def _visit_tree_dfs( - self, - spans_id_tree: OrderedDict, - spans_idx: Dict[str, CreateSpan], - get_metric, - accumulate_metric, - set_metric, - ): - for span_id, children_spans_id_tree in spans_id_tree.items(): - cumulated_metric = get_metric(spans_idx[span_id]) - - self._visit_tree_dfs( - children_spans_id_tree, - spans_idx, - get_metric, - accumulate_metric, - set_metric, - ) - - for child_span_id in children_spans_id_tree.keys(): - child_metric = get_metric(spans_idx[child_span_id]) - cumulated_metric = accumulate_metric(cumulated_metric, child_metric) - - set_metric(spans_idx[span_id], cumulated_metric) - - def _cumulate_costs( - self, - spans_id_tree: OrderedDict, - spans_idx: Dict[str, dict], - ): - def _get(span): - return span["cost"] - - def _acc(a, b): - return a + b - - def _set(span, cost): - span["cost"] = cost - - self._visit_tree_dfs(spans_id_tree, spans_idx, _get, _acc, _set) + def is_processing(self) -> bool: + return not self.inline_processor.is_done() - def _cumulate_tokens( + def get_inline_trace( self, - spans_id_tree: OrderedDict, - spans_idx: Dict[str, dict], - ): - def _get(span): - return span["tokens"] - - def _acc(a, b): - return { - "prompt_tokens": a["prompt_tokens"] + b["prompt_tokens"], - "completion_tokens": a["completion_tokens"] + b["completion_tokens"], - "total_tokens": a["total_tokens"] + b["total_tokens"], - } - - def _set(span, tokens): - span["tokens"] = tokens - - self._visit_tree_dfs(spans_id_tree, spans_idx, _get, _acc, _set) + trace_id_only: bool = False, + ) -> Dict[str, Any]: + if trace_id_only: + return {"trace_id": list(self.spans.keys())[0]} + else: + return inline_get_trace(self.spans) diff --git a/agenta-cli/agenta/sdk/tracing/processors.py b/agenta-cli/agenta/sdk/tracing/processors.py new file mode 100644 index 0000000000..d113e4cfb1 --- /dev/null +++ b/agenta-cli/agenta/sdk/tracing/processors.py @@ -0,0 +1,68 @@ +from typing import Optional, Any, Dict + +from opentelemetry.context import Context +from opentelemetry.sdk.trace import Span +from opentelemetry.sdk.trace.export import ( + SpanExporter, + ReadableSpan, + BatchSpanProcessor, + _DEFAULT_EXPORT_TIMEOUT_MILLIS, + _DEFAULT_MAX_QUEUE_SIZE, +) + + +class TraceProcessor(BatchSpanProcessor): + + def __init__( + self, + span_exporter: SpanExporter, + scope: Dict[str, Any] = None, + max_queue_size: int = None, + schedule_delay_millis: float = None, + max_export_batch_size: int = None, + export_timeout_millis: float = None, + ): + super().__init__( + span_exporter, + _DEFAULT_MAX_QUEUE_SIZE, + 60 * 60 * 1000, # 1 hour + _DEFAULT_MAX_QUEUE_SIZE, + _DEFAULT_EXPORT_TIMEOUT_MILLIS, + ) + + self.registry = dict() + self.scope = scope + + def on_start( + self, + span: Span, + parent_context: Optional[Context] = None, + ) -> None: + super().on_start(span, parent_context=parent_context) + + span.set_attributes( + attributes={f"ag.extra.{k}": v for k, v in self.scope.items()} + ) + + if span.context.trace_id not in self.registry: + self.registry[span.context.trace_id] = dict() + + self.registry[span.context.trace_id][span.context.span_id] = True + + def on_end( + self, + span: ReadableSpan, + ): + super().on_end(span) + + del self.registry[span.context.trace_id][span.context.span_id] + + if self.is_done(): + self.force_flush() + + def is_done( + self, + ): + return all( + not len(self.registry[trace_id]) for trace_id in self.registry.keys() + ) diff --git a/agenta-cli/agenta/sdk/tracing/spans.py b/agenta-cli/agenta/sdk/tracing/spans.py new file mode 100644 index 0000000000..73aed3a012 --- /dev/null +++ b/agenta-cli/agenta/sdk/tracing/spans.py @@ -0,0 +1,100 @@ +from json import loads, dumps +from typing import Optional, Any, Dict + +from opentelemetry.sdk.trace import Span, Status, StatusCode +from opentelemetry.sdk.trace.export import ReadableSpan + + +def set_status(span: Span, status: str, message: Optional[str] = None) -> None: + if status == "OK": + if span.status.status_code != StatusCode.ERROR: + span.set_status( + Status(status_code=StatusCode.OK, description=message), + ) + elif status == "ERROR": + span.set_status( + Status(status_code=StatusCode.ERROR, description=message), + ) + + +def add_event(span: Span, name, attributes=None, timestamp=None) -> None: + span.add_event( + name=name, + attributes=attributes, + timestamp=timestamp, + ) + + +def record_exception(span: Span, exception, attributes=None, timestamp=None) -> None: + span.record_exception( + exception=exception, + attributes=attributes, + timestamp=timestamp, + escaped=None, + ) + + +def set_attributes(span: Span, namespace: str, attributes: Dict[str, Any]) -> None: + for key, value in attributes.items(): + span.set_attribute( + _encode_key(namespace, key), + _encode_value(value), + ) + + +def get_attributes(span: ReadableSpan | Span, namespace: str): + return { + _decode_key(namespace, key): _decode_value(value) + for key, value in span.attributes.items() + if key != _decode_key(namespace, key) + } + + +def _encode_key(namespace, key: str) -> str: + return f"ag.{namespace}.{key}" + + +def _decode_key(namespace, key: str) -> str: + return key.replace(f"ag.{namespace}.", "") + + +def _encode_value(value: Any) -> Any: + return _encode(value) + + +def _decode_value(value: Any) -> Any: + return _decode(value) + + +def _encode(dec): + if dec is None: + enc = "@ag.type=none:" + return enc + + if isinstance(dec, (str, int, float, bool, bytes)): + return dec + + if isinstance(dec, dict) or isinstance(dec, list): + json_enc = dumps(dec) + enc = "@ag.type=json:" + json_enc + return enc + + return repr(dec) + + +def _decode(enc): + if isinstance(enc, (int, float, bool, bytes)): + return enc + + if isinstance(enc, str): + if enc == "@ag.type=none:": + return None + + if enc.startswith("@ag.type=json:"): + json_enc = enc[len("@ag.type=json:") :] + dec = loads(json_enc) + return dec + + return enc + + return enc diff --git a/agenta-cli/agenta/sdk/tracing/trace_tree.py b/agenta-cli/agenta/sdk/tracing/trace_tree.py new file mode 100644 index 0000000000..6238ff427e --- /dev/null +++ b/agenta-cli/agenta/sdk/tracing/trace_tree.py @@ -0,0 +1,88 @@ +from typing import Dict +from collections import OrderedDict + + +from agenta.sdk.utils.logging import log + + +def make_spans_id_tree( + spans: Dict[str, dict], +): + tree = OrderedDict() + index = {} + + def push(span) -> None: + if span["parent_span_id"] is None: + tree[span["id"]] = OrderedDict() + index[span["id"]] = tree[span["id"]] + elif span["parent_span_id"] in index: + index[span["parent_span_id"]][span["id"]] = OrderedDict() + index[span["id"]] = index[span["parent_span_id"]][span["id"]] + else: + log.error("The parent span id should have been in the tracing tree.") + + for span in sorted(spans.values(), key=lambda span: span["start_time"]): + push(span) + + return tree + + +def cumulate_costs( + spans_id_tree: OrderedDict, + spans_idx: Dict[str, dict], +): + def _get(span): + return span["cost"] + + def _acc(a, b): + return a + b + + def _set(span, cost): + span["cost"] = cost + + _visit_tree_dfs(spans_id_tree, spans_idx, _get, _acc, _set) + + +def cumulate_tokens( + spans_id_tree: OrderedDict, + spans_idx: Dict[str, dict], +): + def _get(span): + return span["tokens"] + + def _acc(a, b): + return { + "prompt_tokens": a["prompt_tokens"] + b["prompt_tokens"], + "completion_tokens": a["completion_tokens"] + b["completion_tokens"], + "total_tokens": a["total_tokens"] + b["total_tokens"], + } + + def _set(span, tokens): + span["tokens"] = tokens + + _visit_tree_dfs(spans_id_tree, spans_idx, _get, _acc, _set) + + +def _visit_tree_dfs( + spans_id_tree: OrderedDict, + spans_idx: Dict[str, dict], + get_metric, + accumulate_metric, + set_metric, +): + for span_id, children_spans_id_tree in spans_id_tree.items(): + cumulated_metric = get_metric(spans_idx[span_id]) + + _visit_tree_dfs( + children_spans_id_tree, + spans_idx, + get_metric, + accumulate_metric, + set_metric, + ) + + for child_span_id in children_spans_id_tree.keys(): + child_metric = get_metric(spans_idx[child_span_id]) + cumulated_metric = accumulate_metric(cumulated_metric, child_metric) + + set_metric(spans_idx[span_id], cumulated_metric) diff --git a/agenta-cli/agenta/sdk/tracing/tracers.py b/agenta-cli/agenta/sdk/tracing/tracers.py new file mode 100644 index 0000000000..49d3a89bc8 --- /dev/null +++ b/agenta-cli/agenta/sdk/tracing/tracers.py @@ -0,0 +1,61 @@ +from typing import Any, Dict +from contextlib import suppress + +from opentelemetry.sdk.resources import Resource +from opentelemetry.sdk.trace import TracerProvider, ConcurrentMultiSpanProcessor +from opentelemetry.sdk.trace.export import ReadableSpan +from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter + +from agenta.sdk.tracing.processors import TraceProcessor +from agenta.sdk.tracing.exporters import InlineTraceExporter + + +class ConcurrentTracerProvider(TracerProvider): + + def __init__( + self, + name: str, + version: str, + ) -> None: + self.name = name + self.version = version + + processor = ConcurrentMultiSpanProcessor(num_threads=2) + + base_shutdown = processor.shutdown + + def safe_shutdown(): + with suppress(Exception): + base_shutdown() + + processor.shutdown = safe_shutdown + + super().__init__( + active_span_processor=processor, + resource=Resource( + attributes={"service.name": name, "service.version": version} + ), + ) + + def add_inline_processor( + self, + registry: Dict[str, ReadableSpan], + scope: Dict[str, Any], + ) -> TraceProcessor: + processor = TraceProcessor(InlineTraceExporter(registry), scope) + + self.add_span_processor(processor) + + return processor + + def add_otlp_processor( + self, + endpoint: str, + headers: Dict[str, str], + scope: Dict[str, Any], + ): + processor = TraceProcessor(OTLPSpanExporter(endpoint, headers), scope) + + self.add_span_processor(self.processor) + + return processor diff --git a/agenta-cli/agenta/sdk/utils/singleton.py b/agenta-cli/agenta/sdk/utils/singleton.py new file mode 100644 index 0000000000..37c711c19b --- /dev/null +++ b/agenta-cli/agenta/sdk/utils/singleton.py @@ -0,0 +1,29 @@ +from threading import Lock + + +class SingletonMeta(type): + """ + Thread-safe implementation of Singleton. + """ + + _instances = {} # type: ignore + + # We need the lock mechanism to synchronize threads \ + # during the initial access to the Singleton object. + _lock: Lock = Lock() + + def __call__(cls, *args, **kwargs): + """ + Ensures that changes to the `__init__` arguments do not affect the + returned instance. + + Uses a lock to make this method thread-safe. If an instance of the class + does not already exist, it creates one. Otherwise, it returns the + existing instance. + """ + + with cls._lock: + if cls not in cls._instances: + instance = super().__call__(*args, **kwargs) + cls._instances[cls] = instance + return cls._instances[cls] From 2c3dc4c3024ccd31365b9e0a9bdd7f79a7ed7090 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 10 Sep 2024 16:09:59 +0200 Subject: [PATCH 021/305] clean up --- agenta-cli/agenta/sdk/tracing/spans.py | 50 +++++++++++--------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/spans.py b/agenta-cli/agenta/sdk/tracing/spans.py index 73aed3a012..9ab45a33ef 100644 --- a/agenta-cli/agenta/sdk/tracing/spans.py +++ b/agenta-cli/agenta/sdk/tracing/spans.py @@ -59,42 +59,34 @@ def _decode_key(namespace, key: str) -> str: def _encode_value(value: Any) -> Any: - return _encode(value) + if value is None: + value = "@ag.type=none:" + return value + if isinstance(value, (str, int, float, bool, bytes)): + return value -def _decode_value(value: Any) -> Any: - return _decode(value) - - -def _encode(dec): - if dec is None: - enc = "@ag.type=none:" - return enc + if isinstance(value, dict) or isinstance(value, list): + encoded = dumps(value) + value = "@ag.type=json:" + encoded + return value - if isinstance(dec, (str, int, float, bool, bytes)): - return dec + return repr(value) - if isinstance(dec, dict) or isinstance(dec, list): - json_enc = dumps(dec) - enc = "@ag.type=json:" + json_enc - return enc - return repr(dec) - - -def _decode(enc): - if isinstance(enc, (int, float, bool, bytes)): - return enc +def _decode_value(value: Any) -> Any: + if isinstance(value, (int, float, bool, bytes)): + return value - if isinstance(enc, str): - if enc == "@ag.type=none:": + if isinstance(value, str): + if value == "@ag.type=none:": return None - if enc.startswith("@ag.type=json:"): - json_enc = enc[len("@ag.type=json:") :] - dec = loads(json_enc) - return dec + if value.startswith("@ag.type=json:"): + encoded = value[len("@ag.type=json:") :] + value = loads(encoded) + return value - return enc + return value - return enc + return value From b3bbeb633f487894cae7c7b884574a5827446052 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 10 Sep 2024 16:29:06 +0200 Subject: [PATCH 022/305] Fix integration issues --- agenta-cli/agenta/sdk/tracing/opentelemetry.py | 2 +- agenta-cli/agenta/sdk/tracing/tracers.py | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index 223800f371..04699bca87 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -76,7 +76,7 @@ def __init__( scope={"app_id": self.app_id}, ) except: - log.info(f"Connection failed.") + log.warning(f"Connection failed.") log.error( f"Warning: Your traces will not be exported since {self.url} is unreachable." ) diff --git a/agenta-cli/agenta/sdk/tracing/tracers.py b/agenta-cli/agenta/sdk/tracing/tracers.py index 49d3a89bc8..e30f1d9da8 100644 --- a/agenta-cli/agenta/sdk/tracing/tracers.py +++ b/agenta-cli/agenta/sdk/tracing/tracers.py @@ -42,7 +42,10 @@ def add_inline_processor( registry: Dict[str, ReadableSpan], scope: Dict[str, Any], ) -> TraceProcessor: - processor = TraceProcessor(InlineTraceExporter(registry), scope) + processor = TraceProcessor( + InlineTraceExporter(registry=registry), + scope, + ) self.add_span_processor(processor) @@ -54,8 +57,11 @@ def add_otlp_processor( headers: Dict[str, str], scope: Dict[str, Any], ): - processor = TraceProcessor(OTLPSpanExporter(endpoint, headers), scope) + processor = TraceProcessor( + OTLPSpanExporter(endpoint=endpoint, headers=headers), + scope, + ) - self.add_span_processor(self.processor) + self.add_span_processor(processor) return processor From 076f04169706c89dde5b923f8208d5aedbe5c361 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 10 Sep 2024 16:38:07 +0200 Subject: [PATCH 023/305] apply black --- agenta-cli/agenta/client/backend/client.py | 36 +++++++++++------- .../agenta/client/backend/core/http_client.py | 38 +++++++++++-------- .../client/backend/core/serialization.py | 6 +-- .../client/backend/types/evaluator_config.py | 6 +-- 4 files changed, 51 insertions(+), 35 deletions(-) diff --git a/agenta-cli/agenta/client/backend/client.py b/agenta-cli/agenta/client/backend/client.py index dd07ef54f0..ddadce90ae 100644 --- a/agenta-cli/agenta/client/backend/client.py +++ b/agenta-cli/agenta/client/backend/client.py @@ -88,13 +88,17 @@ def __init__( self._client_wrapper = SyncClientWrapper( base_url=base_url, api_key=api_key, - httpx_client=httpx_client - if httpx_client is not None - else httpx.Client( - timeout=_defaulted_timeout, follow_redirects=follow_redirects - ) - if follow_redirects is not None - else httpx.Client(timeout=_defaulted_timeout), + httpx_client=( + httpx_client + if httpx_client is not None + else ( + httpx.Client( + timeout=_defaulted_timeout, follow_redirects=follow_redirects + ) + if follow_redirects is not None + else httpx.Client(timeout=_defaulted_timeout) + ) + ), timeout=_defaulted_timeout, ) self.observability = ObservabilityClient(client_wrapper=self._client_wrapper) @@ -1603,13 +1607,17 @@ def __init__( self._client_wrapper = AsyncClientWrapper( base_url=base_url, api_key=api_key, - httpx_client=httpx_client - if httpx_client is not None - else httpx.AsyncClient( - timeout=_defaulted_timeout, follow_redirects=follow_redirects - ) - if follow_redirects is not None - else httpx.AsyncClient(timeout=_defaulted_timeout), + httpx_client=( + httpx_client + if httpx_client is not None + else ( + httpx.AsyncClient( + timeout=_defaulted_timeout, follow_redirects=follow_redirects + ) + if follow_redirects is not None + else httpx.AsyncClient(timeout=_defaulted_timeout) + ) + ), timeout=_defaulted_timeout, ) self.observability = AsyncObservabilityClient( diff --git a/agenta-cli/agenta/client/backend/core/http_client.py b/agenta-cli/agenta/client/backend/core/http_client.py index e0155905b4..d91178e2d9 100644 --- a/agenta-cli/agenta/client/backend/core/http_client.py +++ b/agenta-cli/agenta/client/backend/core/http_client.py @@ -148,9 +148,9 @@ def get_request_body( json_body = maybe_filter_request_body(json, request_options, omit) # If you have an empty JSON body, you should just send None - return ( - json_body if json_body != {} else None - ), data_body if data_body != {} else None + return (json_body if json_body != {} else None), ( + data_body if data_body != {} else None + ) class HttpClient: @@ -246,9 +246,11 @@ def request( json=json_body, data=data_body, content=content, - files=convert_file_dict_to_httpx_tuples(remove_none_from_dict(files)) - if files is not None - else None, + files=( + convert_file_dict_to_httpx_tuples(remove_none_from_dict(files)) + if files is not None + else None + ), timeout=timeout, ) @@ -345,9 +347,11 @@ def stream( json=json_body, data=data_body, content=content, - files=convert_file_dict_to_httpx_tuples(remove_none_from_dict(files)) - if files is not None - else None, + files=( + convert_file_dict_to_httpx_tuples(remove_none_from_dict(files)) + if files is not None + else None + ), timeout=timeout, ) as stream: yield stream @@ -447,9 +451,11 @@ async def request( json=json_body, data=data_body, content=content, - files=convert_file_dict_to_httpx_tuples(remove_none_from_dict(files)) - if files is not None - else None, + files=( + convert_file_dict_to_httpx_tuples(remove_none_from_dict(files)) + if files is not None + else None + ), timeout=timeout, ) @@ -545,9 +551,11 @@ async def stream( json=json_body, data=data_body, content=content, - files=convert_file_dict_to_httpx_tuples(remove_none_from_dict(files)) - if files is not None - else None, + files=( + convert_file_dict_to_httpx_tuples(remove_none_from_dict(files)) + if files is not None + else None + ), timeout=timeout, ) as stream: yield stream diff --git a/agenta-cli/agenta/client/backend/core/serialization.py b/agenta-cli/agenta/client/backend/core/serialization.py index 9c8ce988b3..5400ca0bc3 100644 --- a/agenta-cli/agenta/client/backend/core/serialization.py +++ b/agenta-cli/agenta/client/backend/core/serialization.py @@ -130,9 +130,9 @@ def _convert_typeddict( if type_ is None: converted_object[key] = value else: - converted_object[ - _alias_key(key, type_) - ] = convert_and_respect_annotation_metadata(object_=value, annotation=type_) + converted_object[_alias_key(key, type_)] = ( + convert_and_respect_annotation_metadata(object_=value, annotation=type_) + ) return converted_object diff --git a/agenta-cli/agenta/client/backend/types/evaluator_config.py b/agenta-cli/agenta/client/backend/types/evaluator_config.py index b9aa6c37b8..f73d4ec1fd 100644 --- a/agenta-cli/agenta/client/backend/types/evaluator_config.py +++ b/agenta-cli/agenta/client/backend/types/evaluator_config.py @@ -10,9 +10,9 @@ class EvaluatorConfig(UniversalBaseModel): id: str name: str evaluator_key: str - settings_values: typing.Optional[ - typing.Dict[str, typing.Optional[typing.Any]] - ] = None + settings_values: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = ( + None + ) created_at: str updated_at: str From 79afaa4bb0bc4d2b1dc81f85f341095fa92a96c0 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 10 Sep 2024 16:40:18 +0200 Subject: [PATCH 024/305] apply black --- agenta-cli/agenta/client/backend/core/serialization.py | 6 +++--- agenta-cli/agenta/client/backend/types/evaluator_config.py | 6 +++--- agenta-cli/agenta/sdk/agenta_init.py | 1 - agenta-cli/agenta/sdk/decorators/tracing.py | 4 +++- agenta-cli/agenta/sdk/tracing/exporters.py | 1 - agenta-cli/agenta/sdk/tracing/opentelemetry.py | 1 - agenta-cli/agenta/sdk/tracing/processors.py | 1 - agenta-cli/agenta/sdk/tracing/tracers.py | 1 - 8 files changed, 9 insertions(+), 12 deletions(-) diff --git a/agenta-cli/agenta/client/backend/core/serialization.py b/agenta-cli/agenta/client/backend/core/serialization.py index 5400ca0bc3..9c8ce988b3 100644 --- a/agenta-cli/agenta/client/backend/core/serialization.py +++ b/agenta-cli/agenta/client/backend/core/serialization.py @@ -130,9 +130,9 @@ def _convert_typeddict( if type_ is None: converted_object[key] = value else: - converted_object[_alias_key(key, type_)] = ( - convert_and_respect_annotation_metadata(object_=value, annotation=type_) - ) + converted_object[ + _alias_key(key, type_) + ] = convert_and_respect_annotation_metadata(object_=value, annotation=type_) return converted_object diff --git a/agenta-cli/agenta/client/backend/types/evaluator_config.py b/agenta-cli/agenta/client/backend/types/evaluator_config.py index f73d4ec1fd..b9aa6c37b8 100644 --- a/agenta-cli/agenta/client/backend/types/evaluator_config.py +++ b/agenta-cli/agenta/client/backend/types/evaluator_config.py @@ -10,9 +10,9 @@ class EvaluatorConfig(UniversalBaseModel): id: str name: str evaluator_key: str - settings_values: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = ( - None - ) + settings_values: typing.Optional[ + typing.Dict[str, typing.Optional[typing.Any]] + ] = None created_at: str updated_at: str diff --git a/agenta-cli/agenta/sdk/agenta_init.py b/agenta-cli/agenta/sdk/agenta_init.py index 537070103f..4bc4b425ad 100644 --- a/agenta-cli/agenta/sdk/agenta_init.py +++ b/agenta-cli/agenta/sdk/agenta_init.py @@ -101,7 +101,6 @@ def init( class Config: - def __init__( self, host: str, diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 30ea7f8239..887efd07a9 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -50,7 +50,9 @@ def redact( not in ( ignore if isinstance(ignore, list) - else io.keys() if ignore is True else [] + else io.keys() + if ignore is True + else [] ) } diff --git a/agenta-cli/agenta/sdk/tracing/exporters.py b/agenta-cli/agenta/sdk/tracing/exporters.py index fc04f0a8b2..ca9fafd3f7 100644 --- a/agenta-cli/agenta/sdk/tracing/exporters.py +++ b/agenta-cli/agenta/sdk/tracing/exporters.py @@ -8,7 +8,6 @@ class InlineTraceExporter(SpanExporter): - def __init__( self, registry: Dict[str, ReadableSpan], diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index 04699bca87..5048d4aa13 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -38,7 +38,6 @@ def __init__( app_id: Optional[str] = None, api_key: Optional[str] = None, ) -> None: - # ENDPOINT self.url = url # "http://localhost:4318/v1/traces" # AUTHENTICATION diff --git a/agenta-cli/agenta/sdk/tracing/processors.py b/agenta-cli/agenta/sdk/tracing/processors.py index d113e4cfb1..2d0cce41ec 100644 --- a/agenta-cli/agenta/sdk/tracing/processors.py +++ b/agenta-cli/agenta/sdk/tracing/processors.py @@ -12,7 +12,6 @@ class TraceProcessor(BatchSpanProcessor): - def __init__( self, span_exporter: SpanExporter, diff --git a/agenta-cli/agenta/sdk/tracing/tracers.py b/agenta-cli/agenta/sdk/tracing/tracers.py index e30f1d9da8..ea9f3b836e 100644 --- a/agenta-cli/agenta/sdk/tracing/tracers.py +++ b/agenta-cli/agenta/sdk/tracing/tracers.py @@ -11,7 +11,6 @@ class ConcurrentTracerProvider(TracerProvider): - def __init__( self, name: str, From 6316562542494cf01c446d6a1dcc407d918d3a71 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 10 Sep 2024 16:42:00 +0200 Subject: [PATCH 025/305] fix typing --- agenta-cli/agenta/sdk/tracing/spans.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/spans.py b/agenta-cli/agenta/sdk/tracing/spans.py index 9ab45a33ef..421023b92f 100644 --- a/agenta-cli/agenta/sdk/tracing/spans.py +++ b/agenta-cli/agenta/sdk/tracing/spans.py @@ -1,5 +1,5 @@ from json import loads, dumps -from typing import Optional, Any, Dict +from typing import Optional, Union, Any, Dict from opentelemetry.sdk.trace import Span, Status, StatusCode from opentelemetry.sdk.trace.export import ReadableSpan @@ -42,7 +42,7 @@ def set_attributes(span: Span, namespace: str, attributes: Dict[str, Any]) -> No ) -def get_attributes(span: ReadableSpan | Span, namespace: str): +def get_attributes(span: Union[ReadableSpan, Span], namespace: str): return { _decode_key(namespace, key): _decode_value(value) for key, value in span.attributes.items() From 9685269773c721e7d305e706723c5d2ac1d40348 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 10 Sep 2024 16:44:21 +0200 Subject: [PATCH 026/305] fix typing --- agenta-cli/agenta/sdk/tracing/opentelemetry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index 5048d4aa13..0e9cb4a01a 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -1,6 +1,6 @@ import httpx -from typing import Optional, Any, Dict +from typing import Optional, Union, Any, Dict from contextlib import contextmanager from opentelemetry.trace import set_tracer_provider @@ -157,7 +157,7 @@ def set_attributes( def get_attributes( self, namespace: Namespace, - span: Optional[ReadableSpan | Span] = None, + span: Optional[Union[ReadableSpan, Span]] = None, ) -> Dict[str, Any]: if span is None: span = get_current_span() From b6b4bfc7c76b81c3122b6f14d935e096cfae673c Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 10 Sep 2024 16:46:19 +0200 Subject: [PATCH 027/305] fix typo --- agenta-cli/agenta/sdk/agenta_init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-cli/agenta/sdk/agenta_init.py b/agenta-cli/agenta/sdk/agenta_init.py index 4bc4b425ad..07997a537d 100644 --- a/agenta-cli/agenta/sdk/agenta_init.py +++ b/agenta-cli/agenta/sdk/agenta_init.py @@ -75,7 +75,7 @@ def init( self.app_id = app_id or config.get("app_id") or os.environ.get("AGENTA_APP_ID") if not self.app_id: raise ValueError( - "Project ID must be specified. You can provide it in one of the following ways:\n" + "App ID must be specified. You can provide it in one of the following ways:\n" "1. As an argument when calling ag.init(app_id='your_app_id').\n" "2. In the configuration file specified by config_fname.\n" "3. As an environment variable 'AGENTA_APP_ID'." From 5fcb5461f54fbf79195dde213467fa71928a3320 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 12 Sep 2024 15:54:36 +0200 Subject: [PATCH 028/305] fix use of deprecated ag.config.all() --- agenta-cli/agenta/sdk/decorators/routing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index 5ac129182c..42cc591c79 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -233,7 +233,7 @@ async def wrapper_deployed(*args, **kwargs) -> Any: ag.config.pull(config_name="default") with routing_context_manager( - config=ag.config.all(), + config=config_params, variant=kwargs["config"], environment=kwargs["environment"], ): From 738a1fbb66d43ab3da44df33ccee79440b3fe523 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 12 Sep 2024 16:03:29 +0200 Subject: [PATCH 029/305] Add typing and docstrings --- agenta-cli/agenta/sdk/decorators/routing.py | 21 +++++++++++++++++++- agenta-cli/agenta/sdk/decorators/tracing.py | 22 +++++++++++++++++---- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index 42cc591c79..c5297b405c 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -325,7 +325,26 @@ def ingest_files( if name in func_params and func_params[name] is not None: func_params[name] = self.ingest_file(func_params[name]) - def patch_result(self, result): + def patch_result(self, result: Any): + """ + Patch the result to only include the message if the result is a FuncResponse-style dictionary with message, cost, and usage keys. + + Example: + ```python + result = { + "message": "Hello, world!", + "cost": 0.5, + "usage": { + "prompt_tokens": 10, + "completion_tokens": 20, + "total_tokens": 30 + } + } + result = patch_result(result) + print(result) + # Output: "Hello, world!" + ``` + """ data = ( result["message"] if isinstance(result, dict) diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 887efd07a9..78676a99ca 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -43,6 +43,14 @@ def parse(*args, **kwargs) -> Dict[str, Any]: def redact( io: Dict[str, Any], ignore: List[str] | bool = False ) -> Dict[str, Any]: + """ + Redact user-defined sensitive information from inputs and outputs as defined by the ignore list or boolean flag. + + Example: + - ignore = ["password"] -> {"username": "admin", "password": "********"} -> {"username": "admin"} + - ignore = True -> {"username": "admin", "password": "********"} -> {} + - ignore = False -> {"username": "admin", "password": "********"} -> {"username": "admin", "password": "********"} + """ io = { key: value for key, value in io.items() @@ -50,15 +58,21 @@ def redact( not in ( ignore if isinstance(ignore, list) - else io.keys() - if ignore is True - else [] + else io.keys() if ignore is True else [] ) } return io - def patch(result): + def patch(result: Any) -> Dict[str, Any]: + """ + Patch the result to ensure that it is a dictionary, with a default key when necessary. + + Example: + - result = "Hello, World!" -> {"__default__": "Hello, World!"} + - result = {"message": "Hello, World!", "cost": 0.0, "usage": {}} -> {"__default__": "Hello, World!"} + - result = {"message": "Hello, World!"} -> {"message": "Hello, World!"} + """ outputs = ( {instrument.DEFAULT_KEY: result} if not isinstance(result, dict) From 2fe9f02c6a709993b4197c542ba40d42e1e63aaf Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 12 Sep 2024 16:17:26 +0200 Subject: [PATCH 030/305] Fix regression bug: generate_deployed now doesn't wait for inline trace, only returns trace_id --- agenta-cli/agenta/sdk/decorators/routing.py | 19 ++++++++------ agenta-cli/agenta/sdk/tracing/inline_trace.py | 25 +++++++++++++++++++ .../agenta/sdk/tracing/opentelemetry.py | 14 +++++------ 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index c5297b405c..c921488325 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -343,7 +343,7 @@ def patch_result(self, result: Any): result = patch_result(result) print(result) # Output: "Hello, world!" - ``` + ``` """ data = ( result["message"] @@ -392,16 +392,19 @@ async def execute_function( data = self.patch_result(result) - if WAIT_FOR_SPANS: - remaining_steps = NOFSTEPS + if inline_trace: + if WAIT_FOR_SPANS: + remaining_steps = NOFSTEPS - while ag.tracing.is_processing() and remaining_steps > 0: - await asyncio.sleep(TIMESTEP) - remaining_steps -= 1 + while ag.tracing.is_processing() and remaining_steps > 0: + await asyncio.sleep(TIMESTEP) + remaining_steps -= 1 - await asyncio.sleep(FINALSTEP) + await asyncio.sleep(FINALSTEP) - trace = ag.tracing.get_inline_trace(trace_id_only=(not inline_trace)) + trace = ag.tracing.get_inline_trace() + else: + trace = ag.tracing.get_trace_id_only() response = BaseResponse(data=data, trace=trace) diff --git a/agenta-cli/agenta/sdk/tracing/inline_trace.py b/agenta-cli/agenta/sdk/tracing/inline_trace.py index 9e1127e4cc..a99cd26f40 100644 --- a/agenta-cli/agenta/sdk/tracing/inline_trace.py +++ b/agenta-cli/agenta/sdk/tracing/inline_trace.py @@ -15,6 +15,31 @@ ) +def get_trace_id(spans: Dict[str, ReadableSpan]): + traces_idx: Dict[str, Dict[str, ReadableSpan]] = dict() + + for span in spans.values(): + span: ReadableSpan + + trace_id = span.context.trace_id.to_bytes(16, "big").hex() + span_id = span.context.span_id.to_bytes(8, "big").hex() + + if trace_id not in traces_idx: + traces_idx[trace_id] = list() + + traces_idx[trace_id].append(span_id) + + spans.clear() # might need to be moved to a context variable + + if len(traces_idx) > 1: + log.error("Error while parsing inline trace: too many traces.") + trace_id = list(traces_idx.keys())[0] + + return { + "trace_id": trace_id, + } + + def get_trace(spans: Dict[str, ReadableSpan], trace_id_only: bool = False): traces_idx: Dict[str, Dict[str, ReadableSpan]] = dict() diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index 0e9cb4a01a..dc82777c03 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -21,6 +21,7 @@ ) from agenta.sdk.tracing.inline_trace import ( get_trace as inline_get_trace, + get_trace_id as inline_get_trace_id, ) _AGENTA_API_KEY_HEADER = "Authorization" @@ -178,11 +179,8 @@ def store_internals( def is_processing(self) -> bool: return not self.inline_processor.is_done() - def get_inline_trace( - self, - trace_id_only: bool = False, - ) -> Dict[str, Any]: - if trace_id_only: - return {"trace_id": list(self.spans.keys())[0]} - else: - return inline_get_trace(self.spans) + def get_inline_trace(self) -> Dict[str, Any]: + return inline_get_trace(self.spans) + + def get_trace_id_only(self) -> Dict[str, Any]: + return inline_get_trace_id(self.spans) From f259683155563d9110e423c619c55cca2be0ca11 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 12 Sep 2024 16:30:53 +0200 Subject: [PATCH 031/305] Fix exception handling to suppress tracing errors --- agenta-cli/agenta/sdk/decorators/routing.py | 33 ++----- agenta-cli/agenta/sdk/decorators/tracing.py | 44 +++++----- .../agenta/sdk/tracing/opentelemetry.py | 88 +++++++++++++------ 3 files changed, 94 insertions(+), 71 deletions(-) diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index c921488325..ea72c81385 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -13,7 +13,7 @@ from tempfile import NamedTemporaryFile from typing import Any, Callable, Dict, Optional, Tuple, List from importlib.metadata import version - +from contextlib import suppress from fastapi.middleware.cors import CORSMiddleware from fastapi import Body, FastAPI, UploadFile, HTTPException @@ -383,6 +383,9 @@ async def execute_function( FINALSTEP = 0.001 NOFSTEPS = TIMEOUT / TIMESTEP + data = None + trace = {} + try: result = ( await func(*args, **func_params["params"]) @@ -391,7 +394,10 @@ async def execute_function( ) data = self.patch_result(result) + except Exception as e: + self.handle_exception(e) + with suppress(Exception): if inline_trace: if WAIT_FOR_SPANS: remaining_steps = NOFSTEPS @@ -406,30 +412,9 @@ async def execute_function( else: trace = ag.tracing.get_trace_id_only() - response = BaseResponse(data=data, trace=trace) - - return response - - except Exception as e: - if WAIT_FOR_SPANS: - remaining_steps = NOFSTEPS + response = BaseResponse(data=data, trace=trace) - while ag.tracing.is_processing() and remaining_steps > 0: - await asyncio.sleep(TIMESTEP) - remaining_steps -= 1 - - await asyncio.sleep(FINALSTEP) - - trace = ag.tracing.get_inline_trace(trace_id_only=(not inline_trace)) - - log.info("========= Error ==========") - log.info("") - - print("-> trace") - log.info(trace) - print("-> exception") - - self.handle_exception(e) + return response def handle_exception(self, e: Exception): status_code = e.status_code if hasattr(e, "status_code") else 500 diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 78676a99ca..72ebdef907 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -1,8 +1,8 @@ import inspect from functools import wraps from itertools import chain -from contextvars import ContextVar -from typing import Callable, Optional, Union, Any, Dict, List +from contextlib import suppress +from typing import Callable, Optional, Any, Dict, List import agenta as ag @@ -89,7 +89,7 @@ def patch(result: Any) -> Dict[str, Any]: async def async_wrapper(*args, **kwargs): async def wrapped_func(*args, **kwargs): with ag.tracing.start_as_current_span(func.__name__, self.kind): - try: + with suppress(Exception): rctx = tracing_context.get() ag.tracing.set_attributes( "metadata", {"config": rctx.get("config", {})} @@ -109,8 +109,16 @@ async def wrapped_func(*args, **kwargs): redact(parse(*args, **kwargs), self.ignore_inputs), ) + try: result = await func(*args, **kwargs) + except Exception as e: + ag.tracing.record_exception(e) + + ag.tracing.set_status("ERROR") + + raise e + with suppress(Exception): cost = 0.0 usage = {} if isinstance(result, dict): @@ -139,14 +147,7 @@ async def wrapped_func(*args, **kwargs): ag.tracing.set_status("OK") - return result - - except Exception as e: - ag.tracing.record_exception(e) - - ag.tracing.set_status("ERROR") - - raise e + return result return await wrapped_func(*args, **kwargs) @@ -154,7 +155,7 @@ async def wrapped_func(*args, **kwargs): def sync_wrapper(*args, **kwargs): def wrapped_func(*args, **kwargs): with ag.tracing.start_as_current_span(func.__name__, self.kind): - try: + with suppress(Exception): rctx = tracing_context.get() ag.tracing.set_attributes( "metadata", {"config": rctx.get("config", {})} @@ -174,8 +175,16 @@ def wrapped_func(*args, **kwargs): redact(parse(*args, **kwargs), self.ignore_inputs), ) + try: result = func(*args, **kwargs) + except Exception as e: + ag.tracing.record_exception(e) + + ag.tracing.set_status("ERROR") + + raise e + with suppress(Exception): cost = 0.0 usage = {} if isinstance(result, dict): @@ -196,20 +205,15 @@ def wrapped_func(*args, **kwargs): } ), ) + ag.tracing.set_attributes( "data.outputs", redact(patch(result), self.ignore_outputs), ) - ag.tracing.set_status("OK") - - return result - - except Exception as e: - ag.tracing.record_exception(e) - ag.tracing.set_status("ERROR") + ag.tracing.set_status("OK") - raise e + return result return wrapped_func(*args, **kwargs) diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py index dc82777c03..331405baeb 100644 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ b/agenta-cli/agenta/sdk/tracing/opentelemetry.py @@ -1,7 +1,7 @@ import httpx from typing import Optional, Union, Any, Dict -from contextlib import contextmanager +from contextlib import contextmanager, suppress from opentelemetry.trace import set_tracer_provider from opentelemetry.trace.propagation import get_current_span @@ -89,25 +89,31 @@ def __init__( @contextmanager def start_as_current_span(self, name: str, kind: str): - with self.tracer.start_as_current_span(name) as span: + try: + with self.tracer.start_as_current_span(name) as span: + self.set_attributes( + namespace="extra", + attributes={"kind": kind}, + span=span, + ) + + yield span + except Exception as e: + yield None + + def start_span(self, name: str, kind: str) -> Optional[Span]: + try: + span = self.tracer.start_span(name) + self.set_attributes( namespace="extra", attributes={"kind": kind}, span=span, ) - yield span - - def start_span(self, name: str, kind: str) -> Span: - span = self.tracer.start_span(name) - - self.set_attributes( - namespace="extra", - attributes={"kind": kind}, - span=span, - ) - - return span + return span + except Exception as e: + return None def set_status( self, @@ -118,7 +124,8 @@ def set_status( if span is None: span = get_current_span() - otel_set_status(span, status, message) + with suppress(Exception): + otel_set_status(span, status, message) def add_event( self, @@ -130,7 +137,8 @@ def add_event( if span is None: span = get_current_span() - otel_add_event(span, name, attributes, timestamp) + with suppress(Exception): + otel_add_event(span, name, attributes, timestamp) def record_exception( self, @@ -142,7 +150,8 @@ def record_exception( if span is None: span = get_current_span() - otel_record_exception(span, exception, attributes, timestamp) + with suppress(Exception): + otel_record_exception(span, exception, attributes, timestamp) def set_attributes( self, @@ -153,7 +162,8 @@ def set_attributes( if span is None: span = get_current_span() - otel_set_attributes(span, namespace, attributes) + with suppress(Exception): + otel_set_attributes(span, namespace, attributes) def get_attributes( self, @@ -163,24 +173,48 @@ def get_attributes( if span is None: span = get_current_span() - return otel_get_attributes(span, namespace) + attributes = {} + + with suppress(Exception): + attributes = otel_get_attributes(span, namespace) + + return attributes def store_internals( self, attributes: Dict[str, Any], span: Optional[Span] = None, ) -> None: - self.set_attributes( - namespace="data.internals", - attributes=attributes, - span=span, - ) + if span is None: + span = get_current_span() + + with suppress(Exception): + self.set_attributes( + namespace="data.internals", + attributes=attributes, + span=span, + ) def is_processing(self) -> bool: - return not self.inline_processor.is_done() + processing = False + + with suppress(Exception): + processing = self.inline_processor.is_done() + + return processing def get_inline_trace(self) -> Dict[str, Any]: - return inline_get_trace(self.spans) + trace = {} + + with suppress(Exception): + trace = inline_get_trace(self.spans) + + return trace def get_trace_id_only(self) -> Dict[str, Any]: - return inline_get_trace_id(self.spans) + trace = {} + + with suppress(Exception): + trace = inline_get_trace_id(self.spans) + + return trace From 74f9f9f9114b3ce2f736bfc3c8a93d57dae483a3 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 12 Sep 2024 16:40:06 +0200 Subject: [PATCH 032/305] Remove superfluous requirements.txt file --- agenta-cli/requirements.txt | 183 ------------------------------------ 1 file changed, 183 deletions(-) delete mode 100644 agenta-cli/requirements.txt diff --git a/agenta-cli/requirements.txt b/agenta-cli/requirements.txt deleted file mode 100644 index 6f11f4d0a0..0000000000 --- a/agenta-cli/requirements.txt +++ /dev/null @@ -1,183 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.12 -# by the following command: -# -# pip-compile --output-file=requirements.txt --strip-extras pyproject.toml -# -annotated-types==0.7.0 - # via pydantic -anyio==4.4.0 - # via - # httpx - # starlette -asttokens==2.4.1 - # via stack-data -backoff==2.2.1 - # via posthog -cachetools==5.5.0 - # via agenta (pyproject.toml) -certifi==2024.8.30 - # via - # httpcore - # httpx - # requests -charset-normalizer==3.3.2 - # via requests -click==8.1.7 - # via agenta (pyproject.toml) -decorator==5.1.1 - # via - # ipdb - # ipython -deprecated==1.2.14 - # via - # opentelemetry-api - # opentelemetry-exporter-otlp-proto-grpc - # opentelemetry-exporter-otlp-proto-http - # opentelemetry-semantic-conventions -dnspython==2.6.1 - # via pymongo -docker==7.1.0 - # via agenta (pyproject.toml) -executing==2.1.0 - # via stack-data -fastapi==0.112.3 - # via agenta (pyproject.toml) -googleapis-common-protos==1.65.0 - # via - # opentelemetry-exporter-otlp-proto-grpc - # opentelemetry-exporter-otlp-proto-http -grpcio==1.66.1 - # via opentelemetry-exporter-otlp-proto-grpc -h11==0.14.0 - # via httpcore -httpcore==1.0.5 - # via httpx -httpx==0.27.2 - # via agenta (pyproject.toml) -idna==3.8 - # via - # anyio - # httpx - # requests -importlib-metadata==8.4.0 - # via - # agenta (pyproject.toml) - # opentelemetry-api -ipdb==0.13.13 - # via agenta (pyproject.toml) -ipython==8.18.0 - # via ipdb -jedi==0.19.1 - # via ipython -matplotlib-inline==0.1.7 - # via ipython -monotonic==1.6 - # via posthog -opentelemetry-api==1.27.0 - # via - # agenta (pyproject.toml) - # opentelemetry-exporter-otlp-proto-grpc - # opentelemetry-exporter-otlp-proto-http - # opentelemetry-sdk - # opentelemetry-semantic-conventions -opentelemetry-exporter-otlp==1.27.0 - # via agenta (pyproject.toml) -opentelemetry-exporter-otlp-proto-common==1.27.0 - # via - # opentelemetry-exporter-otlp-proto-grpc - # opentelemetry-exporter-otlp-proto-http -opentelemetry-exporter-otlp-proto-grpc==1.27.0 - # via opentelemetry-exporter-otlp -opentelemetry-exporter-otlp-proto-http==1.27.0 - # via opentelemetry-exporter-otlp -opentelemetry-proto==1.27.0 - # via - # opentelemetry-exporter-otlp-proto-common - # opentelemetry-exporter-otlp-proto-grpc - # opentelemetry-exporter-otlp-proto-http -opentelemetry-sdk==1.27.0 - # via - # agenta (pyproject.toml) - # opentelemetry-exporter-otlp-proto-grpc - # opentelemetry-exporter-otlp-proto-http -opentelemetry-semantic-conventions==0.48b0 - # via opentelemetry-sdk -parso==0.8.4 - # via jedi -pexpect==4.9.0 - # via ipython -posthog==3.6.3 - # via agenta (pyproject.toml) -prompt-toolkit==3.0.36 - # via - # ipython - # questionary -protobuf==4.25.4 - # via - # googleapis-common-protos - # opentelemetry-proto -ptyprocess==0.7.0 - # via pexpect -pure-eval==0.2.3 - # via stack-data -pydantic==2.8.2 - # via - # agenta (pyproject.toml) - # fastapi -pydantic-core==2.20.1 - # via pydantic -pygments==2.18.0 - # via ipython -pymongo==4.8.0 - # via agenta (pyproject.toml) -python-dateutil==2.9.0.post0 - # via posthog -python-dotenv==1.0.1 - # via agenta (pyproject.toml) -python-multipart==0.0.9 - # via agenta (pyproject.toml) -pyyaml==6.0.2 - # via agenta (pyproject.toml) -questionary==2.0.1 - # via agenta (pyproject.toml) -requests==2.32.3 - # via - # docker - # opentelemetry-exporter-otlp-proto-http - # posthog -six==1.16.0 - # via - # asttokens - # posthog - # python-dateutil -sniffio==1.3.1 - # via - # anyio - # httpx -stack-data==0.6.3 - # via ipython -starlette==0.38.4 - # via fastapi -toml==0.10.2 - # via agenta (pyproject.toml) -traitlets==5.14.3 - # via - # ipython - # matplotlib-inline -typing-extensions==4.12.2 - # via - # fastapi - # opentelemetry-sdk - # pydantic - # pydantic-core -urllib3==2.2.2 - # via - # docker - # requests -wcwidth==0.2.13 - # via prompt-toolkit -wrapt==1.16.0 - # via deprecated -zipp==3.20.1 - # via importlib-metadata From c72a03b7f903061a048b25c01a7b5719747b023a Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 12 Sep 2024 16:58:39 +0200 Subject: [PATCH 033/305] Add (de-)serialization of attributes in all otel functions --- agenta-cli/agenta/sdk/tracing/spans.py | 58 ++++++++++++++++++++------ 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/spans.py b/agenta-cli/agenta/sdk/tracing/spans.py index 421023b92f..244e852ce4 100644 --- a/agenta-cli/agenta/sdk/tracing/spans.py +++ b/agenta-cli/agenta/sdk/tracing/spans.py @@ -20,7 +20,7 @@ def set_status(span: Span, status: str, message: Optional[str] = None) -> None: def add_event(span: Span, name, attributes=None, timestamp=None) -> None: span.add_event( name=name, - attributes=attributes, + attributes=_serialize_attributes(None, attributes), timestamp=timestamp, ) @@ -28,33 +28,67 @@ def add_event(span: Span, name, attributes=None, timestamp=None) -> None: def record_exception(span: Span, exception, attributes=None, timestamp=None) -> None: span.record_exception( exception=exception, - attributes=attributes, + attributes=_serialize_attributes(None, attributes), timestamp=timestamp, escaped=None, ) -def set_attributes(span: Span, namespace: str, attributes: Dict[str, Any]) -> None: - for key, value in attributes.items(): - span.set_attribute( - _encode_key(namespace, key), - _encode_value(value), - ) +def set_attributes( + span: Span, namespace: Optional[str], attributes: Dict[str, Any] +) -> None: + if isinstance(attributes, dict): + span.set_attributes(_serialize_attributes(namespace, attributes).items()) def get_attributes(span: Union[ReadableSpan, Span], namespace: str): + return _deserialize_attributes( + namespace, + { + key: value + for key, value in span.attributes.items() + if key != _decode_key(namespace, key) + }, + ) + + +def _serialize_attributes( + namespace: str, + attributes: Dict[str, Any], +) -> Dict[str, str]: + if not isinstance(attributes, dict): + return {} + + return { + _encode_key(namespace, key): _encode_value(value) + for key, value in attributes.items() + } + + +def _deserialize_attributes( + namespace: str, + attributes: Dict[str, Any], +) -> Dict[str, Any]: + if not isinstance(attributes, dict): + return {} + return { _decode_key(namespace, key): _decode_value(value) - for key, value in span.attributes.items() - if key != _decode_key(namespace, key) + for key, value in attributes.items() } -def _encode_key(namespace, key: str) -> str: +def _encode_key(namespace: Optional[str] = None, key: str = "") -> str: + if namespace is None: + return key + return f"ag.{namespace}.{key}" -def _decode_key(namespace, key: str) -> str: +def _decode_key(namespace: Optional[str] = None, key: str = "") -> str: + if namespace is None: + return key + return key.replace(f"ag.{namespace}.", "") From 8389dcfab091273a367f91339723a52edefdb89e Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sat, 28 Sep 2024 19:44:16 +0200 Subject: [PATCH 034/305] fix(frontend): removed submenu from observability link --- agenta-web/src/components/Sidebar/config.tsx | 24 +------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/agenta-web/src/components/Sidebar/config.tsx b/agenta-web/src/components/Sidebar/config.tsx index bdf9a25a6e..a4729d907d 100644 --- a/agenta-web/src/components/Sidebar/config.tsx +++ b/agenta-web/src/components/Sidebar/config.tsx @@ -105,31 +105,9 @@ export const useSidebarConfig = () => { icon: , isHidden: !appId && !recentlyVisitedAppId, isCloudFeature: true && isOss, + link: `/apps/${appId || recentlyVisitedAppId}/observability`, cloudFeatureTooltip: "Observability available in Cloud/Enterprise editions only", tag: "beta", - submenu: [ - { - key: "app-observability-dashboard-link", - title: "Dashboard", - tooltip: "Dashboard view of traces and generations", - link: `/apps/${appId || recentlyVisitedAppId}/observability`, - icon: , - }, - { - key: "app-observability-traces-link", - title: "Traces", - tooltip: "Traces and their details", - link: `/apps/${appId || recentlyVisitedAppId}/observability/traces`, - icon: , - }, - { - key: "app-observability-generations-link", - title: "Generations", - tooltip: "Generations and their details", - link: `/apps/${appId || recentlyVisitedAppId}/observability/generations`, - icon: , - }, - ], }, { key: "invite-teammate-link", From 85cdc1bb50b41f29686ea240635d7641db930e52 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sun, 29 Sep 2024 23:19:12 +0200 Subject: [PATCH 035/305] fix(frontend): removed beta tag from observability menu item link --- agenta-web/src/components/Sidebar/config.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/agenta-web/src/components/Sidebar/config.tsx b/agenta-web/src/components/Sidebar/config.tsx index a4729d907d..e842f9ffe1 100644 --- a/agenta-web/src/components/Sidebar/config.tsx +++ b/agenta-web/src/components/Sidebar/config.tsx @@ -107,7 +107,6 @@ export const useSidebarConfig = () => { isCloudFeature: true && isOss, link: `/apps/${appId || recentlyVisitedAppId}/observability`, cloudFeatureTooltip: "Observability available in Cloud/Enterprise editions only", - tag: "beta", }, { key: "invite-teammate-link", From 2cbb5d065459c93e5ee0e61fff6f8f29d9c2ae8d Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Tue, 1 Oct 2024 11:31:52 +0200 Subject: [PATCH 036/305] fix(frontend): added custom tag component "ResultTag" --- .../src/components/ResultTag/ResultTag.tsx | 42 +++++++++++++++++++ .../pages/overview/variants/VariantDrawer.tsx | 31 +++----------- 2 files changed, 47 insertions(+), 26 deletions(-) create mode 100644 agenta-web/src/components/ResultTag/ResultTag.tsx diff --git a/agenta-web/src/components/ResultTag/ResultTag.tsx b/agenta-web/src/components/ResultTag/ResultTag.tsx new file mode 100644 index 0000000000..934224d57e --- /dev/null +++ b/agenta-web/src/components/ResultTag/ResultTag.tsx @@ -0,0 +1,42 @@ +import {JSSTheme} from "@/lib/Types" +import React from "react" +import {createUseStyles} from "react-jss" + +interface ResultTagProps { + title: string + value: any +} + +const useStyles = createUseStyles((theme: JSSTheme) => ({ + resultTag: { + display: "flex", + borderRadius: theme.borderRadiusSM, + border: `1px solid ${theme.colorBorder}`, + width: "fit-content", + textAlign: "center", + "& > div:nth-child(1)": { + backgroundColor: "rgba(0, 0, 0, 0.02)", + lineHeight: theme.lineHeight, + flex: 1, + minWidth: 50, + borderRight: `1px solid ${theme.colorBorder}`, + padding: "0 7px", + }, + "& > div:nth-child(2)": { + padding: "0 7px", + }, + }, +})) + +const ResultTag = ({title, value}: ResultTagProps) => { + const classes = useStyles() + + return ( +
+
{title}
+
{JSON.stringify(value)}
+
+ ) +} + +export default ResultTag diff --git a/agenta-web/src/components/pages/overview/variants/VariantDrawer.tsx b/agenta-web/src/components/pages/overview/variants/VariantDrawer.tsx index 45beb1e158..7e8ab8c9e4 100644 --- a/agenta-web/src/components/pages/overview/variants/VariantDrawer.tsx +++ b/agenta-web/src/components/pages/overview/variants/VariantDrawer.tsx @@ -1,3 +1,4 @@ +import ResultTag from "@/components/ResultTag/ResultTag" import {useAppId} from "@/hooks/useAppId" import {filterVariantParameters, formatVariantIdWithHash} from "@/lib/helpers/utils" import {variantNameWithRev} from "@/lib/helpers/variantHelper" @@ -34,24 +35,6 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ lineHeight: theme.lineHeight, fontWeight: theme.fontWeightMedium, }, - resultTag: { - minWidth: 150, - display: "flex", - borderRadius: theme.borderRadiusSM, - border: `1px solid ${theme.colorBorder}`, - textAlign: "center", - "& > div:nth-child(1)": { - backgroundColor: "rgba(0, 0, 0, 0.02)", - lineHeight: theme.lineHeight, - flex: 1, - minWidth: 50, - borderRight: `1px solid ${theme.colorBorder}`, - padding: "0 7px", - }, - "& > div:nth-child(2)": { - padding: "0 7px", - }, - }, promptTextField: { padding: theme.paddingXS, backgroundColor: theme.colorBgContainerDisabled, @@ -187,15 +170,11 @@ const VariantDrawer = ({ include: false, }), ).map(([key, value], index) => ( -
-
{key}
-
- {JSON.stringify(value)} -
-
+ title={key} + value={value} + /> ))} From 14f3ae325e14619c016ce8c4f51ca137c2a75a84 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Wed, 2 Oct 2024 17:53:39 +0200 Subject: [PATCH 037/305] fix(frontend): improve ResultTag component --- .../src/components/ResultTag/ResultTag.tsx | 73 +++++++++++++------ .../pages/overview/variants/VariantDrawer.tsx | 4 +- 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/agenta-web/src/components/ResultTag/ResultTag.tsx b/agenta-web/src/components/ResultTag/ResultTag.tsx index 934224d57e..35ab111f7b 100644 --- a/agenta-web/src/components/ResultTag/ResultTag.tsx +++ b/agenta-web/src/components/ResultTag/ResultTag.tsx @@ -1,42 +1,73 @@ import {JSSTheme} from "@/lib/Types" -import React from "react" +import {Popover, Tag} from "antd" +import React, {memo} from "react" import {createUseStyles} from "react-jss" -interface ResultTagProps { - title: string - value: any -} +type ResultTagProps = { + popoverContent?: React.ReactNode + value1: string | React.ReactNode + value2?: React.ReactNode +} & React.ComponentProps const useStyles = createUseStyles((theme: JSSTheme) => ({ resultTag: { display: "flex", - borderRadius: theme.borderRadiusSM, - border: `1px solid ${theme.colorBorder}`, + alignItems: "center", width: "fit-content", - textAlign: "center", - "& > div:nth-child(1)": { + padding: 0, + cursor: "pointer", + "& > span.value1": { backgroundColor: "rgba(0, 0, 0, 0.02)", - lineHeight: theme.lineHeight, flex: 1, - minWidth: 50, + padding: "0px 8px", borderRight: `1px solid ${theme.colorBorder}`, - padding: "0 7px", }, - "& > div:nth-child(2)": { - padding: "0 7px", + "& > span.value2": { + background: theme.colorBgContainer, + padding: "0px 8px 0px 4px", + borderRadius: "inherit", + }, + "& > div.singleValue": { + padding: "0px 8px", + display: "flex", + alignItems: "center", + gap: 8, }, }, })) -const ResultTag = ({title, value}: ResultTagProps) => { +const ResultTag = memo(({value1, value2, popoverContent, ...props}: ResultTagProps) => { const classes = useStyles() - return ( -
-
{title}
-
{JSON.stringify(value)}
-
+ const content = + value2 !== undefined ? ( + <> + {value1} + {value2} + + ) : ( +
{value1}
+ ) + + const tag = ( + + {content} + + ) + + return popoverContent ? ( + + {tag} + + ) : ( + tag ) -} +}) export default ResultTag diff --git a/agenta-web/src/components/pages/overview/variants/VariantDrawer.tsx b/agenta-web/src/components/pages/overview/variants/VariantDrawer.tsx index 7e8ab8c9e4..87fc78fb80 100644 --- a/agenta-web/src/components/pages/overview/variants/VariantDrawer.tsx +++ b/agenta-web/src/components/pages/overview/variants/VariantDrawer.tsx @@ -172,8 +172,8 @@ const VariantDrawer = ({ ).map(([key, value], index) => ( ))} From 9e1765213bd37a3f9fade4a5bb903dd01ddccae9 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Fri, 4 Oct 2024 13:30:10 +0200 Subject: [PATCH 038/305] fix(frontend): improve Result Tag component --- agenta-web/src/components/ResultTag/ResultTag.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agenta-web/src/components/ResultTag/ResultTag.tsx b/agenta-web/src/components/ResultTag/ResultTag.tsx index 35ab111f7b..a64e3de80f 100644 --- a/agenta-web/src/components/ResultTag/ResultTag.tsx +++ b/agenta-web/src/components/ResultTag/ResultTag.tsx @@ -1,5 +1,5 @@ import {JSSTheme} from "@/lib/Types" -import {Popover, Tag} from "antd" +import {Popover, Space, Tag} from "antd" import React, {memo} from "react" import {createUseStyles} from "react-jss" @@ -61,7 +61,7 @@ const ResultTag = memo(({value1, value2, popoverContent, ...props}: ResultTagPro trigger="click" overlayStyle={{width: 256}} arrow={false} - title={popoverContent} + title={{popoverContent}} > {tag} From 9a4dda91c80892bd4d4a3febc651a7ec06eaafb2 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Fri, 4 Oct 2024 13:30:50 +0200 Subject: [PATCH 039/305] feat(frontend): added observability view and drawer component --- .../observability/drawer/TraceContent.tsx | 279 +++++++++++++++++ .../pages/observability/drawer/TraceTree.tsx | 290 ++++++++++++++++++ .../pages/observability/drawer/index.tsx | 94 ++++++ .../apps/[app_id]/observability/index.tsx | 36 +++ 4 files changed, 699 insertions(+) create mode 100644 agenta-web/src/components/pages/observability/drawer/TraceContent.tsx create mode 100644 agenta-web/src/components/pages/observability/drawer/TraceTree.tsx create mode 100644 agenta-web/src/components/pages/observability/drawer/index.tsx create mode 100644 agenta-web/src/pages/apps/[app_id]/observability/index.tsx diff --git a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx new file mode 100644 index 0000000000..49128920a2 --- /dev/null +++ b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx @@ -0,0 +1,279 @@ +import CopyButton from "@/components/CopyButton/CopyButton" +import ResultTag from "@/components/ResultTag/ResultTag" +import {JSSTheme} from "@/lib/Types" +import {ArrowRight, Database, PlusCircle, Rocket, Sparkle, Timer} from "@phosphor-icons/react" +import {Button, Collapse, CollapseProps, Divider, Space, Tabs, TabsProps, Typography} from "antd" +import React, {useState} from "react" +import {createUseStyles} from "react-jss" +import {IBM_Plex_Mono} from "next/font/google" + +const ibm_plex_mono = IBM_Plex_Mono({ + subsets: ["latin"], + weight: ["400", "500", "600"], +}) + +interface TraceContentProps {} + +const useStyles = createUseStyles((theme: JSSTheme) => ({ + container: { + flex: 1, + border: `1px solid ${theme.colorBorder}`, + borderRadius: theme.borderRadius, + display: "flex", + height: "100%", + "& .ant-tag": { + margin: 0, + display: "flex", + alignItems: "center", + gap: 8, + }, + }, + title: { + fontSize: theme.fontSizeHeading5, + fontWeight: theme.fontWeightMedium, + lineHeight: theme.lineHeightHeading5, + }, + tabs: { + height: "100%", + display: "flex", + flexDirection: "column", + "& .ant-tabs-nav": { + marginBottom: 8, + "& .ant-tabs-nav-wrap": { + padding: "0 16px", + }, + }, + "& .ant-tabs-content-holder": { + padding: theme.padding, + flex: 1, + overflowY: "auto", + }, + }, + collapseContainer: { + backgroundColor: "unset", + "& .ant-collapse-item": { + marginBottom: 24, + background: theme.colorFillAlter, + borderRadius: `${theme.borderRadiusLG}px !important`, + border: `1px solid ${theme.colorBorder}`, + borderBottom: "in", + }, + "& .ant-collapse-item:last-child": { + borderBottom: `1px solid ${theme.colorBorder}`, + }, + "& .ant-collapse-header": { + alignItems: "center !important", + }, + "& .ant-collapse-content": { + borderTop: `1px solid ${theme.colorBorder} !important`, + padding: theme.padding, + lineHeight: theme.lineHeight, + backgroundColor: `${theme.colorBgContainer} !important`, + borderBottomLeftRadius: theme.borderRadius, + borderBottomRightRadius: theme.borderRadius, + fontSize: theme.fontSize, + "& .ant-collapse-content-box": { + padding: "0px !important", + }, + }, + }, +})) + +const TraceContent = ({}: TraceContentProps) => { + const classes = useStyles() + const [tab, setTab] = useState("overview") + + const accordionItems: CollapseProps["items"] = [ + { + key: "1", + label: "System", + children: ( +
+ You are an expert Q&A system that is trusted around the world. Always answer the + query using the provided context information, and not prior knowledge. Some + rules to follow: 1. Never directly reference the given context in your answer. + 2. Avoid statements like 'Based on the context, ...' or 'The context information + ...' or anything along those lines. +
+ ), + extra: , + }, + { + key: "2", + label: "User", + children: ( +
+ You are an expert Q&A system that is trusted around the world. Always answer the + query using the provided context information, and not prior knowledge. Some + rules to follow: 1. Never directly reference the given context in your answer. + 2. Avoid statements like 'Based on the context, ...' or 'The context information + ...' or anything along those lines. +
+ ), + extra: , + }, + { + key: "3", + label: "Assistant / Output", + children: ( +
+ You are an expert Q&A system that is trusted around the world. Always answer the + query using the provided context information, and not prior knowledge. Some + rules to follow: 1. Never directly reference the given context in your answer. + 2. Avoid statements like 'Based on the context, ...' or 'The context information + ...' or anything along those lines. +
+ ), + extra: , + }, + { + key: "4", + label: "Assistant / Output", + children: ( +
+ You are an expert Q&A system that is trusted around the world. Always answer the + query using the provided context information, and not prior knowledge. Some + rules to follow: 1. Never directly reference the given context in your answer. + 2. Avoid statements like 'Based on the context, ...' or 'The context information + ...' or anything along those lines. +
+ ), + extra: , + }, + { + key: "4", + label: "Assistant / Output", + children: ( +
+ You are an expert Q&A system that is trusted around the world. Always answer the + query using the provided context information, and not prior knowledge. Some + rules to follow: 1. Never directly reference the given context in your answer. + 2. Avoid statements like 'Based on the context, ...' or 'The context information + ...' or anything along those lines. +
+ ), + extra: , + }, + ] + + const items: TabsProps["items"] = [ + { + key: "overview", + label: "Overview", + children: ( + + + Summary + + + + + + + + + + + + + + ), + }, + { + key: "raw_data", + label: "Raw Data", + children: "Content of Tab Pane 2", + }, + ] + + return ( +
+
+
+
+ generation + + + + + +
+ +
+
+ + LLM + + } + /> + + 08/29/2024, 10:35:01 AM 08/29/2024, + 10:35:03 AM + + } + /> + + 0.02 + + } + /> + + + 79 / $0.005 + + } + popoverContent={ + <> + Prompt tokens + Completion tokens + + } + /> +
+
+ +
+
+ +
+ Evaluation + + + + + + + +
+
+ ) +} + +export default TraceContent diff --git a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx new file mode 100644 index 0000000000..d22efdf9fd --- /dev/null +++ b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx @@ -0,0 +1,290 @@ +import {JSSTheme} from "@/lib/Types" +import { + Coins, + Database, + Download, + FileArrowDown, + ListChecks, + PlusCircle, + Sparkle, + Timer, + TreeStructure, +} from "@phosphor-icons/react" +import {Avatar, Space, Tree, TreeDataNode, Typography} from "antd" +import React from "react" +import {createUseStyles} from "react-jss" + +const useStyles = createUseStyles((theme: JSSTheme) => ({ + tree: { + overflowY: "auto", + "& .ant-tree-node-content-wrapper": { + width: 240, + }, + "& .ant-tree-node-selected": { + border: `1px solid ${theme.colorBorder}`, + }, + }, + treeTitle: { + fontSize: theme.fontSizeLG, + lineHeight: theme.lineHeightLG, + textOverflow: "ellipsis", + whiteSpace: "nowrap", + overflow: "hidden", + height: "100%", + width: "calc(210px - 40px)", + }, + treeContent: { + color: theme.colorTextSecondary, + "& div": { + display: "flex", + alignItems: "center", + gap: 4, + fontSize: theme.fontSize, + }, + }, +})) + +const TraceTree = () => { + const classes = useStyles() + + const treeData: TreeDataNode[] = [ + { + title: ( +
+ } + /> +
+ Root trace + +
+ + 983ms +
+
+ + $0.002 +
+
+ + 72 +
+
+
+
+ ), + key: "0-0", + children: [ + { + title: ( +
+ } + /> +
+ + Retrieval + + +
+ + 983ms +
+
+ + $0.002 +
+
+ + 72 +
+
+
+
+ ), + key: "0-0-1-0", + }, + { + title: ( +
+ } + /> +
+ + vector-store + + +
+ + 983ms +
+
+ + $0.002 +
+
+ + 72 +
+
+
+
+ ), + key: "0-0-1-1", + }, + { + title: ( +
+ } + /> +
+ + prompt-embedding + + +
+ + 983ms +
+
+ + $0.002 +
+
+ + 72 +
+
+
+
+ ), + key: "0-0-1-2", + }, + { + title: ( +
+ } + /> +
+ + context-encoding + + +
+ + 983ms +
+
+ + $0.002 +
+
+ + 72 +
+
+
+
+ ), + key: "0-0-1-3", + }, + ], + }, + { + title: ( +
+ } + /> +
+ + fetch-prompt-from-langfuse dendeinde deindeindeiw dwidnwndienw + + +
+ + 983ms +
+
+ + $0.002 +
+
+ + 72 +
+
+
+
+ ), + key: "0-1", + }, + { + title: ( +
+ } + /> +
+ generation + +
+ + 983ms +
+
+ + $0.002 +
+
+ + 72 +
+
+
+
+ ), + key: "0-2", + }, + ] + + return ( +
+ +
+ ) +} + +export default TraceTree diff --git a/agenta-web/src/components/pages/observability/drawer/index.tsx b/agenta-web/src/components/pages/observability/drawer/index.tsx new file mode 100644 index 0000000000..bc9e6dc4ba --- /dev/null +++ b/agenta-web/src/components/pages/observability/drawer/index.tsx @@ -0,0 +1,94 @@ +import {JSSTheme} from "@/lib/Types" +import { + CloseOutlined, + DeleteOutlined, + FullscreenExitOutlined, + FullscreenOutlined, +} from "@ant-design/icons" +import {CaretDown, CaretUp} from "@phosphor-icons/react" +import {Button, Drawer, Space, Tag, Typography} from "antd" +import React, {useState} from "react" +import {createUseStyles} from "react-jss" +import TraceTree from "./TraceTree" +import TraceContent from "./TraceContent" + +type TraceDrawerProps = {} & React.ComponentProps + +const useStyles = createUseStyles((theme: JSSTheme) => ({ + title: { + fontSize: theme.fontSizeHeading5, + fontWeight: theme.fontWeightMedium, + lineHeight: theme.lineHeightHeading5, + }, +})) + +const TraceDrawer = ({...props}: TraceDrawerProps) => { + const classes = useStyles() + const [drawerWidth, setDrawerWidth] = useState(1200) + + return ( + + + + + setSelectedTraceId("")} /> + + ) +} + +export default ObservabilityDashboard From 72bc444c674d05ac43cbd61f2cb3ceb6baa76646 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Fri, 4 Oct 2024 13:31:53 +0200 Subject: [PATCH 040/305] fix(frontend): removed isCloudFeature flag from observability menu item link --- agenta-web/src/components/Sidebar/config.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/agenta-web/src/components/Sidebar/config.tsx b/agenta-web/src/components/Sidebar/config.tsx index e842f9ffe1..259dfd6a1f 100644 --- a/agenta-web/src/components/Sidebar/config.tsx +++ b/agenta-web/src/components/Sidebar/config.tsx @@ -104,7 +104,6 @@ export const useSidebarConfig = () => { title: "Observability", icon: , isHidden: !appId && !recentlyVisitedAppId, - isCloudFeature: true && isOss, link: `/apps/${appId || recentlyVisitedAppId}/observability`, cloudFeatureTooltip: "Observability available in Cloud/Enterprise editions only", }, From 24a4f5421f6df4b26beb950bfb47066dc23c20d4 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sat, 5 Oct 2024 23:34:33 +0200 Subject: [PATCH 041/305] fix(frontend): replaced Observability specific drawer with generic drawer component --- .../src/components/GenericDrawer/index.tsx | 61 ++++++++++++ .../observability/drawer/TraceHeader.tsx | 37 ++++++++ .../pages/observability/drawer/index.tsx | 94 ------------------- .../apps/[app_id]/observability/index.tsx | 14 ++- 4 files changed, 109 insertions(+), 97 deletions(-) create mode 100644 agenta-web/src/components/GenericDrawer/index.tsx create mode 100644 agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx delete mode 100644 agenta-web/src/components/pages/observability/drawer/index.tsx diff --git a/agenta-web/src/components/GenericDrawer/index.tsx b/agenta-web/src/components/GenericDrawer/index.tsx new file mode 100644 index 0000000000..e8c4818792 --- /dev/null +++ b/agenta-web/src/components/GenericDrawer/index.tsx @@ -0,0 +1,61 @@ +import {CloseOutlined, FullscreenExitOutlined, FullscreenOutlined} from "@ant-design/icons" +import {Button, Drawer, Flex} from "antd" +import React, {ReactNode, useState} from "react" +import TraceTree from "../pages/observability/drawer/TraceTree" +import TraceContent from "../pages/observability/drawer/TraceContent" + +type GenericDrawerProps = { + expandable?: boolean + headerExtra?: ReactNode +} & React.ComponentProps + +const GenericDrawer = ({...props}: GenericDrawerProps) => { + const [drawerWidth, setDrawerWidth] = useState(1200) + + return ( + + - setSelectedTraceId("")} /> + setSelectedTraceId("")} + expandable + headerExtra={} + /> ) } From 038c3831822447d78b2df91019b20e013ee9479a Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sat, 5 Oct 2024 23:34:53 +0200 Subject: [PATCH 042/305] fix(frontend): scroll issue on overflow Y --- .../src/components/pages/observability/drawer/TraceTree.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx index d22efdf9fd..aec7963296 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx @@ -17,6 +17,7 @@ import {createUseStyles} from "react-jss" const useStyles = createUseStyles((theme: JSSTheme) => ({ tree: { overflowY: "auto", + height: "100%", "& .ant-tree-node-content-wrapper": { width: 240, }, From 1cadbf4bc5070ed3367383d355282e956cfb1587 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sun, 6 Oct 2024 18:48:11 +0200 Subject: [PATCH 043/305] fix(frontend): improved generic drawer component --- agenta-web/src/components/GenericDrawer/index.tsx | 10 ++++++---- .../src/pages/apps/[app_id]/observability/index.tsx | 8 +++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/agenta-web/src/components/GenericDrawer/index.tsx b/agenta-web/src/components/GenericDrawer/index.tsx index e8c4818792..8209095527 100644 --- a/agenta-web/src/components/GenericDrawer/index.tsx +++ b/agenta-web/src/components/GenericDrawer/index.tsx @@ -1,12 +1,12 @@ import {CloseOutlined, FullscreenExitOutlined, FullscreenOutlined} from "@ant-design/icons" import {Button, Drawer, Flex} from "antd" import React, {ReactNode, useState} from "react" -import TraceTree from "../pages/observability/drawer/TraceTree" -import TraceContent from "../pages/observability/drawer/TraceContent" type GenericDrawerProps = { expandable?: boolean headerExtra?: ReactNode + mainContent: ReactNode + sideContent?: ReactNode } & React.ComponentProps const GenericDrawer = ({...props}: GenericDrawerProps) => { @@ -51,8 +51,10 @@ const GenericDrawer = ({...props}: GenericDrawerProps) => { {...props} >
- - + {props.sideContent && ( +
{props.sideContent}
+ )} + {props.mainContent}
) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index fe275d2de2..a653a15eeb 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -1,10 +1,10 @@ import GenericDrawer from "@/components/GenericDrawer" +import TraceContent from "@/components/pages/observability/drawer/TraceContent" import TraceHeader from "@/components/pages/observability/drawer/TraceHeader" +import TraceTree from "@/components/pages/observability/drawer/TraceTree" import {useQueryParam} from "@/hooks/useQuery" import {JSSTheme} from "@/lib/Types" -import {DeleteOutlined} from "@ant-design/icons" -import {CaretDown, CaretUp} from "@phosphor-icons/react" -import {Button, Space, Tag, Typography} from "antd" +import {Button, Typography} from "antd" import React from "react" import {createUseStyles} from "react-jss" @@ -36,6 +36,8 @@ const ObservabilityDashboard: React.FC = () => { onClose={() => setSelectedTraceId("")} expandable headerExtra={} + mainContent={} + sideContent={} /> ) From 3614a38640b382a9ac75a13eeb7c9fc87fad5656 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sun, 6 Oct 2024 18:50:36 +0200 Subject: [PATCH 044/305] fix(frontend): increased ant tree switcher leaf line height and width --- .../pages/observability/drawer/TraceTree.tsx | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx index aec7963296..d934673436 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx @@ -24,6 +24,15 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ "& .ant-tree-node-selected": { border: `1px solid ${theme.colorBorder}`, }, + "& .ant-tree-switcher-leaf-line": { + "&:after": { + height: "36px !important", + width: 13, + }, + }, + "& .ant-tree-treenode-leaf-last .ant-tree-switcher-leaf-line:before": { + height: "36px !important", + }, }, treeTitle: { fontSize: theme.fontSizeLG, @@ -274,17 +283,15 @@ const TraceTree = () => { ] return ( -
- -
+ ) } From 2081e62a6331f218c4e48fab1af10177bdc897f1 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 8 Oct 2024 17:42:50 +0200 Subject: [PATCH 045/305] Fix SDK to make it closer to OTEL --- agenta-cli/agenta/__init__.py | 5 +- agenta-cli/agenta/sdk/__init__.py | 27 +- agenta-cli/agenta/sdk/agenta_init.py | 28 +- agenta-cli/agenta/sdk/decorators/routing.py | 47 +- agenta-cli/agenta/sdk/decorators/tracing.py | 211 ++- agenta-cli/agenta/sdk/litellm/__init__.py | 1 + agenta-cli/agenta/sdk/litellm/litellm.py | 275 ++++ agenta-cli/agenta/sdk/tracing/__init__.py | 1 + agenta-cli/agenta/sdk/tracing/attributes.py | 181 +++ agenta-cli/agenta/sdk/tracing/context.py | 21 + agenta-cli/agenta/sdk/tracing/conventions.py | 41 +- agenta-cli/agenta/sdk/tracing/exporters.py | 48 +- agenta-cli/agenta/sdk/tracing/inline.py | 1162 +++++++++++++++++ agenta-cli/agenta/sdk/tracing/inline_trace.py | 145 -- agenta-cli/agenta/sdk/tracing/litellm.py | 268 ---- .../agenta/sdk/tracing/opentelemetry.py | 220 ---- agenta-cli/agenta/sdk/tracing/processors.py | 49 +- agenta-cli/agenta/sdk/tracing/spans.py | 220 ++-- agenta-cli/agenta/sdk/tracing/tracers.py | 66 - agenta-cli/agenta/sdk/tracing/tracing.py | 172 +++ agenta-cli/agenta/sdk/tracing/x_inline.py | 152 +++ .../sdk/tracing/{trace_tree.py => x_tree.py} | 0 agenta-cli/agenta/sdk/utils/exceptions.py | 18 + agenta-cli/agenta/sdk/utils/singleton.py | 22 +- 24 files changed, 2395 insertions(+), 985 deletions(-) create mode 100644 agenta-cli/agenta/sdk/litellm/__init__.py create mode 100644 agenta-cli/agenta/sdk/litellm/litellm.py create mode 100644 agenta-cli/agenta/sdk/tracing/attributes.py create mode 100644 agenta-cli/agenta/sdk/tracing/context.py create mode 100644 agenta-cli/agenta/sdk/tracing/inline.py delete mode 100644 agenta-cli/agenta/sdk/tracing/inline_trace.py delete mode 100644 agenta-cli/agenta/sdk/tracing/litellm.py delete mode 100644 agenta-cli/agenta/sdk/tracing/opentelemetry.py delete mode 100644 agenta-cli/agenta/sdk/tracing/tracers.py create mode 100644 agenta-cli/agenta/sdk/tracing/tracing.py create mode 100644 agenta-cli/agenta/sdk/tracing/x_inline.py rename agenta-cli/agenta/sdk/tracing/{trace_tree.py => x_tree.py} (100%) create mode 100644 agenta-cli/agenta/sdk/utils/exceptions.py diff --git a/agenta-cli/agenta/__init__.py b/agenta-cli/agenta/__init__.py index a4cf8bdadb..82efa4d0fa 100644 --- a/agenta-cli/agenta/__init__.py +++ b/agenta-cli/agenta/__init__.py @@ -14,15 +14,16 @@ ) from .sdk.utils.logging import log as logging -from .sdk.tracing.opentelemetry import Tracing +from .sdk.tracing import Tracing from .sdk.decorators.tracing import instrument from .sdk.decorators.routing import entrypoint, app, route from .sdk.agenta_init import Config, AgentaSingleton, init from .sdk.utils.costs import calculate_token_usage from .sdk.client import Agenta -from .sdk.tracing import litellm as callbacks +from .sdk.litellm import litellm as callbacks from .sdk.config_manager import ConfigManager from .sdk import assets as assets +from .sdk import tracer config = PreInitObject("agenta.config", Config) DEFAULT_AGENTA_SINGLETON_INSTANCE = AgentaSingleton() diff --git a/agenta-cli/agenta/sdk/__init__.py b/agenta-cli/agenta/sdk/__init__.py index 02e3794795..ca691bcb3d 100644 --- a/agenta-cli/agenta/sdk/__init__.py +++ b/agenta-cli/agenta/sdk/__init__.py @@ -1,3 +1,5 @@ +from typing import Optional + from .utils.preinit import PreInitObject # always the first import! from .types import ( DictInput, @@ -13,13 +15,34 @@ BinaryParam, ) -from .tracing.opentelemetry import Tracing +from .tracing import Tracing, get_tracer from .decorators.tracing import instrument from .decorators.routing import entrypoint, app, route -from .agenta_init import Config, AgentaSingleton, init +from .agenta_init import Config, AgentaSingleton, init as _init from .utils.costs import calculate_token_usage from .config_manager import ConfigManager config = PreInitObject("agenta.config", Config) DEFAULT_AGENTA_SINGLETON_INSTANCE = AgentaSingleton() tracing = DEFAULT_AGENTA_SINGLETON_INSTANCE.tracing # type: ignore + +tracer = get_tracer(tracing) + + +def init( + host: Optional[str] = None, + app_id: Optional[str] = None, + api_key: Optional[str] = None, + config_fname: Optional[str] = None, +): + global tracing + global tracer + + _init( + host=host, + app_id=app_id, + api_key=api_key, + config_fname=config_fname, + ) + + tracer = get_tracer(tracing) diff --git a/agenta-cli/agenta/sdk/agenta_init.py b/agenta-cli/agenta/sdk/agenta_init.py index 07997a537d..a4fa82a42e 100644 --- a/agenta-cli/agenta/sdk/agenta_init.py +++ b/agenta-cli/agenta/sdk/agenta_init.py @@ -7,7 +7,7 @@ from agenta.sdk.utils.logging import log from agenta.sdk.utils.globals import set_global from agenta.client.backend.client import AgentaApi -from agenta.sdk.tracing.opentelemetry import Tracing +from agenta.sdk.tracing import Tracing from agenta.client.exceptions import APIRequestError @@ -29,10 +29,13 @@ def __new__(cls): def init( self, - app_id: Optional[str] = None, + *, host: Optional[str] = None, + project_id: Optional[str] = None, api_key: Optional[str] = None, config_fname: Optional[str] = None, + # + app_id: Optional[str] = None, ) -> None: log.info(f"\n--------------------------------") log.info(f"Using Agenta Python SDK version: {version('agenta')}") @@ -81,14 +84,25 @@ def init( "3. As an environment variable 'AGENTA_APP_ID'." ) + self.project_id = ( + project_id + or config.get("project_id") + or os.environ.get("AGENTA_PROJECT_ID") + ) + self.api_key = ( api_key or config.get("api_key") or os.environ.get("AGENTA_API_KEY") ) self.tracing = Tracing( url=f"{self.host}/api/observability/v1/traces", # type: ignore - app_id=self.app_id, + ) + + self.tracing.configure( + project_id=self.project_id, api_key=self.api_key, + # DEPRECATED + app_id=self.app_id, ) self.base_id = os.environ.get("AGENTA_BASE_ID") @@ -237,9 +251,11 @@ def dump(self): def init( host: Optional[str] = None, - app_id: Optional[str] = None, + project_id: Optional[str] = None, api_key: Optional[str] = None, config_fname: Optional[str] = None, + # DEPRECATED + app_id: Optional[str] = None, ): """Main function to initialize the agenta sdk. @@ -267,9 +283,11 @@ def init( singleton.init( host=host, - app_id=app_id, + project_id=project_id, api_key=api_key, config_fname=config_fname, + # DEPRECATED + app_id=app_id, ) set_global( diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index ea72c81385..608562cc19 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -13,7 +13,6 @@ from tempfile import NamedTemporaryFile from typing import Any, Callable, Dict, Optional, Tuple, List from importlib.metadata import version -from contextlib import suppress from fastapi.middleware.cors import CORSMiddleware from fastapi import Body, FastAPI, UploadFile, HTTPException @@ -22,6 +21,7 @@ from agenta.sdk.context.routing import routing_context_manager, routing_context from agenta.sdk.context.tracing import tracing_context from agenta.sdk.router import router as router +from agenta.sdk.utils.exceptions import suppress from agenta.sdk.utils.logging import log from agenta.sdk.types import ( DictInput, @@ -397,20 +397,29 @@ async def execute_function( except Exception as e: self.handle_exception(e) - with suppress(Exception): - if inline_trace: - if WAIT_FOR_SPANS: - remaining_steps = NOFSTEPS + with suppress(): + root_context: Dict[str, Any] = tracing_context.get().get("root") - while ag.tracing.is_processing() and remaining_steps > 0: - await asyncio.sleep(TIMESTEP) - remaining_steps -= 1 + trace_id = root_context.get("trace_id") if root_context else None - await asyncio.sleep(FINALSTEP) + if trace_id is not None: + if inline_trace: + if WAIT_FOR_SPANS: + remaining_steps = NOFSTEPS - trace = ag.tracing.get_inline_trace() - else: - trace = ag.tracing.get_trace_id_only() + while ( + not ag.tracing.is_inline_trace_ready(trace_id) + and remaining_steps > 0 + ): + await asyncio.sleep(TIMESTEP) + + remaining_steps -= 1 + + await asyncio.sleep(FINALSTEP) + + trace = ag.tracing.get_inline_trace(trace_id) + else: + trace = {"trace_id": trace_id} response = BaseResponse(data=data, trace=trace) @@ -651,9 +660,9 @@ def handle_terminal_run( log.info(f"trace_id: {result.trace['trace_id']}") if SHOW_DETAILS: - log.info(f"latency: {result.trace['latency']}") - log.info(f"cost: {result.trace['cost']}") - log.info(f"tokens: {list(result.trace['tokens'].values())}") + log.info(f"latency: {result.trace.get('latency')}") + log.info(f"cost: {result.trace.get('cost')}") + log.info(f"tokens: {list(result.trace.get('tokens', {}).values())}") if SHOW_DATA: log.info(" ") @@ -664,13 +673,7 @@ def handle_terminal_run( log.info(" ") log.info(f"trace:") log.info(f"----------------") - for span in result.trace["spans"]: - log.info( - f"{span['id']} {span['spankind'].ljust(10)} {span['status'].ljust(5)} {span['name']}" - ) - if SHOW_SPAN_ATTRIBUTES: - [log.info(f" {attribute}") for attribute in span["attributes"]] - log.info(" ") + log.info(json.dumps(result.trace.get("spans", []), indent=2)) log.info(f"----------------") log.info("\n==========================\n") diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 72ebdef907..83c8ec1ded 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -1,12 +1,15 @@ import inspect from functools import wraps from itertools import chain -from contextlib import suppress from typing import Callable, Optional, Any, Dict, List import agenta as ag + +from agenta.sdk.utils.exceptions import suppress + from agenta.sdk.context.tracing import tracing_context +from agenta.sdk.tracing.conventions import parse_span_kind class instrument: @@ -14,17 +17,19 @@ class instrument: def __init__( self, - kind: str = "UNKNOWN", + kind: str = "task", config: Optional[Dict[str, Any]] = None, ignore_inputs: Optional[bool] = None, ignore_outputs: Optional[bool] = None, + max_depth: Optional[int] = 2, # DEPRECATED - spankind: Optional[str] = "UNKNOWN", + spankind: Optional[str] = "TASK", ) -> None: self.kind = spankind if spankind is not None else kind self.config = config self.ignore_inputs = ignore_inputs self.ignore_outputs = ignore_outputs + self.max_depth = max_depth def __call__(self, func: Callable[..., Any]): is_coroutine_function = inspect.iscoroutinefunction(func) @@ -88,64 +93,98 @@ def patch(result: Any) -> Dict[str, Any]: @wraps(func) async def async_wrapper(*args, **kwargs): async def wrapped_func(*args, **kwargs): - with ag.tracing.start_as_current_span(func.__name__, self.kind): - with suppress(Exception): - rctx = tracing_context.get() - ag.tracing.set_attributes( - "metadata", {"config": rctx.get("config", {})} - ) - ag.tracing.set_attributes( - "metadata", {"environment": rctx.get("environment", {})} - ) - ag.tracing.set_attributes( - "metadata", {"version": rctx.get("version", {})} - ) - ag.tracing.set_attributes( - "metadata", {"variant": rctx.get("variant", {})} + if not ag.tracing.get_current_span().is_recording(): + self.kind = "workflow" + + kind = parse_span_kind(self.kind) + + with ag.tracer.start_as_current_span(func.__name__, kind=kind): + span = ag.tracing.get_current_span() + + with suppress(): + span.set_attributes( + attributes={"node": self.kind}, + namespace="type", ) - ag.tracing.set_attributes( - "data.inputs", - redact(parse(*args, **kwargs), self.ignore_inputs), + if span.parent is None: + rctx = tracing_context.get() + + span.set_attributes( + attributes={"configuration": rctx.get("config", {})}, + namespace="meta", + ) + span.set_attributes( + attributes={"environment": rctx.get("environment", {})}, + namespace="meta", + ) + span.set_attributes( + attributes={"version": rctx.get("version", {})}, + namespace="meta", + ) + span.set_attributes( + attributes={"variant": rctx.get("variant", {})}, + namespace="meta", + ) + + _inputs = redact(parse(*args, **kwargs), self.ignore_inputs) + span.set_attributes( + attributes={"inputs": _inputs}, + namespace="data", + max_depth=self.max_depth, ) try: result = await func(*args, **kwargs) except Exception as e: - ag.tracing.record_exception(e) + span.record_exception(e) - ag.tracing.set_status("ERROR") + span.set_status("ERROR") raise e - with suppress(Exception): - cost = 0.0 + with suppress(): + cost = None usage = {} if isinstance(result, dict): - cost = result.get("cost", 0.0) + cost = result.get("cost", None) usage = result.get("usage", {}) - ag.tracing.set_attributes( - namespace="metrics.marginal.costs", + span.set_attributes( attributes={"total": cost}, + namespace="metrics.unit.costs", ) - ag.tracing.set_attributes( - namespace="metrics.marginal.tokens", + span.set_attributes( attributes=( { - "prompt": usage.get("prompt_tokens", 0), - "completion": usage.get("completion_tokens", 0), - "total": usage.get("total_tokens", 0), + "prompt": usage.get("prompt_tokens", None), + "completion": usage.get("completion_tokens", None), + "total": usage.get("total_tokens", None), } ), + namespace="metrics.unit.tokens", ) - ag.tracing.set_attributes( - "data.outputs", - redact(patch(result), self.ignore_outputs), + _outputs = redact(patch(result), self.ignore_outputs) + span.set_attributes( + attributes={"outputs": _outputs}, + namespace="data", + max_depth=self.max_depth, ) - ag.tracing.set_status("OK") + span.set_status("OK") + + with suppress(): + if hasattr(span, "parent") and span.parent is None: + tracing_context.set( + tracing_context.get() + | { + "root": { + "trace_id": span.get_span_context().trace_id, + "span_id": span.get_span_context().span_id, + } + } + ) return result @@ -154,64 +193,98 @@ async def wrapped_func(*args, **kwargs): @wraps(func) def sync_wrapper(*args, **kwargs): def wrapped_func(*args, **kwargs): - with ag.tracing.start_as_current_span(func.__name__, self.kind): - with suppress(Exception): - rctx = tracing_context.get() - ag.tracing.set_attributes( - "metadata", {"config": rctx.get("config", {})} - ) - ag.tracing.set_attributes( - "metadata", {"environment": rctx.get("environment", {})} - ) - ag.tracing.set_attributes( - "metadata", {"version": rctx.get("version", {})} - ) - ag.tracing.set_attributes( - "metadata", {"variant": rctx.get("variant", {})} + if not ag.tracing.get_current_span().is_recording(): + self.kind = "workflow" + + kind = parse_span_kind(self.kind) + + with ag.tracer.start_as_current_span(func.__name__, kind=kind): + span = ag.tracing.get_current_span() + + with suppress(): + span.set_attributes( + attributes={"node": self.kind}, + namespace="type", ) - ag.tracing.set_attributes( - "data.inputs", - redact(parse(*args, **kwargs), self.ignore_inputs), + if span.parent is None: + rctx = tracing_context.get() + + span.set_attributes( + attributes={"configuration": rctx.get("config", {})}, + namespace="meta", + ) + span.set_attributes( + attributes={"environment": rctx.get("environment", {})}, + namespace="meta", + ) + span.set_attributes( + attributes={"version": rctx.get("version", {})}, + namespace="meta", + ) + span.set_attributes( + attributes={"variant": rctx.get("variant", {})}, + namespace="meta", + ) + + _inputs = redact(parse(*args, **kwargs), self.ignore_inputs) + span.set_attributes( + attributes={"inputs": _inputs}, + namespace="data", + max_depth=self.max_depth, ) try: result = func(*args, **kwargs) except Exception as e: - ag.tracing.record_exception(e) + span.record_exception(e) - ag.tracing.set_status("ERROR") + span.set_status("ERROR") raise e - with suppress(Exception): - cost = 0.0 + with suppress(): + cost = None usage = {} if isinstance(result, dict): - cost = result.get("cost", 0.0) + cost = result.get("cost", None) usage = result.get("usage", {}) - ag.tracing.set_attributes( - namespace="metrics.marginal.costs", + span.set_attributes( attributes={"total": cost}, + namespace="metrics.unit.costs", ) - ag.tracing.set_attributes( - namespace="metrics.marginal.tokens", + span.set_attributes( attributes=( { - "prompt": usage.get("prompt_tokens", 0), - "completion": usage.get("completion_tokens", 0), - "total": usage.get("total_tokens", 0), + "prompt": usage.get("prompt_tokens", None), + "completion": usage.get("completion_tokens", None), + "total": usage.get("total_tokens", None), } ), + namespace="metrics.unit.tokens", ) - ag.tracing.set_attributes( - "data.outputs", - redact(patch(result), self.ignore_outputs), + _outputs = redact(patch(result), self.ignore_outputs) + span.set_attributes( + attributes={"outputs": _outputs}, + namespace="data", + max_depth=self.max_depth, ) - ag.tracing.set_status("OK") + span.set_status("OK") + + with suppress(): + if hasattr(span, "parent") and span.parent is None: + tracing_context.set( + tracing_context.get() + | { + "root": { + "trace_id": span.get_span_context().trace_id, + "span_id": span.get_span_context().span_id, + } + } + ) return result diff --git a/agenta-cli/agenta/sdk/litellm/__init__.py b/agenta-cli/agenta/sdk/litellm/__init__.py new file mode 100644 index 0000000000..e9ce42ea24 --- /dev/null +++ b/agenta-cli/agenta/sdk/litellm/__init__.py @@ -0,0 +1 @@ +from .litellm import litellm_handler diff --git a/agenta-cli/agenta/sdk/litellm/litellm.py b/agenta-cli/agenta/sdk/litellm/litellm.py new file mode 100644 index 0000000000..43f608e397 --- /dev/null +++ b/agenta-cli/agenta/sdk/litellm/litellm.py @@ -0,0 +1,275 @@ +import agenta as ag + +from agenta.sdk.tracing.spans import CustomSpan +from agenta.sdk.utils.exceptions import suppress +from agenta.sdk.utils.logging import log + + +def litellm_handler(): + try: + from litellm.utils import ModelResponse + from litellm.integrations.custom_logger import ( + CustomLogger as LitellmCustomLogger, + ) + except ImportError as exc: + raise ImportError( + "The litellm SDK is not installed. Please install it using `pip install litellm`." + ) from exc + except Exception as exc: + raise Exception( + "Unexpected error occurred when importing litellm: {}".format(exc) + ) from exc + + class LitellmHandler(LitellmCustomLogger): + """This handler is responsible for instrumenting certain events when using litellm to call LLMs. + + Args: + LitellmCustomLogger (object): custom logger that allows us to override the events to capture. + """ + + def __init__(self): + self.span = None + + def log_pre_api_call( + self, + model, + messages, + kwargs, + ): + type = ( + "chat" + if kwargs.get("call_type") in ["completion", "acompletion"] + else "embedding" + ) + + kind = "CLIENT" + + self.span = CustomSpan( + ag.tracer.start_span(name=f"litellm_{kind.lower()}", kind=kind) + ) + + self.span.set_attributes( + attributes={"node": type}, + namespace="type", + ) + + if not self.span: + log.error("LiteLLM callback error: span not found.") + return + + log.info(f"log_pre_api_call({hex(self.span.context.span_id)[2:]})") + + self.span.set_attributes( + attributes={"inputs": {"messages": kwargs["messages"]}}, + namespace="data", + ) + + self.span.set_attributes( + attributes={ + "configuration": { + "model": kwargs.get("model"), + **kwargs.get("optional_params"), + } + }, + namespace="meta", + ) + + def log_stream_event( + self, + kwargs, + response_obj, + start_time, + end_time, + ): + if not self.span: + log.error("LiteLLM callback error: span not found.") + return + + # log.info(f"log_stream({hex(self.span.context.span_id)[2:]})") + + self.span.set_attributes( + attributes={ + "output": {"__default__": kwargs.get("complete_streaming_response")} + }, + namespace="data", + ) + + self.span.set_attributes( + attributes={"total": kwargs.get("response_cost")}, + namespace="metrics.unit.costs", + ) + + self.span.set_attributes( + attributes=( + { + "prompt": response_obj.usage.prompt_tokens, + "completion": response_obj.usage.completion_tokens, + "total": response_obj.usage.total_tokens, + } + ), + namespace="metrics.unit.tokens", + ) + + self.span.set_status(status="OK") + + self.span.end() + + def log_success_event( + self, + kwargs, + response_obj, + start_time, + end_time, + ): + if not self.span: + log.error("LiteLLM callback error: span not found.") + return + + # log.info(f"log_success({hex(self.span.context.span_id)[2:]})") + + self.span.set_attributes( + attributes={ + "output": {"__default__": response_obj.choices[0].message.content} + }, + namespace="data", + ) + + self.span.set_attributes( + attributes={"total": kwargs.get("response_cost")}, + namespace="metrics.unit.costs", + ) + + self.span.set_attributes( + attributes=( + { + "prompt": response_obj.usage.prompt_tokens, + "completion": response_obj.usage.completion_tokens, + "total": response_obj.usage.total_tokens, + } + ), + namespace="metrics.unit.tokens", + ) + + self.span.set_status(status="OK") + + self.span.end() + + def log_failure_event( + self, + kwargs, + response_obj, + start_time, + end_time, + ): + if not self.span: + log.error("LiteLLM callback error: span not found.") + return + + # log.info(f"log_failure({hex(self.span.context.span_id)[2:]})") + + self.span.record_exception(kwargs["exception"]) + + self.span.set_status(status="ERROR") + + self.span.end() + + async def async_log_stream_event( + self, + kwargs, + response_obj, + start_time, + end_time, + ): + if not self.span: + log.error("LiteLLM callback error: span not found.") + return + + # log.info(f"async_log_stream({hex(self.span.context.span_id)[2:]})") + + self.span.set_attributes( + attributes={ + "output": {"__default__": kwargs.get("complete_streaming_response")} + }, + namespace="data", + ) + + self.span.set_attributes( + attributes={"total": kwargs.get("response_cost")}, + namespace="metrics.unit.costs", + ) + + self.span.set_attributes( + attributes=( + { + "prompt": response_obj.usage.prompt_tokens, + "completion": response_obj.usage.completion_tokens, + "total": response_obj.usage.total_tokens, + } + ), + namespace="metrics.unit.tokens", + ) + + self.span.set_status(status="OK") + + self.span.end() + + async def async_log_success_event( + self, + kwargs, + response_obj, + start_time, + end_time, + ): + if not self.span: + log.error("LiteLLM callback error: span not found.") + return + + log.info(f"async_log_success({hex(self.span.context.span_id)[2:]})") + + self.span.set_attributes( + attributes={ + "output": {"__default__": kwargs.get("complete_streaming_response")} + }, + namespace="data", + ) + + self.span.set_attributes( + attributes={"total": kwargs.get("response_cost")}, + namespace="metrics.unit.costs", + ) + + self.span.set_attributes( + attributes=( + { + "prompt": response_obj.usage.prompt_tokens, + "completion": response_obj.usage.completion_tokens, + "total": response_obj.usage.total_tokens, + } + ), + namespace="metrics.unit.tokens", + ) + + self.span.set_status(status="OK") + + self.span.end() + + async def async_log_failure_event( + self, + kwargs, + response_obj, + start_time, + end_time, + ): + if not self.span: + log.error("LiteLLM callback error: span not found.") + return + + # log.info(f"async_log_failure({hex(self.span.context.span_id)[2:]})") + + self.span.record_exception(kwargs["exception"]) + + self.span.set_status(status="ERROR") + + self.span.end() + + return LitellmHandler() diff --git a/agenta-cli/agenta/sdk/tracing/__init__.py b/agenta-cli/agenta/sdk/tracing/__init__.py index e69de29bb2..734c38b64d 100644 --- a/agenta-cli/agenta/sdk/tracing/__init__.py +++ b/agenta-cli/agenta/sdk/tracing/__init__.py @@ -0,0 +1 @@ +from .tracing import Tracing, get_tracer diff --git a/agenta-cli/agenta/sdk/tracing/attributes.py b/agenta-cli/agenta/sdk/tracing/attributes.py new file mode 100644 index 0000000000..0c6894f1d2 --- /dev/null +++ b/agenta-cli/agenta/sdk/tracing/attributes.py @@ -0,0 +1,181 @@ +from json import loads, dumps +from typing import Optional, Union, Sequence, Any, Dict + +Primitive = Union[str, int, float, bool, bytes] +PrimitivesSequence = Sequence[Primitive] +Attribute = Union[Primitive, PrimitivesSequence] + + +def _marshal( + unmarshalled: Dict[str, Any], + *, + parent_key: Optional[str] = "", + depth: Optional[int] = 0, + max_depth: Optional[int] = None, +) -> Dict[str, Any]: + """ + Marshals a dictionary of unmarshalled attributes into a flat dictionary + + Example: + unmarshalled = { + "ag": { + "type": "tree", + "node": { + "name": "root", + "children": [ + { + "name": "child1", + }, + { + "name": "child2", + } + ] + } + } + } + marshalled = { + "ag.type": "tree", + "ag.node.name": "root", + "ag.node.children.0.name": "child1", + "ag.node.children.1.name": "child2" + } + """ + marshalled = {} + + # If max_depth is set and we've reached it, + # just return the unmarshalled attributes + if max_depth is not None and depth >= max_depth: + marshalled[parent_key] = unmarshalled + # MISSING ENCODING TO JSON IF NOT PRIMITIVE + + return marshalled + + # Otherwise, + # iterate over the unmarshalled attributes and marshall them + for key, value in unmarshalled.items(): + child_key = f"{parent_key}.{key}" if parent_key else key + + if isinstance(value, dict): + dict_key = child_key + + marshalled.update( + _marshal( + value, + parent_key=dict_key, + depth=depth + 1, + max_depth=max_depth, + ) + ) + elif isinstance(value, list): + if max_depth is not None and depth + 1 >= max_depth: + marshalled[child_key] = value + # MISSING ENCODING TO JSON IF NOT PRIMITIVE + else: + for i, item in enumerate(value): + list_key = f"{child_key}.{i}" + + if isinstance(item, (dict, list)): + marshalled.update( + _marshal( + item, + parent_key=list_key, + depth=depth + 1, + max_depth=max_depth, + ) + ) + else: + marshalled[list_key] = item + # MISSING ENCODING TO JSON IF NOT PRIMITIVE + else: + marshalled[child_key] = value + # MISSING ENCODING TO JSON IF NOT PRIMITIVE + + return marshalled + + +def _encode_key(namespace: Optional[str] = None, key: str = "") -> str: + if namespace is None: + return key + + return f"ag.{namespace}.{key}" + + +def _encode_value(value: Any) -> Optional[Attribute]: + if value is None: + return None + + if isinstance(value, (str, int, float, bool, bytes)): + return value + + if isinstance(value, dict) or isinstance(value, list): + encoded = dumps(value) + value = "@ag.type=json:" + encoded + return value + + return repr(value) + + +def serialize( + *, + namespace: str, + attributes: Dict[str, Any], + max_depth: Optional[int] = None, +) -> Dict[str, str]: + if not isinstance(attributes, dict): + return {} + + _attributes = { + k: v + for k, v in { + _encode_key(namespace, key): _encode_value(value) + for key, value in _marshal(attributes, max_depth=max_depth).items() + }.items() + if v is not None + } + + return _attributes + + +""" +def _decode_key(namespace: Optional[str] = None, key: str = "") -> str: + if namespace is None: + return key + + return key.replace(f"ag.{namespace}.", "") +""" + +""" +def _decode_value(value: Attribute) -> Any: + if isinstance(value, (int, float, bool, bytes)): + return value + + if isinstance(value, str): + if value == "@ag.type=none:": + return None + + if value.startswith("@ag.type=json:"): + encoded = value[len("@ag.type=json:") :] + value = loads(encoded) + return value + + return value + + return value +""" + +""" +def deserialize( + *, + namespace: str, + attributes: Dict[str, Any], + max_depth: Optional[int] = None, +) -> Dict[str, Any]: + if not isinstance(attributes, dict): + return {} + + return { + _decode_key(namespace, key): _decode_value(value) + for key, value in attributes + if key != _decode_key(namespace, key) + } +""" diff --git a/agenta-cli/agenta/sdk/tracing/context.py b/agenta-cli/agenta/sdk/tracing/context.py new file mode 100644 index 0000000000..f0798d8e8f --- /dev/null +++ b/agenta-cli/agenta/sdk/tracing/context.py @@ -0,0 +1,21 @@ +from contextvars import ContextVar +from contextlib import contextmanager +from traceback import format_exc + +from agenta.sdk.utils.logging import log + +tracing_context = ContextVar("tracing_context", default={}) + + +@contextmanager +def tracing_context_manager(): + _tracing_context = {"health": {"status": "ok"}} + + token = tracing_context.set(_tracing_context) + try: + yield + except Exception as e: + log.error(f"Error with tracing context: {_tracing_context}") + log.error(f"Exception: {format_exc()}") + finally: + tracing_context.reset(token) diff --git a/agenta-cli/agenta/sdk/tracing/conventions.py b/agenta-cli/agenta/sdk/tracing/conventions.py index 16ced5d08c..a3423782a3 100644 --- a/agenta-cli/agenta/sdk/tracing/conventions.py +++ b/agenta-cli/agenta/sdk/tracing/conventions.py @@ -1,24 +1,43 @@ from typing import Literal +from opentelemetry.trace import SpanKind + Namespace = Literal[ "data.inputs", "data.internals", "data.outputs", "metrics.scores", - "metrics.marginal.costs", - "metrics.marginal.tokens", - "metadata.config", - "metadata.version", + "metrics.unit.costs", + "metrics.unit.tokens", + "meta.configuration", + "meta.version", "tags", - "resource.project", - "resource.experiment", - "resource.application", - "resource.configuration", - "resource.service", - "extra", + "refs", ] -Status = Literal[ +Code = Literal[ "OK", + "UNSET", "ERROR", ] + + +def parse_span_kind(type: str) -> SpanKind: + kind = SpanKind.INTERNAL + if type in [ + "agent", + "chain", + "workflow", + ]: + kind = SpanKind.SERVER + elif type in [ + "tool", + "embedding", + "query", + "completion", + "chat", + "rerank", + ]: + kind = SpanKind.CLIENT + + return kind diff --git a/agenta-cli/agenta/sdk/tracing/exporters.py b/agenta-cli/agenta/sdk/tracing/exporters.py index ca9fafd3f7..26cae81284 100644 --- a/agenta-cli/agenta/sdk/tracing/exporters.py +++ b/agenta-cli/agenta/sdk/tracing/exporters.py @@ -1,17 +1,20 @@ -from typing import Sequence, Dict +from typing import Sequence, Dict, List from opentelemetry.sdk.trace.export import ( - SpanExportResult, + ConsoleSpanExporter, SpanExporter, + SpanExportResult, ReadableSpan, ) +from opentelemetry.exporter.otlp.proto.http.trace_exporter import ( + OTLPSpanExporter, +) + +from agenta.sdk.utils.exceptions import suppress class InlineTraceExporter(SpanExporter): - def __init__( - self, - registry: Dict[str, ReadableSpan], - ): + def __init__(self, registry: Dict[str, List[ReadableSpan]]): self._shutdown = False self._registry = registry @@ -22,18 +25,29 @@ def export( if self._shutdown: return - for span in spans: - self._registry.update( - **{span.context.span_id.to_bytes(8, "big").hex(): span} - ) + with suppress(): + for span in spans: + trace_id = span.get_span_context().trace_id - def shutdown( - self, - ) -> None: + if trace_id not in self._registry: + self._registry[trace_id] = [] + + self._registry[trace_id].append(span) + + def shutdown(self) -> None: self._shutdown = True - def force_flush( - self, - timeout_millis: int = 30000, - ) -> bool: + def force_flush(self, timeout_millis: int = 30000) -> bool: return True + + def fetch(self, trace_id: int) -> List[ReadableSpan]: + trace = self._registry.get(trace_id, []) + + del self._registry[trace_id] + + return trace + + +ConsoleExporter = ConsoleSpanExporter +InlineExporter = InlineTraceExporter +OTLPExporter = OTLPSpanExporter diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py new file mode 100644 index 0000000000..40c96a3a09 --- /dev/null +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -0,0 +1,1162 @@ +############################ +### services.shared.dtos ### +############################ + + +from typing import Optional + +from pydantic import BaseModel +from uuid import UUID +from datetime import datetime +from enum import Enum +from collections import OrderedDict + + +## --- DISPLAY --- ## + + +NOF_CHARS = 8 + + +def _p_id(id): + return repr(str(id)[:NOF_CHARS]) + + +def _p_osa(o): + elements = [] + + for i in OrderedDict(sorted(o.items())).items(): + if not i[0].startswith("_"): + if i[1].__class__.__module__ != "builtins": + if repr(i[1]).startswith("<"): + elements.append(f"{i[0]}: {i[1].name}") + elif repr(i[1]).startswith("UUID("): + elements.append(f"{i[0]}: {_p_id(i[1])}") + else: + elements.append(f"{i[0]}: {i[1].__str__()}") + else: + if isinstance(i[1], list): + elements.append( + f"{i[0]}: [" + ", ".join([el.__str__() for el in i[1]]) + "]" + ) + elif isinstance(i[1], dict): + elements.append(f"{i[0]}: {{{_p_osa(i[1])}}}") + else: + if i[1] is not None: + if i[0] == "slug": + elements.append(f"{i[0]}: {repr(i[1][:8])}") + else: + elements.append(f"{i[0]}: {repr(i[1])}") + + return ", ".join(elements) + + +def _p_ora(o, open="{", close="}", sep=": ", foo=repr): + if o.__class__.__module__ != "builtins": + if o.__class__.__name__ == "UUID": + return repr(o) + if isinstance(o, Enum): + return o + if isinstance(o, datetime): + return o.isoformat() + return f"{o.__class__.__name__}({_p_ora(o.__dict__, open='', close='', sep='=', foo=lambda x : x)})" + elif isinstance(o, list): + return f"[{', '.join([repr(el) for el in o])}]" + elif isinstance(o, dict): + o = OrderedDict(sorted(o.items())) + return f"{open}{', '.join([f"{foo(elk)}{sep}{_p_ora(elv)}" for elk, elv in o.items()])}{close}" + else: + if o is not None: + return repr(o) + + +def _str(o): + return f"{{{_p_osa(o.__dict__)}}}" + + +def _repr(o): + return _p_ora(o) + + +class DisplayBase(BaseModel): + def __str__(self): + return _str(self) + + def __repr__(self): + return _repr(self) + + +## --- SCOPE --- ## + + +class ProjectScopeDTO(DisplayBase): + project_id: UUID + + +## --- LIFECYCLE --- ## + + +class LifecycleDTO(DisplayBase): + created_at: datetime + updated_at: Optional[datetime] = None + + updated_by_id: Optional[UUID] = None + + +################################### +### services.observability.dtos ### +################################### + + +from typing import List, Dict, Any, Union, Optional, Sequence + +from enum import Enum +from datetime import datetime +from uuid import UUID + +## --- TIME --- ## + + +class TimeDTO(DisplayBase): + start: datetime + end: datetime + span: int + + +## --- STATUS --- ## + + +class StatusCode(Enum): + UNSET = "UNSET" + OK = "OK" + ERROR = "ERROR" + + +class StatusDTO(DisplayBase): + code: StatusCode + message: Optional[str] = None + stacktrace: Optional[str] = None + + +## --- ATTRIBUTES --- ## + +AttributeValueType = Any # +""" +AttributeValueType = Union[ + str, + bool, + int, + float, + Sequence[Union[str, bool, int, float]], +] +""" +Attributes = Dict[str, AttributeValueType] + + +class AttributesDTO(DisplayBase): + data: Optional[Attributes] = None + metrics: Optional[Attributes] = None + meta: Optional[Attributes] = None + tags: Optional[Attributes] = None + semconv: Optional[Attributes] = None + + +## --- HIERARCHICAL STRUCTURE --- ## + + +class TreeType(Enum): + # --- VARIANTS --- # + INVOCATION = "invocation" + # --- VARIANTS --- # + + +class NodeType(Enum): + # --- VARIANTS --- # + ## SPAN_KIND_SERVER + AGENT = "agent" + WORKFLOW = "workflow" + CHAIN = "chain" + ## SPAN_KIND_INTERNAL + TASK = "task" + ## SPAN_KIND_CLIENT + TOOL = "tool" + EMBEDDING = "embedding" + QUERY = "query" + COMPLETION = "completion" + CHAT = "chat" + RERANK = "rerank" + # --- VARIANTS --- # + + +class RootDTO(DisplayBase): + id: UUID + + +class TreeDTO(DisplayBase): + id: UUID + type: Optional[TreeType] = None + + +class NodeDTO(DisplayBase): + id: UUID + type: Optional[NodeType] = None + name: str + + +Data = Dict[str, Any] +Metrics = Dict[str, Any] +Metadata = Dict[str, Any] +Tags = Dict[str, str] +Refs = Dict[str, str] + + +class LinkDTO(DisplayBase): + type: str + id: UUID + tree_id: Optional[UUID] = None + + +class ParentDTO(DisplayBase): + id: UUID + + +class OTelSpanKind(Enum): + SPAN_KIND_UNSPECIFIED = "SPAN_KIND_UNSPECIFIED" + # INTERNAL + SPAN_KIND_INTERNAL = "SPAN_KIND_INTERNAL" + # SYNCHRONOUS + SPAN_KIND_SERVER = "SPAN_KIND_SERVER" + SPAN_KIND_CLIENT = "SPAN_KIND_CLIENT" + # ASYNCHRONOUS + SPAN_KIND_PRODUCER = "SPAN_KIND_PRODUCER" + SPAN_KIND_CONSUMER = "SPAN_KIND_CONSUMER" + + +class OTelStatusCode(Enum): + STATUS_CODE_OK = "STATUS_CODE_OK" + STATUS_CODE_ERROR = "STATUS_CODE_ERROR" + STATUS_CODE_UNSET = "STATUS_CODE_UNSET" + + +class OTelContextDTO(DisplayBase): + trace_id: str + span_id: str + + +class OTelEventDTO(DisplayBase): + name: str + timestamp: datetime + + attributes: Optional[Attributes] = None + + +class OTelLinkDTO(DisplayBase): + context: OTelContextDTO + + attributes: Optional[Attributes] = None + + +class OTelExtraDTO(DisplayBase): + kind: Optional[str] = None + + attributes: Optional[Attributes] = None + events: Optional[List[OTelEventDTO]] = None + links: Optional[List[OTelLinkDTO]] = None + + +## --- ENTITIES --- ## + + +class SpanDTO(DisplayBase): # DBE + scope: ProjectScopeDTO # DBA + + lifecycle: LifecycleDTO + + root: RootDTO + tree: TreeDTO + node: NodeDTO + + parent: Optional[ParentDTO] = None + + time: TimeDTO + status: StatusDTO + + data: Optional[Data] = None + metrics: Optional[Metrics] = None + meta: Optional[Metadata] = None + tags: Optional[Tags] = None + refs: Optional[Refs] = None + + links: Optional[List[LinkDTO]] = None + + otel: Optional[OTelExtraDTO] = None + + nodes: Optional[Dict[str, Union["SpanDTO", List["SpanDTO"]]]] = None + + +class SpanCreateDTO(DisplayBase): # DBE + scope: ProjectScopeDTO # DBA + + root: RootDTO + tree: TreeDTO + node: NodeDTO + + parent: Optional[ParentDTO] = None + + time: TimeDTO + status: StatusDTO + + data: Optional[Data] = None + metrics: Optional[Metrics] = None + meta: Optional[Metadata] = None + tags: Optional[Tags] = None + refs: Optional[Refs] = None + + links: Optional[List[LinkDTO]] = None + + otel: Optional[OTelExtraDTO] = None + + +class OTelSpanDTO(DisplayBase): + context: OTelContextDTO + + name: str + kind: OTelSpanKind = OTelSpanKind.SPAN_KIND_UNSPECIFIED + + start_time: datetime + end_time: datetime + + status_code: OTelStatusCode = OTelStatusCode.STATUS_CODE_UNSET + status_message: Optional[str] = None + + attributes: Optional[Attributes] = None + events: Optional[List[OTelEventDTO]] = None + + parent: Optional[OTelContextDTO] = None + links: Optional[List[OTelLinkDTO]] = None + + +################################### +### services.observability.utils ## +################################### + + +from typing import List, Dict, OrderedDict + + +def parse_span_dtos_to_span_idx( + span_dtos: List[SpanCreateDTO], +) -> Dict[str, SpanCreateDTO]: + + span_idx = {span_dto.node.id: span_dto for span_dto in span_dtos} + + return span_idx + + +def parse_span_idx_to_span_id_tree( + span_idx: Dict[str, SpanCreateDTO], +) -> OrderedDict: + + span_id_tree = OrderedDict() + index = {} + + def push(span_dto: SpanCreateDTO) -> None: + if span_dto.parent is None: + span_id_tree[span_dto.node.id] = OrderedDict() + index[span_dto.node.id] = span_id_tree[span_dto.node.id] + elif span_dto.parent.id in index: + index[span_dto.parent.id][span_dto.node.id] = OrderedDict() + index[span_dto.node.id] = index[span_dto.parent.id][span_dto.node.id] + + for span_dto in sorted(span_idx.values(), key=lambda span_dto: span_dto.time.start): + push(span_dto) + + return span_id_tree + + +def cumulate_costs( + spans_id_tree: OrderedDict, + spans_idx: Dict[str, SpanCreateDTO], +) -> None: + + def _get_unit(span: SpanCreateDTO): + if span.metrics is not None: + return span.metrics.get("unit.costs.total", 0.0) + + return 0.0 + + def _get_acc(span: SpanCreateDTO): + if span.metrics is not None: + return span.metrics.get("acc.costs.total", 0.0) + + return 0.0 + + def _acc(a: float, b: float): + return a + b + + def _set(span: SpanCreateDTO, cost: float): + if span.metrics is None: + span.metrics = {} + + if cost != 0.0: + span.metrics["acc.costs.total"] = cost + + _cumulate_tree_dfs(spans_id_tree, spans_idx, _get_unit, _get_acc, _acc, _set) + + +def cumulate_tokens( + spans_id_tree: OrderedDict, + spans_idx: Dict[str, dict], +) -> None: + + def _get_unit(span: SpanCreateDTO): + _tokens = { + "prompt": 0.0, + "completion": 0.0, + "total": 0.0, + } + + if span.metrics is not None: + return { + "prompt": span.metrics.get("unit.tokens.prompt", 0.0), + "completion": span.metrics.get("unit.tokens.completion", 0.0), + "total": span.metrics.get("unit.tokens.total", 0.0), + } + + return _tokens + + def _get_acc(span: SpanCreateDTO): + _tokens = { + "prompt": 0.0, + "completion": 0.0, + "total": 0.0, + } + + if span.metrics is not None: + return { + "prompt": span.metrics.get("acc.tokens.prompt", 0.0), + "completion": span.metrics.get("acc.tokens.completion", 0.0), + "total": span.metrics.get("acc.tokens.total", 0.0), + } + + return _tokens + + def _acc(a: dict, b: dict): + return { + "prompt": a.get("prompt", 0.0) + b.get("prompt", 0.0), + "completion": a.get("completion", 0.0) + b.get("completion", 0.0), + "total": a.get("total", 0.0) + b.get("total", 0.0), + } + + def _set(span: SpanCreateDTO, tokens: dict): + if span.metrics is None: + span.metrics = {} + + if tokens.get("prompt", 0.0) != 0.0: + span.metrics["acc.tokens.prompt"] = tokens.get("prompt", 0.0) + if tokens.get("completion", 0.0) != 0.0: + span.metrics["acc.tokens.completion"] = tokens.get("completion", 0.0) if tokens.get("completion", 0.0) != 0.0 else None + if tokens.get("total", 0.0) != 0.0: + span.metrics["acc.tokens.total"] = tokens.get("total", 0.0) if tokens.get("total", 0.0) != 0.0 else None + + _cumulate_tree_dfs(spans_id_tree, spans_idx, _get_unit, _get_acc, _acc, _set) + + +def _cumulate_tree_dfs( + spans_id_tree: OrderedDict, + spans_idx: Dict[str, SpanCreateDTO], + get_unit_metric, + get_acc_metric, + accumulate_metric, + set_metric, +): + for span_id, children_spans_id_tree in spans_id_tree.items(): + children_spans_id_tree: OrderedDict + + cumulated_metric = get_unit_metric(spans_idx[span_id]) + + _cumulate_tree_dfs( + children_spans_id_tree, + spans_idx, + get_unit_metric, + get_acc_metric, + accumulate_metric, + set_metric, + ) + + for child_span_id in children_spans_id_tree.keys(): + marginal_metric = get_acc_metric(spans_idx[child_span_id]) + cumulated_metric = accumulate_metric(cumulated_metric, marginal_metric) + + set_metric(spans_idx[span_id], cumulated_metric) + + +def connect_children( + spans_id_tree: OrderedDict, + spans_idx: Dict[str, dict], +) -> None: + _connect_tree_dfs(spans_id_tree, spans_idx) + + +def _connect_tree_dfs( + spans_id_tree: OrderedDict, + spans_idx: Dict[str, SpanDTO], +): + for span_id, children_spans_id_tree in spans_id_tree.items(): + children_spans_id_tree: OrderedDict + + parent_span = spans_idx[span_id] + + parent_span.nodes = dict() + + _connect_tree_dfs(children_spans_id_tree, spans_idx) + + for child_span_id in children_spans_id_tree.keys(): + child_span_name = spans_idx[child_span_id].node.name + if child_span_name not in parent_span.nodes: + parent_span.nodes[child_span_name] = spans_idx[child_span_id] + else: + if not isinstance(parent_span.nodes[child_span_name], list): + parent_span.nodes[child_span_name] = [ + parent_span.nodes[child_span_name] + ] + + parent_span.nodes[child_span_name].append(spans_idx[child_span_id]) + + if len(parent_span.nodes) == 0: + parent_span.nodes = None + + +######################################################## +### apis.fastapi.observability.opentelemetry.semconv ### +######################################################## + + +VERSION = "0.4.1" + +V_0_4_1_ATTRIBUTES_EXACT = [ + ("gen_ai.system", "ag.meta.system"), + ("gen_ai.request.base_url", "ag.meta.request.base_url"), + ("gen_ai.request.endpoint", "ag.meta.request.endpoint"), + ("gen_ai.request.headers", "ag.meta.request.headers"), + ("gen_ai.request.type", "ag.type.node"), + ("gen_ai.request.streaming", "ag.meta.request.streaming"), + ("gen_ai.request.model", "ag.meta.request.model"), + ("gen_ai.request.max_tokens", "ag.meta.request.max_tokens"), + ("gen_ai.request.temperature", "ag.meta.request.temperature"), + ("gen_ai.request.top_p", "ag.meta.request.top_p"), + ("gen_ai.response.model", "ag.meta.response.model"), + ("gen_ai.usage.prompt_tokens", "ag.metrics.unit.tokens.prompt"), + ("gen_ai.usage.completion_tokens", "ag.metrics.unit.tokens.completion"), + ("gen_ai.usage.total_tokens", "ag.metrics.unit.tokens.total"), + ("llm.headers", "ag.meta.request.headers"), + ("llm.request.type", "ag.type.node"), + ("llm.top_k", "ag.meta.request.top_k"), + ("llm.is_streaming", "ag.meta.request.streaming"), + ("llm.usage.total_tokens", "ag.metrics.unit.tokens.total"), + ("gen_ai.openai.api_base", "ag.meta.request.base_url"), + ("db.system", "ag.meta.system"), + ("db.vector.query.top_k", "ag.meta.request.top_k"), + ("pinecone.query.top_k", "ag.meta.request.top_k"), + ("traceloop.span.kind", "ag.type.node"), +] +V_0_4_1_ATTRIBUTES_PREFIX = [ + ("gen_ai.prompt", "ag.data.inputs.prompt"), + ("gen_ai.completion", "ag.data.outputs.completion"), +] + +V_0_4_1_MAPS = { + "attributes": { + "exact": { + "from": {otel: agenta for otel, agenta in V_0_4_1_ATTRIBUTES_EXACT[::-1]}, + "to": {agenta: otel for otel, agenta in V_0_4_1_ATTRIBUTES_EXACT[::-1]}, + }, + "prefix": { + "from": {otel: agenta for otel, agenta in V_0_4_1_ATTRIBUTES_PREFIX[::-1]}, + "to": {agenta: otel for otel, agenta in V_0_4_1_ATTRIBUTES_PREFIX[::-1]}, + }, + }, +} +V_0_4_1_KEYS = { + "attributes": { + "exact": { + "from": list(V_0_4_1_MAPS["attributes"]["exact"]["from"].keys()), + "to": list(V_0_4_1_MAPS["attributes"]["exact"]["to"].keys()), + }, + "prefix": { + "from": list(V_0_4_1_MAPS["attributes"]["prefix"]["from"].keys()), + "to": list(V_0_4_1_MAPS["attributes"]["prefix"]["to"].keys()), + }, + }, +} + + +MAPS = { + "0.4.1": V_0_4_1_MAPS, # LATEST +} +KEYS = { + "0.4.1": V_0_4_1_KEYS, # LATEST +} + +CODEX = {"maps": MAPS[VERSION], "keys": KEYS[VERSION]} + + +######################################## +### apis.fastapi.observability.utils ### +######################################## + + +from typing import Optional, Union, Tuple, Any, List, Dict +from uuid import UUID +from collections import OrderedDict +from json import loads, JSONDecodeError, dumps +from copy import copy + + +def _unmarshal_attributes( + marshalled: Dict[str, Any], +) -> Dict[str, Any]: + """ + Unmarshals a dictionary of marshalled attributes into a nested dictionary + + Example: + marshalled = { + "ag.type": "tree", + "ag.node.name": "root", + "ag.node.children.0.name": "child1", + "ag.node.children.1.name": "child2" + } + unmarshalled = { + "ag": { + "type": "tree", + "node": { + "name": "root", + "children": [ + { + "name": "child1", + }, + { + "name": "child2", + } + ] + } + } + } + """ + unmarshalled = {} + + for key, value in marshalled.items(): + keys = key.split(".") + + level = unmarshalled + + for i, part in enumerate(keys[:-1]): + + if part.isdigit(): + part = int(part) + + if not isinstance(level, list): + level = [] + + while len(level) <= part: + level.append({}) + + level = level[part] + + else: + if part not in level: + level[part] = {} if not keys[i + 1].isdigit() else [] + + level = level[part] + + last_key = keys[-1] + + if last_key.isdigit(): + last_key = int(last_key) + + if not isinstance(level, list): + level = [] + + while len(level) <= last_key: + level.append(None) + + level[last_key] = value + + else: + level[last_key] = value + + return unmarshalled + + +def _encode_key( + namespace, + key: str, +) -> str: + return f"ag.{namespace}.{key}" + + +def _decode_key( + namespace, + key: str, +) -> str: + return key.replace(f"ag.{namespace}.", "") + + +def _encode_value(value: Any) -> Optional[Any]: + if value is None: + return None + + if isinstance(value, (str, int, float, bool, bytes)): + return value + + if isinstance(value, dict) or isinstance(value, list): + encoded = dumps(value) + value = "@ag.type=json:" + encoded + return value + + return repr(value) + + +def _decode_value( + value: Any, +) -> Any: + if isinstance(value, (int, float, bool, bytes)): + return value + + if isinstance(value, str): + if value == "@ag.type=none:": + return None + + if value.startswith("@ag.type=json:"): + encoded = value[len("@ag.type=json:") :] + value = loads(encoded) + return value + + return value + + return value + + +def _get_attributes( + attributes: Attributes, + namespace: str, +): + return { + _decode_key(namespace, key): _decode_value(value) + for key, value in attributes.items() + if key != _decode_key(namespace, key) + } + + +def _parse_from_types( + otel_span_dto: OTelSpanDTO, +) -> dict: + types = _get_attributes(otel_span_dto.attributes, "type") + + if types.get("tree"): + del otel_span_dto.attributes[_encode_key("type", "tree")] + + if types.get("node"): + del otel_span_dto.attributes[_encode_key("type", "node")] + + return types + + +def _parse_from_semconv( + attributes: Attributes, +) -> None: + _attributes = copy(attributes) + + for old_key, value in _attributes.items(): + if old_key in CODEX["keys"]["attributes"]["exact"]["from"]: + new_key = CODEX["maps"]["attributes"]["exact"]["from"][old_key] + + attributes[new_key] = value + + del attributes[old_key] + + else: + for prefix_key in CODEX["keys"]["attributes"]["prefix"]["from"]: + if old_key.startswith(prefix_key): + prefix = CODEX["maps"]["attributes"]["prefix"]["from"][prefix_key] + + new_key = old_key.replace(prefix_key, prefix) + + attributes[new_key] = value + + del attributes[old_key] + + +def _parse_from_links( + otel_span_dto: OTelSpanDTO, +) -> dict: + # TESTING + otel_span_dto.links = [ + OTelLinkDTO( + context=otel_span_dto.context, + attributes={"ag.type.link": "testcase"}, + ) + ] + # ------- + + # LINKS + links = None + otel_links = None + + if otel_span_dto.links: + links = list() + otel_links = list() + + for link in otel_span_dto.links: + _links = _get_attributes(link.attributes, "type") + + if _links: + link_type = _links.get("link") + link_tree_id = str(UUID(link.context.trace_id[2:])) + link_node_id = str( + UUID(link.context.trace_id[2 + 16 :] + link.context.span_id[2:]) + ) + + links.append( + LinkDTO( + type=link_type, + tree_id=link_tree_id, + id=link_node_id, + ) + ) + else: + otel_links.append(link) + + links = links if links else None + otel_links = otel_links if otel_links else None + + otel_span_dto.links = otel_links + + return links + + +def _parse_from_attributes( + otel_span_dto: OTelSpanDTO, +) -> Tuple[dict, dict, dict, dict, dict]: + + # DATA + _data = _get_attributes(otel_span_dto.attributes, "data") + + for key in _data.keys(): + del otel_span_dto.attributes[_encode_key("data", key)] + + # _data = _unmarshal_attributes(_data) + _data = _data if _data else None + + # METRICS + _metrics = _get_attributes(otel_span_dto.attributes, "metrics") + + for key in _metrics.keys(): + del otel_span_dto.attributes[_encode_key("metrics", key)] + + # _metrics = _unmarshal_attributes(_metrics) + _metrics = _metrics if _metrics else None + + # META + _meta = _get_attributes(otel_span_dto.attributes, "meta") + + for key in _meta.keys(): + del otel_span_dto.attributes[_encode_key("meta", key)] + + # _meta = _unmarshal_attributes(_meta) + _meta = _meta if _meta else None + + # TAGS + _tags = _get_attributes(otel_span_dto.attributes, "tags") + + for key in _tags.keys(): + del otel_span_dto.attributes[_encode_key("tags", key)] + + _tags = _tags if _tags else None + + # REFS + _refs = _get_attributes(otel_span_dto.attributes, "refs") + + for key in _refs.keys(): + del otel_span_dto.attributes[_encode_key("refs", key)] + + _refs = _refs if _refs else None + + if len(otel_span_dto.attributes.keys()) < 1: + otel_span_dto.attributes = None + + return _data, _metrics, _meta, _tags, _refs + + +def parse_from_otel_span_dto( + project_id: str, + otel_span_dto: OTelSpanDTO, +) -> SpanDTO: + scope = ProjectScopeDTO(project_id=UUID(project_id)) + + lifecyle = LifecycleDTO( + created_at=datetime.now(), + ) + + _parse_from_semconv(otel_span_dto.attributes) + + types = _parse_from_types(otel_span_dto) + + tree_id = UUID(otel_span_dto.context.trace_id[2:]) + + tree_type: str = types.get("tree") + + tree = TreeDTO( + id=tree_id, + type=tree_type.lower() if tree_type else None, + ) + + node_id = UUID(tree_id.hex[16:] + otel_span_dto.context.span_id[2:]) + + node_type: str = types.get("node") + + node = NodeDTO( + id=node_id, + type=node_type.lower() if node_type else None, + name=otel_span_dto.name, + ) + + parent = ( + ParentDTO( + id=( + UUID( + otel_span_dto.parent.trace_id[2 + 16 :] + + otel_span_dto.parent.span_id[2:] + ) + ) + ) + if otel_span_dto.parent + else None + ) + + duration = (otel_span_dto.end_time - otel_span_dto.start_time).total_seconds() + + time = TimeDTO( + start=otel_span_dto.start_time, + end=otel_span_dto.end_time, + span=round(duration * 1_000_000), # microseconds + ) + + status = StatusDTO( + code=otel_span_dto.status_code.value.replace("STATUS_CODE_", ""), + message=otel_span_dto.status_message, + ) + + links = _parse_from_links(otel_span_dto) + + data, metrics, meta, tags, refs = _parse_from_attributes(otel_span_dto) + + root_id = str(tree_id) + if refs is not None: + root_id = refs.get("scenario.id", root_id) + + root = RootDTO(id=UUID(root_id)) + + otel = OTelExtraDTO( + kind=otel_span_dto.kind.value, + attributes=otel_span_dto.attributes, + events=otel_span_dto.events, + links=otel_span_dto.links, + ) + + span_dto = SpanDTO( + scope=scope, + lifecycle=lifecyle, + root=root, + tree=tree, + node=node, + parent=parent, + time=time, + status=status, + data=data, + metrics=metrics, + meta=meta, + tags=tags, + refs=refs, + links=links, + otel=otel, + ) + + return span_dto + + +def parse_to_agenta_span_dto( + span_dto: SpanDTO, +) -> SpanDTO: + # DATA + if span_dto.data: + span_dto.data = _unmarshal_attributes(span_dto.data) + + if "outputs" in span_dto.data: + if "__default__" in span_dto.data["outputs"]: + span_dto.data["outputs"] = span_dto.data["outputs"]["__default__"] + + # METRICS + if span_dto.metrics: + span_dto.metrics = _unmarshal_attributes(span_dto.metrics) + + # META + if span_dto.meta: + span_dto.meta = _unmarshal_attributes(span_dto.meta) + + # TAGS + if span_dto.tags: + span_dto.tags = _unmarshal_attributes(span_dto.tags) + + # REFS + if span_dto.refs: + span_dto.refs = _unmarshal_attributes(span_dto.refs) + + for link in span_dto.links: + link.tree_id = None + + if span_dto.nodes: + for v in span_dto.nodes.values(): + if isinstance(v, list): + for n in v: + parse_to_agenta_span_dto(n) + else: + parse_to_agenta_span_dto(v) + + return span_dto + + +################# +### THIS FILE ### +################# + + +from typing import Dict + +from opentelemetry.sdk.trace import ReadableSpan + + +def parse_inline_trace( + project_id: str, + spans: Dict[str, ReadableSpan], +): + print("-----------------------------") + + otel_span_dtos = _parse_readable_spans(spans) + + ############################################################ + ### apis.fastapi.observability.api.otlp_collect_traces() ### + ############################################################ + + span_dtos = [ + parse_from_otel_span_dto(project_id, otel_span_dto) + for otel_span_dto in otel_span_dtos + ] + + ##################################################### + ### services.observability.service.ingest/query() ### + ##################################################### + + span_idx = parse_span_dtos_to_span_idx(span_dtos) + + span_id_tree = parse_span_idx_to_span_id_tree(span_idx) + + ############################################### + ### services.observability.service.ingest() ### + ############################################### + + cumulate_costs(span_id_tree, span_idx) + + cumulate_tokens(span_id_tree, span_idx) + + ############################################## + ### services.observability.service.query() ### + ############################################## + + connect_children(span_id_tree, span_idx) + + root_span_dtos = [span_dto for span_dto in span_idx.values()] + + ############################################################ + ### apis.fastapi.observability.api.query() ### + ############################################################ + + agenta_span_dtos = [parse_to_agenta_span_dto(span_dto) for span_dto in root_span_dtos] + + _spans = [ + loads(span_dto.model_dump_json(exclude_none=True)) for span_dto in agenta_span_dtos + ] + + [print(_span) for _span in _spans] + + print("-----------------------------") + + return _spans + + +def _parse_readable_spans( + spans: List[ReadableSpan], +) -> List[OTelSpanDTO]: + otel_span_dtos = list() + + for span in spans: + otel_span_dto = OTelSpanDTO( + context=OTelContextDTO( + trace_id=_int_to_hex(span.get_span_context().trace_id, 128), + span_id=_int_to_hex(span.get_span_context().span_id, 64), + ), + name=span.name, + kind=OTelSpanKind("SPAN_KIND_" + (span.kind if isinstance(span.kind, str) else span.kind.name)), + start_time=_timestamp_ns_to_datetime(span.start_time), + end_time=_timestamp_ns_to_datetime(span.end_time), + status_code=OTelStatusCode("STATUS_CODE_" + span.status.status_code.name), + status_message=span.status.description, + attributes=span.attributes, + events=[ + OTelEventDTO( + name=event.name, + timestamp=_timestamp_ns_to_datetime(event.timestamp), + attributes=event.attributes, + ) + for event in span.events + ], + parent=OTelContextDTO( + trace_id=_int_to_hex(span.parent.trace_id, 128), + span_id=_int_to_hex(span.parent.span_id, 64) + ) if span.parent else None, + links=[ + OTelLinkDTO( + context=OTelContextDTO( + trace_id=_int_to_hex(link.context.trace_id, 128), + span_id=_int_to_hex(link.context.span_id, 64), + ), + attributes=link.attributes, + ) + for link in span.links + ], + ) + + + otel_span_dtos.append(otel_span_dto) + + return otel_span_dtos + + +def _int_to_hex(integer, bits): + _hex = hex(integer)[2:] + + _hex = _hex.zfill(bits // 4) + + _hex = "0x" + _hex + + return _hex + + +def _timestamp_ns_to_datetime(timestamp_ns): + _datetime = datetime.fromtimestamp( + timestamp_ns / 1_000_000_000, + ).isoformat( + timespec="microseconds", + ) + + return _datetime diff --git a/agenta-cli/agenta/sdk/tracing/inline_trace.py b/agenta-cli/agenta/sdk/tracing/inline_trace.py deleted file mode 100644 index a99cd26f40..0000000000 --- a/agenta-cli/agenta/sdk/tracing/inline_trace.py +++ /dev/null @@ -1,145 +0,0 @@ -from typing import Dict -from json import loads -from datetime import datetime - -from opentelemetry.sdk.trace.export import ReadableSpan - -from agenta.client.backend.types.create_span import CreateSpan -from agenta.client.backend.types.llm_tokens import LlmTokens -from agenta.sdk.utils.logging import log -from agenta.sdk.tracing.spans import get_attributes -from agenta.sdk.tracing.trace_tree import ( - make_spans_id_tree, - cumulate_costs, - cumulate_tokens, -) - - -def get_trace_id(spans: Dict[str, ReadableSpan]): - traces_idx: Dict[str, Dict[str, ReadableSpan]] = dict() - - for span in spans.values(): - span: ReadableSpan - - trace_id = span.context.trace_id.to_bytes(16, "big").hex() - span_id = span.context.span_id.to_bytes(8, "big").hex() - - if trace_id not in traces_idx: - traces_idx[trace_id] = list() - - traces_idx[trace_id].append(span_id) - - spans.clear() # might need to be moved to a context variable - - if len(traces_idx) > 1: - log.error("Error while parsing inline trace: too many traces.") - trace_id = list(traces_idx.keys())[0] - - return { - "trace_id": trace_id, - } - - -def get_trace(spans: Dict[str, ReadableSpan], trace_id_only: bool = False): - traces_idx: Dict[str, Dict[str, ReadableSpan]] = dict() - - for span in spans.values(): - span: ReadableSpan - - trace_id = span.context.trace_id.to_bytes(16, "big").hex() - span_id = span.context.span_id.to_bytes(8, "big").hex() - - if trace_id not in traces_idx: - traces_idx[trace_id] = dict() - - if not trace_id_only: - traces_idx[trace_id][span_id] = _parse_to_legacy_span(span) - - spans.clear() # might need to be moved to a context variable - - if len(traces_idx) > 1: - log.error("Error while parsing inline trace: too many traces.") - trace_id = list(traces_idx.keys())[0] - - spans_idx = traces_idx[trace_id] - spans_id_tree = make_spans_id_tree(spans_idx) - - if len(spans_id_tree) > 1: - log.error("Error while parsing inline trace: too many root spans.") - root_span_id = list(spans_id_tree.keys())[0] - - cumulate_costs(spans_id_tree, spans_idx) - cumulate_tokens(spans_id_tree, spans_idx) - - inline_trace = { - "trace_id": trace_id, - "cost": spans_idx[root_span_id]["cost"], - "tokens": spans_idx[root_span_id]["tokens"], - "latency": datetime.fromisoformat( - spans_idx[root_span_id]["end_time"].replace("Z", "+00:00") - ).timestamp() - - datetime.fromisoformat( - spans_idx[root_span_id]["start_time"].replace("Z", "+00:00") - ).timestamp(), - "spans": list(spans_idx.values()), - } - - return inline_trace - - -def _parse_to_legacy_span(span: ReadableSpan): - attributes = dict(span.attributes) - - for event in span.events: - if event.name == "exception": - attributes.update(**event.attributes) - - legacy_span = CreateSpan( - id=span.context.span_id.to_bytes(8, "big").hex(), - spankind=get_attributes(span, "extra").get("kind", "UNKNOWN"), - name=span.name, - status=str(span.status.status_code.name), - # - start_time=datetime.fromtimestamp( - span.start_time / 1_000_000_000, - ).isoformat(), - end_time=datetime.fromtimestamp( - span.end_time / 1_000_000_000, - ).isoformat(), - # - parent_span_id=( - span.parent.span_id.to_bytes(8, "big").hex() if span.parent else None - ), - # - inputs=get_attributes(span, "data.inputs"), - internals=get_attributes(span, "data.internals"), - outputs=get_attributes(span, "data.outputs"), - # - config=get_attributes(span, "metadata").get("config", {}), - # - tokens=LlmTokens( - prompt_tokens=get_attributes(span, "metrics.marginal.tokens").get( - "prompt", 0 - ), - completion_tokens=get_attributes(span, "metrics.marginal.tokens").get( - "completion", 0 - ), - total_tokens=get_attributes(span, "metrics.marginal.tokens").get( - "total", 0 - ), - ), - cost=get_attributes(span, "metrics.marginal.costs").get("total", 0.0), - # - app_id=get_attributes(span, "extra").get("app_id", ""), - environment=get_attributes(span, "metadata").get("environment"), - # - attributes=attributes, - # - variant_id=None, - variant_name=None, - tags=None, - token_consumption=None, - user=None, - ) - - return loads(legacy_span.model_dump_json()) diff --git a/agenta-cli/agenta/sdk/tracing/litellm.py b/agenta-cli/agenta/sdk/tracing/litellm.py deleted file mode 100644 index 2839a5f1d9..0000000000 --- a/agenta-cli/agenta/sdk/tracing/litellm.py +++ /dev/null @@ -1,268 +0,0 @@ -import agenta as ag - - -def litellm_handler(): - try: - from litellm.utils import ModelResponse - from litellm.integrations.custom_logger import ( - CustomLogger as LitellmCustomLogger, - ) - except ImportError as exc: - raise ImportError( - "The litellm SDK is not installed. Please install it using `pip install litellm`." - ) from exc - except Exception as exc: - raise Exception( - "Unexpected error occurred when importing litellm: {}".format(exc) - ) from exc - - class LitellmHandler(LitellmCustomLogger): - """This handler is responsible for instrumenting certain events when using litellm to call LLMs. - - Args: - LitellmCustomLogger (object): custom logger that allows us to override the events to capture. - """ - - def __init__(self): - self.span = None - - def log_pre_api_call( - self, - model, - messages, - kwargs, - ): - kind = ( - "GENERATION" - if kwargs.get("call_type") in ["completion", "acompletion"] - else "EMBEDDING" - ) - - self.span = ag.tracing.start_span(name=f"litellm_{kind.lower()}", kind=kind) - - ag.logging.warning(f"------> {self.span}") - - if not self.span: - ag.logging.error("LiteLLM callback error: span not found.") - return - - ag.logging.info(f"log_pre_api_call({hex(self.span.context.span_id)[2:]})") - - ag.tracing.set_attributes( - namespace="data.inputs", - attributes={"messages": kwargs["messages"]}, - span=self.span, - ) - - ag.tracing.set_attributes( - namespace="metadata.config", - attributes={ - "model": kwargs.get("model"), - **kwargs.get("optional_params"), - }, - span=self.span, - ) - - def log_stream_event( - self, - kwargs, - response_obj, - start_time, - end_time, - ): - if not self.span: - ag.logging.error("LiteLLM callback error: span not found.") - return - - # ag.logging.info(f"log_stream({hex(self.span.context.span_id)[2:]})") - - ag.tracing.set_attributes( - namespace="data.outputs", - attributes={"__default__": kwargs.get("complete_streaming_response")}, - span=self.span, - ) - - ag.tracing.set_attributes( - namespace="metrics.marginal.costs", - attributes={"marginal": kwargs.get("response_cost")}, - span=self.span, - ) - - ag.tracing.set_attributes( - namespace="metrics.marginal.tokens", - attributes=( - { - "prompt": response_obj.usage.prompt_tokens, - "completion": response_obj.usage.completion_tokens, - "total": response_obj.usage.total_tokens, - } - ), - span=self.span, - ) - - ag.tracing.set_status(status="OK", span=self.span) - - self.span.end() - - def log_success_event( - self, - kwargs, - response_obj, - start_time, - end_time, - ): - if not self.span: - ag.logging.error("LiteLLM callback error: span not found.") - return - - # ag.logging.info(f"log_success({hex(self.span.context.span_id)[2:]})") - - ag.tracing.set_attributes( - namespace="data.outputs", - attributes={"__default__": response_obj.choices[0].message.content}, - span=self.span, - ) - - ag.tracing.set_attributes( - namespace="metrics.marginal.costs", - attributes={"marginal": kwargs.get("response_cost")}, - span=self.span, - ) - - ag.tracing.set_attributes( - namespace="metrics.marginal.tokens", - attributes=( - { - "prompt": response_obj.usage.prompt_tokens, - "completion": response_obj.usage.completion_tokens, - "total": response_obj.usage.total_tokens, - } - ), - span=self.span, - ) - - ag.tracing.set_status(status="OK", span=self.span) - - self.span.end() - - def log_failure_event( - self, - kwargs, - response_obj, - start_time, - end_time, - ): - if not self.span: - ag.logging.error("LiteLLM callback error: span not found.") - return - - # ag.logging.info(f"log_failure({hex(self.span.context.span_id)[2:]})") - - ag.tracing.record_exception(kwargs["exception"], span=self.span) - - ag.tracing.set_status(status="ERROR", span=self.span) - - self.span.end() - - async def async_log_stream_event( - self, - kwargs, - response_obj, - start_time, - end_time, - ): - if not self.span: - ag.logging.error("LiteLLM callback error: span not found.") - return - - # ag.logging.info(f"async_log_stream({hex(self.span.context.span_id)[2:]})") - - ag.tracing.set_attributes( - namespace="data.outputs", - attributes={"__default__": kwargs.get("complete_streaming_response")}, - span=self.span, - ) - - ag.tracing.set_attributes( - namespace="metrics.marginal.costs", - attributes={"marginal": kwargs.get("response_cost")}, - span=self.span, - ) - - ag.tracing.set_attributes( - namespace="metrics.marginal.tokens", - attributes=( - { - "prompt": response_obj.usage.prompt_tokens, - "completion": response_obj.usage.completion_tokens, - "total": response_obj.usage.total_tokens, - } - ), - span=self.span, - ) - - ag.tracing.set_status(status="OK", span=self.span) - - self.span.end() - - async def async_log_success_event( - self, - kwargs, - response_obj, - start_time, - end_time, - ): - if not self.span: - ag.logging.error("LiteLLM callback error: span not found.") - return - - ag.logging.info(f"async_log_success({hex(self.span.context.span_id)[2:]})") - - ag.tracing.set_attributes( - namespace="data.outputs", - attributes={"__default__": response_obj.choices[0].message.content}, - span=self.span, - ) - - ag.tracing.set_attributes( - namespace="metrics.marginal.costs", - attributes={"marginal": kwargs.get("response_cost")}, - span=self.span, - ) - - ag.tracing.set_attributes( - namespace="metrics.marginal.tokens", - attributes=( - { - "prompt": response_obj.usage.prompt_tokens, - "completion": response_obj.usage.completion_tokens, - "total": response_obj.usage.total_tokens, - } - ), - span=self.span, - ) - - ag.tracing.set_status(status="OK", span=self.span) - - self.span.end() - - async def async_log_failure_event( - self, - kwargs, - response_obj, - start_time, - end_time, - ): - if not self.span: - ag.logging.error("LiteLLM callback error: span not found.") - return - - # ag.logging.info(f"async_log_failure({hex(self.span.context.span_id)[2:]})") - - ag.tracing.record_exception(kwargs["exception"], span=self.span) - - ag.tracing.set_status(status="ERROR", span=self.span) - - self.span.end() - - return LitellmHandler() diff --git a/agenta-cli/agenta/sdk/tracing/opentelemetry.py b/agenta-cli/agenta/sdk/tracing/opentelemetry.py deleted file mode 100644 index 331405baeb..0000000000 --- a/agenta-cli/agenta/sdk/tracing/opentelemetry.py +++ /dev/null @@ -1,220 +0,0 @@ -import httpx - -from typing import Optional, Union, Any, Dict -from contextlib import contextmanager, suppress - -from opentelemetry.trace import set_tracer_provider -from opentelemetry.trace.propagation import get_current_span -from opentelemetry.sdk.trace import Span -from opentelemetry.sdk.trace.export import ReadableSpan - -from agenta.sdk.utils.logging import log - -from agenta.sdk.tracing.conventions import Namespace, Status -from agenta.sdk.tracing.tracers import ConcurrentTracerProvider -from agenta.sdk.tracing.spans import ( - set_status as otel_set_status, - add_event as otel_add_event, - record_exception as otel_record_exception, - set_attributes as otel_set_attributes, - get_attributes as otel_get_attributes, -) -from agenta.sdk.tracing.inline_trace import ( - get_trace as inline_get_trace, - get_trace_id as inline_get_trace_id, -) - -_AGENTA_API_KEY_HEADER = "Authorization" - -log.setLevel("DEBUG") - - -class Tracing: - VERSION = "0.1.0" - - # @suppress(Exception) - def __init__( - self, - url: str, - app_id: Optional[str] = None, - api_key: Optional[str] = None, - ) -> None: - # ENDPOINT - self.url = url # "http://localhost:4318/v1/traces" - # AUTHENTICATION - self.app_id = app_id - # AUTHORIZATION - self.api_key = api_key - - # HEADERS - self.headers = {} - if api_key: - self.headers.update(**{_AGENTA_API_KEY_HEADER: self.api_key}) - - # SPANS (INLINE TRACE) - self.spans: Dict[str, ReadableSpan] = dict() - - # TRACER PROVIDER - self.tracer_provider = ConcurrentTracerProvider("agenta", Tracing.VERSION) - - # TRACE PROCESSORS - self.inline_processor = self.tracer_provider.add_inline_processor( - registry=self.spans, - scope={"app_id": self.app_id}, - ) - - try: - log.info(f"Connecting to the remote trace receiver at {self.url}...") - - httpx.get(self.url, headers=self.headers) - - log.info(f"Connection established.") - - self.otlp_processor = self.tracer_provider.add_otlp_processor( - endpoint=self.url, - headers=self.headers, - scope={"app_id": self.app_id}, - ) - except: - log.warning(f"Connection failed.") - log.error( - f"Warning: Your traces will not be exported since {self.url} is unreachable." - ) - - # GLOBAL TRACER PROVIDER - set_tracer_provider(self.tracer_provider) - - # TRACER - self.tracer = self.tracer_provider.get_tracer("agenta.tracer") - - @contextmanager - def start_as_current_span(self, name: str, kind: str): - try: - with self.tracer.start_as_current_span(name) as span: - self.set_attributes( - namespace="extra", - attributes={"kind": kind}, - span=span, - ) - - yield span - except Exception as e: - yield None - - def start_span(self, name: str, kind: str) -> Optional[Span]: - try: - span = self.tracer.start_span(name) - - self.set_attributes( - namespace="extra", - attributes={"kind": kind}, - span=span, - ) - - return span - except Exception as e: - return None - - def set_status( - self, - status: Status, - message: Optional[str] = None, - span: Optional[Span] = None, - ) -> None: - if span is None: - span = get_current_span() - - with suppress(Exception): - otel_set_status(span, status, message) - - def add_event( - self, - name, - attributes=None, - timestamp=None, - span: Optional[Span] = None, - ) -> None: - if span is None: - span = get_current_span() - - with suppress(Exception): - otel_add_event(span, name, attributes, timestamp) - - def record_exception( - self, - exception, - attributes=None, - timestamp=None, - span: Optional[Span] = None, - ) -> None: - if span is None: - span = get_current_span() - - with suppress(Exception): - otel_record_exception(span, exception, attributes, timestamp) - - def set_attributes( - self, - namespace: Namespace, - attributes: Dict[str, Any], - span: Optional[Span] = None, - ) -> None: - if span is None: - span = get_current_span() - - with suppress(Exception): - otel_set_attributes(span, namespace, attributes) - - def get_attributes( - self, - namespace: Namespace, - span: Optional[Union[ReadableSpan, Span]] = None, - ) -> Dict[str, Any]: - if span is None: - span = get_current_span() - - attributes = {} - - with suppress(Exception): - attributes = otel_get_attributes(span, namespace) - - return attributes - - def store_internals( - self, - attributes: Dict[str, Any], - span: Optional[Span] = None, - ) -> None: - if span is None: - span = get_current_span() - - with suppress(Exception): - self.set_attributes( - namespace="data.internals", - attributes=attributes, - span=span, - ) - - def is_processing(self) -> bool: - processing = False - - with suppress(Exception): - processing = self.inline_processor.is_done() - - return processing - - def get_inline_trace(self) -> Dict[str, Any]: - trace = {} - - with suppress(Exception): - trace = inline_get_trace(self.spans) - - return trace - - def get_trace_id_only(self) -> Dict[str, Any]: - trace = {} - - with suppress(Exception): - trace = inline_get_trace_id(self.spans) - - return trace diff --git a/agenta-cli/agenta/sdk/tracing/processors.py b/agenta-cli/agenta/sdk/tracing/processors.py index 2d0cce41ec..4ef860d589 100644 --- a/agenta-cli/agenta/sdk/tracing/processors.py +++ b/agenta-cli/agenta/sdk/tracing/processors.py @@ -10,12 +10,13 @@ _DEFAULT_MAX_QUEUE_SIZE, ) +# LOAD CONTEXT, HERE + class TraceProcessor(BatchSpanProcessor): def __init__( self, span_exporter: SpanExporter, - scope: Dict[str, Any] = None, max_queue_size: int = None, schedule_delay_millis: float = None, max_export_batch_size: int = None, @@ -29,39 +30,31 @@ def __init__( _DEFAULT_EXPORT_TIMEOUT_MILLIS, ) - self.registry = dict() - self.scope = scope - - def on_start( - self, - span: Span, - parent_context: Optional[Context] = None, - ) -> None: - super().on_start(span, parent_context=parent_context) + self._registry = dict() + self._exporter = span_exporter - span.set_attributes( - attributes={f"ag.extra.{k}": v for k, v in self.scope.items()} - ) + def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None: + # ADD LINKS FROM CONTEXT, HERE - if span.context.trace_id not in self.registry: - self.registry[span.context.trace_id] = dict() + if span.context.trace_id not in self._registry: + self._registry[span.context.trace_id] = dict() - self.registry[span.context.trace_id][span.context.span_id] = True + self._registry[span.context.trace_id][span.context.span_id] = True - def on_end( - self, - span: ReadableSpan, - ): + def on_end(self, span: ReadableSpan): super().on_end(span) - del self.registry[span.context.trace_id][span.context.span_id] + del self._registry[span.context.trace_id][span.context.span_id] - if self.is_done(): + if self.is_ready(span.get_span_context().trace_id): self.force_flush() - def is_done( - self, - ): - return all( - not len(self.registry[trace_id]) for trace_id in self.registry.keys() - ) + def is_ready(self, trace_id: Optional[int] = None) -> bool: + is_ready = not len(self._registry.get(trace_id, {})) + + return is_ready + + def fetch(self, trace_id: Optional[int] = None) -> Dict[str, ReadableSpan]: + trace = self._exporter.fetch(trace_id) # type: ignore + + return trace diff --git a/agenta-cli/agenta/sdk/tracing/spans.py b/agenta-cli/agenta/sdk/tracing/spans.py index 244e852ce4..e880e78c1f 100644 --- a/agenta-cli/agenta/sdk/tracing/spans.py +++ b/agenta-cli/agenta/sdk/tracing/spans.py @@ -1,126 +1,124 @@ -from json import loads, dumps from typing import Optional, Union, Any, Dict -from opentelemetry.sdk.trace import Span, Status, StatusCode -from opentelemetry.sdk.trace.export import ReadableSpan - - -def set_status(span: Span, status: str, message: Optional[str] = None) -> None: - if status == "OK": - if span.status.status_code != StatusCode.ERROR: - span.set_status( - Status(status_code=StatusCode.OK, description=message), - ) - elif status == "ERROR": - span.set_status( - Status(status_code=StatusCode.ERROR, description=message), +from opentelemetry.trace import SpanContext +from opentelemetry.trace.status import Status, StatusCode +from opentelemetry.sdk.trace import Span + +from agenta.sdk.tracing.attributes import serialize + + +class CustomSpan(Span): # INHERITANCE FOR TYPING ONLY + def __init__(self, span: Span) -> None: + super().__init__( # INHERITANCE FOR TYPING ONLY + name=span.name, + context=span.context, + parent=span.parent, + sampler=span._sampler, + trace_config=span._trace_config, + resource=span.resource, + attributes=span.attributes, + events=span.events, + links=span.links, + kind=span.kind, + span_processor=span._span_processor, + instrumentation_info=span.instrumentation_info, + record_exception=span._record_exception, + set_status_on_exception=span._set_status_on_exception, + limits=span._limits, + instrumentation_scope=span.instrumentation_scope, ) + self._span = span -def add_event(span: Span, name, attributes=None, timestamp=None) -> None: - span.add_event( - name=name, - attributes=_serialize_attributes(None, attributes), - timestamp=timestamp, - ) - - -def record_exception(span: Span, exception, attributes=None, timestamp=None) -> None: - span.record_exception( - exception=exception, - attributes=_serialize_attributes(None, attributes), - timestamp=timestamp, - escaped=None, - ) - - -def set_attributes( - span: Span, namespace: Optional[str], attributes: Dict[str, Any] -) -> None: - if isinstance(attributes, dict): - span.set_attributes(_serialize_attributes(namespace, attributes).items()) - - -def get_attributes(span: Union[ReadableSpan, Span], namespace: str): - return _deserialize_attributes( - namespace, - { - key: value - for key, value in span.attributes.items() - if key != _decode_key(namespace, key) - }, - ) - - -def _serialize_attributes( - namespace: str, - attributes: Dict[str, Any], -) -> Dict[str, str]: - if not isinstance(attributes, dict): - return {} - - return { - _encode_key(namespace, key): _encode_value(value) - for key, value in attributes.items() - } - - -def _deserialize_attributes( - namespace: str, - attributes: Dict[str, Any], -) -> Dict[str, Any]: - if not isinstance(attributes, dict): - return {} + ## --- PROXY METHODS --- ## - return { - _decode_key(namespace, key): _decode_value(value) - for key, value in attributes.items() - } + def get_span_context(self): + return self._span.get_span_context() + def is_recording(self) -> bool: + return self._span.is_recording() -def _encode_key(namespace: Optional[str] = None, key: str = "") -> str: - if namespace is None: - return key + def update_name(self, name: str) -> None: + self._span.update_name(name) - return f"ag.{namespace}.{key}" + def set_status( + self, + status: Union[Status, StatusCode], + description: Optional[str] = None, + ) -> None: + self._span.set_status(status=status, description=description) + def end(self) -> None: + self._span.end() -def _decode_key(namespace: Optional[str] = None, key: str = "") -> str: - if namespace is None: - return key + ## --- CUSTOM METHODS W/ ATTRIBUTES SERALIZATION --- ## - return key.replace(f"ag.{namespace}.", "") - - -def _encode_value(value: Any) -> Any: - if value is None: - value = "@ag.type=none:" - return value - - if isinstance(value, (str, int, float, bool, bytes)): - return value - - if isinstance(value, dict) or isinstance(value, list): - encoded = dumps(value) - value = "@ag.type=json:" + encoded - return value - - return repr(value) - - -def _decode_value(value: Any) -> Any: - if isinstance(value, (int, float, bool, bytes)): - return value - - if isinstance(value, str): - if value == "@ag.type=none:": - return None + def set_attributes( + self, + attributes: Dict[str, Any], + namespace: Optional[str] = None, + max_depth: Optional[int] = None, + ) -> None: + self._span.set_attributes( + attributes=serialize( + namespace=namespace, + attributes=attributes, + max_depth=max_depth, + ) + ) - if value.startswith("@ag.type=json:"): - encoded = value[len("@ag.type=json:") :] - value = loads(encoded) - return value + def set_attribute( + self, + key: str, + value: Any, + namespace: Optional[str] = None, + ) -> None: + self.set_attributes({key: value}, namespace) + + def add_event( + self, + name: str, + attributes: Optional[Dict[str, Any]] = None, + timestamp: Optional[int] = None, + namespace: Optional[str] = None, + ) -> None: + self._span.add_event( + name=name, + attributes=serialize( + namespace=namespace, + attributes=attributes, + ), + timestamp=timestamp, + ) - return value + def add_link( + self, + context: SpanContext, + attributes: Optional[Dict[str, Any]] = None, + namespace: Optional[str] = None, + ) -> None: + self._span.add_link( + context=context, + attributes=serialize( + namespace=namespace, + attributes=attributes, + ), + ) - return value + def record_exception( + self, + exception: BaseException, + attributes: Optional[Dict[str, Any]] = None, + timestamp: Optional[int] = None, + escaped: bool = False, + namespace: Optional[str] = None, + ) -> None: + self._span.record_exception( + exception=exception, + attributes=serialize( + namespace=namespace, + attributes=attributes, + ), + timestamp=timestamp, + escaped=escaped, + ) diff --git a/agenta-cli/agenta/sdk/tracing/tracers.py b/agenta-cli/agenta/sdk/tracing/tracers.py deleted file mode 100644 index ea9f3b836e..0000000000 --- a/agenta-cli/agenta/sdk/tracing/tracers.py +++ /dev/null @@ -1,66 +0,0 @@ -from typing import Any, Dict -from contextlib import suppress - -from opentelemetry.sdk.resources import Resource -from opentelemetry.sdk.trace import TracerProvider, ConcurrentMultiSpanProcessor -from opentelemetry.sdk.trace.export import ReadableSpan -from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter - -from agenta.sdk.tracing.processors import TraceProcessor -from agenta.sdk.tracing.exporters import InlineTraceExporter - - -class ConcurrentTracerProvider(TracerProvider): - def __init__( - self, - name: str, - version: str, - ) -> None: - self.name = name - self.version = version - - processor = ConcurrentMultiSpanProcessor(num_threads=2) - - base_shutdown = processor.shutdown - - def safe_shutdown(): - with suppress(Exception): - base_shutdown() - - processor.shutdown = safe_shutdown - - super().__init__( - active_span_processor=processor, - resource=Resource( - attributes={"service.name": name, "service.version": version} - ), - ) - - def add_inline_processor( - self, - registry: Dict[str, ReadableSpan], - scope: Dict[str, Any], - ) -> TraceProcessor: - processor = TraceProcessor( - InlineTraceExporter(registry=registry), - scope, - ) - - self.add_span_processor(processor) - - return processor - - def add_otlp_processor( - self, - endpoint: str, - headers: Dict[str, str], - scope: Dict[str, Any], - ): - processor = TraceProcessor( - OTLPSpanExporter(endpoint=endpoint, headers=headers), - scope, - ) - - self.add_span_processor(processor) - - return processor diff --git a/agenta-cli/agenta/sdk/tracing/tracing.py b/agenta-cli/agenta/sdk/tracing/tracing.py new file mode 100644 index 0000000000..db0bc691b3 --- /dev/null +++ b/agenta-cli/agenta/sdk/tracing/tracing.py @@ -0,0 +1,172 @@ +from typing import Optional, Any, Dict + +from httpx import get as check + +from opentelemetry.trace import ( + get_current_span, + set_tracer_provider, + get_tracer_provider, + Status, + StatusCode, +) +from opentelemetry.sdk.trace import Span, Tracer, TracerProvider +from opentelemetry.sdk.resources import Resource + +from agenta.sdk.utils.singleton import Singleton +from agenta.sdk.utils.exceptions import suppress # USE IT ! +from agenta.sdk.utils.logging import log + +from agenta.sdk.tracing.processors import TraceProcessor +from agenta.sdk.tracing.exporters import ConsoleExporter, InlineExporter, OTLPExporter +from agenta.sdk.tracing.spans import CustomSpan +from agenta.sdk.tracing.context import tracing_context +from agenta.sdk.tracing.inline import parse_inline_trace + + +class Tracing(metaclass=Singleton): + VERSION = "0.1.0" + + Status = Status + StatusCode = StatusCode + + def __init__( + self, + url: str, + ) -> None: + # ENDPOINT (OTLP) + # self.otlp_url = url + self.otlp_url = "http://127.0.0.1:8000/api/observability/v1/otlp/traces" + # AUTHENTICATION (OTLP) + self.project_id: Optional[str] = None + # AUTHORIZATION (OTLP) + self.api_key: Optional[str] = None + # HEADERS (OTLP) + self.headers: Dict[str, str] = dict() + + # TRACER PROVIDER + self.tracer_provider: Optional[TracerProvider] = None + # TRACER + self.tracer: Optional[Tracer] = None + # INLINE SPANS for INLINE TRACES (INLINE PROCESSOR) + self.inline_spans: Dict[str, Any] = dict() + + # PUBLIC + + def configure( + self, + project_id: Optional[str] = None, + api_key: Optional[str] = None, + # + **kwargs, + ): + # AUTHENTICATION (OTLP) + # self.project_id = project_id + self.project_id = "f7943e42-ec69-498e-bf58-8db034b9286e" + # AUTHORIZATION (OTLP) + self.api_key = api_key + # HEADERS (OTLP) + self.headers = {"AG-PROJECT-ID": self.project_id} + if api_key: + # self.headers.update(**{"Authorization": f"Api-Key {self.api_key}"}) + self.headers.update(**{"Authorization": self.api_key}) + + # TRACER PROVIDER + self.tracer_provider = TracerProvider( + resource=Resource(attributes={"service.name": "agenta-sdk"}) + ) + # TRACE PROCESSORS -- CONSOLE + _console = TraceProcessor(ConsoleExporter()) + self.tracer_provider.add_span_processor(_console) + # TRACE PROCESSORS -- INLINE + self.inline = TraceProcessor( + InlineExporter( + registry=self.inline_spans, + ) + ) + self.tracer_provider.add_span_processor(self.inline) + # TRACE PROCESSORS -- OTLP + try: + log.info(f"Connecting to the remote trace receiver at {self.otlp_url}...") + + check(self.otlp_url, headers=self.headers) + + log.info(f"Connection established.") + + _otlp = TraceProcessor( + OTLPExporter( + endpoint=self.otlp_url, + headers=self.headers, + ) + ) + + self.tracer_provider.add_span_processor(_otlp) + except: + log.warning(f"Connection failed.") + log.warning( + f"Warning: Your traces will not be exported since {self.otlp_url} is unreachable." + ) + # GLOBAL TRACER PROVIDER -- INSTRUMENTATION LIBRARIES + set_tracer_provider(self.tracer_provider) + # TRACER + self.tracer: Tracer = self.tracer_provider.get_tracer("agenta.tracer") + + def get_current_span( + self, + ): + _span = get_current_span() + + if _span.is_recording(): + return CustomSpan(_span) + + return _span + + def store_internals( + self, + attributes: Dict[str, Any], + span: Optional[Span] = None, + ): + if span is None: + span = self.get_current_span() + + span.set_attributes(attributes={"internals": attributes}, namespace="data") + + def is_inline_trace_ready( + self, + trace_id: int, + ) -> bool: + is_ready = self.inline.is_ready(trace_id) + + return is_ready + + def get_inline_trace( + self, + trace_id: int, + ) -> Dict[str, Any]: + if trace_id is None: + return {} + + is_ready = self.inline.is_ready(trace_id) + + if is_ready is False: + return {} + + otel_spans = self.inline.fetch(trace_id) + + if not otel_spans: + return {} + + agenta_spans = parse_inline_trace(self.project_id, otel_spans) + + agenta_spans = { + "trace_id": trace_id, + "spans": [span for span in agenta_spans], + } + + return agenta_spans + + +def get_tracer(tracing: Tracing) -> Tracer: + if tracing is None or tracing.tracer is None or tracing.tracer_provider is None: + return get_tracer_provider().get_tracer("default.tracer") + + return tracing.tracer diff --git a/agenta-cli/agenta/sdk/tracing/x_inline.py b/agenta-cli/agenta/sdk/tracing/x_inline.py new file mode 100644 index 0000000000..10a7536aac --- /dev/null +++ b/agenta-cli/agenta/sdk/tracing/x_inline.py @@ -0,0 +1,152 @@ +from typing import Dict, Union +from json import loads +from datetime import datetime + +from opentelemetry.sdk.trace.export import ReadableSpan + +from agenta.client.backend.types.create_span import CreateSpan +from agenta.client.backend.types.llm_tokens import LlmTokens +from agenta.sdk.utils.logging import log +from agenta.sdk.utils.exceptions import suppress +from agenta.sdk.tracing.attributes import deserialize +from agenta.sdk.tracing.trace_tree import ( + make_spans_id_tree, + cumulate_costs, + cumulate_tokens, +) + + +def get_trace_id(spans: Dict[str, ReadableSpan]): + traces_idx: Dict[str, Dict[str, ReadableSpan]] = dict() + + trace_id = None + + with suppress(): + for span in spans.values(): + span: ReadableSpan + + trace_id = span.context.trace_id.to_bytes(16, "big").hex() + span_id = span.context.span_id.to_bytes(8, "big").hex() + + if trace_id not in traces_idx: + traces_idx[trace_id] = list() + + traces_idx[trace_id].append(span_id) + + spans.clear() # might need to be moved to a context variable + + if len(traces_idx) > 1: + log.error("Error while parsing inline trace: too many traces.") + trace_id = list(traces_idx.keys())[0] + + return { + "trace_id": trace_id, + } + + +def get_trace(spans: Dict[str, ReadableSpan], trace_id_only: bool = False): + traces_idx: Dict[str, Dict[str, ReadableSpan]] = dict() + + inline_trace = None + + with suppress(): + for span in spans.values(): + span: ReadableSpan + + trace_id = span.context.trace_id.to_bytes(16, "big").hex() + span_id = span.context.span_id.to_bytes(8, "big").hex() + + if trace_id not in traces_idx: + traces_idx[trace_id] = dict() + + if not trace_id_only: + traces_idx[trace_id][span_id] = _parse_to_legacy_span(span) + + spans.clear() # might need to be moved to a context variable + + if len(traces_idx) > 1: + log.error("Error while parsing inline trace: too many traces.") + trace_id = list(traces_idx.keys())[0] + + spans_idx = traces_idx[trace_id] + spans_id_tree = make_spans_id_tree(spans_idx) + + if len(spans_id_tree) > 1: + log.error("Error while parsing inline trace: too many root spans.") + root_span_id = list(spans_id_tree.keys())[0] + + cumulate_costs(spans_id_tree, spans_idx) + cumulate_tokens(spans_id_tree, spans_idx) + + inline_trace = { + "trace_id": trace_id, + "cost": spans_idx[root_span_id]["cost"], + "tokens": spans_idx[root_span_id]["tokens"], + "latency": datetime.fromisoformat( + spans_idx[root_span_id]["end_time"].replace("Z", "+00:00") + ).timestamp() + - datetime.fromisoformat( + spans_idx[root_span_id]["start_time"].replace("Z", "+00:00") + ).timestamp(), + "spans": list(spans_idx.values()), + } + + return inline_trace + + +def get_attributes(span: Union[ReadableSpan], namespace: str): + return deserialize(namespace, span.attributes, max_depth=None) + + +def _parse_to_legacy_span(span: ReadableSpan): + attributes = dict(span.attributes) + + for event in span.events: + if event.name == "exception": + attributes.update(**event.attributes) + + legacy_span = CreateSpan( + id=span.context.span_id.to_bytes(8, "big").hex(), + spankind=get_attributes(span, "extra").get("kind", "UNKNOWN"), + name=span.name, + status=str(span.status.status_code.name), + # + start_time=datetime.fromtimestamp( + span.start_time / 1_000_000_000, + ).isoformat(), + end_time=datetime.fromtimestamp( + span.end_time / 1_000_000_000, + ).isoformat(), + # + parent_span_id=( + span.parent.span_id.to_bytes(8, "big").hex() if span.parent else None + ), + # + inputs=get_attributes(span, "data.inputs"), + internals=get_attributes(span, "data.internals"), + outputs=get_attributes(span, "data.outputs"), + # + config=get_attributes(span, "meta").get("configuration", {}), + # + tokens=LlmTokens( + prompt_tokens=get_attributes(span, "metrics.unit.tokens").get("prompt", 0), + completion_tokens=get_attributes(span, "metrics.unit.tokens").get( + "completion", 0 + ), + total_tokens=get_attributes(span, "metrics.unit.tokens").get("total", 0), + ), + cost=get_attributes(span, "metrics.unit.costs").get("total", 0.0), + # + app_id=get_attributes(span, "extra").get("app_id", ""), + environment=get_attributes(span, "meta").get("environment"), + # + attributes=attributes, + # + variant_id=None, + variant_name=None, + tags=None, + token_consumption=None, + user=None, + ) + + return loads(legacy_span.model_dump_json()) diff --git a/agenta-cli/agenta/sdk/tracing/trace_tree.py b/agenta-cli/agenta/sdk/tracing/x_tree.py similarity index 100% rename from agenta-cli/agenta/sdk/tracing/trace_tree.py rename to agenta-cli/agenta/sdk/tracing/x_tree.py diff --git a/agenta-cli/agenta/sdk/utils/exceptions.py b/agenta-cli/agenta/sdk/utils/exceptions.py new file mode 100644 index 0000000000..111f7c552e --- /dev/null +++ b/agenta-cli/agenta/sdk/utils/exceptions.py @@ -0,0 +1,18 @@ +from contextlib import AbstractContextManager +from traceback import format_exc +from agenta.sdk.utils.logging import log + + +class suppress(AbstractContextManager): + def __init__(self): + pass + + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_value, exc_tb): + if exc_type is None: + return + else: + log.error(f"{exc_type.__name__}: {exc_value}\n{format_exc()}") + return diff --git a/agenta-cli/agenta/sdk/utils/singleton.py b/agenta-cli/agenta/sdk/utils/singleton.py index 37c711c19b..5140515331 100644 --- a/agenta-cli/agenta/sdk/utils/singleton.py +++ b/agenta-cli/agenta/sdk/utils/singleton.py @@ -1,29 +1,13 @@ from threading import Lock -class SingletonMeta(type): - """ - Thread-safe implementation of Singleton. - """ +class Singleton(type): + _instances = {} - _instances = {} # type: ignore - - # We need the lock mechanism to synchronize threads \ - # during the initial access to the Singleton object. _lock: Lock = Lock() def __call__(cls, *args, **kwargs): - """ - Ensures that changes to the `__init__` arguments do not affect the - returned instance. - - Uses a lock to make this method thread-safe. If an instance of the class - does not already exist, it creates one. Otherwise, it returns the - existing instance. - """ - with cls._lock: if cls not in cls._instances: - instance = super().__call__(*args, **kwargs) - cls._instances[cls] = instance + cls._instances[cls] = super().__call__(*args, **kwargs) return cls._instances[cls] From 852f6f56797931a2ada20339fc950ab31e97b01f Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 8 Oct 2024 17:44:12 +0200 Subject: [PATCH 046/305] merge main --- agenta-cli/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index 02a2a6172b..e21e98d233 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.24.5a1" +version = "0.24.6a1" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] From 35987deb72fa60a8be5e620a00d5efee77b95dd5 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 8 Oct 2024 17:49:11 +0200 Subject: [PATCH 047/305] clean up old files --- agenta-cli/agenta/sdk/tracing/x_inline.py | 152 ---------------------- agenta-cli/agenta/sdk/tracing/x_tree.py | 88 ------------- 2 files changed, 240 deletions(-) delete mode 100644 agenta-cli/agenta/sdk/tracing/x_inline.py delete mode 100644 agenta-cli/agenta/sdk/tracing/x_tree.py diff --git a/agenta-cli/agenta/sdk/tracing/x_inline.py b/agenta-cli/agenta/sdk/tracing/x_inline.py deleted file mode 100644 index 10a7536aac..0000000000 --- a/agenta-cli/agenta/sdk/tracing/x_inline.py +++ /dev/null @@ -1,152 +0,0 @@ -from typing import Dict, Union -from json import loads -from datetime import datetime - -from opentelemetry.sdk.trace.export import ReadableSpan - -from agenta.client.backend.types.create_span import CreateSpan -from agenta.client.backend.types.llm_tokens import LlmTokens -from agenta.sdk.utils.logging import log -from agenta.sdk.utils.exceptions import suppress -from agenta.sdk.tracing.attributes import deserialize -from agenta.sdk.tracing.trace_tree import ( - make_spans_id_tree, - cumulate_costs, - cumulate_tokens, -) - - -def get_trace_id(spans: Dict[str, ReadableSpan]): - traces_idx: Dict[str, Dict[str, ReadableSpan]] = dict() - - trace_id = None - - with suppress(): - for span in spans.values(): - span: ReadableSpan - - trace_id = span.context.trace_id.to_bytes(16, "big").hex() - span_id = span.context.span_id.to_bytes(8, "big").hex() - - if trace_id not in traces_idx: - traces_idx[trace_id] = list() - - traces_idx[trace_id].append(span_id) - - spans.clear() # might need to be moved to a context variable - - if len(traces_idx) > 1: - log.error("Error while parsing inline trace: too many traces.") - trace_id = list(traces_idx.keys())[0] - - return { - "trace_id": trace_id, - } - - -def get_trace(spans: Dict[str, ReadableSpan], trace_id_only: bool = False): - traces_idx: Dict[str, Dict[str, ReadableSpan]] = dict() - - inline_trace = None - - with suppress(): - for span in spans.values(): - span: ReadableSpan - - trace_id = span.context.trace_id.to_bytes(16, "big").hex() - span_id = span.context.span_id.to_bytes(8, "big").hex() - - if trace_id not in traces_idx: - traces_idx[trace_id] = dict() - - if not trace_id_only: - traces_idx[trace_id][span_id] = _parse_to_legacy_span(span) - - spans.clear() # might need to be moved to a context variable - - if len(traces_idx) > 1: - log.error("Error while parsing inline trace: too many traces.") - trace_id = list(traces_idx.keys())[0] - - spans_idx = traces_idx[trace_id] - spans_id_tree = make_spans_id_tree(spans_idx) - - if len(spans_id_tree) > 1: - log.error("Error while parsing inline trace: too many root spans.") - root_span_id = list(spans_id_tree.keys())[0] - - cumulate_costs(spans_id_tree, spans_idx) - cumulate_tokens(spans_id_tree, spans_idx) - - inline_trace = { - "trace_id": trace_id, - "cost": spans_idx[root_span_id]["cost"], - "tokens": spans_idx[root_span_id]["tokens"], - "latency": datetime.fromisoformat( - spans_idx[root_span_id]["end_time"].replace("Z", "+00:00") - ).timestamp() - - datetime.fromisoformat( - spans_idx[root_span_id]["start_time"].replace("Z", "+00:00") - ).timestamp(), - "spans": list(spans_idx.values()), - } - - return inline_trace - - -def get_attributes(span: Union[ReadableSpan], namespace: str): - return deserialize(namespace, span.attributes, max_depth=None) - - -def _parse_to_legacy_span(span: ReadableSpan): - attributes = dict(span.attributes) - - for event in span.events: - if event.name == "exception": - attributes.update(**event.attributes) - - legacy_span = CreateSpan( - id=span.context.span_id.to_bytes(8, "big").hex(), - spankind=get_attributes(span, "extra").get("kind", "UNKNOWN"), - name=span.name, - status=str(span.status.status_code.name), - # - start_time=datetime.fromtimestamp( - span.start_time / 1_000_000_000, - ).isoformat(), - end_time=datetime.fromtimestamp( - span.end_time / 1_000_000_000, - ).isoformat(), - # - parent_span_id=( - span.parent.span_id.to_bytes(8, "big").hex() if span.parent else None - ), - # - inputs=get_attributes(span, "data.inputs"), - internals=get_attributes(span, "data.internals"), - outputs=get_attributes(span, "data.outputs"), - # - config=get_attributes(span, "meta").get("configuration", {}), - # - tokens=LlmTokens( - prompt_tokens=get_attributes(span, "metrics.unit.tokens").get("prompt", 0), - completion_tokens=get_attributes(span, "metrics.unit.tokens").get( - "completion", 0 - ), - total_tokens=get_attributes(span, "metrics.unit.tokens").get("total", 0), - ), - cost=get_attributes(span, "metrics.unit.costs").get("total", 0.0), - # - app_id=get_attributes(span, "extra").get("app_id", ""), - environment=get_attributes(span, "meta").get("environment"), - # - attributes=attributes, - # - variant_id=None, - variant_name=None, - tags=None, - token_consumption=None, - user=None, - ) - - return loads(legacy_span.model_dump_json()) diff --git a/agenta-cli/agenta/sdk/tracing/x_tree.py b/agenta-cli/agenta/sdk/tracing/x_tree.py deleted file mode 100644 index 6238ff427e..0000000000 --- a/agenta-cli/agenta/sdk/tracing/x_tree.py +++ /dev/null @@ -1,88 +0,0 @@ -from typing import Dict -from collections import OrderedDict - - -from agenta.sdk.utils.logging import log - - -def make_spans_id_tree( - spans: Dict[str, dict], -): - tree = OrderedDict() - index = {} - - def push(span) -> None: - if span["parent_span_id"] is None: - tree[span["id"]] = OrderedDict() - index[span["id"]] = tree[span["id"]] - elif span["parent_span_id"] in index: - index[span["parent_span_id"]][span["id"]] = OrderedDict() - index[span["id"]] = index[span["parent_span_id"]][span["id"]] - else: - log.error("The parent span id should have been in the tracing tree.") - - for span in sorted(spans.values(), key=lambda span: span["start_time"]): - push(span) - - return tree - - -def cumulate_costs( - spans_id_tree: OrderedDict, - spans_idx: Dict[str, dict], -): - def _get(span): - return span["cost"] - - def _acc(a, b): - return a + b - - def _set(span, cost): - span["cost"] = cost - - _visit_tree_dfs(spans_id_tree, spans_idx, _get, _acc, _set) - - -def cumulate_tokens( - spans_id_tree: OrderedDict, - spans_idx: Dict[str, dict], -): - def _get(span): - return span["tokens"] - - def _acc(a, b): - return { - "prompt_tokens": a["prompt_tokens"] + b["prompt_tokens"], - "completion_tokens": a["completion_tokens"] + b["completion_tokens"], - "total_tokens": a["total_tokens"] + b["total_tokens"], - } - - def _set(span, tokens): - span["tokens"] = tokens - - _visit_tree_dfs(spans_id_tree, spans_idx, _get, _acc, _set) - - -def _visit_tree_dfs( - spans_id_tree: OrderedDict, - spans_idx: Dict[str, dict], - get_metric, - accumulate_metric, - set_metric, -): - for span_id, children_spans_id_tree in spans_id_tree.items(): - cumulated_metric = get_metric(spans_idx[span_id]) - - _visit_tree_dfs( - children_spans_id_tree, - spans_idx, - get_metric, - accumulate_metric, - set_metric, - ) - - for child_span_id in children_spans_id_tree.keys(): - child_metric = get_metric(spans_idx[child_span_id]) - cumulated_metric = accumulate_metric(cumulated_metric, child_metric) - - set_metric(spans_idx[span_id], cumulated_metric) From de9c344245cb7d468d51bf0e6f2db07590d1f70b Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 8 Oct 2024 17:51:02 +0200 Subject: [PATCH 048/305] clean up inline.py --- agenta-cli/agenta/sdk/tracing/inline.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py index 40c96a3a09..fe1a730613 100644 --- a/agenta-cli/agenta/sdk/tracing/inline.py +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -1083,6 +1083,10 @@ def parse_inline_trace( agenta_span_dtos = [parse_to_agenta_span_dto(span_dto) for span_dto in root_span_dtos] + ################# + ### THIS FILE ### + ################# + _spans = [ loads(span_dto.model_dump_json(exclude_none=True)) for span_dto in agenta_span_dtos ] From b4583f7357525691c07bf9308a6374877f9e87cf Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Tue, 8 Oct 2024 21:41:03 +0200 Subject: [PATCH 049/305] feat(frontend): added trace mock response, types and mocked api fetch --- agenta-web/src/lib/test_trace.json | 2775 +++++++++++++++++ .../src/services/observability/api/index.ts | 15 + .../src/services/observability/types/index.ts | 104 + 3 files changed, 2894 insertions(+) create mode 100644 agenta-web/src/lib/test_trace.json create mode 100644 agenta-web/src/services/observability/api/index.ts create mode 100644 agenta-web/src/services/observability/types/index.ts diff --git a/agenta-web/src/lib/test_trace.json b/agenta-web/src/lib/test_trace.json new file mode 100644 index 0000000000..ca3ba48916 --- /dev/null +++ b/agenta-web/src/lib/test_trace.json @@ -0,0 +1,2775 @@ +{ + "version": "1.0.0", + "roots": [ + { + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361f9ff" + }, + "trees": [ + { + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "nodes": { + "rag": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361f9ff" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-2d60-d51d0c7dfede", + "type": "workflow", + "name": "rag" + }, + "time": { + "start": "2024-10-08T01:17:08.313554", + "end": "2024-10-08T01:17:14.639759", + "span": 6326205 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "count": 3 + }, + "outputs": "Here are three notable comedy films featuring witches. \n\n*Wicked Stepmother* (1989) humorously explores the chaos that ensues when a mother and daughter duo of witches invade a yuppie family's home. *Hocus Pocus* (1993) is a cult classic where three witch sisters are accidentally resurrected on Halloween, unleashing comedic mayhem in Salem. *Practical Magic* (1998) is a charming romantic comedy about two witch sisters navigating love and family curses with a blend of humor and magic." + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 885.0, + "completion": 355.0, + "total": 1240.0 + } + } + }, + "meta": { + "configuration": { + "retriever_prompt": "Movies about {topic} in the genre of {genre}.", + "retriever_multiplier": 3.0, + "generator_context_prompt": "Given the following list of suggested movies:\n\n{movies}", + "generator_instructions_prompt": "Provide a list of {count} movies about {topic} in the genre of {genre}.", + "generator_model": "gpt-3.5-turbo", + "generator_temperature": 0.8, + "summarizer_context_prompt": "Act as a professional cinema critic.\nBe concise and factual.\nUse one intro sentence, and one sentence per movie.", + "summarizer_instructions_prompt": "Summarize the following recommendations about {topic} in the genre of {genre}:\n\n{report}", + "summarizer_model": "gpt-4o-mini", + "summarizer_temperature": 0.2 + }, + "environment": "terminal" + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-2d60-d51d0c7dfede" + } + ], + "nodes": { + "retriever": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361f9ff" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-44cc-6ddc96192c8c", + "type": "task", + "name": "retriever" + }, + "parent": { + "id": "43c39569-3e04-49aa-2d60-d51d0c7dfede" + }, + "time": { + "start": "2024-10-08T01:17:08.313973", + "end": "2024-10-08T01:17:09.131880", + "span": 817907 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "count": 3 + }, + "internals": { + "prompt": "Movies about witches in the genre of comedy." + }, + "outputs": { + "movies": [ + "The Craft (1996) in ['Drama', 'Fantasy', 'Horror']: A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.", + "Wicked Stepmother (1989) in ['Comedy', 'Fantasy']: A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...", + "Oz the Great and Powerful (2013) in ['Adventure', 'Family', 'Fantasy']: A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.", + "Into the Woods (2014) in ['Adventure', 'Fantasy', 'Musical']: A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.", + "Snow White: A Tale of Terror (1997) in ['Fantasy', 'Horror']: In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.", + "Bedknobs and Broomsticks (1971) in ['Adventure', 'Family', 'Fantasy']: An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.", + "My Neighbor Totoro (1988) in ['Animation', 'Family', 'Fantasy']: When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.", + "Hocus Pocus (1993) in ['Comedy', 'Family', 'Fantasy']: After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.", + "Practical Magic (1998) in ['Comedy', 'Fantasy', 'Romance']: The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + ], + "test": [ + 1, + false, + "test", + { + "test": "test" + } + ] + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 9.0, + "completion": 0.0, + "total": 9.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-44cc-6ddc96192c8c" + } + ], + "nodes": { + "openai.embeddings": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361f9ff" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-1652-1b7965d27817", + "type": "embedding", + "name": "openai.embeddings" + }, + "parent": { + "id": "43c39569-3e04-49aa-44cc-6ddc96192c8c" + }, + "time": { + "start": "2024-10-08T01:17:08.314192", + "end": "2024-10-08T01:17:09.039293", + "span": 725101 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "content": "Movies about witches in the genre of comedy." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 9, + "prompt": 9 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 9, + "completion": 0.0, + "total": 9 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "text-embedding-ada-002", + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "text-embedding-ada-002" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-1652-1b7965d27817" + } + ] + }, + "search": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361f9ff" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-254c-eff4fcfe2c18", + "type": "query", + "name": "search" + }, + "parent": { + "id": "43c39569-3e04-49aa-44cc-6ddc96192c8c" + }, + "time": { + "start": "2024-10-08T01:17:09.039547", + "end": "2024-10-08T01:17:09.131576", + "span": 92029 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "query": [ + 0.0031027994, -0.036517933, -0.0051836898, + -0.02046397, -0.006576323, 0.004403557, + 0.0034203327, -0.036904775, 0.019174494, + -0.0280074, 0.011934091, 0.010225537, + 0.021160286, -0.0015441463, 0.010141721, + 0.0064248093, 0.040102676, -0.004100531, + 0.018220283, -0.029554768, -0.01073488, + -0.0032655955, -0.009129483, 0.012288697, + -0.014596858, 0.008974747, 0.014519489, + -0.020064233, -0.019986864, -0.006328099, + 0.02557029, -0.019006863, 0.0008212344, + -0.019832127, -0.014390541, -0.032701086, + -0.009793563, -0.011424749, -0.0018794099, + -0.0036814513, 0.0060154013, 0.011882513, + -0.006324875, -0.0067632967, 0.0077432976, + 0.013642646, 0.023545815, 0.0020696074, + -0.0036782275, 0.013230015, 0.020090021, + 0.0016279622, -0.025492921, -0.011985671, + 0.010547906, 0.030302664, 0.015241595, + 0.011882513, 0.019548442, -0.020025548, + -0.015860543, -0.004696913, -0.027749503, + -0.007659482, 0.00085750094, -0.010541459, + 0.0004448689, -0.007943166, 0.0029077663, + -0.025583185, 0.042165834, 0.008826457, + -0.004036057, -0.008394483, 0.017691597, + -0.03154056, -0.030147927, 0.02019318, + -0.0013257415, -0.00235168, 0.022411076, + -0.012759356, -0.010573695, 0.013836067, + 0.012288697, 0.01881344, -0.0091552725, + 0.024138974, 0.007227508, -0.008968299, + 0.005528624, 0.0025837857, 0.032778457, + 0.033345826, 0.01495791, 0.012856066, + -0.011263564, 0.03827162, -0.0128367245, + -0.0038426358, -0.00024117214, 0.011411854, + -0.03362951, -0.012166197, 0.014571068, + -0.018207388, 0.0030834572, 0.01210817, + 0.042088468, 0.014700016, -0.028523188, + -0.00867172, 0.022462655, -0.043661624, + -0.004522834, -0.012636855, 0.029632136, + 0.008749088, 0.0013644258, -0.030560559, + 0.026640555, -0.002135693, 0.025235027, + -0.019664494, 0.03778162, -0.0037942803, + -0.00776264, -0.023971342, 0.004748492, + -0.026176345, 0.029632136, -0.019780548, + 0.02787845, -0.020412391, -0.027027396, + 0.009961195, -0.03687899, 0.008072114, + -0.029322663, -0.020747654, -0.0001522185, + 0.03468688, -0.030405821, -0.0011669749, + -0.009393826, 0.00076844654, 0.0101933, + -0.010032116, 0.0042359256, -0.021186076, + 0.027517397, -0.005860664, 0.02119897, + -0.004751716, 0.012417645, 0.009464747, + 0.017033966, 0.009297115, -0.036956355, + -0.028136346, -0.013500804, 0.008845799, + 0.00530619, -0.008890931, 0.008342904, + 0.013107514, 0.024113184, -0.022243446, + -0.006905139, 0.0014063338, 0.018607125, + 0.019496864, -0.030199505, 0.03664688, + -0.0014168107, 0.0044132285, -0.00032337618, + 0.018929495, -0.0065602046, -0.0060121776, + 0.013178435, 0.0020148049, 0.0076723765, + 0.010515669, -0.017846335, 0.00094937603, + -0.01046409, -0.013642646, 0.0040908596, + -0.018555546, -0.008465404, 0.028806873, + -0.008607246, -0.008684615, -0.678367, + -0.014261594, -0.009974089, -0.026666345, + 0.01110238, 0.006286191, 0.02076055, + 0.012752908, -0.03974162, -0.016840544, + 0.004074741, 0.024371078, 0.020012653, + -0.00918751, -0.030792665, -0.027723715, + -0.009741984, -0.013810278, 0.005409348, + -0.0039780308, -0.0016174853, 0.037136883, + -0.0034622406, -0.016221596, 0.028316872, + 0.02527371, 0.0058477693, -0.0014804786, + -0.025995817, 0.0346353, -0.019419495, + 0.026692133, 0.00072371785, 0.0026240817, + 0.041005306, 0.016905017, -0.008594351, + 0.025054501, 0.015899228, 0.029167926, + -0.02945161, 0.004925795, -0.008220403, + 0.0026788844, -0.016298965, 0.010161064, + 0.0037298065, -0.0066214544, 0.0060121776, + 0.009928958, 0.010148169, 0.008684615, + 0.009954748, 0.015061068, 0.01471291, + 0.015486595, 0.037368987, -0.008562114, + 0.013642646, -0.00028348304, -0.027130555, + 0.021276338, -0.0048161894, -0.0067890864, + 0.0016110379, 0.026292397, 0.0077884295, + 0.010244879, 0.0068922443, -0.0145323835, + -0.0075176395, 0.0068793492, -0.018207388, + -0.0046711233, 0.0007555518, 0.0049161236, + 0.025995817, 0.0061475723, -0.0027320753, + -0.0061959275, -0.00014476372, -0.007420929, + -0.008761983, 0.0033494115, 0.02223055, + -0.019728968, -0.005628559, 0.029503189, + 0.005161124, 0.027723715, 0.011940539, + 0.018026862, -0.011979223, 0.008955404, + -0.00010980686, 0.010838038, 0.0033816483, + 0.019999757, 0.02991582, -0.04497689, + 0.00062821613, -0.009084351, -0.010257774, + -0.01908423, -0.006357112, 0.00056333945, + -0.0029254965, 0.021547128, 0.03675004, + -0.00052908773, 0.031127928, -0.0067632967, + -0.0023823052, -0.017214492, -0.01992239, + -0.026434239, 0.042062677, 0.0074467184, + 0.009780669, -0.010360932, 0.0064925067, + 0.00019835753, -0.008936062, -0.0058993483, + -0.010715538, -0.0012137184, -0.0045357286, + 0.01731765, -0.04459005, -0.012101724, + 0.0026273055, 0.012424092, -0.01938081, + -0.004148886, -0.018529756, 0.00867172, + 0.002588621, -0.020850811, 0.038091093, + 0.007801324, 0.0060186246, -0.004032833, + -0.01957423, 0.010483433, 0.008910272, + -0.015976597, -0.0037620435, 0.01215975, + 0.0038361882, 0.010535011, -0.013823173, + 0.001776252, -0.013848962, -0.007079218, + 0.008349351, -0.0060669803, -0.0095936945, + -0.018968178, -0.0068406654, -0.0064570466, + 0.038581096, 0.0053835586, -0.012727119, + 0.0014087515, -0.006008954, -0.0026063514, + 0.00791093, 0.020115811, -0.014687121, + -0.023507131, 0.005776848, -0.014945016, + -0.0012113006, 0.023674762, -0.0046936893, + 0.041778993, 0.002828786, 0.0128818555, + -0.008684615, 0.005973493, -0.012785145, + 0.0023065484, -0.0108058015, 0.0074467184, + 0.018142914, -0.0036814513, 0.014932121, + -0.016157122, -0.0056092166, -0.010689748, + -0.017717388, 0.020231863, 0.029761085, + -0.011940539, -0.0011726164, -0.0025692792, + 0.006750402, -0.0020921733, 0.027104765, + 0.011199091, 0.030792665, -0.005161124, + -0.001620709, -0.018246071, 0.013346067, + -0.027826872, 0.0015457582, -0.021534234, + 0.029167926, 0.0314374, 0.005850993, + -0.010019221, -0.02372634, 0.0028223386, + -0.008529877, 0.0029351676, -0.008355798, + 0.024899764, -0.00815593, -0.008265535, + 0.00082204037, -0.00906501, 0.015280279, + 0.019857915, -0.022643182, -0.012424092, + 0.017511072, -0.022191865, 0.011695539, + -0.0065473095, -0.027646346, 0.00588323, + -0.015486595, 0.008839351, 0.014480805, + -0.020992653, 0.006653691, -0.010870275, + 0.030947402, 0.007627245, -0.00724685, + 0.023442656, 0.015873438, 0.0030012531, + 0.012095276, -0.013346067, 0.025686344, + -0.0052159266, -0.0072404025, 0.00056616013, + -0.026215028, 0.007472508, 0.013294488, + 0.018503968, 0.010322248, -0.008271983, + 0.025763711, -0.015112648, 0.023597393, + 0.036853198, 0.00044043633, -0.02836845, + 0.0050096107, 0.006179809, -0.012320934, + 0.0051901373, 0.014841858, 0.008845799, + -0.015654227, -0.02896161, -0.03208214, + -0.011360276, 0.0020309233, 0.007949614, + 0.0019648375, 0.010489879, -0.019961074, + 0.021082917, 0.00633777, 0.0061346777, + -0.009303562, -0.038348988, 0.017240282, + 0.00785935, -0.017988177, -0.011760012, + -0.022024235, -0.008349351, 0.00032861467, + 0.014558174, 0.006453823, 0.025957134, + -0.009806458, 0.012843172, 0.0025773384, + 0.0069309287, 0.006057309, -0.011611723, + 0.000929228, 0.007175929, -0.0061346777, + -0.0059219142, -0.0116826445, -0.026034502, + -0.008233299, 0.0019728967, -0.022114497, + -0.004036057, -0.005631782, 0.010515669, + 0.0025128645, -0.0010227149, -0.02250134, + 0.008568562, -0.035976354, 0.0061636907, + -0.009774221, -0.01267554, 0.02586687, + 0.008297772, 0.015538175, -0.030225296, + -0.008729747, -0.004932242, 0.09067588, + 0.010444748, 0.008845799, -0.002404871, + 0.0005435943, -0.005302966, -0.023171866, + -0.008065666, 0.0008373529, -0.00864593, + 0.027311081, -0.005067637, 0.038503725, + 0.010535011, 0.017678702, -0.0012048533, + -0.013197777, 0.012817382, 0.029632136, + -0.022359498, -0.014171331, -0.019445283, + 0.0027723713, -0.020773442, -0.0144937, + -0.038632672, 0.016930807, 0.015306069, + -0.007079218, -0.0034912538, -0.029632136, + -0.015654227, -0.013513698, 0.008291325, + 0.019316336, 0.018865021, 0.011818039, + 0.0016360215, 0.040515307, -0.02299134, + 0.020554233, 0.018942388, 0.015576859, + 0.008626589, 0.008259088, -0.017781861, + 0.012211329, 0.027104765, 0.010618827, + -0.022101603, 0.03208214, 0.015602648, + -0.0019342126, 0.011592381, 0.020554233, + 0.014880543, -0.0094325105, 0.021276338, + -0.009542115, 0.01736923, -0.009645274, + -4.7901984e-5, 0.011650407, 0.00633777, + -0.011199091, -0.026743712, -0.018826336, + -0.017278966, 0.0042875046, -0.0030479967, + -0.016028175, -0.019019756, -0.014390541, + 0.001744015, 0.010638169, -0.0022146734, + 0.020231863, -0.015215806, 0.005199808, + 0.011714881, -0.0198966, -0.026369765, + -0.0017472387, -0.041211624, -0.013487909, + 0.007923825, 0.0017327322, 0.02385529, + -0.03626004, -0.008201062, 0.008761983, + 0.031127928, -0.008220403, -0.027104765, + 0.010651064, 0.01292054, 0.017407913, + 0.025957134, -0.0016537518, -0.0052417163, + 0.012050144, -0.031953193, 0.00034130792, + -0.0072081657, 0.006453823, -0.015460806, + -0.008349351, 0.023597393, -0.0076014553, + -0.002962569, 0.015422122, -0.005077308, + 0.02217897, 0.0059509273, 0.0036330959, + 0.027646346, -0.0048580975, 0.014790279, + 0.0049064527, 0.0069438233, -0.00032942058, + -0.013965015, 0.00072170305, 0.015293174, + -0.047297947, 0.014725805, -0.023326604, + -0.038142674, -0.011534355, 0.0059219142, + -0.018723179, 0.022630287, 0.012875409, + 0.0062700724, -0.033268455, -0.020489758, + -0.003417109, 0.008097904, -0.017304756, + -0.0036169775, -0.01921318, 0.0057865195, + -0.003436451, -0.016273174, -0.01653107, + -0.02695003, 0.0070727705, -0.018246071, + 0.002475792, 0.008433167, 0.0019986862, + 0.003106023, -0.00036810484, 0.0196516, + 0.00033062947, -0.018852126, -0.014455016, + -0.022939762, 0.0020099692, 0.018710284, + 0.04551847, 0.012146855, 0.033500563, + -0.0042488202, -0.01070909, 0.01943239, + -0.0012233894, -0.021946866, -0.042191625, + 0.026047396, -0.015486595, -0.0026885555, + 0.020541338, 0.020618707, 0.009245536, + 0.023300814, 0.0080011925, 0.0057832957, + 0.0004283475, -0.022075813, -0.026253711, + -0.002672437, -0.0034848063, -0.0021937194, + 0.014661332, 0.00062055985, 0.02220476, + 0.013965015, -0.004345531, -0.00042028827, + 0.047787946, 0.00026776755, 0.02114739, + 0.009729089, 0.0049999394, -0.040154252, + -0.0065376386, -0.017175809, -0.013126856, + 0.010934749, 0.025518712, -0.011946986, + -0.0037072408, -0.005335203, 0.006389349, + 0.04497689, 0.0077690873, -0.008407378, + 0.010457642, -0.0061185593, -0.017407913, + -0.025918448, -0.019419495, -0.034428984, + 0.017395018, -0.014803174, -0.02839424, + 0.023765026, -0.012366066, -0.017304756, + -0.007975403, -0.016221596, 0.0252995, + 0.018142914, 0.029322663, 0.02692424, + -0.010915406, 0.018555546, 0.026975818, + -0.023584498, -0.0058671115, -0.0016118438, + 0.0034719117, 0.012095276, 0.0026079633, + -0.0061056647, -0.012914093, -0.03417109, + -0.02377792, 0.012346724, 0.015615543, + 0.023881078, -0.014455016, 0.014235805, + 0.0028642465, 0.016943702, -0.012262908, + 0.009142378, -0.023184761, -0.01889081, + -0.014648437, -0.0027111212, -0.0056221113, + 0.0013894094, 0.0020583246, -0.005654348, + 0.016273174, -0.035280038, -0.004754939, + 0.013242909, -0.01813002, 0.042062677, + -0.0024435553, -0.00020671898, 0.018542651, + 0.0024274369, 0.002786878, 0.0056736902, + -0.006679481, 0.027594766, 0.01395212, + 0.022075813, -0.006756849, -0.0035653985, + 0.014648437, -0.008826457, -0.0060025062, + -0.011534355, 0.0021082917, 0.0063119805, + 0.035331618, 0.007943166, -0.0016972716, + 0.0037104646, -0.025995817, 0.01815581, + 0.005470598, -0.032701086, 0.003128589, + -0.021353707, 0.01525449, -0.012288697, + -0.013539488, -0.0052481634, 0.018258967, + -0.006698823, 0.02380371, 0.0093744835, + -0.018478177, -0.0077561927, -0.008478299, + -0.009716195, -0.011824487, -0.001455495, + 0.014622647, -0.020373706, 0.025725028, + -0.0071694814, 0.0016601991, -0.011070143, + -0.008458956, -0.0057155984, 0.011695539, + -0.0116246175, -0.00931001, 0.016427912, + 0.0030818454, -0.027568977, 0.005425466, + 0.014777384, -0.014996595, -0.012656198, + 0.024899764, -0.0034461222, 0.021624496, + 0.009767774, -0.010354484, -0.0040844125, + -0.006076651, -0.009484089, -0.01577028, + -0.006421586, 0.019226072, 0.0016368274, + -0.005554414, -0.006502178, -0.0009622708, + 0.028420031, -0.017614229, 0.019986864, + 0.00038583513, 0.030689506, -7.221816e-6, + 0.03259793, -0.0132171195, 0.0053126374, + -0.025286606, -0.028265294, -0.016028175, + 0.008020535, -0.012707776, 0.03687899, + -0.0018004295, -0.008884483, -0.029632136, + -0.0030899046, -0.021108707, -0.0052191503, + -0.016298965, 0.02328792, 0.021624496, + -0.009271326, -0.012643303, -0.004052175, + 0.0098709315, 0.00576073, -0.039690044, + 0.010844485, -0.00066327373, 0.0201416, + -0.007478955, 0.02095397, -0.02266897, + -0.016118439, 0.019458178, 0.024409764, + -0.0065666516, 0.0057961904, -0.010283563, + -0.030715296, -0.020696076, -0.005776848, + -0.007904482, -0.016260281, 0.01598949, + -0.018542651, 0.009683957, -0.0049161236, + 0.0060605328, -0.017730283, 0.004674347, + 0.012153302, -0.016647123, 0.011927645, + -0.019496864, -0.016905017, -0.0045163864, + -0.006160467, 0.015912123, 0.0049709263, + 0.0068148756, 0.013552383, 0.0014579127, + -0.016969493, -0.021921076, 0.000557295, + 0.019316336, 0.003839412, 0.0040682936, + 0.0027498056, -0.0114054065, -0.022114497, + 0.0007245238, -0.0064377044, -0.0020905614, + -0.031205297, 0.011418302, -0.002292042, + 0.029374242, 0.0033558588, -0.0094905365, + 0.025995817, 0.0013201, 0.014184225, + -0.0067375074, -0.01913581, -0.017021071, + -0.004384215, -0.0044970443, 0.0045163864, + -0.014790279, 0.0045260573, -0.021560023, + -0.014635542, -0.02896161, 0.026434239, + 0.21374339, 0.0019454955, -0.007627245, + 0.058232695, 0.011025012, 0.032752667, + 0.017150018, 0.0028642465, -0.00412632, + 0.011173301, -0.016053965, 0.0034557933, + -0.0069180336, 0.0089940885, -0.007781982, + -0.023004236, -0.024242131, -0.03932899, + -0.033397403, -0.027285293, 0.015976597, + 0.004242373, 0.0009235865, -0.026202133, + 0.013023698, 0.008897378, 0.0051901373, + 0.009232641, 0.02380371, 0.012611066, + 0.009664616, -0.011392512, -0.0015900839, + -0.0024628974, -0.012166197, -0.0006120977, + 0.001671482, -0.009999879, 0.002964181, + 0.016724491, -0.014880543, 0.01736923, + 0.0062475065, 0.0045357286, 0.012475671, + 0.011953434, -0.02174055, 0.011850276, + 0.021031339, -0.004052175, -0.019961074, + -0.005438361, 0.03308793, 0.04342952, + -0.02741424, 0.014145541, 0.024035815, + -0.018246071, -0.002527371, -0.0041617807, + 0.009258431, 0.030302664, -0.03097319, + 0.0087168515, -0.0034977011, 0.019342126, + -0.040489517, -0.018220283, -0.0007164646, + -0.0028819768, -0.019587126, -0.035950564, + -0.004800071, -0.00854922, 0.0011097544, + -0.008839351, 0.015422122, 0.0058703353, + -0.0066923755, 0.025441343, -0.021895286, + 5.938637e-5, 0.025467133, 0.0125594875, + -0.0031721087, -0.01682765, 0.008026983, + 0.018761862, -0.013346067, -0.010799354, + -0.0035267144, -0.008484746, 0.0010178793, + 0.008858694, 0.02141818, 0.013694225, + -0.015447911, 0.017640019, -0.0063925725, + -0.0055963215, -0.014661332, -0.011231327, + 0.031127928, -0.0062700724, -0.021082917, + 0.0012830277, -0.012656198, 0.005725269, + 0.028084766, -0.0059412564, 0.008478299, + -0.019174494, -0.0032333587, -0.011437644, + 0.0038458593, 0.010290011, 0.0059670457, + 0.00040920684, -0.013552383, -0.015925016, + -0.03922583, 0.00354928, -0.00069833134, + -0.008504088, 0.003926452, 0.0027207923, + -0.01582186, 0.0035944118, 0.00077569985, + -0.028574767, 0.03819425, -0.02114739, + 0.0059477035, 0.007401587, 0.0021937194, + 0.009793563, 0.022075813, -0.0017262847, + -0.012217776, 0.0023162195, 0.0052707293, + -0.0077046137, 0.0003106829, 0.014055278, + 0.013926331, -0.018942388, -0.006905139, + 0.0023597393, -0.003716912, -0.012424092, + -0.018877914, -0.034815826, -0.0063957963, + -0.002888424, 0.0206316, -0.017575545, + -0.032314245, -0.028806873, -0.021792129, + 0.02250134, -0.003478359, 0.011663302, + 0.0099483, 0.004809742, 4.586829e-6, + -0.003136648, -0.16443387, 0.01085738, + 0.014171331, -0.009471194, 0.032830033, + -0.0075950082, 0.022243446, -0.00470336, + -0.012630409, -0.014351858, -0.015357648, + -0.0010396392, -0.05524111, -0.0045776363, + 0.007472508, 0.0055673085, -0.0020551009, + 0.026266607, 0.012424092, -0.0014990148, + 0.0019051994, 0.016866334, -0.007472508, + -0.0129979085, 0.017652914, 0.0043584257, + 0.007904482, 0.005170795, -0.029657926, + 0.001992239, 3.838203e-5, 0.0075176395, + 0.011482775, -0.007227508, 0.015138437, + 0.00582198, -0.0058993483, 0.008600798, + 0.015834754, -0.026021607, 0.024990026, + 0.011650407, 0.009193957, -0.020721864, + 0.005225598, 0.0252995, 0.015615543, + -0.0020970087, 0.002993194, -0.008407378, + -0.011798697, -0.014132647, 0.043532677, + -0.023120288, -0.0034009905, 0.008948957, + -0.015757386, 0.01631186, -0.003375201, + -0.008394483, -0.0027224042, -0.024177657, + -0.009071457, 0.0064634937, -0.0071372446, + -0.025054501, -0.0029238847, 0.009123036, + -0.0036459907, -0.0015264162, -0.03816846, + 0.0011129782, 0.0019890154, -0.026240818, + 0.013197777, -0.022514235, -0.022643182, + 0.039561097, -0.0028449043, 0.0028529635, + 0.0053416505, 0.04750426, -0.0022340156, + -0.020825023, 0.020309232, -0.0036588856, + -0.0012854454, -0.008026983, -0.00066327373, + -0.00536744, 0.011469881, -0.006234612, + -0.0034719117, -0.016660018, 0.0044196756, + 0.023842394, -0.008014088, 0.006305533, + 0.03687899, -0.035795826, 0.021044234, + -0.006782639, -0.018091336, 0.018826336, + 0.018865021, 0.032159507, 0.03148898, + 0.007659482, 0.0005738164, 0.008259088, + 0.0015788011, 0.0011186196, 0.011869618, + -0.01267554, 0.009245536, 0.01960002, + 0.030560559, -0.01729186, 0.014235805, + 0.0021066798, 0.03920004, 0.0013861856, + -0.019935284, -0.01753686, -0.021753443, + 0.0053416505, -0.07086955, -0.021753443, + -0.031205297, 0.0058413222, -0.025879765, + 0.018787652, -0.013171988, 0.0125594875, + -0.016479492, 0.0097033, 0.013500804, + -0.021031339, -0.015628438, -0.0028577992, + 0.019690285, 0.0030576678, -0.02793003, + 0.0034332273, -0.0010960537, -0.0015973372, + -0.021598708, 0.008703956, -0.022114497, + -0.02689845, -0.007350008, 0.0029190492, + -0.01061238, 0.018181598, 0.0039909254, + 0.024886869, -0.030792665, -0.0026772725, + -0.0027062858, -0.015589753, 0.015164226, + -0.00011343351, -0.011070143, 0.0013273534, + 0.009896721, -0.034325827, 0.003436451, + -0.0029657925, 0.006782639, 0.0022469102, + -0.02068318, 0.004203689, -0.021998445, + 0.0022855946, 0.007781982, -0.0066214544, + -0.0189166, 0.005273953, -0.024177657, + 0.0011895407, 0.020515548, -0.016672913, + 0.014880543, -0.010199748, -6.2458945e-5, + -0.0097097475, -0.007504745, -0.0038265171, + -0.015628438, 0.026769502, 0.01815581, + -0.025995817, 0.00031833915, 0.0019438836, + 0.008368693, -0.018091336, 0.0016843769, + 0.023249235, -0.021882392, 0.0109669855, + 0.004232702, 0.0012532086, 0.0041650045, + 0.005016058, 0.031901613, -0.024138974, + -0.012391855, -0.026150554, 0.0198966, + -0.024938447, 0.035563722, 0.012037249, + 0.020837918, 0.007678824, -0.008091456, + -0.03324267, -3.8961287e-5, -0.009148826, + 0.01810423, -0.028058978, -0.015306069, + 0.0072404025, 0.0058993483, -0.0067761913, + 0.0351253, 0.036853198, -0.012991461, + -0.005686585, -0.0763885, 0.01292054, + -0.011605276, -0.010051458, -0.012752908, + -0.015628438, 0.013513698, 0.00903922, + 0.0043874388, 0.0048774392, -0.026176345, + 0.01582186, -0.005725269, 0.0116246175, + 0.0059380326, -0.019819232, -0.014210016, + -0.024822395, -0.005999283, -0.014158436, + -0.015538175, -0.006524744, -0.010580143, + 0.0059509273, -0.012050144, -0.0023565157, + -0.022398181, 0.012101724, -0.012191987, + -0.0063442173, 0.0162087, -0.026098976, + 0.0051901373, -0.012411198, 0.005770401, + -0.01815581, -0.023365289, -0.0074918503, + 0.021585813, 0.0252995, -0.010264222, + -0.019367915, 0.025853975, -0.0059670457, + -0.0026676015, 0.006750402, -0.002424213, + 0.0037813857, 0.031824246, -0.00041666164, + 0.01785923, 0.02190818, 0.0055479663, + -0.011108828, -0.010721985, -0.017949494, + 0.0053513213, -0.017652914, -0.0007108231, + -0.014261594, 0.004468031, 0.00894251, + -0.0071694814, -0.0036363197, -0.0017069426, + -0.009864484, 0.016892124, -0.020231863, + 0.010231985, -0.02557029, 0.003900662, + -0.005025729, -0.012727119, 0.03254635, + 0.0069115865, 0.017910808, -0.009445405, + 0.01341054, 0.0038458593, 0.020567127, + -0.016763175, -0.00062620133, -0.022643182, + 0.008742641, 0.028677925, 0.016943702, + -0.021082917, -0.009277773, 0.012952777, + -0.0022098378, 0.007923825, 0.0012322546, + 0.015241595, -0.004996716, 0.0033461878, + 0.009941853, -0.013320277, -0.007459613, + 0.009277773, 0.007175929, -0.0120050125, + -0.0036105302, -0.019264758, -0.015473701, + -0.005905796, 0.0056736902, -0.013926331, + -0.027156346, -0.00658277, 0.010605932, + 0.020090021, 0.007814219, -0.020244759, + 0.0060669803, -0.012856066, 0.006553757, + -0.018942388, -0.019019756, -0.019006863, + 0.01960002, -0.0055189533, 0.02741424, + 0.03417109, -0.0027804307, 0.014932121, + 0.028858451, 0.016737387, -0.016917912, + -0.015073963, -0.011282907, 0.0027691477, + -0.038555305, -0.035589512, 0.003849083, + -0.0006781833, -0.0049386895, -0.0060347435, + 0.031772666, -0.013797383, 0.04809742, + 0.022191865, -0.0058574406, 0.0011178138, + -0.052533213, -0.0024322723, 0.023984237, + 0.007711061, -0.000639902, -0.009761326, + 0.0065666516, -0.005880006, 0.021186076, + -0.00024399286, -0.013681331, -0.009116589, + -0.02584108, -0.0031753324, 0.0038942148, + -0.027749503, 0.027053187, 0.014506594, + 0.04283636, 0.017433703, -0.0019277652, + -0.015202911, 0.010180405, 0.018297652, + -0.021959761, -0.026395554, -0.015796069, + -0.01525449, -0.0049580317, -0.027955819, + 0.029013189, -0.034351617, -0.013797383, + -0.010780011, 0.021843707, 0.028033188, + -0.018748967, 0.012926987, -0.031231087, + -0.0252995, 0.017923703, -0.011070143, + -0.012101724, -0.015886333, -0.031798456 + ], + "topk": 9.0 + }, + "outputs": [ + { + "plot": "A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.", + "genres": ["Drama", "Fantasy", "Horror"], + "title": "The Craft", + "year": 1996 + }, + { + "plot": "A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...", + "genres": ["Comedy", "Fantasy"], + "title": "Wicked Stepmother", + "year": 1989 + }, + { + "plot": "A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.", + "genres": [ + "Adventure", + "Family", + "Fantasy" + ], + "title": "Oz the Great and Powerful", + "year": 2013 + }, + { + "plot": "A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.", + "genres": [ + "Adventure", + "Fantasy", + "Musical" + ], + "title": "Into the Woods", + "year": 2014 + }, + { + "plot": "In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.", + "genres": ["Fantasy", "Horror"], + "title": "Snow White: A Tale of Terror", + "year": 1997 + }, + { + "plot": "An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.", + "genres": [ + "Adventure", + "Family", + "Fantasy" + ], + "title": "Bedknobs and Broomsticks", + "year": 1971 + }, + { + "plot": "When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.", + "genres": [ + "Animation", + "Family", + "Fantasy" + ], + "title": "My Neighbor Totoro", + "year": 1988 + }, + { + "plot": "After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.", + "genres": ["Comedy", "Family", "Fantasy"], + "title": "Hocus Pocus", + "year": 1993 + }, + { + "year": 1998, + "plot": "The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love.", + "genres": ["Comedy", "Fantasy", "Romance"], + "title": "Practical Magic" + } + ] + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 0.0, + "completion": 0.0, + "total": 0.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-254c-eff4fcfe2c18" + } + ] + } + } + }, + "reporter": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361f9ff" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-f638-ccc37deb35c6", + "type": "task", + "name": "reporter" + }, + "parent": { + "id": "43c39569-3e04-49aa-2d60-d51d0c7dfede" + }, + "time": { + "start": "2024-10-08T01:17:09.132009", + "end": "2024-10-08T01:17:10.953560", + "span": 1821551 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "count": 3, + "movies": [ + "The Craft (1996) in ['Drama', 'Fantasy', 'Horror']: A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.", + "Wicked Stepmother (1989) in ['Comedy', 'Fantasy']: A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...", + "Oz the Great and Powerful (2013) in ['Adventure', 'Family', 'Fantasy']: A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.", + "Into the Woods (2014) in ['Adventure', 'Fantasy', 'Musical']: A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.", + "Snow White: A Tale of Terror (1997) in ['Fantasy', 'Horror']: In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.", + "Bedknobs and Broomsticks (1971) in ['Adventure', 'Family', 'Fantasy']: An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.", + "My Neighbor Totoro (1988) in ['Animation', 'Family', 'Fantasy']: When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.", + "Hocus Pocus (1993) in ['Comedy', 'Family', 'Fantasy']: After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.", + "Practical Magic (1998) in ['Comedy', 'Fantasy', 'Romance']: The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + ] + }, + "outputs": { + "report": "Sure! Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches cause havoc in a yuppie family's home in a comedic way.\n2. Hocus Pocus (1993) - Three witch sisters are resurrected in Salem Massachusetts on Halloween night, leading to comedic chaos and a battle to end their reign of terror.\n3. Practical Magic (1998) - The Owens sisters, Sally and Gillian, use their hereditary gift for practical magic to overcome obstacles in love in a wry and comedic romantic tale." + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 536.0, + "completion": 126.0, + "total": 662.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-f638-ccc37deb35c6" + } + ], + "nodes": { + "openai.chat": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361f9ff" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-2ebe-b45375e00a9c", + "type": "chat", + "name": "openai.chat" + }, + "parent": { + "id": "43c39569-3e04-49aa-f638-ccc37deb35c6" + }, + "time": { + "start": "2024-10-08T01:17:09.132381", + "end": "2024-10-08T01:17:10.953258", + "span": 1820877 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "role": "system", + "content": "Given the following list of suggested movies:\n\nThe Craft (1996) in ['Drama', 'Fantasy', 'Horror']: A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.\nWicked Stepmother (1989) in ['Comedy', 'Fantasy']: A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...\nOz the Great and Powerful (2013) in ['Adventure', 'Family', 'Fantasy']: A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.\nInto the Woods (2014) in ['Adventure', 'Fantasy', 'Musical']: A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.\nSnow White: A Tale of Terror (1997) in ['Fantasy', 'Horror']: In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.\nBedknobs and Broomsticks (1971) in ['Adventure', 'Family', 'Fantasy']: An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.\nMy Neighbor Totoro (1988) in ['Animation', 'Family', 'Fantasy']: When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.\nHocus Pocus (1993) in ['Comedy', 'Family', 'Fantasy']: After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.\nPractical Magic (1998) in ['Comedy', 'Fantasy', 'Romance']: The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + }, + { + "role": "user", + "content": "Provide a list of 3 movies about witches in the genre of comedy." + } + ] + }, + "outputs": { + "completion": [ + { + "finish_reason": "stop", + "role": "assistant", + "content": "Sure! Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches cause havoc in a yuppie family's home in a comedic way.\n2. Hocus Pocus (1993) - Three witch sisters are resurrected in Salem Massachusetts on Halloween night, leading to comedic chaos and a battle to end their reign of terror.\n3. Practical Magic (1998) - The Owens sisters, Sally and Gillian, use their hereditary gift for practical magic to overcome obstacles in love in a wry and comedic romantic tale." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 662, + "completion": 126, + "prompt": 536 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 536, + "completion": 126, + "total": 662 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "gpt-3.5-turbo", + "temperature": 0.8, + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "gpt-3.5-turbo-0125" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-2ebe-b45375e00a9c" + } + ] + } + } + }, + "summarizer": [ + { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361f9ff" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-e2a6-0ea40ffabc06", + "type": "task", + "name": "summarizer" + }, + "parent": { + "id": "43c39569-3e04-49aa-2d60-d51d0c7dfede" + }, + "time": { + "start": "2024-10-08T01:17:10.953757", + "end": "2024-10-08T01:17:13.033807", + "span": 2080050 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "report": "Sure! Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches cause havoc in a yuppie family's home in a comedic way.\n2. Hocus Pocus (1993) - Three witch sisters are resurrected in Salem Massachusetts on Halloween night, leading to comedic chaos and a battle to end their reign of terror.\n3. Practical Magic (1998) - The Owens sisters, Sally and Gillian, use their hereditary gift for practical magic to overcome obstacles in love in a wry and comedic romantic tale." + }, + "outputs": { + "report": "Here are three notable comedy films featuring witches. \n\n1. *Wicked Stepmother* (1989) - This film humorously explores the chaos that ensues when a mother and daughter duo of witches invade a yuppie family's home. \n2. *Hocus Pocus* (1993) - A cult classic where three witch sisters are accidentally resurrected on Halloween, unleashing comedic mayhem in Salem. \n3. *Practical Magic* (1998) - A charming romantic comedy about two witch sisters navigating love and family curses with a blend of humor and magic." + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 172.0, + "completion": 120.0, + "total": 292.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-e2a6-0ea40ffabc06" + } + ], + "nodes": { + "openai.chat": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361f9ff" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-54f6-bd98032e1a49", + "type": "chat", + "name": "openai.chat" + }, + "parent": { + "id": "43c39569-3e04-49aa-e2a6-0ea40ffabc06" + }, + "time": { + "start": "2024-10-08T01:17:10.953998", + "end": "2024-10-08T01:17:13.033642", + "span": 2079644 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "role": "system", + "content": "Act as a professional cinema critic.\nBe concise and factual.\nUse one intro sentence, and one sentence per movie." + }, + { + "role": "user", + "content": "Summarize the following recommendations about witches in the genre of comedy:\n\nSure! Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches cause havoc in a yuppie family's home in a comedic way.\n2. Hocus Pocus (1993) - Three witch sisters are resurrected in Salem Massachusetts on Halloween night, leading to comedic chaos and a battle to end their reign of terror.\n3. Practical Magic (1998) - The Owens sisters, Sally and Gillian, use their hereditary gift for practical magic to overcome obstacles in love in a wry and comedic romantic tale." + } + ] + }, + "outputs": { + "completion": [ + { + "finish_reason": "stop", + "role": "assistant", + "content": "Here are three notable comedy films featuring witches. \n\n1. *Wicked Stepmother* (1989) - This film humorously explores the chaos that ensues when a mother and daughter duo of witches invade a yuppie family's home. \n2. *Hocus Pocus* (1993) - A cult classic where three witch sisters are accidentally resurrected on Halloween, unleashing comedic mayhem in Salem. \n3. *Practical Magic* (1998) - A charming romantic comedy about two witch sisters navigating love and family curses with a blend of humor and magic." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 292, + "completion": 120, + "prompt": 172 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 172, + "completion": 120, + "total": 292 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "gpt-4o-mini", + "temperature": 0.2, + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "gpt-4o-mini-2024-07-18" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-54f6-bd98032e1a49" + } + ] + } + } + }, + { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361f9ff" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-e106-ba0319c6e51f", + "type": "task", + "name": "summarizer" + }, + "parent": { + "id": "43c39569-3e04-49aa-2d60-d51d0c7dfede" + }, + "time": { + "start": "2024-10-08T01:17:13.033927", + "end": "2024-10-08T01:17:14.639404", + "span": 1605477 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "report": "Here are three notable comedy films featuring witches. \n\n1. *Wicked Stepmother* (1989) - This film humorously explores the chaos that ensues when a mother and daughter duo of witches invade a yuppie family's home. \n2. *Hocus Pocus* (1993) - A cult classic where three witch sisters are accidentally resurrected on Halloween, unleashing comedic mayhem in Salem. \n3. *Practical Magic* (1998) - A charming romantic comedy about two witch sisters navigating love and family curses with a blend of humor and magic." + }, + "outputs": { + "report": "Here are three notable comedy films featuring witches. \n\n*Wicked Stepmother* (1989) humorously explores the chaos that ensues when a mother and daughter duo of witches invade a yuppie family's home. *Hocus Pocus* (1993) is a cult classic where three witch sisters are accidentally resurrected on Halloween, unleashing comedic mayhem in Salem. *Practical Magic* (1998) is a charming romantic comedy about two witch sisters navigating love and family curses with a blend of humor and magic." + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 168.0, + "completion": 109.0, + "total": 277.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-e106-ba0319c6e51f" + } + ], + "nodes": { + "openai.chat": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361f9ff" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-5699-abba488eec45", + "type": "chat", + "name": "openai.chat" + }, + "parent": { + "id": "43c39569-3e04-49aa-e106-ba0319c6e51f" + }, + "time": { + "start": "2024-10-08T01:17:13.034164", + "end": "2024-10-08T01:17:14.639038", + "span": 1604874 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "role": "system", + "content": "Act as a professional cinema critic.\nBe concise and factual.\nUse one intro sentence, and one sentence per movie." + }, + { + "role": "user", + "content": "Summarize the following recommendations about witches in the genre of comedy:\n\nHere are three notable comedy films featuring witches. \n\n1. *Wicked Stepmother* (1989) - This film humorously explores the chaos that ensues when a mother and daughter duo of witches invade a yuppie family's home. \n2. *Hocus Pocus* (1993) - A cult classic where three witch sisters are accidentally resurrected on Halloween, unleashing comedic mayhem in Salem. \n3. *Practical Magic* (1998) - A charming romantic comedy about two witch sisters navigating love and family curses with a blend of humor and magic." + } + ] + }, + "outputs": { + "completion": [ + { + "finish_reason": "stop", + "role": "assistant", + "content": "Here are three notable comedy films featuring witches. \n\n*Wicked Stepmother* (1989) humorously explores the chaos that ensues when a mother and daughter duo of witches invade a yuppie family's home. *Hocus Pocus* (1993) is a cult classic where three witch sisters are accidentally resurrected on Halloween, unleashing comedic mayhem in Salem. *Practical Magic* (1998) is a charming romantic comedy about two witch sisters navigating love and family curses with a blend of humor and magic." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 277, + "completion": 109, + "prompt": 168 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 168, + "completion": 109, + "total": 277 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "gpt-4o-mini", + "temperature": 0.2, + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "gpt-4o-mini-2024-07-18" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-5699-abba488eec45" + } + ] + } + } + } + ] + } + } + } + } + ] + }, + { + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f900" + }, + "trees": [ + { + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "nodes": { + "rag": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f900" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-3839-39eb32070bbc", + "type": "workflow", + "name": "rag" + }, + "time": { + "start": "2024-10-08T01:22:06.513070", + "end": "2024-10-08T01:22:14.424053", + "span": 7910983 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "count": 3 + }, + "outputs": "These three comedy films showcase witches in entertaining and unconventional ways. \n\n\"Wicked Stepmother\" (1989) features a mother/daughter duo sharing a body as they disrupt a yuppie family's life. \n\n\"Hocus Pocus\" (1993) is a Halloween favorite where two teens and an immortal cat try to stop the resurrection of three mischievous witch sisters. \n\n\"Practical Magic\" (1998) blends romance and humor, following the Owens sisters as they deal with love and their magical family legacy." + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 939.0, + "completion": 407.0, + "total": 1346.0 + } + } + }, + "meta": { + "configuration": { + "retriever_prompt": "Movies about {topic} in the genre of {genre}.", + "retriever_multiplier": 3.0, + "generator_context_prompt": "Given the following list of suggested movies:\n\n{movies}", + "generator_instructions_prompt": "Provide a list of {count} movies about {topic} in the genre of {genre}.", + "generator_model": "gpt-3.5-turbo", + "generator_temperature": 0.8, + "summarizer_context_prompt": "Act as a professional cinema critic.\nBe concise and factual.\nUse one intro sentence, and one sentence per movie.", + "summarizer_instructions_prompt": "Summarize the following recommendations about {topic} in the genre of {genre}:\n\n{report}", + "summarizer_model": "gpt-4o-mini", + "summarizer_temperature": 0.2 + }, + "environment": "terminal" + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-3839-39eb32070bbc" + } + ], + "nodes": { + "retriever": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f900" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-ef78-9bdcad4f3199", + "type": "task", + "name": "retriever" + }, + "parent": { + "id": "3283749b-be79-15d6-3839-39eb32070bbc" + }, + "time": { + "start": "2024-10-08T01:22:06.513540", + "end": "2024-10-08T01:22:07.337752", + "span": 824212 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "count": 3 + }, + "internals": { + "prompt": "Movies about witches in the genre of comedy." + }, + "outputs": { + "movies": [ + "The Craft (1996) in ['Drama', 'Fantasy', 'Horror']: A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.", + "Wicked Stepmother (1989) in ['Comedy', 'Fantasy']: A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...", + "Oz the Great and Powerful (2013) in ['Adventure', 'Family', 'Fantasy']: A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.", + "Into the Woods (2014) in ['Adventure', 'Fantasy', 'Musical']: A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.", + "Snow White: A Tale of Terror (1997) in ['Fantasy', 'Horror']: In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.", + "Bedknobs and Broomsticks (1971) in ['Adventure', 'Family', 'Fantasy']: An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.", + "My Neighbor Totoro (1988) in ['Animation', 'Family', 'Fantasy']: When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.", + "Hocus Pocus (1993) in ['Comedy', 'Family', 'Fantasy']: After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.", + "Practical Magic (1998) in ['Comedy', 'Fantasy', 'Romance']: The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + ], + "test": [ + 1, + false, + "test", + { + "test": "test" + } + ] + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 9.0, + "completion": 0.0, + "total": 9.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-ef78-9bdcad4f3199" + } + ], + "nodes": { + "openai.embeddings": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f900" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-94b9-72820ad2ef90", + "type": "embedding", + "name": "openai.embeddings" + }, + "parent": { + "id": "3283749b-be79-15d6-ef78-9bdcad4f3199" + }, + "time": { + "start": "2024-10-08T01:22:06.513792", + "end": "2024-10-08T01:22:07.237280", + "span": 723488 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "content": "Movies about witches in the genre of comedy." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 9, + "prompt": 9 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 9, + "completion": 0.0, + "total": 9 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "text-embedding-ada-002", + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "text-embedding-ada-002" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-94b9-72820ad2ef90" + } + ] + }, + "search": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f900" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-a84a-c487893ed246", + "type": "query", + "name": "search" + }, + "parent": { + "id": "3283749b-be79-15d6-ef78-9bdcad4f3199" + }, + "time": { + "start": "2024-10-08T01:22:07.237749", + "end": "2024-10-08T01:22:07.337318", + "span": 99569 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "query": [ + 0.0031027994, -0.036517933, -0.0051836898, + -0.02046397, -0.006576323, 0.004403557, + 0.0034203327, -0.036904775, 0.019174494, + -0.0280074, 0.011934091, 0.010225537, + 0.021160286, -0.0015441463, 0.010141721, + 0.0064248093, 0.040102676, -0.004100531, + 0.018220283, -0.029554768, -0.01073488, + -0.0032655955, -0.009129483, 0.012288697, + -0.014596858, 0.008974747, 0.014519489, + -0.020064233, -0.019986864, -0.006328099, + 0.02557029, -0.019006863, 0.0008212344, + -0.019832127, -0.014390541, -0.032701086, + -0.009793563, -0.011424749, -0.0018794099, + -0.0036814513, 0.0060154013, 0.011882513, + -0.006324875, -0.0067632967, 0.0077432976, + 0.013642646, 0.023545815, 0.0020696074, + -0.0036782275, 0.013230015, 0.020090021, + 0.0016279622, -0.025492921, -0.011985671, + 0.010547906, 0.030302664, 0.015241595, + 0.011882513, 0.019548442, -0.020025548, + -0.015860543, -0.004696913, -0.027749503, + -0.007659482, 0.00085750094, -0.010541459, + 0.0004448689, -0.007943166, 0.0029077663, + -0.025583185, 0.042165834, 0.008826457, + -0.004036057, -0.008394483, 0.017691597, + -0.03154056, -0.030147927, 0.02019318, + -0.0013257415, -0.00235168, 0.022411076, + -0.012759356, -0.010573695, 0.013836067, + 0.012288697, 0.01881344, -0.0091552725, + 0.024138974, 0.007227508, -0.008968299, + 0.005528624, 0.0025837857, 0.032778457, + 0.033345826, 0.01495791, 0.012856066, + -0.011263564, 0.03827162, -0.0128367245, + -0.0038426358, -0.00024117214, 0.011411854, + -0.03362951, -0.012166197, 0.014571068, + -0.018207388, 0.0030834572, 0.01210817, + 0.042088468, 0.014700016, -0.028523188, + -0.00867172, 0.022462655, -0.043661624, + -0.004522834, -0.012636855, 0.029632136, + 0.008749088, 0.0013644258, -0.030560559, + 0.026640555, -0.002135693, 0.025235027, + -0.019664494, 0.03778162, -0.0037942803, + -0.00776264, -0.023971342, 0.004748492, + -0.026176345, 0.029632136, -0.019780548, + 0.02787845, -0.020412391, -0.027027396, + 0.009961195, -0.03687899, 0.008072114, + -0.029322663, -0.020747654, -0.0001522185, + 0.03468688, -0.030405821, -0.0011669749, + -0.009393826, 0.00076844654, 0.0101933, + -0.010032116, 0.0042359256, -0.021186076, + 0.027517397, -0.005860664, 0.02119897, + -0.004751716, 0.012417645, 0.009464747, + 0.017033966, 0.009297115, -0.036956355, + -0.028136346, -0.013500804, 0.008845799, + 0.00530619, -0.008890931, 0.008342904, + 0.013107514, 0.024113184, -0.022243446, + -0.006905139, 0.0014063338, 0.018607125, + 0.019496864, -0.030199505, 0.03664688, + -0.0014168107, 0.0044132285, -0.00032337618, + 0.018929495, -0.0065602046, -0.0060121776, + 0.013178435, 0.0020148049, 0.0076723765, + 0.010515669, -0.017846335, 0.00094937603, + -0.01046409, -0.013642646, 0.0040908596, + -0.018555546, -0.008465404, 0.028806873, + -0.008607246, -0.008684615, -0.678367, + -0.014261594, -0.009974089, -0.026666345, + 0.01110238, 0.006286191, 0.02076055, + 0.012752908, -0.03974162, -0.016840544, + 0.004074741, 0.024371078, 0.020012653, + -0.00918751, -0.030792665, -0.027723715, + -0.009741984, -0.013810278, 0.005409348, + -0.0039780308, -0.0016174853, 0.037136883, + -0.0034622406, -0.016221596, 0.028316872, + 0.02527371, 0.0058477693, -0.0014804786, + -0.025995817, 0.0346353, -0.019419495, + 0.026692133, 0.00072371785, 0.0026240817, + 0.041005306, 0.016905017, -0.008594351, + 0.025054501, 0.015899228, 0.029167926, + -0.02945161, 0.004925795, -0.008220403, + 0.0026788844, -0.016298965, 0.010161064, + 0.0037298065, -0.0066214544, 0.0060121776, + 0.009928958, 0.010148169, 0.008684615, + 0.009954748, 0.015061068, 0.01471291, + 0.015486595, 0.037368987, -0.008562114, + 0.013642646, -0.00028348304, -0.027130555, + 0.021276338, -0.0048161894, -0.0067890864, + 0.0016110379, 0.026292397, 0.0077884295, + 0.010244879, 0.0068922443, -0.0145323835, + -0.0075176395, 0.0068793492, -0.018207388, + -0.0046711233, 0.0007555518, 0.0049161236, + 0.025995817, 0.0061475723, -0.0027320753, + -0.0061959275, -0.00014476372, -0.007420929, + -0.008761983, 0.0033494115, 0.02223055, + -0.019728968, -0.005628559, 0.029503189, + 0.005161124, 0.027723715, 0.011940539, + 0.018026862, -0.011979223, 0.008955404, + -0.00010980686, 0.010838038, 0.0033816483, + 0.019999757, 0.02991582, -0.04497689, + 0.00062821613, -0.009084351, -0.010257774, + -0.01908423, -0.006357112, 0.00056333945, + -0.0029254965, 0.021547128, 0.03675004, + -0.00052908773, 0.031127928, -0.0067632967, + -0.0023823052, -0.017214492, -0.01992239, + -0.026434239, 0.042062677, 0.0074467184, + 0.009780669, -0.010360932, 0.0064925067, + 0.00019835753, -0.008936062, -0.0058993483, + -0.010715538, -0.0012137184, -0.0045357286, + 0.01731765, -0.04459005, -0.012101724, + 0.0026273055, 0.012424092, -0.01938081, + -0.004148886, -0.018529756, 0.00867172, + 0.002588621, -0.020850811, 0.038091093, + 0.007801324, 0.0060186246, -0.004032833, + -0.01957423, 0.010483433, 0.008910272, + -0.015976597, -0.0037620435, 0.01215975, + 0.0038361882, 0.010535011, -0.013823173, + 0.001776252, -0.013848962, -0.007079218, + 0.008349351, -0.0060669803, -0.0095936945, + -0.018968178, -0.0068406654, -0.0064570466, + 0.038581096, 0.0053835586, -0.012727119, + 0.0014087515, -0.006008954, -0.0026063514, + 0.00791093, 0.020115811, -0.014687121, + -0.023507131, 0.005776848, -0.014945016, + -0.0012113006, 0.023674762, -0.0046936893, + 0.041778993, 0.002828786, 0.0128818555, + -0.008684615, 0.005973493, -0.012785145, + 0.0023065484, -0.0108058015, 0.0074467184, + 0.018142914, -0.0036814513, 0.014932121, + -0.016157122, -0.0056092166, -0.010689748, + -0.017717388, 0.020231863, 0.029761085, + -0.011940539, -0.0011726164, -0.0025692792, + 0.006750402, -0.0020921733, 0.027104765, + 0.011199091, 0.030792665, -0.005161124, + -0.001620709, -0.018246071, 0.013346067, + -0.027826872, 0.0015457582, -0.021534234, + 0.029167926, 0.0314374, 0.005850993, + -0.010019221, -0.02372634, 0.0028223386, + -0.008529877, 0.0029351676, -0.008355798, + 0.024899764, -0.00815593, -0.008265535, + 0.00082204037, -0.00906501, 0.015280279, + 0.019857915, -0.022643182, -0.012424092, + 0.017511072, -0.022191865, 0.011695539, + -0.0065473095, -0.027646346, 0.00588323, + -0.015486595, 0.008839351, 0.014480805, + -0.020992653, 0.006653691, -0.010870275, + 0.030947402, 0.007627245, -0.00724685, + 0.023442656, 0.015873438, 0.0030012531, + 0.012095276, -0.013346067, 0.025686344, + -0.0052159266, -0.0072404025, 0.00056616013, + -0.026215028, 0.007472508, 0.013294488, + 0.018503968, 0.010322248, -0.008271983, + 0.025763711, -0.015112648, 0.023597393, + 0.036853198, 0.00044043633, -0.02836845, + 0.0050096107, 0.006179809, -0.012320934, + 0.0051901373, 0.014841858, 0.008845799, + -0.015654227, -0.02896161, -0.03208214, + -0.011360276, 0.0020309233, 0.007949614, + 0.0019648375, 0.010489879, -0.019961074, + 0.021082917, 0.00633777, 0.0061346777, + -0.009303562, -0.038348988, 0.017240282, + 0.00785935, -0.017988177, -0.011760012, + -0.022024235, -0.008349351, 0.00032861467, + 0.014558174, 0.006453823, 0.025957134, + -0.009806458, 0.012843172, 0.0025773384, + 0.0069309287, 0.006057309, -0.011611723, + 0.000929228, 0.007175929, -0.0061346777, + -0.0059219142, -0.0116826445, -0.026034502, + -0.008233299, 0.0019728967, -0.022114497, + -0.004036057, -0.005631782, 0.010515669, + 0.0025128645, -0.0010227149, -0.02250134, + 0.008568562, -0.035976354, 0.0061636907, + -0.009774221, -0.01267554, 0.02586687, + 0.008297772, 0.015538175, -0.030225296, + -0.008729747, -0.004932242, 0.09067588, + 0.010444748, 0.008845799, -0.002404871, + 0.0005435943, -0.005302966, -0.023171866, + -0.008065666, 0.0008373529, -0.00864593, + 0.027311081, -0.005067637, 0.038503725, + 0.010535011, 0.017678702, -0.0012048533, + -0.013197777, 0.012817382, 0.029632136, + -0.022359498, -0.014171331, -0.019445283, + 0.0027723713, -0.020773442, -0.0144937, + -0.038632672, 0.016930807, 0.015306069, + -0.007079218, -0.0034912538, -0.029632136, + -0.015654227, -0.013513698, 0.008291325, + 0.019316336, 0.018865021, 0.011818039, + 0.0016360215, 0.040515307, -0.02299134, + 0.020554233, 0.018942388, 0.015576859, + 0.008626589, 0.008259088, -0.017781861, + 0.012211329, 0.027104765, 0.010618827, + -0.022101603, 0.03208214, 0.015602648, + -0.0019342126, 0.011592381, 0.020554233, + 0.014880543, -0.0094325105, 0.021276338, + -0.009542115, 0.01736923, -0.009645274, + -4.7901984e-5, 0.011650407, 0.00633777, + -0.011199091, -0.026743712, -0.018826336, + -0.017278966, 0.0042875046, -0.0030479967, + -0.016028175, -0.019019756, -0.014390541, + 0.001744015, 0.010638169, -0.0022146734, + 0.020231863, -0.015215806, 0.005199808, + 0.011714881, -0.0198966, -0.026369765, + -0.0017472387, -0.041211624, -0.013487909, + 0.007923825, 0.0017327322, 0.02385529, + -0.03626004, -0.008201062, 0.008761983, + 0.031127928, -0.008220403, -0.027104765, + 0.010651064, 0.01292054, 0.017407913, + 0.025957134, -0.0016537518, -0.0052417163, + 0.012050144, -0.031953193, 0.00034130792, + -0.0072081657, 0.006453823, -0.015460806, + -0.008349351, 0.023597393, -0.0076014553, + -0.002962569, 0.015422122, -0.005077308, + 0.02217897, 0.0059509273, 0.0036330959, + 0.027646346, -0.0048580975, 0.014790279, + 0.0049064527, 0.0069438233, -0.00032942058, + -0.013965015, 0.00072170305, 0.015293174, + -0.047297947, 0.014725805, -0.023326604, + -0.038142674, -0.011534355, 0.0059219142, + -0.018723179, 0.022630287, 0.012875409, + 0.0062700724, -0.033268455, -0.020489758, + -0.003417109, 0.008097904, -0.017304756, + -0.0036169775, -0.01921318, 0.0057865195, + -0.003436451, -0.016273174, -0.01653107, + -0.02695003, 0.0070727705, -0.018246071, + 0.002475792, 0.008433167, 0.0019986862, + 0.003106023, -0.00036810484, 0.0196516, + 0.00033062947, -0.018852126, -0.014455016, + -0.022939762, 0.0020099692, 0.018710284, + 0.04551847, 0.012146855, 0.033500563, + -0.0042488202, -0.01070909, 0.01943239, + -0.0012233894, -0.021946866, -0.042191625, + 0.026047396, -0.015486595, -0.0026885555, + 0.020541338, 0.020618707, 0.009245536, + 0.023300814, 0.0080011925, 0.0057832957, + 0.0004283475, -0.022075813, -0.026253711, + -0.002672437, -0.0034848063, -0.0021937194, + 0.014661332, 0.00062055985, 0.02220476, + 0.013965015, -0.004345531, -0.00042028827, + 0.047787946, 0.00026776755, 0.02114739, + 0.009729089, 0.0049999394, -0.040154252, + -0.0065376386, -0.017175809, -0.013126856, + 0.010934749, 0.025518712, -0.011946986, + -0.0037072408, -0.005335203, 0.006389349, + 0.04497689, 0.0077690873, -0.008407378, + 0.010457642, -0.0061185593, -0.017407913, + -0.025918448, -0.019419495, -0.034428984, + 0.017395018, -0.014803174, -0.02839424, + 0.023765026, -0.012366066, -0.017304756, + -0.007975403, -0.016221596, 0.0252995, + 0.018142914, 0.029322663, 0.02692424, + -0.010915406, 0.018555546, 0.026975818, + -0.023584498, -0.0058671115, -0.0016118438, + 0.0034719117, 0.012095276, 0.0026079633, + -0.0061056647, -0.012914093, -0.03417109, + -0.02377792, 0.012346724, 0.015615543, + 0.023881078, -0.014455016, 0.014235805, + 0.0028642465, 0.016943702, -0.012262908, + 0.009142378, -0.023184761, -0.01889081, + -0.014648437, -0.0027111212, -0.0056221113, + 0.0013894094, 0.0020583246, -0.005654348, + 0.016273174, -0.035280038, -0.004754939, + 0.013242909, -0.01813002, 0.042062677, + -0.0024435553, -0.00020671898, 0.018542651, + 0.0024274369, 0.002786878, 0.0056736902, + -0.006679481, 0.027594766, 0.01395212, + 0.022075813, -0.006756849, -0.0035653985, + 0.014648437, -0.008826457, -0.0060025062, + -0.011534355, 0.0021082917, 0.0063119805, + 0.035331618, 0.007943166, -0.0016972716, + 0.0037104646, -0.025995817, 0.01815581, + 0.005470598, -0.032701086, 0.003128589, + -0.021353707, 0.01525449, -0.012288697, + -0.013539488, -0.0052481634, 0.018258967, + -0.006698823, 0.02380371, 0.0093744835, + -0.018478177, -0.0077561927, -0.008478299, + -0.009716195, -0.011824487, -0.001455495, + 0.014622647, -0.020373706, 0.025725028, + -0.0071694814, 0.0016601991, -0.011070143, + -0.008458956, -0.0057155984, 0.011695539, + -0.0116246175, -0.00931001, 0.016427912, + 0.0030818454, -0.027568977, 0.005425466, + 0.014777384, -0.014996595, -0.012656198, + 0.024899764, -0.0034461222, 0.021624496, + 0.009767774, -0.010354484, -0.0040844125, + -0.006076651, -0.009484089, -0.01577028, + -0.006421586, 0.019226072, 0.0016368274, + -0.005554414, -0.006502178, -0.0009622708, + 0.028420031, -0.017614229, 0.019986864, + 0.00038583513, 0.030689506, -7.221816e-6, + 0.03259793, -0.0132171195, 0.0053126374, + -0.025286606, -0.028265294, -0.016028175, + 0.008020535, -0.012707776, 0.03687899, + -0.0018004295, -0.008884483, -0.029632136, + -0.0030899046, -0.021108707, -0.0052191503, + -0.016298965, 0.02328792, 0.021624496, + -0.009271326, -0.012643303, -0.004052175, + 0.0098709315, 0.00576073, -0.039690044, + 0.010844485, -0.00066327373, 0.0201416, + -0.007478955, 0.02095397, -0.02266897, + -0.016118439, 0.019458178, 0.024409764, + -0.0065666516, 0.0057961904, -0.010283563, + -0.030715296, -0.020696076, -0.005776848, + -0.007904482, -0.016260281, 0.01598949, + -0.018542651, 0.009683957, -0.0049161236, + 0.0060605328, -0.017730283, 0.004674347, + 0.012153302, -0.016647123, 0.011927645, + -0.019496864, -0.016905017, -0.0045163864, + -0.006160467, 0.015912123, 0.0049709263, + 0.0068148756, 0.013552383, 0.0014579127, + -0.016969493, -0.021921076, 0.000557295, + 0.019316336, 0.003839412, 0.0040682936, + 0.0027498056, -0.0114054065, -0.022114497, + 0.0007245238, -0.0064377044, -0.0020905614, + -0.031205297, 0.011418302, -0.002292042, + 0.029374242, 0.0033558588, -0.0094905365, + 0.025995817, 0.0013201, 0.014184225, + -0.0067375074, -0.01913581, -0.017021071, + -0.004384215, -0.0044970443, 0.0045163864, + -0.014790279, 0.0045260573, -0.021560023, + -0.014635542, -0.02896161, 0.026434239, + 0.21374339, 0.0019454955, -0.007627245, + 0.058232695, 0.011025012, 0.032752667, + 0.017150018, 0.0028642465, -0.00412632, + 0.011173301, -0.016053965, 0.0034557933, + -0.0069180336, 0.0089940885, -0.007781982, + -0.023004236, -0.024242131, -0.03932899, + -0.033397403, -0.027285293, 0.015976597, + 0.004242373, 0.0009235865, -0.026202133, + 0.013023698, 0.008897378, 0.0051901373, + 0.009232641, 0.02380371, 0.012611066, + 0.009664616, -0.011392512, -0.0015900839, + -0.0024628974, -0.012166197, -0.0006120977, + 0.001671482, -0.009999879, 0.002964181, + 0.016724491, -0.014880543, 0.01736923, + 0.0062475065, 0.0045357286, 0.012475671, + 0.011953434, -0.02174055, 0.011850276, + 0.021031339, -0.004052175, -0.019961074, + -0.005438361, 0.03308793, 0.04342952, + -0.02741424, 0.014145541, 0.024035815, + -0.018246071, -0.002527371, -0.0041617807, + 0.009258431, 0.030302664, -0.03097319, + 0.0087168515, -0.0034977011, 0.019342126, + -0.040489517, -0.018220283, -0.0007164646, + -0.0028819768, -0.019587126, -0.035950564, + -0.004800071, -0.00854922, 0.0011097544, + -0.008839351, 0.015422122, 0.0058703353, + -0.0066923755, 0.025441343, -0.021895286, + 5.938637e-5, 0.025467133, 0.0125594875, + -0.0031721087, -0.01682765, 0.008026983, + 0.018761862, -0.013346067, -0.010799354, + -0.0035267144, -0.008484746, 0.0010178793, + 0.008858694, 0.02141818, 0.013694225, + -0.015447911, 0.017640019, -0.0063925725, + -0.0055963215, -0.014661332, -0.011231327, + 0.031127928, -0.0062700724, -0.021082917, + 0.0012830277, -0.012656198, 0.005725269, + 0.028084766, -0.0059412564, 0.008478299, + -0.019174494, -0.0032333587, -0.011437644, + 0.0038458593, 0.010290011, 0.0059670457, + 0.00040920684, -0.013552383, -0.015925016, + -0.03922583, 0.00354928, -0.00069833134, + -0.008504088, 0.003926452, 0.0027207923, + -0.01582186, 0.0035944118, 0.00077569985, + -0.028574767, 0.03819425, -0.02114739, + 0.0059477035, 0.007401587, 0.0021937194, + 0.009793563, 0.022075813, -0.0017262847, + -0.012217776, 0.0023162195, 0.0052707293, + -0.0077046137, 0.0003106829, 0.014055278, + 0.013926331, -0.018942388, -0.006905139, + 0.0023597393, -0.003716912, -0.012424092, + -0.018877914, -0.034815826, -0.0063957963, + -0.002888424, 0.0206316, -0.017575545, + -0.032314245, -0.028806873, -0.021792129, + 0.02250134, -0.003478359, 0.011663302, + 0.0099483, 0.004809742, 4.586829e-6, + -0.003136648, -0.16443387, 0.01085738, + 0.014171331, -0.009471194, 0.032830033, + -0.0075950082, 0.022243446, -0.00470336, + -0.012630409, -0.014351858, -0.015357648, + -0.0010396392, -0.05524111, -0.0045776363, + 0.007472508, 0.0055673085, -0.0020551009, + 0.026266607, 0.012424092, -0.0014990148, + 0.0019051994, 0.016866334, -0.007472508, + -0.0129979085, 0.017652914, 0.0043584257, + 0.007904482, 0.005170795, -0.029657926, + 0.001992239, 3.838203e-5, 0.0075176395, + 0.011482775, -0.007227508, 0.015138437, + 0.00582198, -0.0058993483, 0.008600798, + 0.015834754, -0.026021607, 0.024990026, + 0.011650407, 0.009193957, -0.020721864, + 0.005225598, 0.0252995, 0.015615543, + -0.0020970087, 0.002993194, -0.008407378, + -0.011798697, -0.014132647, 0.043532677, + -0.023120288, -0.0034009905, 0.008948957, + -0.015757386, 0.01631186, -0.003375201, + -0.008394483, -0.0027224042, -0.024177657, + -0.009071457, 0.0064634937, -0.0071372446, + -0.025054501, -0.0029238847, 0.009123036, + -0.0036459907, -0.0015264162, -0.03816846, + 0.0011129782, 0.0019890154, -0.026240818, + 0.013197777, -0.022514235, -0.022643182, + 0.039561097, -0.0028449043, 0.0028529635, + 0.0053416505, 0.04750426, -0.0022340156, + -0.020825023, 0.020309232, -0.0036588856, + -0.0012854454, -0.008026983, -0.00066327373, + -0.00536744, 0.011469881, -0.006234612, + -0.0034719117, -0.016660018, 0.0044196756, + 0.023842394, -0.008014088, 0.006305533, + 0.03687899, -0.035795826, 0.021044234, + -0.006782639, -0.018091336, 0.018826336, + 0.018865021, 0.032159507, 0.03148898, + 0.007659482, 0.0005738164, 0.008259088, + 0.0015788011, 0.0011186196, 0.011869618, + -0.01267554, 0.009245536, 0.01960002, + 0.030560559, -0.01729186, 0.014235805, + 0.0021066798, 0.03920004, 0.0013861856, + -0.019935284, -0.01753686, -0.021753443, + 0.0053416505, -0.07086955, -0.021753443, + -0.031205297, 0.0058413222, -0.025879765, + 0.018787652, -0.013171988, 0.0125594875, + -0.016479492, 0.0097033, 0.013500804, + -0.021031339, -0.015628438, -0.0028577992, + 0.019690285, 0.0030576678, -0.02793003, + 0.0034332273, -0.0010960537, -0.0015973372, + -0.021598708, 0.008703956, -0.022114497, + -0.02689845, -0.007350008, 0.0029190492, + -0.01061238, 0.018181598, 0.0039909254, + 0.024886869, -0.030792665, -0.0026772725, + -0.0027062858, -0.015589753, 0.015164226, + -0.00011343351, -0.011070143, 0.0013273534, + 0.009896721, -0.034325827, 0.003436451, + -0.0029657925, 0.006782639, 0.0022469102, + -0.02068318, 0.004203689, -0.021998445, + 0.0022855946, 0.007781982, -0.0066214544, + -0.0189166, 0.005273953, -0.024177657, + 0.0011895407, 0.020515548, -0.016672913, + 0.014880543, -0.010199748, -6.2458945e-5, + -0.0097097475, -0.007504745, -0.0038265171, + -0.015628438, 0.026769502, 0.01815581, + -0.025995817, 0.00031833915, 0.0019438836, + 0.008368693, -0.018091336, 0.0016843769, + 0.023249235, -0.021882392, 0.0109669855, + 0.004232702, 0.0012532086, 0.0041650045, + 0.005016058, 0.031901613, -0.024138974, + -0.012391855, -0.026150554, 0.0198966, + -0.024938447, 0.035563722, 0.012037249, + 0.020837918, 0.007678824, -0.008091456, + -0.03324267, -3.8961287e-5, -0.009148826, + 0.01810423, -0.028058978, -0.015306069, + 0.0072404025, 0.0058993483, -0.0067761913, + 0.0351253, 0.036853198, -0.012991461, + -0.005686585, -0.0763885, 0.01292054, + -0.011605276, -0.010051458, -0.012752908, + -0.015628438, 0.013513698, 0.00903922, + 0.0043874388, 0.0048774392, -0.026176345, + 0.01582186, -0.005725269, 0.0116246175, + 0.0059380326, -0.019819232, -0.014210016, + -0.024822395, -0.005999283, -0.014158436, + -0.015538175, -0.006524744, -0.010580143, + 0.0059509273, -0.012050144, -0.0023565157, + -0.022398181, 0.012101724, -0.012191987, + -0.0063442173, 0.0162087, -0.026098976, + 0.0051901373, -0.012411198, 0.005770401, + -0.01815581, -0.023365289, -0.0074918503, + 0.021585813, 0.0252995, -0.010264222, + -0.019367915, 0.025853975, -0.0059670457, + -0.0026676015, 0.006750402, -0.002424213, + 0.0037813857, 0.031824246, -0.00041666164, + 0.01785923, 0.02190818, 0.0055479663, + -0.011108828, -0.010721985, -0.017949494, + 0.0053513213, -0.017652914, -0.0007108231, + -0.014261594, 0.004468031, 0.00894251, + -0.0071694814, -0.0036363197, -0.0017069426, + -0.009864484, 0.016892124, -0.020231863, + 0.010231985, -0.02557029, 0.003900662, + -0.005025729, -0.012727119, 0.03254635, + 0.0069115865, 0.017910808, -0.009445405, + 0.01341054, 0.0038458593, 0.020567127, + -0.016763175, -0.00062620133, -0.022643182, + 0.008742641, 0.028677925, 0.016943702, + -0.021082917, -0.009277773, 0.012952777, + -0.0022098378, 0.007923825, 0.0012322546, + 0.015241595, -0.004996716, 0.0033461878, + 0.009941853, -0.013320277, -0.007459613, + 0.009277773, 0.007175929, -0.0120050125, + -0.0036105302, -0.019264758, -0.015473701, + -0.005905796, 0.0056736902, -0.013926331, + -0.027156346, -0.00658277, 0.010605932, + 0.020090021, 0.007814219, -0.020244759, + 0.0060669803, -0.012856066, 0.006553757, + -0.018942388, -0.019019756, -0.019006863, + 0.01960002, -0.0055189533, 0.02741424, + 0.03417109, -0.0027804307, 0.014932121, + 0.028858451, 0.016737387, -0.016917912, + -0.015073963, -0.011282907, 0.0027691477, + -0.038555305, -0.035589512, 0.003849083, + -0.0006781833, -0.0049386895, -0.0060347435, + 0.031772666, -0.013797383, 0.04809742, + 0.022191865, -0.0058574406, 0.0011178138, + -0.052533213, -0.0024322723, 0.023984237, + 0.007711061, -0.000639902, -0.009761326, + 0.0065666516, -0.005880006, 0.021186076, + -0.00024399286, -0.013681331, -0.009116589, + -0.02584108, -0.0031753324, 0.0038942148, + -0.027749503, 0.027053187, 0.014506594, + 0.04283636, 0.017433703, -0.0019277652, + -0.015202911, 0.010180405, 0.018297652, + -0.021959761, -0.026395554, -0.015796069, + -0.01525449, -0.0049580317, -0.027955819, + 0.029013189, -0.034351617, -0.013797383, + -0.010780011, 0.021843707, 0.028033188, + -0.018748967, 0.012926987, -0.031231087, + -0.0252995, 0.017923703, -0.011070143, + -0.012101724, -0.015886333, -0.031798456 + ], + "topk": 9.0 + }, + "outputs": [ + { + "plot": "A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.", + "genres": ["Drama", "Fantasy", "Horror"], + "title": "The Craft", + "year": 1996 + }, + { + "plot": "A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...", + "genres": ["Comedy", "Fantasy"], + "title": "Wicked Stepmother", + "year": 1989 + }, + { + "plot": "A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.", + "genres": [ + "Adventure", + "Family", + "Fantasy" + ], + "title": "Oz the Great and Powerful", + "year": 2013 + }, + { + "plot": "A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.", + "genres": [ + "Adventure", + "Fantasy", + "Musical" + ], + "title": "Into the Woods", + "year": 2014 + }, + { + "plot": "In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.", + "genres": ["Fantasy", "Horror"], + "title": "Snow White: A Tale of Terror", + "year": 1997 + }, + { + "plot": "An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.", + "genres": [ + "Adventure", + "Family", + "Fantasy" + ], + "title": "Bedknobs and Broomsticks", + "year": 1971 + }, + { + "plot": "When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.", + "genres": [ + "Animation", + "Family", + "Fantasy" + ], + "title": "My Neighbor Totoro", + "year": 1988 + }, + { + "plot": "After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.", + "genres": ["Comedy", "Family", "Fantasy"], + "title": "Hocus Pocus", + "year": 1993 + }, + { + "year": 1998, + "plot": "The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love.", + "genres": ["Comedy", "Fantasy", "Romance"], + "title": "Practical Magic" + } + ] + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 0.0, + "completion": 0.0, + "total": 0.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-a84a-c487893ed246" + } + ] + } + } + }, + "reporter": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f900" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-048d-1fa32d25288b", + "type": "task", + "name": "reporter" + }, + "parent": { + "id": "3283749b-be79-15d6-3839-39eb32070bbc" + }, + "time": { + "start": "2024-10-08T01:22:07.337940", + "end": "2024-10-08T01:22:10.473229", + "span": 3135289 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "count": 3, + "movies": [ + "The Craft (1996) in ['Drama', 'Fantasy', 'Horror']: A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.", + "Wicked Stepmother (1989) in ['Comedy', 'Fantasy']: A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...", + "Oz the Great and Powerful (2013) in ['Adventure', 'Family', 'Fantasy']: A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.", + "Into the Woods (2014) in ['Adventure', 'Fantasy', 'Musical']: A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.", + "Snow White: A Tale of Terror (1997) in ['Fantasy', 'Horror']: In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.", + "Bedknobs and Broomsticks (1971) in ['Adventure', 'Family', 'Fantasy']: An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.", + "My Neighbor Totoro (1988) in ['Animation', 'Family', 'Fantasy']: When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.", + "Hocus Pocus (1993) in ['Comedy', 'Family', 'Fantasy']: After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.", + "Practical Magic (1998) in ['Comedy', 'Fantasy', 'Romance']: The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + ] + }, + "outputs": { + "report": "Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...\n\n2. Hocus Pocus (1993) - After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.\n\n3. Practical Magic (1998) - The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 536.0, + "completion": 181.0, + "total": 717.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-048d-1fa32d25288b" + } + ], + "nodes": { + "openai.chat": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f900" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-7647-7397bc185832", + "type": "chat", + "name": "openai.chat" + }, + "parent": { + "id": "3283749b-be79-15d6-048d-1fa32d25288b" + }, + "time": { + "start": "2024-10-08T01:22:07.338575", + "end": "2024-10-08T01:22:10.473021", + "span": 3134446 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "role": "system", + "content": "Given the following list of suggested movies:\n\nThe Craft (1996) in ['Drama', 'Fantasy', 'Horror']: A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.\nWicked Stepmother (1989) in ['Comedy', 'Fantasy']: A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...\nOz the Great and Powerful (2013) in ['Adventure', 'Family', 'Fantasy']: A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.\nInto the Woods (2014) in ['Adventure', 'Fantasy', 'Musical']: A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.\nSnow White: A Tale of Terror (1997) in ['Fantasy', 'Horror']: In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.\nBedknobs and Broomsticks (1971) in ['Adventure', 'Family', 'Fantasy']: An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.\nMy Neighbor Totoro (1988) in ['Animation', 'Family', 'Fantasy']: When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.\nHocus Pocus (1993) in ['Comedy', 'Family', 'Fantasy']: After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.\nPractical Magic (1998) in ['Comedy', 'Fantasy', 'Romance']: The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + }, + { + "role": "user", + "content": "Provide a list of 3 movies about witches in the genre of comedy." + } + ] + }, + "outputs": { + "completion": [ + { + "finish_reason": "stop", + "role": "assistant", + "content": "Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...\n\n2. Hocus Pocus (1993) - After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.\n\n3. Practical Magic (1998) - The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 717, + "completion": 181, + "prompt": 536 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 536, + "completion": 181, + "total": 717 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "gpt-3.5-turbo", + "temperature": 0.8, + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "gpt-3.5-turbo-0125" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-7647-7397bc185832" + } + ] + } + } + }, + "summarizer": [ + { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f900" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-2cb7-0d88553280b1", + "type": "task", + "name": "summarizer" + }, + "parent": { + "id": "3283749b-be79-15d6-3839-39eb32070bbc" + }, + "time": { + "start": "2024-10-08T01:22:10.473347", + "end": "2024-10-08T01:22:12.752959", + "span": 2279612 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "report": "Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...\n\n2. Hocus Pocus (1993) - After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.\n\n3. Practical Magic (1998) - The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + }, + "outputs": { + "report": "Here are three notable comedy films featuring witches. \n\n\"Wicked Stepmother\" (1989) presents a unique twist on the witch trope, where a mother/daughter duo wreaks havoc on a yuppie family while sharing a single body. \n\n\"Hocus Pocus\" (1993) is a beloved Halloween classic that follows two teenagers and an immortal cat as they attempt to thwart the chaotic return of three witch sisters in Salem. \n\n\"Practical Magic\" (1998) combines romance and comedy, focusing on the Owens sisters as they navigate love and the challenges of their magical heritage." + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 226.0, + "completion": 120.0, + "total": 346.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-2cb7-0d88553280b1" + } + ], + "nodes": { + "openai.chat": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f900" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-e5d2-54ae9f3b0529", + "type": "chat", + "name": "openai.chat" + }, + "parent": { + "id": "3283749b-be79-15d6-2cb7-0d88553280b1" + }, + "time": { + "start": "2024-10-08T01:22:10.473559", + "end": "2024-10-08T01:22:12.752582", + "span": 2279023 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "role": "system", + "content": "Act as a professional cinema critic.\nBe concise and factual.\nUse one intro sentence, and one sentence per movie." + }, + { + "role": "user", + "content": "Summarize the following recommendations about witches in the genre of comedy:\n\nHere are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...\n\n2. Hocus Pocus (1993) - After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.\n\n3. Practical Magic (1998) - The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + } + ] + }, + "outputs": { + "completion": [ + { + "finish_reason": "stop", + "role": "assistant", + "content": "Here are three notable comedy films featuring witches. \n\n\"Wicked Stepmother\" (1989) presents a unique twist on the witch trope, where a mother/daughter duo wreaks havoc on a yuppie family while sharing a single body. \n\n\"Hocus Pocus\" (1993) is a beloved Halloween classic that follows two teenagers and an immortal cat as they attempt to thwart the chaotic return of three witch sisters in Salem. \n\n\"Practical Magic\" (1998) combines romance and comedy, focusing on the Owens sisters as they navigate love and the challenges of their magical heritage." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 346, + "completion": 120, + "prompt": 226 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 226, + "completion": 120, + "total": 346 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "gpt-4o-mini", + "temperature": 0.2, + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "gpt-4o-mini-2024-07-18" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-e5d2-54ae9f3b0529" + } + ] + } + } + }, + { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f900" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-4cdb-4f10ec83e5c4", + "type": "task", + "name": "summarizer" + }, + "parent": { + "id": "3283749b-be79-15d6-3839-39eb32070bbc" + }, + "time": { + "start": "2024-10-08T01:22:12.753232", + "end": "2024-10-08T01:22:14.423907", + "span": 1670675 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "report": "Here are three notable comedy films featuring witches. \n\n\"Wicked Stepmother\" (1989) presents a unique twist on the witch trope, where a mother/daughter duo wreaks havoc on a yuppie family while sharing a single body. \n\n\"Hocus Pocus\" (1993) is a beloved Halloween classic that follows two teenagers and an immortal cat as they attempt to thwart the chaotic return of three witch sisters in Salem. \n\n\"Practical Magic\" (1998) combines romance and comedy, focusing on the Owens sisters as they navigate love and the challenges of their magical heritage." + }, + "outputs": { + "report": "These three comedy films showcase witches in entertaining and unconventional ways. \n\n\"Wicked Stepmother\" (1989) features a mother/daughter duo sharing a body as they disrupt a yuppie family's life. \n\n\"Hocus Pocus\" (1993) is a Halloween favorite where two teens and an immortal cat try to stop the resurrection of three mischievous witch sisters. \n\n\"Practical Magic\" (1998) blends romance and humor, following the Owens sisters as they deal with love and their magical family legacy." + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 168.0, + "completion": 106.0, + "total": 274.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-4cdb-4f10ec83e5c4" + } + ], + "nodes": { + "openai.chat": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f900" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-2420-816ed54fd495", + "type": "chat", + "name": "openai.chat" + }, + "parent": { + "id": "3283749b-be79-15d6-4cdb-4f10ec83e5c4" + }, + "time": { + "start": "2024-10-08T01:22:12.753687", + "end": "2024-10-08T01:22:14.423633", + "span": 1669946 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "role": "system", + "content": "Act as a professional cinema critic.\nBe concise and factual.\nUse one intro sentence, and one sentence per movie." + }, + { + "role": "user", + "content": "Summarize the following recommendations about witches in the genre of comedy:\n\nHere are three notable comedy films featuring witches. \n\n\"Wicked Stepmother\" (1989) presents a unique twist on the witch trope, where a mother/daughter duo wreaks havoc on a yuppie family while sharing a single body. \n\n\"Hocus Pocus\" (1993) is a beloved Halloween classic that follows two teenagers and an immortal cat as they attempt to thwart the chaotic return of three witch sisters in Salem. \n\n\"Practical Magic\" (1998) combines romance and comedy, focusing on the Owens sisters as they navigate love and the challenges of their magical heritage." + } + ] + }, + "outputs": { + "completion": [ + { + "finish_reason": "stop", + "role": "assistant", + "content": "These three comedy films showcase witches in entertaining and unconventional ways. \n\n\"Wicked Stepmother\" (1989) features a mother/daughter duo sharing a body as they disrupt a yuppie family's life. \n\n\"Hocus Pocus\" (1993) is a Halloween favorite where two teens and an immortal cat try to stop the resurrection of three mischievous witch sisters. \n\n\"Practical Magic\" (1998) blends romance and humor, following the Owens sisters as they deal with love and their magical family legacy." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 274, + "completion": 106, + "prompt": 168 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 168, + "completion": 106, + "total": 274 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "gpt-4o-mini", + "temperature": 0.2, + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "gpt-4o-mini-2024-07-18" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-2420-816ed54fd495" + } + ] + } + } + } + ] + } + } + } + } + ] + } + ] +} diff --git a/agenta-web/src/services/observability/api/index.ts b/agenta-web/src/services/observability/api/index.ts new file mode 100644 index 0000000000..11aa5d90a7 --- /dev/null +++ b/agenta-web/src/services/observability/api/index.ts @@ -0,0 +1,15 @@ +import {delay} from "@/lib/helpers/utils" +import data from "@/lib/test_trace.json" +import {AgentaRootsResponse} from "../types" + +//Prefix convention: +// - fetch: GET single entity from server +// - fetchAll: GET all entities from server +// - create: POST data to server +// - update: PUT data to server +// - delete: DELETE data from server + +export const fetchAllTraces = async () => { + await delay(1000) + return data as AgentaRootsResponse +} diff --git a/agenta-web/src/services/observability/types/index.ts b/agenta-web/src/services/observability/types/index.ts new file mode 100644 index 0000000000..dd6f16032e --- /dev/null +++ b/agenta-web/src/services/observability/types/index.ts @@ -0,0 +1,104 @@ +export interface AgentaRootsResponse { + version: string + roots: AgentaRootsDTO[] +} + +export interface AgentaRootsDTO { + root: RootContextDTO + trees: AgentaTreeDTO[] +} + +interface RootContextDTO { + id: string +} + +interface AgentaTreeDTO { + tree: TreeContextDTO + nodes: Record +} + +interface TreeContextDTO { + id: string +} + +interface AgentaNodeDTO extends NodeDTO {} + +interface NodeDTO { + scope: ProjectScopeDTO + lifecycle: LifecycleDTO + root: RootContextDTO + tree: TreeContextDTO + node: NodeContextDTO + parent?: ParentContextDTO | null + time: TimeDTO + status: StatusDTO + data?: Data | null + metrics?: Metrics | null + meta?: Metadata | null + tags?: Tags | null + refs?: Refs | null + links?: LinkDTO[] | null + nodes?: Record | null +} + +type Data = Record +type Metrics = Record +type Metadata = Record +type Tags = Record +type Refs = Record +type LinkDTO = { + type: string + id: string + tree_id?: string | null +} + +interface ProjectScopeDTO { + project_id: string +} + +interface LifecycleDTO { + created_at: string + updated_at?: string | null + updated_by_id?: string | null +} + +interface NodeContextDTO { + id: string + type?: NodeType | null + name: string +} + +enum NodeType { + AGENT = "agent", + WORKFLOW = "workflow", + CHAIN = "chain", + TASK = "task", + TOOL = "tool", + EMBEDDING = "embedding", + QUERY = "query", + COMPLETION = "completion", + CHAT = "chat", + RERANK = "rerank", +} + +enum StatusCode { + UNSET = "UNSET", + OK = "OK", + ERROR = "ERROR", +} + +interface ParentContextDTO { + id: string +} + +interface TimeDTO { + start: string + end: string + span: number +} + +interface StatusDTO { + code: StatusCode + message?: string | null + stacktrace?: string | null +} From 4f61b584754c8afcbf5ea2e26d35441ff2a0fddb Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Tue, 8 Oct 2024 21:49:35 +0200 Subject: [PATCH 050/305] feat(frontend): updated trace mock response --- agenta-web/src/lib/test_trace.json | 2770 ++++++++++++++++++++++++++++ 1 file changed, 2770 insertions(+) diff --git a/agenta-web/src/lib/test_trace.json b/agenta-web/src/lib/test_trace.json index ca3ba48916..b4e5fa71d7 100644 --- a/agenta-web/src/lib/test_trace.json +++ b/agenta-web/src/lib/test_trace.json @@ -2770,6 +2770,2776 @@ } } ] + }, + { + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f924" + }, + "trees": [ + { + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "nodes": { + "rag": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f924" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-3839-39eb32070bbc", + "type": "workflow", + "name": "rag" + }, + "time": { + "start": "2024-10-08T01:22:06.513070", + "end": "2024-10-08T01:22:14.424053", + "span": 7910983 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "count": 3 + }, + "outputs": "These three comedy films showcase witches in entertaining and unconventional ways. \n\n\"Wicked Stepmother\" (1989) features a mother/daughter duo sharing a body as they disrupt a yuppie family's life. \n\n\"Hocus Pocus\" (1993) is a Halloween favorite where two teens and an immortal cat try to stop the resurrection of three mischievous witch sisters. \n\n\"Practical Magic\" (1998) blends romance and humor, following the Owens sisters as they deal with love and their magical family legacy." + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 939.0, + "completion": 407.0, + "total": 1346.0 + } + } + }, + "meta": { + "configuration": { + "retriever_prompt": "Movies about {topic} in the genre of {genre}.", + "retriever_multiplier": 3.0, + "generator_context_prompt": "Given the following list of suggested movies:\n\n{movies}", + "generator_instructions_prompt": "Provide a list of {count} movies about {topic} in the genre of {genre}.", + "generator_model": "gpt-3.5-turbo", + "generator_temperature": 0.8, + "summarizer_context_prompt": "Act as a professional cinema critic.\nBe concise and factual.\nUse one intro sentence, and one sentence per movie.", + "summarizer_instructions_prompt": "Summarize the following recommendations about {topic} in the genre of {genre}:\n\n{report}", + "summarizer_model": "gpt-4o-mini", + "summarizer_temperature": 0.2 + }, + "environment": "terminal" + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-3839-39eb32070bbc" + } + ], + "nodes": { + "retriever": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f924" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-ef78-9bdcad4f3199", + "type": "task", + "name": "retriever" + }, + "parent": { + "id": "3283749b-be79-15d6-3839-39eb32070bbc" + }, + "time": { + "start": "2024-10-08T01:22:06.513540", + "end": "2024-10-08T01:22:07.337752", + "span": 824212 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "count": 3 + }, + "internals": { + "prompt": "Movies about witches in the genre of comedy." + }, + "outputs": { + "movies": [ + "The Craft (1996) in ['Drama', 'Fantasy', 'Horror']: A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.", + "Wicked Stepmother (1989) in ['Comedy', 'Fantasy']: A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...", + "Oz the Great and Powerful (2013) in ['Adventure', 'Family', 'Fantasy']: A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.", + "Into the Woods (2014) in ['Adventure', 'Fantasy', 'Musical']: A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.", + "Snow White: A Tale of Terror (1997) in ['Fantasy', 'Horror']: In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.", + "Bedknobs and Broomsticks (1971) in ['Adventure', 'Family', 'Fantasy']: An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.", + "My Neighbor Totoro (1988) in ['Animation', 'Family', 'Fantasy']: When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.", + "Hocus Pocus (1993) in ['Comedy', 'Family', 'Fantasy']: After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.", + "Practical Magic (1998) in ['Comedy', 'Fantasy', 'Romance']: The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + ], + "test": [ + 1, + false, + "test", + { + "test": "test" + } + ] + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 9.0, + "completion": 0.0, + "total": 9.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-ef78-9bdcad4f3199" + } + ], + "nodes": { + "openai.embeddings": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f924" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-94b9-72820ad2ef90", + "type": "embedding", + "name": "openai.embeddings" + }, + "parent": { + "id": "3283749b-be79-15d6-ef78-9bdcad4f3199" + }, + "time": { + "start": "2024-10-08T01:22:06.513792", + "end": "2024-10-08T01:22:07.237280", + "span": 723488 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "content": "Movies about witches in the genre of comedy." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 9, + "prompt": 9 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 9, + "completion": 0.0, + "total": 9 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "text-embedding-ada-002", + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "text-embedding-ada-002" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-94b9-72820ad2ef90" + } + ] + }, + "search": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f924" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-a84a-c487893ed246", + "type": "query", + "name": "search" + }, + "parent": { + "id": "3283749b-be79-15d6-ef78-9bdcad4f3199" + }, + "time": { + "start": "2024-10-08T01:22:07.237749", + "end": "2024-10-08T01:22:07.337318", + "span": 99569 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "query": [ + 0.0031027994, -0.036517933, -0.0051836898, + -0.02046397, -0.006576323, 0.004403557, + 0.0034203327, -0.036904775, 0.019174494, + -0.0280074, 0.011934091, 0.010225537, + 0.021160286, -0.0015441463, 0.010141721, + 0.0064248093, 0.040102676, -0.004100531, + 0.018220283, -0.029554768, -0.01073488, + -0.0032655955, -0.009129483, 0.012288697, + -0.014596858, 0.008974747, 0.014519489, + -0.020064233, -0.019986864, -0.006328099, + 0.02557029, -0.019006863, 0.0008212344, + -0.019832127, -0.014390541, -0.032701086, + -0.009793563, -0.011424749, -0.0018794099, + -0.0036814513, 0.0060154013, 0.011882513, + -0.006324875, -0.0067632967, 0.0077432976, + 0.013642646, 0.023545815, 0.0020696074, + -0.0036782275, 0.013230015, 0.020090021, + 0.0016279622, -0.025492921, -0.011985671, + 0.010547906, 0.030302664, 0.015241595, + 0.011882513, 0.019548442, -0.020025548, + -0.015860543, -0.004696913, -0.027749503, + -0.007659482, 0.00085750094, -0.010541459, + 0.0004448689, -0.007943166, 0.0029077663, + -0.025583185, 0.042165834, 0.008826457, + -0.004036057, -0.008394483, 0.017691597, + -0.03154056, -0.030147927, 0.02019318, + -0.0013257415, -0.00235168, 0.022411076, + -0.012759356, -0.010573695, 0.013836067, + 0.012288697, 0.01881344, -0.0091552725, + 0.024138974, 0.007227508, -0.008968299, + 0.005528624, 0.0025837857, 0.032778457, + 0.033345826, 0.01495791, 0.012856066, + -0.011263564, 0.03827162, -0.0128367245, + -0.0038426358, -0.00024117214, 0.011411854, + -0.03362951, -0.012166197, 0.014571068, + -0.018207388, 0.0030834572, 0.01210817, + 0.042088468, 0.014700016, -0.028523188, + -0.00867172, 0.022462655, -0.043661624, + -0.004522834, -0.012636855, 0.029632136, + 0.008749088, 0.0013644258, -0.030560559, + 0.026640555, -0.002135693, 0.025235027, + -0.019664494, 0.03778162, -0.0037942803, + -0.00776264, -0.023971342, 0.004748492, + -0.026176345, 0.029632136, -0.019780548, + 0.02787845, -0.020412391, -0.027027396, + 0.009961195, -0.03687899, 0.008072114, + -0.029322663, -0.020747654, -0.0001522185, + 0.03468688, -0.030405821, -0.0011669749, + -0.009393826, 0.00076844654, 0.0101933, + -0.010032116, 0.0042359256, -0.021186076, + 0.027517397, -0.005860664, 0.02119897, + -0.004751716, 0.012417645, 0.009464747, + 0.017033966, 0.009297115, -0.036956355, + -0.028136346, -0.013500804, 0.008845799, + 0.00530619, -0.008890931, 0.008342904, + 0.013107514, 0.024113184, -0.022243446, + -0.006905139, 0.0014063338, 0.018607125, + 0.019496864, -0.030199505, 0.03664688, + -0.0014168107, 0.0044132285, -0.00032337618, + 0.018929495, -0.0065602046, -0.0060121776, + 0.013178435, 0.0020148049, 0.0076723765, + 0.010515669, -0.017846335, 0.00094937603, + -0.01046409, -0.013642646, 0.0040908596, + -0.018555546, -0.008465404, 0.028806873, + -0.008607246, -0.008684615, -0.678367, + -0.014261594, -0.009974089, -0.026666345, + 0.01110238, 0.006286191, 0.02076055, + 0.012752908, -0.03974162, -0.016840544, + 0.004074741, 0.024371078, 0.020012653, + -0.00918751, -0.030792665, -0.027723715, + -0.009741984, -0.013810278, 0.005409348, + -0.0039780308, -0.0016174853, 0.037136883, + -0.0034622406, -0.016221596, 0.028316872, + 0.02527371, 0.0058477693, -0.0014804786, + -0.025995817, 0.0346353, -0.019419495, + 0.026692133, 0.00072371785, 0.0026240817, + 0.041005306, 0.016905017, -0.008594351, + 0.025054501, 0.015899228, 0.029167926, + -0.02945161, 0.004925795, -0.008220403, + 0.0026788844, -0.016298965, 0.010161064, + 0.0037298065, -0.0066214544, 0.0060121776, + 0.009928958, 0.010148169, 0.008684615, + 0.009954748, 0.015061068, 0.01471291, + 0.015486595, 0.037368987, -0.008562114, + 0.013642646, -0.00028348304, -0.027130555, + 0.021276338, -0.0048161894, -0.0067890864, + 0.0016110379, 0.026292397, 0.0077884295, + 0.010244879, 0.0068922443, -0.0145323835, + -0.0075176395, 0.0068793492, -0.018207388, + -0.0046711233, 0.0007555518, 0.0049161236, + 0.025995817, 0.0061475723, -0.0027320753, + -0.0061959275, -0.00014476372, -0.007420929, + -0.008761983, 0.0033494115, 0.02223055, + -0.019728968, -0.005628559, 0.029503189, + 0.005161124, 0.027723715, 0.011940539, + 0.018026862, -0.011979223, 0.008955404, + -0.00010980686, 0.010838038, 0.0033816483, + 0.019999757, 0.02991582, -0.04497689, + 0.00062821613, -0.009084351, -0.010257774, + -0.01908423, -0.006357112, 0.00056333945, + -0.0029254965, 0.021547128, 0.03675004, + -0.00052908773, 0.031127928, -0.0067632967, + -0.0023823052, -0.017214492, -0.01992239, + -0.026434239, 0.042062677, 0.0074467184, + 0.009780669, -0.010360932, 0.0064925067, + 0.00019835753, -0.008936062, -0.0058993483, + -0.010715538, -0.0012137184, -0.0045357286, + 0.01731765, -0.04459005, -0.012101724, + 0.0026273055, 0.012424092, -0.01938081, + -0.004148886, -0.018529756, 0.00867172, + 0.002588621, -0.020850811, 0.038091093, + 0.007801324, 0.0060186246, -0.004032833, + -0.01957423, 0.010483433, 0.008910272, + -0.015976597, -0.0037620435, 0.01215975, + 0.0038361882, 0.010535011, -0.013823173, + 0.001776252, -0.013848962, -0.007079218, + 0.008349351, -0.0060669803, -0.0095936945, + -0.018968178, -0.0068406654, -0.0064570466, + 0.038581096, 0.0053835586, -0.012727119, + 0.0014087515, -0.006008954, -0.0026063514, + 0.00791093, 0.020115811, -0.014687121, + -0.023507131, 0.005776848, -0.014945016, + -0.0012113006, 0.023674762, -0.0046936893, + 0.041778993, 0.002828786, 0.0128818555, + -0.008684615, 0.005973493, -0.012785145, + 0.0023065484, -0.0108058015, 0.0074467184, + 0.018142914, -0.0036814513, 0.014932121, + -0.016157122, -0.0056092166, -0.010689748, + -0.017717388, 0.020231863, 0.029761085, + -0.011940539, -0.0011726164, -0.0025692792, + 0.006750402, -0.0020921733, 0.027104765, + 0.011199091, 0.030792665, -0.005161124, + -0.001620709, -0.018246071, 0.013346067, + -0.027826872, 0.0015457582, -0.021534234, + 0.029167926, 0.0314374, 0.005850993, + -0.010019221, -0.02372634, 0.0028223386, + -0.008529877, 0.0029351676, -0.008355798, + 0.024899764, -0.00815593, -0.008265535, + 0.00082204037, -0.00906501, 0.015280279, + 0.019857915, -0.022643182, -0.012424092, + 0.017511072, -0.022191865, 0.011695539, + -0.0065473095, -0.027646346, 0.00588323, + -0.015486595, 0.008839351, 0.014480805, + -0.020992653, 0.006653691, -0.010870275, + 0.030947402, 0.007627245, -0.00724685, + 0.023442656, 0.015873438, 0.0030012531, + 0.012095276, -0.013346067, 0.025686344, + -0.0052159266, -0.0072404025, 0.00056616013, + -0.026215028, 0.007472508, 0.013294488, + 0.018503968, 0.010322248, -0.008271983, + 0.025763711, -0.015112648, 0.023597393, + 0.036853198, 0.00044043633, -0.02836845, + 0.0050096107, 0.006179809, -0.012320934, + 0.0051901373, 0.014841858, 0.008845799, + -0.015654227, -0.02896161, -0.03208214, + -0.011360276, 0.0020309233, 0.007949614, + 0.0019648375, 0.010489879, -0.019961074, + 0.021082917, 0.00633777, 0.0061346777, + -0.009303562, -0.038348988, 0.017240282, + 0.00785935, -0.017988177, -0.011760012, + -0.022024235, -0.008349351, 0.00032861467, + 0.014558174, 0.006453823, 0.025957134, + -0.009806458, 0.012843172, 0.0025773384, + 0.0069309287, 0.006057309, -0.011611723, + 0.000929228, 0.007175929, -0.0061346777, + -0.0059219142, -0.0116826445, -0.026034502, + -0.008233299, 0.0019728967, -0.022114497, + -0.004036057, -0.005631782, 0.010515669, + 0.0025128645, -0.0010227149, -0.02250134, + 0.008568562, -0.035976354, 0.0061636907, + -0.009774221, -0.01267554, 0.02586687, + 0.008297772, 0.015538175, -0.030225296, + -0.008729747, -0.004932242, 0.09067588, + 0.010444748, 0.008845799, -0.002404871, + 0.0005435943, -0.005302966, -0.023171866, + -0.008065666, 0.0008373529, -0.00864593, + 0.027311081, -0.005067637, 0.038503725, + 0.010535011, 0.017678702, -0.0012048533, + -0.013197777, 0.012817382, 0.029632136, + -0.022359498, -0.014171331, -0.019445283, + 0.0027723713, -0.020773442, -0.0144937, + -0.038632672, 0.016930807, 0.015306069, + -0.007079218, -0.0034912538, -0.029632136, + -0.015654227, -0.013513698, 0.008291325, + 0.019316336, 0.018865021, 0.011818039, + 0.0016360215, 0.040515307, -0.02299134, + 0.020554233, 0.018942388, 0.015576859, + 0.008626589, 0.008259088, -0.017781861, + 0.012211329, 0.027104765, 0.010618827, + -0.022101603, 0.03208214, 0.015602648, + -0.0019342126, 0.011592381, 0.020554233, + 0.014880543, -0.0094325105, 0.021276338, + -0.009542115, 0.01736923, -0.009645274, + -4.7901984e-5, 0.011650407, 0.00633777, + -0.011199091, -0.026743712, -0.018826336, + -0.017278966, 0.0042875046, -0.0030479967, + -0.016028175, -0.019019756, -0.014390541, + 0.001744015, 0.010638169, -0.0022146734, + 0.020231863, -0.015215806, 0.005199808, + 0.011714881, -0.0198966, -0.026369765, + -0.0017472387, -0.041211624, -0.013487909, + 0.007923825, 0.0017327322, 0.02385529, + -0.03626004, -0.008201062, 0.008761983, + 0.031127928, -0.008220403, -0.027104765, + 0.010651064, 0.01292054, 0.017407913, + 0.025957134, -0.0016537518, -0.0052417163, + 0.012050144, -0.031953193, 0.00034130792, + -0.0072081657, 0.006453823, -0.015460806, + -0.008349351, 0.023597393, -0.0076014553, + -0.002962569, 0.015422122, -0.005077308, + 0.02217897, 0.0059509273, 0.0036330959, + 0.027646346, -0.0048580975, 0.014790279, + 0.0049064527, 0.0069438233, -0.00032942058, + -0.013965015, 0.00072170305, 0.015293174, + -0.047297947, 0.014725805, -0.023326604, + -0.038142674, -0.011534355, 0.0059219142, + -0.018723179, 0.022630287, 0.012875409, + 0.0062700724, -0.033268455, -0.020489758, + -0.003417109, 0.008097904, -0.017304756, + -0.0036169775, -0.01921318, 0.0057865195, + -0.003436451, -0.016273174, -0.01653107, + -0.02695003, 0.0070727705, -0.018246071, + 0.002475792, 0.008433167, 0.0019986862, + 0.003106023, -0.00036810484, 0.0196516, + 0.00033062947, -0.018852126, -0.014455016, + -0.022939762, 0.0020099692, 0.018710284, + 0.04551847, 0.012146855, 0.033500563, + -0.0042488202, -0.01070909, 0.01943239, + -0.0012233894, -0.021946866, -0.042191625, + 0.026047396, -0.015486595, -0.0026885555, + 0.020541338, 0.020618707, 0.009245536, + 0.023300814, 0.0080011925, 0.0057832957, + 0.0004283475, -0.022075813, -0.026253711, + -0.002672437, -0.0034848063, -0.0021937194, + 0.014661332, 0.00062055985, 0.02220476, + 0.013965015, -0.004345531, -0.00042028827, + 0.047787946, 0.00026776755, 0.02114739, + 0.009729089, 0.0049999394, -0.040154252, + -0.0065376386, -0.017175809, -0.013126856, + 0.010934749, 0.025518712, -0.011946986, + -0.0037072408, -0.005335203, 0.006389349, + 0.04497689, 0.0077690873, -0.008407378, + 0.010457642, -0.0061185593, -0.017407913, + -0.025918448, -0.019419495, -0.034428984, + 0.017395018, -0.014803174, -0.02839424, + 0.023765026, -0.012366066, -0.017304756, + -0.007975403, -0.016221596, 0.0252995, + 0.018142914, 0.029322663, 0.02692424, + -0.010915406, 0.018555546, 0.026975818, + -0.023584498, -0.0058671115, -0.0016118438, + 0.0034719117, 0.012095276, 0.0026079633, + -0.0061056647, -0.012914093, -0.03417109, + -0.02377792, 0.012346724, 0.015615543, + 0.023881078, -0.014455016, 0.014235805, + 0.0028642465, 0.016943702, -0.012262908, + 0.009142378, -0.023184761, -0.01889081, + -0.014648437, -0.0027111212, -0.0056221113, + 0.0013894094, 0.0020583246, -0.005654348, + 0.016273174, -0.035280038, -0.004754939, + 0.013242909, -0.01813002, 0.042062677, + -0.0024435553, -0.00020671898, 0.018542651, + 0.0024274369, 0.002786878, 0.0056736902, + -0.006679481, 0.027594766, 0.01395212, + 0.022075813, -0.006756849, -0.0035653985, + 0.014648437, -0.008826457, -0.0060025062, + -0.011534355, 0.0021082917, 0.0063119805, + 0.035331618, 0.007943166, -0.0016972716, + 0.0037104646, -0.025995817, 0.01815581, + 0.005470598, -0.032701086, 0.003128589, + -0.021353707, 0.01525449, -0.012288697, + -0.013539488, -0.0052481634, 0.018258967, + -0.006698823, 0.02380371, 0.0093744835, + -0.018478177, -0.0077561927, -0.008478299, + -0.009716195, -0.011824487, -0.001455495, + 0.014622647, -0.020373706, 0.025725028, + -0.0071694814, 0.0016601991, -0.011070143, + -0.008458956, -0.0057155984, 0.011695539, + -0.0116246175, -0.00931001, 0.016427912, + 0.0030818454, -0.027568977, 0.005425466, + 0.014777384, -0.014996595, -0.012656198, + 0.024899764, -0.0034461222, 0.021624496, + 0.009767774, -0.010354484, -0.0040844125, + -0.006076651, -0.009484089, -0.01577028, + -0.006421586, 0.019226072, 0.0016368274, + -0.005554414, -0.006502178, -0.0009622708, + 0.028420031, -0.017614229, 0.019986864, + 0.00038583513, 0.030689506, -7.221816e-6, + 0.03259793, -0.0132171195, 0.0053126374, + -0.025286606, -0.028265294, -0.016028175, + 0.008020535, -0.012707776, 0.03687899, + -0.0018004295, -0.008884483, -0.029632136, + -0.0030899046, -0.021108707, -0.0052191503, + -0.016298965, 0.02328792, 0.021624496, + -0.009271326, -0.012643303, -0.004052175, + 0.0098709315, 0.00576073, -0.039690044, + 0.010844485, -0.00066327373, 0.0201416, + -0.007478955, 0.02095397, -0.02266897, + -0.016118439, 0.019458178, 0.024409764, + -0.0065666516, 0.0057961904, -0.010283563, + -0.030715296, -0.020696076, -0.005776848, + -0.007904482, -0.016260281, 0.01598949, + -0.018542651, 0.009683957, -0.0049161236, + 0.0060605328, -0.017730283, 0.004674347, + 0.012153302, -0.016647123, 0.011927645, + -0.019496864, -0.016905017, -0.0045163864, + -0.006160467, 0.015912123, 0.0049709263, + 0.0068148756, 0.013552383, 0.0014579127, + -0.016969493, -0.021921076, 0.000557295, + 0.019316336, 0.003839412, 0.0040682936, + 0.0027498056, -0.0114054065, -0.022114497, + 0.0007245238, -0.0064377044, -0.0020905614, + -0.031205297, 0.011418302, -0.002292042, + 0.029374242, 0.0033558588, -0.0094905365, + 0.025995817, 0.0013201, 0.014184225, + -0.0067375074, -0.01913581, -0.017021071, + -0.004384215, -0.0044970443, 0.0045163864, + -0.014790279, 0.0045260573, -0.021560023, + -0.014635542, -0.02896161, 0.026434239, + 0.21374339, 0.0019454955, -0.007627245, + 0.058232695, 0.011025012, 0.032752667, + 0.017150018, 0.0028642465, -0.00412632, + 0.011173301, -0.016053965, 0.0034557933, + -0.0069180336, 0.0089940885, -0.007781982, + -0.023004236, -0.024242131, -0.03932899, + -0.033397403, -0.027285293, 0.015976597, + 0.004242373, 0.0009235865, -0.026202133, + 0.013023698, 0.008897378, 0.0051901373, + 0.009232641, 0.02380371, 0.012611066, + 0.009664616, -0.011392512, -0.0015900839, + -0.0024628974, -0.012166197, -0.0006120977, + 0.001671482, -0.009999879, 0.002964181, + 0.016724491, -0.014880543, 0.01736923, + 0.0062475065, 0.0045357286, 0.012475671, + 0.011953434, -0.02174055, 0.011850276, + 0.021031339, -0.004052175, -0.019961074, + -0.005438361, 0.03308793, 0.04342952, + -0.02741424, 0.014145541, 0.024035815, + -0.018246071, -0.002527371, -0.0041617807, + 0.009258431, 0.030302664, -0.03097319, + 0.0087168515, -0.0034977011, 0.019342126, + -0.040489517, -0.018220283, -0.0007164646, + -0.0028819768, -0.019587126, -0.035950564, + -0.004800071, -0.00854922, 0.0011097544, + -0.008839351, 0.015422122, 0.0058703353, + -0.0066923755, 0.025441343, -0.021895286, + 5.938637e-5, 0.025467133, 0.0125594875, + -0.0031721087, -0.01682765, 0.008026983, + 0.018761862, -0.013346067, -0.010799354, + -0.0035267144, -0.008484746, 0.0010178793, + 0.008858694, 0.02141818, 0.013694225, + -0.015447911, 0.017640019, -0.0063925725, + -0.0055963215, -0.014661332, -0.011231327, + 0.031127928, -0.0062700724, -0.021082917, + 0.0012830277, -0.012656198, 0.005725269, + 0.028084766, -0.0059412564, 0.008478299, + -0.019174494, -0.0032333587, -0.011437644, + 0.0038458593, 0.010290011, 0.0059670457, + 0.00040920684, -0.013552383, -0.015925016, + -0.03922583, 0.00354928, -0.00069833134, + -0.008504088, 0.003926452, 0.0027207923, + -0.01582186, 0.0035944118, 0.00077569985, + -0.028574767, 0.03819425, -0.02114739, + 0.0059477035, 0.007401587, 0.0021937194, + 0.009793563, 0.022075813, -0.0017262847, + -0.012217776, 0.0023162195, 0.0052707293, + -0.0077046137, 0.0003106829, 0.014055278, + 0.013926331, -0.018942388, -0.006905139, + 0.0023597393, -0.003716912, -0.012424092, + -0.018877914, -0.034815826, -0.0063957963, + -0.002888424, 0.0206316, -0.017575545, + -0.032314245, -0.028806873, -0.021792129, + 0.02250134, -0.003478359, 0.011663302, + 0.0099483, 0.004809742, 4.586829e-6, + -0.003136648, -0.16443387, 0.01085738, + 0.014171331, -0.009471194, 0.032830033, + -0.0075950082, 0.022243446, -0.00470336, + -0.012630409, -0.014351858, -0.015357648, + -0.0010396392, -0.05524111, -0.0045776363, + 0.007472508, 0.0055673085, -0.0020551009, + 0.026266607, 0.012424092, -0.0014990148, + 0.0019051994, 0.016866334, -0.007472508, + -0.0129979085, 0.017652914, 0.0043584257, + 0.007904482, 0.005170795, -0.029657926, + 0.001992239, 3.838203e-5, 0.0075176395, + 0.011482775, -0.007227508, 0.015138437, + 0.00582198, -0.0058993483, 0.008600798, + 0.015834754, -0.026021607, 0.024990026, + 0.011650407, 0.009193957, -0.020721864, + 0.005225598, 0.0252995, 0.015615543, + -0.0020970087, 0.002993194, -0.008407378, + -0.011798697, -0.014132647, 0.043532677, + -0.023120288, -0.0034009905, 0.008948957, + -0.015757386, 0.01631186, -0.003375201, + -0.008394483, -0.0027224042, -0.024177657, + -0.009071457, 0.0064634937, -0.0071372446, + -0.025054501, -0.0029238847, 0.009123036, + -0.0036459907, -0.0015264162, -0.03816846, + 0.0011129782, 0.0019890154, -0.026240818, + 0.013197777, -0.022514235, -0.022643182, + 0.039561097, -0.0028449043, 0.0028529635, + 0.0053416505, 0.04750426, -0.0022340156, + -0.020825023, 0.020309232, -0.0036588856, + -0.0012854454, -0.008026983, -0.00066327373, + -0.00536744, 0.011469881, -0.006234612, + -0.0034719117, -0.016660018, 0.0044196756, + 0.023842394, -0.008014088, 0.006305533, + 0.03687899, -0.035795826, 0.021044234, + -0.006782639, -0.018091336, 0.018826336, + 0.018865021, 0.032159507, 0.03148898, + 0.007659482, 0.0005738164, 0.008259088, + 0.0015788011, 0.0011186196, 0.011869618, + -0.01267554, 0.009245536, 0.01960002, + 0.030560559, -0.01729186, 0.014235805, + 0.0021066798, 0.03920004, 0.0013861856, + -0.019935284, -0.01753686, -0.021753443, + 0.0053416505, -0.07086955, -0.021753443, + -0.031205297, 0.0058413222, -0.025879765, + 0.018787652, -0.013171988, 0.0125594875, + -0.016479492, 0.0097033, 0.013500804, + -0.021031339, -0.015628438, -0.0028577992, + 0.019690285, 0.0030576678, -0.02793003, + 0.0034332273, -0.0010960537, -0.0015973372, + -0.021598708, 0.008703956, -0.022114497, + -0.02689845, -0.007350008, 0.0029190492, + -0.01061238, 0.018181598, 0.0039909254, + 0.024886869, -0.030792665, -0.0026772725, + -0.0027062858, -0.015589753, 0.015164226, + -0.00011343351, -0.011070143, 0.0013273534, + 0.009896721, -0.034325827, 0.003436451, + -0.0029657925, 0.006782639, 0.0022469102, + -0.02068318, 0.004203689, -0.021998445, + 0.0022855946, 0.007781982, -0.0066214544, + -0.0189166, 0.005273953, -0.024177657, + 0.0011895407, 0.020515548, -0.016672913, + 0.014880543, -0.010199748, -6.2458945e-5, + -0.0097097475, -0.007504745, -0.0038265171, + -0.015628438, 0.026769502, 0.01815581, + -0.025995817, 0.00031833915, 0.0019438836, + 0.008368693, -0.018091336, 0.0016843769, + 0.023249235, -0.021882392, 0.0109669855, + 0.004232702, 0.0012532086, 0.0041650045, + 0.005016058, 0.031901613, -0.024138974, + -0.012391855, -0.026150554, 0.0198966, + -0.024938447, 0.035563722, 0.012037249, + 0.020837918, 0.007678824, -0.008091456, + -0.03324267, -3.8961287e-5, -0.009148826, + 0.01810423, -0.028058978, -0.015306069, + 0.0072404025, 0.0058993483, -0.0067761913, + 0.0351253, 0.036853198, -0.012991461, + -0.005686585, -0.0763885, 0.01292054, + -0.011605276, -0.010051458, -0.012752908, + -0.015628438, 0.013513698, 0.00903922, + 0.0043874388, 0.0048774392, -0.026176345, + 0.01582186, -0.005725269, 0.0116246175, + 0.0059380326, -0.019819232, -0.014210016, + -0.024822395, -0.005999283, -0.014158436, + -0.015538175, -0.006524744, -0.010580143, + 0.0059509273, -0.012050144, -0.0023565157, + -0.022398181, 0.012101724, -0.012191987, + -0.0063442173, 0.0162087, -0.026098976, + 0.0051901373, -0.012411198, 0.005770401, + -0.01815581, -0.023365289, -0.0074918503, + 0.021585813, 0.0252995, -0.010264222, + -0.019367915, 0.025853975, -0.0059670457, + -0.0026676015, 0.006750402, -0.002424213, + 0.0037813857, 0.031824246, -0.00041666164, + 0.01785923, 0.02190818, 0.0055479663, + -0.011108828, -0.010721985, -0.017949494, + 0.0053513213, -0.017652914, -0.0007108231, + -0.014261594, 0.004468031, 0.00894251, + -0.0071694814, -0.0036363197, -0.0017069426, + -0.009864484, 0.016892124, -0.020231863, + 0.010231985, -0.02557029, 0.003900662, + -0.005025729, -0.012727119, 0.03254635, + 0.0069115865, 0.017910808, -0.009445405, + 0.01341054, 0.0038458593, 0.020567127, + -0.016763175, -0.00062620133, -0.022643182, + 0.008742641, 0.028677925, 0.016943702, + -0.021082917, -0.009277773, 0.012952777, + -0.0022098378, 0.007923825, 0.0012322546, + 0.015241595, -0.004996716, 0.0033461878, + 0.009941853, -0.013320277, -0.007459613, + 0.009277773, 0.007175929, -0.0120050125, + -0.0036105302, -0.019264758, -0.015473701, + -0.005905796, 0.0056736902, -0.013926331, + -0.027156346, -0.00658277, 0.010605932, + 0.020090021, 0.007814219, -0.020244759, + 0.0060669803, -0.012856066, 0.006553757, + -0.018942388, -0.019019756, -0.019006863, + 0.01960002, -0.0055189533, 0.02741424, + 0.03417109, -0.0027804307, 0.014932121, + 0.028858451, 0.016737387, -0.016917912, + -0.015073963, -0.011282907, 0.0027691477, + -0.038555305, -0.035589512, 0.003849083, + -0.0006781833, -0.0049386895, -0.0060347435, + 0.031772666, -0.013797383, 0.04809742, + 0.022191865, -0.0058574406, 0.0011178138, + -0.052533213, -0.0024322723, 0.023984237, + 0.007711061, -0.000639902, -0.009761326, + 0.0065666516, -0.005880006, 0.021186076, + -0.00024399286, -0.013681331, -0.009116589, + -0.02584108, -0.0031753324, 0.0038942148, + -0.027749503, 0.027053187, 0.014506594, + 0.04283636, 0.017433703, -0.0019277652, + -0.015202911, 0.010180405, 0.018297652, + -0.021959761, -0.026395554, -0.015796069, + -0.01525449, -0.0049580317, -0.027955819, + 0.029013189, -0.034351617, -0.013797383, + -0.010780011, 0.021843707, 0.028033188, + -0.018748967, 0.012926987, -0.031231087, + -0.0252995, 0.017923703, -0.011070143, + -0.012101724, -0.015886333, -0.031798456 + ], + "topk": 9.0 + }, + "outputs": [ + { + "plot": "A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.", + "genres": ["Drama", "Fantasy", "Horror"], + "title": "The Craft", + "year": 1996 + }, + { + "plot": "A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...", + "genres": ["Comedy", "Fantasy"], + "title": "Wicked Stepmother", + "year": 1989 + }, + { + "plot": "A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.", + "genres": [ + "Adventure", + "Family", + "Fantasy" + ], + "title": "Oz the Great and Powerful", + "year": 2013 + }, + { + "plot": "A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.", + "genres": [ + "Adventure", + "Fantasy", + "Musical" + ], + "title": "Into the Woods", + "year": 2014 + }, + { + "plot": "In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.", + "genres": ["Fantasy", "Horror"], + "title": "Snow White: A Tale of Terror", + "year": 1997 + }, + { + "plot": "An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.", + "genres": [ + "Adventure", + "Family", + "Fantasy" + ], + "title": "Bedknobs and Broomsticks", + "year": 1971 + }, + { + "plot": "When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.", + "genres": [ + "Animation", + "Family", + "Fantasy" + ], + "title": "My Neighbor Totoro", + "year": 1988 + }, + { + "plot": "After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.", + "genres": ["Comedy", "Family", "Fantasy"], + "title": "Hocus Pocus", + "year": 1993 + }, + { + "year": 1998, + "plot": "The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love.", + "genres": ["Comedy", "Fantasy", "Romance"], + "title": "Practical Magic" + } + ] + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 0.0, + "completion": 0.0, + "total": 0.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-a84a-c487893ed246" + } + ] + } + } + }, + "reporter": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f924" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-048d-1fa32d25288b", + "type": "task", + "name": "reporter" + }, + "parent": { + "id": "3283749b-be79-15d6-3839-39eb32070bbc" + }, + "time": { + "start": "2024-10-08T01:22:07.337940", + "end": "2024-10-08T01:22:10.473229", + "span": 3135289 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "count": 3, + "movies": [ + "The Craft (1996) in ['Drama', 'Fantasy', 'Horror']: A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.", + "Wicked Stepmother (1989) in ['Comedy', 'Fantasy']: A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...", + "Oz the Great and Powerful (2013) in ['Adventure', 'Family', 'Fantasy']: A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.", + "Into the Woods (2014) in ['Adventure', 'Fantasy', 'Musical']: A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.", + "Snow White: A Tale of Terror (1997) in ['Fantasy', 'Horror']: In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.", + "Bedknobs and Broomsticks (1971) in ['Adventure', 'Family', 'Fantasy']: An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.", + "My Neighbor Totoro (1988) in ['Animation', 'Family', 'Fantasy']: When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.", + "Hocus Pocus (1993) in ['Comedy', 'Family', 'Fantasy']: After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.", + "Practical Magic (1998) in ['Comedy', 'Fantasy', 'Romance']: The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + ] + }, + "outputs": { + "report": "Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...\n\n2. Hocus Pocus (1993) - After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.\n\n3. Practical Magic (1998) - The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 536.0, + "completion": 181.0, + "total": 717.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-048d-1fa32d25288b" + } + ], + "nodes": { + "openai.chat": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f924" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-7647-7397bc185832", + "type": "chat", + "name": "openai.chat" + }, + "parent": { + "id": "3283749b-be79-15d6-048d-1fa32d25288b" + }, + "time": { + "start": "2024-10-08T01:22:07.338575", + "end": "2024-10-08T01:22:10.473021", + "span": 3134446 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "role": "system", + "content": "Given the following list of suggested movies:\n\nThe Craft (1996) in ['Drama', 'Fantasy', 'Horror']: A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.\nWicked Stepmother (1989) in ['Comedy', 'Fantasy']: A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...\nOz the Great and Powerful (2013) in ['Adventure', 'Family', 'Fantasy']: A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.\nInto the Woods (2014) in ['Adventure', 'Fantasy', 'Musical']: A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.\nSnow White: A Tale of Terror (1997) in ['Fantasy', 'Horror']: In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.\nBedknobs and Broomsticks (1971) in ['Adventure', 'Family', 'Fantasy']: An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.\nMy Neighbor Totoro (1988) in ['Animation', 'Family', 'Fantasy']: When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.\nHocus Pocus (1993) in ['Comedy', 'Family', 'Fantasy']: After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.\nPractical Magic (1998) in ['Comedy', 'Fantasy', 'Romance']: The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + }, + { + "role": "user", + "content": "Provide a list of 3 movies about witches in the genre of comedy." + } + ] + }, + "outputs": { + "completion": [ + { + "finish_reason": "stop", + "role": "assistant", + "content": "Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...\n\n2. Hocus Pocus (1993) - After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.\n\n3. Practical Magic (1998) - The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 717, + "completion": 181, + "prompt": 536 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 536, + "completion": 181, + "total": 717 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "gpt-3.5-turbo", + "temperature": 0.8, + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "gpt-3.5-turbo-0125" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-7647-7397bc185832" + } + ] + } + } + }, + "summarizer": [ + { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f924" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-2cb7-0d88553280b1", + "type": "task", + "name": "summarizer" + }, + "parent": { + "id": "3283749b-be79-15d6-3839-39eb32070bbc" + }, + "time": { + "start": "2024-10-08T01:22:10.473347", + "end": "2024-10-08T01:22:12.752959", + "span": 2279612 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "report": "Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...\n\n2. Hocus Pocus (1993) - After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.\n\n3. Practical Magic (1998) - The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + }, + "outputs": { + "report": "Here are three notable comedy films featuring witches. \n\n\"Wicked Stepmother\" (1989) presents a unique twist on the witch trope, where a mother/daughter duo wreaks havoc on a yuppie family while sharing a single body. \n\n\"Hocus Pocus\" (1993) is a beloved Halloween classic that follows two teenagers and an immortal cat as they attempt to thwart the chaotic return of three witch sisters in Salem. \n\n\"Practical Magic\" (1998) combines romance and comedy, focusing on the Owens sisters as they navigate love and the challenges of their magical heritage." + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 226.0, + "completion": 120.0, + "total": 346.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-2cb7-0d88553280b1" + } + ], + "nodes": { + "openai.chat": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f924" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-e5d2-54ae9f3b0529", + "type": "chat", + "name": "openai.chat" + }, + "parent": { + "id": "3283749b-be79-15d6-2cb7-0d88553280b1" + }, + "time": { + "start": "2024-10-08T01:22:10.473559", + "end": "2024-10-08T01:22:12.752582", + "span": 2279023 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "role": "system", + "content": "Act as a professional cinema critic.\nBe concise and factual.\nUse one intro sentence, and one sentence per movie." + }, + { + "role": "user", + "content": "Summarize the following recommendations about witches in the genre of comedy:\n\nHere are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...\n\n2. Hocus Pocus (1993) - After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.\n\n3. Practical Magic (1998) - The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + } + ] + }, + "outputs": { + "completion": [ + { + "finish_reason": "stop", + "role": "assistant", + "content": "Here are three notable comedy films featuring witches. \n\n\"Wicked Stepmother\" (1989) presents a unique twist on the witch trope, where a mother/daughter duo wreaks havoc on a yuppie family while sharing a single body. \n\n\"Hocus Pocus\" (1993) is a beloved Halloween classic that follows two teenagers and an immortal cat as they attempt to thwart the chaotic return of three witch sisters in Salem. \n\n\"Practical Magic\" (1998) combines romance and comedy, focusing on the Owens sisters as they navigate love and the challenges of their magical heritage." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 346, + "completion": 120, + "prompt": 226 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 226, + "completion": 120, + "total": 346 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "gpt-4o-mini", + "temperature": 0.2, + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "gpt-4o-mini-2024-07-18" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-e5d2-54ae9f3b0529" + } + ] + } + } + }, + { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f924" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-4cdb-4f10ec83e5c4", + "type": "task", + "name": "summarizer" + }, + "parent": { + "id": "3283749b-be79-15d6-3839-39eb32070bbc" + }, + "time": { + "start": "2024-10-08T01:22:12.753232", + "end": "2024-10-08T01:22:14.423907", + "span": 1670675 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "report": "Here are three notable comedy films featuring witches. \n\n\"Wicked Stepmother\" (1989) presents a unique twist on the witch trope, where a mother/daughter duo wreaks havoc on a yuppie family while sharing a single body. \n\n\"Hocus Pocus\" (1993) is a beloved Halloween classic that follows two teenagers and an immortal cat as they attempt to thwart the chaotic return of three witch sisters in Salem. \n\n\"Practical Magic\" (1998) combines romance and comedy, focusing on the Owens sisters as they navigate love and the challenges of their magical heritage." + }, + "outputs": { + "report": "These three comedy films showcase witches in entertaining and unconventional ways. \n\n\"Wicked Stepmother\" (1989) features a mother/daughter duo sharing a body as they disrupt a yuppie family's life. \n\n\"Hocus Pocus\" (1993) is a Halloween favorite where two teens and an immortal cat try to stop the resurrection of three mischievous witch sisters. \n\n\"Practical Magic\" (1998) blends romance and humor, following the Owens sisters as they deal with love and their magical family legacy." + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 168.0, + "completion": 106.0, + "total": 274.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-4cdb-4f10ec83e5c4" + } + ], + "nodes": { + "openai.chat": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:22:14.475693" + }, + "root": { + "id": "70befa7f-3cf2-4485-8396-73c8a361f924" + }, + "tree": { + "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" + }, + "node": { + "id": "3283749b-be79-15d6-2420-816ed54fd495", + "type": "chat", + "name": "openai.chat" + }, + "parent": { + "id": "3283749b-be79-15d6-4cdb-4f10ec83e5c4" + }, + "time": { + "start": "2024-10-08T01:22:12.753687", + "end": "2024-10-08T01:22:14.423633", + "span": 1669946 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "role": "system", + "content": "Act as a professional cinema critic.\nBe concise and factual.\nUse one intro sentence, and one sentence per movie." + }, + { + "role": "user", + "content": "Summarize the following recommendations about witches in the genre of comedy:\n\nHere are three notable comedy films featuring witches. \n\n\"Wicked Stepmother\" (1989) presents a unique twist on the witch trope, where a mother/daughter duo wreaks havoc on a yuppie family while sharing a single body. \n\n\"Hocus Pocus\" (1993) is a beloved Halloween classic that follows two teenagers and an immortal cat as they attempt to thwart the chaotic return of three witch sisters in Salem. \n\n\"Practical Magic\" (1998) combines romance and comedy, focusing on the Owens sisters as they navigate love and the challenges of their magical heritage." + } + ] + }, + "outputs": { + "completion": [ + { + "finish_reason": "stop", + "role": "assistant", + "content": "These three comedy films showcase witches in entertaining and unconventional ways. \n\n\"Wicked Stepmother\" (1989) features a mother/daughter duo sharing a body as they disrupt a yuppie family's life. \n\n\"Hocus Pocus\" (1993) is a Halloween favorite where two teens and an immortal cat try to stop the resurrection of three mischievous witch sisters. \n\n\"Practical Magic\" (1998) blends romance and humor, following the Owens sisters as they deal with love and their magical family legacy." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 274, + "completion": 106, + "prompt": 168 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 168, + "completion": 106, + "total": 274 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "gpt-4o-mini", + "temperature": 0.2, + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "gpt-4o-mini-2024-07-18" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "3283749b-be79-15d6-2420-816ed54fd495" + } + ] + } + } + } + ] + } + } + } + } + ] + }, + { + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361defs" + }, + "trees": [ + { + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "nodes": { + "rag": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361defs" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-2d60-d51d0c7dfede", + "type": "workflow", + "name": "rag" + }, + "time": { + "start": "2024-10-08T01:17:08.313554", + "end": "2024-10-08T01:17:14.639759", + "span": 6326205 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "count": 3 + }, + "outputs": "Here are three notable comedy films featuring witches. \n\n*Wicked Stepmother* (1989) humorously explores the chaos that ensues when a mother and daughter duo of witches invade a yuppie family's home. *Hocus Pocus* (1993) is a cult classic where three witch sisters are accidentally resurrected on Halloween, unleashing comedic mayhem in Salem. *Practical Magic* (1998) is a charming romantic comedy about two witch sisters navigating love and family curses with a blend of humor and magic." + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 885.0, + "completion": 355.0, + "total": 1240.0 + } + } + }, + "meta": { + "configuration": { + "retriever_prompt": "Movies about {topic} in the genre of {genre}.", + "retriever_multiplier": 3.0, + "generator_context_prompt": "Given the following list of suggested movies:\n\n{movies}", + "generator_instructions_prompt": "Provide a list of {count} movies about {topic} in the genre of {genre}.", + "generator_model": "gpt-3.5-turbo", + "generator_temperature": 0.8, + "summarizer_context_prompt": "Act as a professional cinema critic.\nBe concise and factual.\nUse one intro sentence, and one sentence per movie.", + "summarizer_instructions_prompt": "Summarize the following recommendations about {topic} in the genre of {genre}:\n\n{report}", + "summarizer_model": "gpt-4o-mini", + "summarizer_temperature": 0.2 + }, + "environment": "terminal" + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-2d60-d51d0c7dfede" + } + ], + "nodes": { + "retriever": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361defs" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-44cc-6ddc96192c8c", + "type": "task", + "name": "retriever" + }, + "parent": { + "id": "43c39569-3e04-49aa-2d60-d51d0c7dfede" + }, + "time": { + "start": "2024-10-08T01:17:08.313973", + "end": "2024-10-08T01:17:09.131880", + "span": 817907 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "count": 3 + }, + "internals": { + "prompt": "Movies about witches in the genre of comedy." + }, + "outputs": { + "movies": [ + "The Craft (1996) in ['Drama', 'Fantasy', 'Horror']: A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.", + "Wicked Stepmother (1989) in ['Comedy', 'Fantasy']: A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...", + "Oz the Great and Powerful (2013) in ['Adventure', 'Family', 'Fantasy']: A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.", + "Into the Woods (2014) in ['Adventure', 'Fantasy', 'Musical']: A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.", + "Snow White: A Tale of Terror (1997) in ['Fantasy', 'Horror']: In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.", + "Bedknobs and Broomsticks (1971) in ['Adventure', 'Family', 'Fantasy']: An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.", + "My Neighbor Totoro (1988) in ['Animation', 'Family', 'Fantasy']: When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.", + "Hocus Pocus (1993) in ['Comedy', 'Family', 'Fantasy']: After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.", + "Practical Magic (1998) in ['Comedy', 'Fantasy', 'Romance']: The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + ], + "test": [ + 1, + false, + "test", + { + "test": "test" + } + ] + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 9.0, + "completion": 0.0, + "total": 9.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-44cc-6ddc96192c8c" + } + ], + "nodes": { + "openai.embeddings": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361defs" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-1652-1b7965d27817", + "type": "embedding", + "name": "openai.embeddings" + }, + "parent": { + "id": "43c39569-3e04-49aa-44cc-6ddc96192c8c" + }, + "time": { + "start": "2024-10-08T01:17:08.314192", + "end": "2024-10-08T01:17:09.039293", + "span": 725101 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "content": "Movies about witches in the genre of comedy." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 9, + "prompt": 9 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 9, + "completion": 0.0, + "total": 9 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "text-embedding-ada-002", + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "text-embedding-ada-002" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-1652-1b7965d27817" + } + ] + }, + "search": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361defs" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-254c-eff4fcfe2c18", + "type": "query", + "name": "search" + }, + "parent": { + "id": "43c39569-3e04-49aa-44cc-6ddc96192c8c" + }, + "time": { + "start": "2024-10-08T01:17:09.039547", + "end": "2024-10-08T01:17:09.131576", + "span": 92029 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "query": [ + 0.0031027994, -0.036517933, -0.0051836898, + -0.02046397, -0.006576323, 0.004403557, + 0.0034203327, -0.036904775, 0.019174494, + -0.0280074, 0.011934091, 0.010225537, + 0.021160286, -0.0015441463, 0.010141721, + 0.0064248093, 0.040102676, -0.004100531, + 0.018220283, -0.029554768, -0.01073488, + -0.0032655955, -0.009129483, 0.012288697, + -0.014596858, 0.008974747, 0.014519489, + -0.020064233, -0.019986864, -0.006328099, + 0.02557029, -0.019006863, 0.0008212344, + -0.019832127, -0.014390541, -0.032701086, + -0.009793563, -0.011424749, -0.0018794099, + -0.0036814513, 0.0060154013, 0.011882513, + -0.006324875, -0.0067632967, 0.0077432976, + 0.013642646, 0.023545815, 0.0020696074, + -0.0036782275, 0.013230015, 0.020090021, + 0.0016279622, -0.025492921, -0.011985671, + 0.010547906, 0.030302664, 0.015241595, + 0.011882513, 0.019548442, -0.020025548, + -0.015860543, -0.004696913, -0.027749503, + -0.007659482, 0.00085750094, -0.010541459, + 0.0004448689, -0.007943166, 0.0029077663, + -0.025583185, 0.042165834, 0.008826457, + -0.004036057, -0.008394483, 0.017691597, + -0.03154056, -0.030147927, 0.02019318, + -0.0013257415, -0.00235168, 0.022411076, + -0.012759356, -0.010573695, 0.013836067, + 0.012288697, 0.01881344, -0.0091552725, + 0.024138974, 0.007227508, -0.008968299, + 0.005528624, 0.0025837857, 0.032778457, + 0.033345826, 0.01495791, 0.012856066, + -0.011263564, 0.03827162, -0.0128367245, + -0.0038426358, -0.00024117214, 0.011411854, + -0.03362951, -0.012166197, 0.014571068, + -0.018207388, 0.0030834572, 0.01210817, + 0.042088468, 0.014700016, -0.028523188, + -0.00867172, 0.022462655, -0.043661624, + -0.004522834, -0.012636855, 0.029632136, + 0.008749088, 0.0013644258, -0.030560559, + 0.026640555, -0.002135693, 0.025235027, + -0.019664494, 0.03778162, -0.0037942803, + -0.00776264, -0.023971342, 0.004748492, + -0.026176345, 0.029632136, -0.019780548, + 0.02787845, -0.020412391, -0.027027396, + 0.009961195, -0.03687899, 0.008072114, + -0.029322663, -0.020747654, -0.0001522185, + 0.03468688, -0.030405821, -0.0011669749, + -0.009393826, 0.00076844654, 0.0101933, + -0.010032116, 0.0042359256, -0.021186076, + 0.027517397, -0.005860664, 0.02119897, + -0.004751716, 0.012417645, 0.009464747, + 0.017033966, 0.009297115, -0.036956355, + -0.028136346, -0.013500804, 0.008845799, + 0.00530619, -0.008890931, 0.008342904, + 0.013107514, 0.024113184, -0.022243446, + -0.006905139, 0.0014063338, 0.018607125, + 0.019496864, -0.030199505, 0.03664688, + -0.0014168107, 0.0044132285, -0.00032337618, + 0.018929495, -0.0065602046, -0.0060121776, + 0.013178435, 0.0020148049, 0.0076723765, + 0.010515669, -0.017846335, 0.00094937603, + -0.01046409, -0.013642646, 0.0040908596, + -0.018555546, -0.008465404, 0.028806873, + -0.008607246, -0.008684615, -0.678367, + -0.014261594, -0.009974089, -0.026666345, + 0.01110238, 0.006286191, 0.02076055, + 0.012752908, -0.03974162, -0.016840544, + 0.004074741, 0.024371078, 0.020012653, + -0.00918751, -0.030792665, -0.027723715, + -0.009741984, -0.013810278, 0.005409348, + -0.0039780308, -0.0016174853, 0.037136883, + -0.0034622406, -0.016221596, 0.028316872, + 0.02527371, 0.0058477693, -0.0014804786, + -0.025995817, 0.0346353, -0.019419495, + 0.026692133, 0.00072371785, 0.0026240817, + 0.041005306, 0.016905017, -0.008594351, + 0.025054501, 0.015899228, 0.029167926, + -0.02945161, 0.004925795, -0.008220403, + 0.0026788844, -0.016298965, 0.010161064, + 0.0037298065, -0.0066214544, 0.0060121776, + 0.009928958, 0.010148169, 0.008684615, + 0.009954748, 0.015061068, 0.01471291, + 0.015486595, 0.037368987, -0.008562114, + 0.013642646, -0.00028348304, -0.027130555, + 0.021276338, -0.0048161894, -0.0067890864, + 0.0016110379, 0.026292397, 0.0077884295, + 0.010244879, 0.0068922443, -0.0145323835, + -0.0075176395, 0.0068793492, -0.018207388, + -0.0046711233, 0.0007555518, 0.0049161236, + 0.025995817, 0.0061475723, -0.0027320753, + -0.0061959275, -0.00014476372, -0.007420929, + -0.008761983, 0.0033494115, 0.02223055, + -0.019728968, -0.005628559, 0.029503189, + 0.005161124, 0.027723715, 0.011940539, + 0.018026862, -0.011979223, 0.008955404, + -0.00010980686, 0.010838038, 0.0033816483, + 0.019999757, 0.02991582, -0.04497689, + 0.00062821613, -0.009084351, -0.010257774, + -0.01908423, -0.006357112, 0.00056333945, + -0.0029254965, 0.021547128, 0.03675004, + -0.00052908773, 0.031127928, -0.0067632967, + -0.0023823052, -0.017214492, -0.01992239, + -0.026434239, 0.042062677, 0.0074467184, + 0.009780669, -0.010360932, 0.0064925067, + 0.00019835753, -0.008936062, -0.0058993483, + -0.010715538, -0.0012137184, -0.0045357286, + 0.01731765, -0.04459005, -0.012101724, + 0.0026273055, 0.012424092, -0.01938081, + -0.004148886, -0.018529756, 0.00867172, + 0.002588621, -0.020850811, 0.038091093, + 0.007801324, 0.0060186246, -0.004032833, + -0.01957423, 0.010483433, 0.008910272, + -0.015976597, -0.0037620435, 0.01215975, + 0.0038361882, 0.010535011, -0.013823173, + 0.001776252, -0.013848962, -0.007079218, + 0.008349351, -0.0060669803, -0.0095936945, + -0.018968178, -0.0068406654, -0.0064570466, + 0.038581096, 0.0053835586, -0.012727119, + 0.0014087515, -0.006008954, -0.0026063514, + 0.00791093, 0.020115811, -0.014687121, + -0.023507131, 0.005776848, -0.014945016, + -0.0012113006, 0.023674762, -0.0046936893, + 0.041778993, 0.002828786, 0.0128818555, + -0.008684615, 0.005973493, -0.012785145, + 0.0023065484, -0.0108058015, 0.0074467184, + 0.018142914, -0.0036814513, 0.014932121, + -0.016157122, -0.0056092166, -0.010689748, + -0.017717388, 0.020231863, 0.029761085, + -0.011940539, -0.0011726164, -0.0025692792, + 0.006750402, -0.0020921733, 0.027104765, + 0.011199091, 0.030792665, -0.005161124, + -0.001620709, -0.018246071, 0.013346067, + -0.027826872, 0.0015457582, -0.021534234, + 0.029167926, 0.0314374, 0.005850993, + -0.010019221, -0.02372634, 0.0028223386, + -0.008529877, 0.0029351676, -0.008355798, + 0.024899764, -0.00815593, -0.008265535, + 0.00082204037, -0.00906501, 0.015280279, + 0.019857915, -0.022643182, -0.012424092, + 0.017511072, -0.022191865, 0.011695539, + -0.0065473095, -0.027646346, 0.00588323, + -0.015486595, 0.008839351, 0.014480805, + -0.020992653, 0.006653691, -0.010870275, + 0.030947402, 0.007627245, -0.00724685, + 0.023442656, 0.015873438, 0.0030012531, + 0.012095276, -0.013346067, 0.025686344, + -0.0052159266, -0.0072404025, 0.00056616013, + -0.026215028, 0.007472508, 0.013294488, + 0.018503968, 0.010322248, -0.008271983, + 0.025763711, -0.015112648, 0.023597393, + 0.036853198, 0.00044043633, -0.02836845, + 0.0050096107, 0.006179809, -0.012320934, + 0.0051901373, 0.014841858, 0.008845799, + -0.015654227, -0.02896161, -0.03208214, + -0.011360276, 0.0020309233, 0.007949614, + 0.0019648375, 0.010489879, -0.019961074, + 0.021082917, 0.00633777, 0.0061346777, + -0.009303562, -0.038348988, 0.017240282, + 0.00785935, -0.017988177, -0.011760012, + -0.022024235, -0.008349351, 0.00032861467, + 0.014558174, 0.006453823, 0.025957134, + -0.009806458, 0.012843172, 0.0025773384, + 0.0069309287, 0.006057309, -0.011611723, + 0.000929228, 0.007175929, -0.0061346777, + -0.0059219142, -0.0116826445, -0.026034502, + -0.008233299, 0.0019728967, -0.022114497, + -0.004036057, -0.005631782, 0.010515669, + 0.0025128645, -0.0010227149, -0.02250134, + 0.008568562, -0.035976354, 0.0061636907, + -0.009774221, -0.01267554, 0.02586687, + 0.008297772, 0.015538175, -0.030225296, + -0.008729747, -0.004932242, 0.09067588, + 0.010444748, 0.008845799, -0.002404871, + 0.0005435943, -0.005302966, -0.023171866, + -0.008065666, 0.0008373529, -0.00864593, + 0.027311081, -0.005067637, 0.038503725, + 0.010535011, 0.017678702, -0.0012048533, + -0.013197777, 0.012817382, 0.029632136, + -0.022359498, -0.014171331, -0.019445283, + 0.0027723713, -0.020773442, -0.0144937, + -0.038632672, 0.016930807, 0.015306069, + -0.007079218, -0.0034912538, -0.029632136, + -0.015654227, -0.013513698, 0.008291325, + 0.019316336, 0.018865021, 0.011818039, + 0.0016360215, 0.040515307, -0.02299134, + 0.020554233, 0.018942388, 0.015576859, + 0.008626589, 0.008259088, -0.017781861, + 0.012211329, 0.027104765, 0.010618827, + -0.022101603, 0.03208214, 0.015602648, + -0.0019342126, 0.011592381, 0.020554233, + 0.014880543, -0.0094325105, 0.021276338, + -0.009542115, 0.01736923, -0.009645274, + -4.7901984e-5, 0.011650407, 0.00633777, + -0.011199091, -0.026743712, -0.018826336, + -0.017278966, 0.0042875046, -0.0030479967, + -0.016028175, -0.019019756, -0.014390541, + 0.001744015, 0.010638169, -0.0022146734, + 0.020231863, -0.015215806, 0.005199808, + 0.011714881, -0.0198966, -0.026369765, + -0.0017472387, -0.041211624, -0.013487909, + 0.007923825, 0.0017327322, 0.02385529, + -0.03626004, -0.008201062, 0.008761983, + 0.031127928, -0.008220403, -0.027104765, + 0.010651064, 0.01292054, 0.017407913, + 0.025957134, -0.0016537518, -0.0052417163, + 0.012050144, -0.031953193, 0.00034130792, + -0.0072081657, 0.006453823, -0.015460806, + -0.008349351, 0.023597393, -0.0076014553, + -0.002962569, 0.015422122, -0.005077308, + 0.02217897, 0.0059509273, 0.0036330959, + 0.027646346, -0.0048580975, 0.014790279, + 0.0049064527, 0.0069438233, -0.00032942058, + -0.013965015, 0.00072170305, 0.015293174, + -0.047297947, 0.014725805, -0.023326604, + -0.038142674, -0.011534355, 0.0059219142, + -0.018723179, 0.022630287, 0.012875409, + 0.0062700724, -0.033268455, -0.020489758, + -0.003417109, 0.008097904, -0.017304756, + -0.0036169775, -0.01921318, 0.0057865195, + -0.003436451, -0.016273174, -0.01653107, + -0.02695003, 0.0070727705, -0.018246071, + 0.002475792, 0.008433167, 0.0019986862, + 0.003106023, -0.00036810484, 0.0196516, + 0.00033062947, -0.018852126, -0.014455016, + -0.022939762, 0.0020099692, 0.018710284, + 0.04551847, 0.012146855, 0.033500563, + -0.0042488202, -0.01070909, 0.01943239, + -0.0012233894, -0.021946866, -0.042191625, + 0.026047396, -0.015486595, -0.0026885555, + 0.020541338, 0.020618707, 0.009245536, + 0.023300814, 0.0080011925, 0.0057832957, + 0.0004283475, -0.022075813, -0.026253711, + -0.002672437, -0.0034848063, -0.0021937194, + 0.014661332, 0.00062055985, 0.02220476, + 0.013965015, -0.004345531, -0.00042028827, + 0.047787946, 0.00026776755, 0.02114739, + 0.009729089, 0.0049999394, -0.040154252, + -0.0065376386, -0.017175809, -0.013126856, + 0.010934749, 0.025518712, -0.011946986, + -0.0037072408, -0.005335203, 0.006389349, + 0.04497689, 0.0077690873, -0.008407378, + 0.010457642, -0.0061185593, -0.017407913, + -0.025918448, -0.019419495, -0.034428984, + 0.017395018, -0.014803174, -0.02839424, + 0.023765026, -0.012366066, -0.017304756, + -0.007975403, -0.016221596, 0.0252995, + 0.018142914, 0.029322663, 0.02692424, + -0.010915406, 0.018555546, 0.026975818, + -0.023584498, -0.0058671115, -0.0016118438, + 0.0034719117, 0.012095276, 0.0026079633, + -0.0061056647, -0.012914093, -0.03417109, + -0.02377792, 0.012346724, 0.015615543, + 0.023881078, -0.014455016, 0.014235805, + 0.0028642465, 0.016943702, -0.012262908, + 0.009142378, -0.023184761, -0.01889081, + -0.014648437, -0.0027111212, -0.0056221113, + 0.0013894094, 0.0020583246, -0.005654348, + 0.016273174, -0.035280038, -0.004754939, + 0.013242909, -0.01813002, 0.042062677, + -0.0024435553, -0.00020671898, 0.018542651, + 0.0024274369, 0.002786878, 0.0056736902, + -0.006679481, 0.027594766, 0.01395212, + 0.022075813, -0.006756849, -0.0035653985, + 0.014648437, -0.008826457, -0.0060025062, + -0.011534355, 0.0021082917, 0.0063119805, + 0.035331618, 0.007943166, -0.0016972716, + 0.0037104646, -0.025995817, 0.01815581, + 0.005470598, -0.032701086, 0.003128589, + -0.021353707, 0.01525449, -0.012288697, + -0.013539488, -0.0052481634, 0.018258967, + -0.006698823, 0.02380371, 0.0093744835, + -0.018478177, -0.0077561927, -0.008478299, + -0.009716195, -0.011824487, -0.001455495, + 0.014622647, -0.020373706, 0.025725028, + -0.0071694814, 0.0016601991, -0.011070143, + -0.008458956, -0.0057155984, 0.011695539, + -0.0116246175, -0.00931001, 0.016427912, + 0.0030818454, -0.027568977, 0.005425466, + 0.014777384, -0.014996595, -0.012656198, + 0.024899764, -0.0034461222, 0.021624496, + 0.009767774, -0.010354484, -0.0040844125, + -0.006076651, -0.009484089, -0.01577028, + -0.006421586, 0.019226072, 0.0016368274, + -0.005554414, -0.006502178, -0.0009622708, + 0.028420031, -0.017614229, 0.019986864, + 0.00038583513, 0.030689506, -7.221816e-6, + 0.03259793, -0.0132171195, 0.0053126374, + -0.025286606, -0.028265294, -0.016028175, + 0.008020535, -0.012707776, 0.03687899, + -0.0018004295, -0.008884483, -0.029632136, + -0.0030899046, -0.021108707, -0.0052191503, + -0.016298965, 0.02328792, 0.021624496, + -0.009271326, -0.012643303, -0.004052175, + 0.0098709315, 0.00576073, -0.039690044, + 0.010844485, -0.00066327373, 0.0201416, + -0.007478955, 0.02095397, -0.02266897, + -0.016118439, 0.019458178, 0.024409764, + -0.0065666516, 0.0057961904, -0.010283563, + -0.030715296, -0.020696076, -0.005776848, + -0.007904482, -0.016260281, 0.01598949, + -0.018542651, 0.009683957, -0.0049161236, + 0.0060605328, -0.017730283, 0.004674347, + 0.012153302, -0.016647123, 0.011927645, + -0.019496864, -0.016905017, -0.0045163864, + -0.006160467, 0.015912123, 0.0049709263, + 0.0068148756, 0.013552383, 0.0014579127, + -0.016969493, -0.021921076, 0.000557295, + 0.019316336, 0.003839412, 0.0040682936, + 0.0027498056, -0.0114054065, -0.022114497, + 0.0007245238, -0.0064377044, -0.0020905614, + -0.031205297, 0.011418302, -0.002292042, + 0.029374242, 0.0033558588, -0.0094905365, + 0.025995817, 0.0013201, 0.014184225, + -0.0067375074, -0.01913581, -0.017021071, + -0.004384215, -0.0044970443, 0.0045163864, + -0.014790279, 0.0045260573, -0.021560023, + -0.014635542, -0.02896161, 0.026434239, + 0.21374339, 0.0019454955, -0.007627245, + 0.058232695, 0.011025012, 0.032752667, + 0.017150018, 0.0028642465, -0.00412632, + 0.011173301, -0.016053965, 0.0034557933, + -0.0069180336, 0.0089940885, -0.007781982, + -0.023004236, -0.024242131, -0.03932899, + -0.033397403, -0.027285293, 0.015976597, + 0.004242373, 0.0009235865, -0.026202133, + 0.013023698, 0.008897378, 0.0051901373, + 0.009232641, 0.02380371, 0.012611066, + 0.009664616, -0.011392512, -0.0015900839, + -0.0024628974, -0.012166197, -0.0006120977, + 0.001671482, -0.009999879, 0.002964181, + 0.016724491, -0.014880543, 0.01736923, + 0.0062475065, 0.0045357286, 0.012475671, + 0.011953434, -0.02174055, 0.011850276, + 0.021031339, -0.004052175, -0.019961074, + -0.005438361, 0.03308793, 0.04342952, + -0.02741424, 0.014145541, 0.024035815, + -0.018246071, -0.002527371, -0.0041617807, + 0.009258431, 0.030302664, -0.03097319, + 0.0087168515, -0.0034977011, 0.019342126, + -0.040489517, -0.018220283, -0.0007164646, + -0.0028819768, -0.019587126, -0.035950564, + -0.004800071, -0.00854922, 0.0011097544, + -0.008839351, 0.015422122, 0.0058703353, + -0.0066923755, 0.025441343, -0.021895286, + 5.938637e-5, 0.025467133, 0.0125594875, + -0.0031721087, -0.01682765, 0.008026983, + 0.018761862, -0.013346067, -0.010799354, + -0.0035267144, -0.008484746, 0.0010178793, + 0.008858694, 0.02141818, 0.013694225, + -0.015447911, 0.017640019, -0.0063925725, + -0.0055963215, -0.014661332, -0.011231327, + 0.031127928, -0.0062700724, -0.021082917, + 0.0012830277, -0.012656198, 0.005725269, + 0.028084766, -0.0059412564, 0.008478299, + -0.019174494, -0.0032333587, -0.011437644, + 0.0038458593, 0.010290011, 0.0059670457, + 0.00040920684, -0.013552383, -0.015925016, + -0.03922583, 0.00354928, -0.00069833134, + -0.008504088, 0.003926452, 0.0027207923, + -0.01582186, 0.0035944118, 0.00077569985, + -0.028574767, 0.03819425, -0.02114739, + 0.0059477035, 0.007401587, 0.0021937194, + 0.009793563, 0.022075813, -0.0017262847, + -0.012217776, 0.0023162195, 0.0052707293, + -0.0077046137, 0.0003106829, 0.014055278, + 0.013926331, -0.018942388, -0.006905139, + 0.0023597393, -0.003716912, -0.012424092, + -0.018877914, -0.034815826, -0.0063957963, + -0.002888424, 0.0206316, -0.017575545, + -0.032314245, -0.028806873, -0.021792129, + 0.02250134, -0.003478359, 0.011663302, + 0.0099483, 0.004809742, 4.586829e-6, + -0.003136648, -0.16443387, 0.01085738, + 0.014171331, -0.009471194, 0.032830033, + -0.0075950082, 0.022243446, -0.00470336, + -0.012630409, -0.014351858, -0.015357648, + -0.0010396392, -0.05524111, -0.0045776363, + 0.007472508, 0.0055673085, -0.0020551009, + 0.026266607, 0.012424092, -0.0014990148, + 0.0019051994, 0.016866334, -0.007472508, + -0.0129979085, 0.017652914, 0.0043584257, + 0.007904482, 0.005170795, -0.029657926, + 0.001992239, 3.838203e-5, 0.0075176395, + 0.011482775, -0.007227508, 0.015138437, + 0.00582198, -0.0058993483, 0.008600798, + 0.015834754, -0.026021607, 0.024990026, + 0.011650407, 0.009193957, -0.020721864, + 0.005225598, 0.0252995, 0.015615543, + -0.0020970087, 0.002993194, -0.008407378, + -0.011798697, -0.014132647, 0.043532677, + -0.023120288, -0.0034009905, 0.008948957, + -0.015757386, 0.01631186, -0.003375201, + -0.008394483, -0.0027224042, -0.024177657, + -0.009071457, 0.0064634937, -0.0071372446, + -0.025054501, -0.0029238847, 0.009123036, + -0.0036459907, -0.0015264162, -0.03816846, + 0.0011129782, 0.0019890154, -0.026240818, + 0.013197777, -0.022514235, -0.022643182, + 0.039561097, -0.0028449043, 0.0028529635, + 0.0053416505, 0.04750426, -0.0022340156, + -0.020825023, 0.020309232, -0.0036588856, + -0.0012854454, -0.008026983, -0.00066327373, + -0.00536744, 0.011469881, -0.006234612, + -0.0034719117, -0.016660018, 0.0044196756, + 0.023842394, -0.008014088, 0.006305533, + 0.03687899, -0.035795826, 0.021044234, + -0.006782639, -0.018091336, 0.018826336, + 0.018865021, 0.032159507, 0.03148898, + 0.007659482, 0.0005738164, 0.008259088, + 0.0015788011, 0.0011186196, 0.011869618, + -0.01267554, 0.009245536, 0.01960002, + 0.030560559, -0.01729186, 0.014235805, + 0.0021066798, 0.03920004, 0.0013861856, + -0.019935284, -0.01753686, -0.021753443, + 0.0053416505, -0.07086955, -0.021753443, + -0.031205297, 0.0058413222, -0.025879765, + 0.018787652, -0.013171988, 0.0125594875, + -0.016479492, 0.0097033, 0.013500804, + -0.021031339, -0.015628438, -0.0028577992, + 0.019690285, 0.0030576678, -0.02793003, + 0.0034332273, -0.0010960537, -0.0015973372, + -0.021598708, 0.008703956, -0.022114497, + -0.02689845, -0.007350008, 0.0029190492, + -0.01061238, 0.018181598, 0.0039909254, + 0.024886869, -0.030792665, -0.0026772725, + -0.0027062858, -0.015589753, 0.015164226, + -0.00011343351, -0.011070143, 0.0013273534, + 0.009896721, -0.034325827, 0.003436451, + -0.0029657925, 0.006782639, 0.0022469102, + -0.02068318, 0.004203689, -0.021998445, + 0.0022855946, 0.007781982, -0.0066214544, + -0.0189166, 0.005273953, -0.024177657, + 0.0011895407, 0.020515548, -0.016672913, + 0.014880543, -0.010199748, -6.2458945e-5, + -0.0097097475, -0.007504745, -0.0038265171, + -0.015628438, 0.026769502, 0.01815581, + -0.025995817, 0.00031833915, 0.0019438836, + 0.008368693, -0.018091336, 0.0016843769, + 0.023249235, -0.021882392, 0.0109669855, + 0.004232702, 0.0012532086, 0.0041650045, + 0.005016058, 0.031901613, -0.024138974, + -0.012391855, -0.026150554, 0.0198966, + -0.024938447, 0.035563722, 0.012037249, + 0.020837918, 0.007678824, -0.008091456, + -0.03324267, -3.8961287e-5, -0.009148826, + 0.01810423, -0.028058978, -0.015306069, + 0.0072404025, 0.0058993483, -0.0067761913, + 0.0351253, 0.036853198, -0.012991461, + -0.005686585, -0.0763885, 0.01292054, + -0.011605276, -0.010051458, -0.012752908, + -0.015628438, 0.013513698, 0.00903922, + 0.0043874388, 0.0048774392, -0.026176345, + 0.01582186, -0.005725269, 0.0116246175, + 0.0059380326, -0.019819232, -0.014210016, + -0.024822395, -0.005999283, -0.014158436, + -0.015538175, -0.006524744, -0.010580143, + 0.0059509273, -0.012050144, -0.0023565157, + -0.022398181, 0.012101724, -0.012191987, + -0.0063442173, 0.0162087, -0.026098976, + 0.0051901373, -0.012411198, 0.005770401, + -0.01815581, -0.023365289, -0.0074918503, + 0.021585813, 0.0252995, -0.010264222, + -0.019367915, 0.025853975, -0.0059670457, + -0.0026676015, 0.006750402, -0.002424213, + 0.0037813857, 0.031824246, -0.00041666164, + 0.01785923, 0.02190818, 0.0055479663, + -0.011108828, -0.010721985, -0.017949494, + 0.0053513213, -0.017652914, -0.0007108231, + -0.014261594, 0.004468031, 0.00894251, + -0.0071694814, -0.0036363197, -0.0017069426, + -0.009864484, 0.016892124, -0.020231863, + 0.010231985, -0.02557029, 0.003900662, + -0.005025729, -0.012727119, 0.03254635, + 0.0069115865, 0.017910808, -0.009445405, + 0.01341054, 0.0038458593, 0.020567127, + -0.016763175, -0.00062620133, -0.022643182, + 0.008742641, 0.028677925, 0.016943702, + -0.021082917, -0.009277773, 0.012952777, + -0.0022098378, 0.007923825, 0.0012322546, + 0.015241595, -0.004996716, 0.0033461878, + 0.009941853, -0.013320277, -0.007459613, + 0.009277773, 0.007175929, -0.0120050125, + -0.0036105302, -0.019264758, -0.015473701, + -0.005905796, 0.0056736902, -0.013926331, + -0.027156346, -0.00658277, 0.010605932, + 0.020090021, 0.007814219, -0.020244759, + 0.0060669803, -0.012856066, 0.006553757, + -0.018942388, -0.019019756, -0.019006863, + 0.01960002, -0.0055189533, 0.02741424, + 0.03417109, -0.0027804307, 0.014932121, + 0.028858451, 0.016737387, -0.016917912, + -0.015073963, -0.011282907, 0.0027691477, + -0.038555305, -0.035589512, 0.003849083, + -0.0006781833, -0.0049386895, -0.0060347435, + 0.031772666, -0.013797383, 0.04809742, + 0.022191865, -0.0058574406, 0.0011178138, + -0.052533213, -0.0024322723, 0.023984237, + 0.007711061, -0.000639902, -0.009761326, + 0.0065666516, -0.005880006, 0.021186076, + -0.00024399286, -0.013681331, -0.009116589, + -0.02584108, -0.0031753324, 0.0038942148, + -0.027749503, 0.027053187, 0.014506594, + 0.04283636, 0.017433703, -0.0019277652, + -0.015202911, 0.010180405, 0.018297652, + -0.021959761, -0.026395554, -0.015796069, + -0.01525449, -0.0049580317, -0.027955819, + 0.029013189, -0.034351617, -0.013797383, + -0.010780011, 0.021843707, 0.028033188, + -0.018748967, 0.012926987, -0.031231087, + -0.0252995, 0.017923703, -0.011070143, + -0.012101724, -0.015886333, -0.031798456 + ], + "topk": 9.0 + }, + "outputs": [ + { + "plot": "A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.", + "genres": ["Drama", "Fantasy", "Horror"], + "title": "The Craft", + "year": 1996 + }, + { + "plot": "A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...", + "genres": ["Comedy", "Fantasy"], + "title": "Wicked Stepmother", + "year": 1989 + }, + { + "plot": "A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.", + "genres": [ + "Adventure", + "Family", + "Fantasy" + ], + "title": "Oz the Great and Powerful", + "year": 2013 + }, + { + "plot": "A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.", + "genres": [ + "Adventure", + "Fantasy", + "Musical" + ], + "title": "Into the Woods", + "year": 2014 + }, + { + "plot": "In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.", + "genres": ["Fantasy", "Horror"], + "title": "Snow White: A Tale of Terror", + "year": 1997 + }, + { + "plot": "An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.", + "genres": [ + "Adventure", + "Family", + "Fantasy" + ], + "title": "Bedknobs and Broomsticks", + "year": 1971 + }, + { + "plot": "When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.", + "genres": [ + "Animation", + "Family", + "Fantasy" + ], + "title": "My Neighbor Totoro", + "year": 1988 + }, + { + "plot": "After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.", + "genres": ["Comedy", "Family", "Fantasy"], + "title": "Hocus Pocus", + "year": 1993 + }, + { + "year": 1998, + "plot": "The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love.", + "genres": ["Comedy", "Fantasy", "Romance"], + "title": "Practical Magic" + } + ] + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 0.0, + "completion": 0.0, + "total": 0.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-254c-eff4fcfe2c18" + } + ] + } + } + }, + "reporter": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361defs" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-f638-ccc37deb35c6", + "type": "task", + "name": "reporter" + }, + "parent": { + "id": "43c39569-3e04-49aa-2d60-d51d0c7dfede" + }, + "time": { + "start": "2024-10-08T01:17:09.132009", + "end": "2024-10-08T01:17:10.953560", + "span": 1821551 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "count": 3, + "movies": [ + "The Craft (1996) in ['Drama', 'Fantasy', 'Horror']: A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.", + "Wicked Stepmother (1989) in ['Comedy', 'Fantasy']: A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...", + "Oz the Great and Powerful (2013) in ['Adventure', 'Family', 'Fantasy']: A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.", + "Into the Woods (2014) in ['Adventure', 'Fantasy', 'Musical']: A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.", + "Snow White: A Tale of Terror (1997) in ['Fantasy', 'Horror']: In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.", + "Bedknobs and Broomsticks (1971) in ['Adventure', 'Family', 'Fantasy']: An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.", + "My Neighbor Totoro (1988) in ['Animation', 'Family', 'Fantasy']: When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.", + "Hocus Pocus (1993) in ['Comedy', 'Family', 'Fantasy']: After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.", + "Practical Magic (1998) in ['Comedy', 'Fantasy', 'Romance']: The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + ] + }, + "outputs": { + "report": "Sure! Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches cause havoc in a yuppie family's home in a comedic way.\n2. Hocus Pocus (1993) - Three witch sisters are resurrected in Salem Massachusetts on Halloween night, leading to comedic chaos and a battle to end their reign of terror.\n3. Practical Magic (1998) - The Owens sisters, Sally and Gillian, use their hereditary gift for practical magic to overcome obstacles in love in a wry and comedic romantic tale." + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 536.0, + "completion": 126.0, + "total": 662.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-f638-ccc37deb35c6" + } + ], + "nodes": { + "openai.chat": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361defs" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-2ebe-b45375e00a9c", + "type": "chat", + "name": "openai.chat" + }, + "parent": { + "id": "43c39569-3e04-49aa-f638-ccc37deb35c6" + }, + "time": { + "start": "2024-10-08T01:17:09.132381", + "end": "2024-10-08T01:17:10.953258", + "span": 1820877 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "role": "system", + "content": "Given the following list of suggested movies:\n\nThe Craft (1996) in ['Drama', 'Fantasy', 'Horror']: A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.\nWicked Stepmother (1989) in ['Comedy', 'Fantasy']: A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...\nOz the Great and Powerful (2013) in ['Adventure', 'Family', 'Fantasy']: A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.\nInto the Woods (2014) in ['Adventure', 'Fantasy', 'Musical']: A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.\nSnow White: A Tale of Terror (1997) in ['Fantasy', 'Horror']: In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.\nBedknobs and Broomsticks (1971) in ['Adventure', 'Family', 'Fantasy']: An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.\nMy Neighbor Totoro (1988) in ['Animation', 'Family', 'Fantasy']: When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.\nHocus Pocus (1993) in ['Comedy', 'Family', 'Fantasy']: After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.\nPractical Magic (1998) in ['Comedy', 'Fantasy', 'Romance']: The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love." + }, + { + "role": "user", + "content": "Provide a list of 3 movies about witches in the genre of comedy." + } + ] + }, + "outputs": { + "completion": [ + { + "finish_reason": "stop", + "role": "assistant", + "content": "Sure! Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches cause havoc in a yuppie family's home in a comedic way.\n2. Hocus Pocus (1993) - Three witch sisters are resurrected in Salem Massachusetts on Halloween night, leading to comedic chaos and a battle to end their reign of terror.\n3. Practical Magic (1998) - The Owens sisters, Sally and Gillian, use their hereditary gift for practical magic to overcome obstacles in love in a wry and comedic romantic tale." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 662, + "completion": 126, + "prompt": 536 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 536, + "completion": 126, + "total": 662 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "gpt-3.5-turbo", + "temperature": 0.8, + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "gpt-3.5-turbo-0125" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-2ebe-b45375e00a9c" + } + ] + } + } + }, + "summarizer": [ + { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361defs" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-e2a6-0ea40ffabc06", + "type": "task", + "name": "summarizer" + }, + "parent": { + "id": "43c39569-3e04-49aa-2d60-d51d0c7dfede" + }, + "time": { + "start": "2024-10-08T01:17:10.953757", + "end": "2024-10-08T01:17:13.033807", + "span": 2080050 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "report": "Sure! Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches cause havoc in a yuppie family's home in a comedic way.\n2. Hocus Pocus (1993) - Three witch sisters are resurrected in Salem Massachusetts on Halloween night, leading to comedic chaos and a battle to end their reign of terror.\n3. Practical Magic (1998) - The Owens sisters, Sally and Gillian, use their hereditary gift for practical magic to overcome obstacles in love in a wry and comedic romantic tale." + }, + "outputs": { + "report": "Here are three notable comedy films featuring witches. \n\n1. *Wicked Stepmother* (1989) - This film humorously explores the chaos that ensues when a mother and daughter duo of witches invade a yuppie family's home. \n2. *Hocus Pocus* (1993) - A cult classic where three witch sisters are accidentally resurrected on Halloween, unleashing comedic mayhem in Salem. \n3. *Practical Magic* (1998) - A charming romantic comedy about two witch sisters navigating love and family curses with a blend of humor and magic." + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 172.0, + "completion": 120.0, + "total": 292.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-e2a6-0ea40ffabc06" + } + ], + "nodes": { + "openai.chat": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361defs" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-54f6-bd98032e1a49", + "type": "chat", + "name": "openai.chat" + }, + "parent": { + "id": "43c39569-3e04-49aa-e2a6-0ea40ffabc06" + }, + "time": { + "start": "2024-10-08T01:17:10.953998", + "end": "2024-10-08T01:17:13.033642", + "span": 2079644 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "role": "system", + "content": "Act as a professional cinema critic.\nBe concise and factual.\nUse one intro sentence, and one sentence per movie." + }, + { + "role": "user", + "content": "Summarize the following recommendations about witches in the genre of comedy:\n\nSure! Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989) - A mother/daughter pair of witches cause havoc in a yuppie family's home in a comedic way.\n2. Hocus Pocus (1993) - Three witch sisters are resurrected in Salem Massachusetts on Halloween night, leading to comedic chaos and a battle to end their reign of terror.\n3. Practical Magic (1998) - The Owens sisters, Sally and Gillian, use their hereditary gift for practical magic to overcome obstacles in love in a wry and comedic romantic tale." + } + ] + }, + "outputs": { + "completion": [ + { + "finish_reason": "stop", + "role": "assistant", + "content": "Here are three notable comedy films featuring witches. \n\n1. *Wicked Stepmother* (1989) - This film humorously explores the chaos that ensues when a mother and daughter duo of witches invade a yuppie family's home. \n2. *Hocus Pocus* (1993) - A cult classic where three witch sisters are accidentally resurrected on Halloween, unleashing comedic mayhem in Salem. \n3. *Practical Magic* (1998) - A charming romantic comedy about two witch sisters navigating love and family curses with a blend of humor and magic." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 292, + "completion": 120, + "prompt": 172 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 172, + "completion": 120, + "total": 292 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "gpt-4o-mini", + "temperature": 0.2, + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "gpt-4o-mini-2024-07-18" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-54f6-bd98032e1a49" + } + ] + } + } + }, + { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361defs" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-e106-ba0319c6e51f", + "type": "task", + "name": "summarizer" + }, + "parent": { + "id": "43c39569-3e04-49aa-2d60-d51d0c7dfede" + }, + "time": { + "start": "2024-10-08T01:17:13.033927", + "end": "2024-10-08T01:17:14.639404", + "span": 1605477 + }, + "status": { + "code": "OK" + }, + "data": { + "inputs": { + "topic": "witches", + "genre": "comedy", + "report": "Here are three notable comedy films featuring witches. \n\n1. *Wicked Stepmother* (1989) - This film humorously explores the chaos that ensues when a mother and daughter duo of witches invade a yuppie family's home. \n2. *Hocus Pocus* (1993) - A cult classic where three witch sisters are accidentally resurrected on Halloween, unleashing comedic mayhem in Salem. \n3. *Practical Magic* (1998) - A charming romantic comedy about two witch sisters navigating love and family curses with a blend of humor and magic." + }, + "outputs": { + "report": "Here are three notable comedy films featuring witches. \n\n*Wicked Stepmother* (1989) humorously explores the chaos that ensues when a mother and daughter duo of witches invade a yuppie family's home. *Hocus Pocus* (1993) is a cult classic where three witch sisters are accidentally resurrected on Halloween, unleashing comedic mayhem in Salem. *Practical Magic* (1998) is a charming romantic comedy about two witch sisters navigating love and family curses with a blend of humor and magic." + } + }, + "metrics": { + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 168.0, + "completion": 109.0, + "total": 277.0 + } + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-e106-ba0319c6e51f" + } + ], + "nodes": { + "openai.chat": { + "scope": { + "project_id": "f7943e42-ec69-498e-bf58-8db034b9286e" + }, + "lifecycle": { + "created_at": "2024-10-07T23:17:14.694049" + }, + "root": { + "id": "07befa7f-3cf2-4485-8396-73c8a361defs" + }, + "tree": { + "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" + }, + "node": { + "id": "43c39569-3e04-49aa-5699-abba488eec45", + "type": "chat", + "name": "openai.chat" + }, + "parent": { + "id": "43c39569-3e04-49aa-e106-ba0319c6e51f" + }, + "time": { + "start": "2024-10-08T01:17:13.034164", + "end": "2024-10-08T01:17:14.639038", + "span": 1604874 + }, + "status": { + "code": "UNSET" + }, + "data": { + "inputs": { + "prompt": [ + { + "role": "system", + "content": "Act as a professional cinema critic.\nBe concise and factual.\nUse one intro sentence, and one sentence per movie." + }, + { + "role": "user", + "content": "Summarize the following recommendations about witches in the genre of comedy:\n\nHere are three notable comedy films featuring witches. \n\n1. *Wicked Stepmother* (1989) - This film humorously explores the chaos that ensues when a mother and daughter duo of witches invade a yuppie family's home. \n2. *Hocus Pocus* (1993) - A cult classic where three witch sisters are accidentally resurrected on Halloween, unleashing comedic mayhem in Salem. \n3. *Practical Magic* (1998) - A charming romantic comedy about two witch sisters navigating love and family curses with a blend of humor and magic." + } + ] + }, + "outputs": { + "completion": [ + { + "finish_reason": "stop", + "role": "assistant", + "content": "Here are three notable comedy films featuring witches. \n\n*Wicked Stepmother* (1989) humorously explores the chaos that ensues when a mother and daughter duo of witches invade a yuppie family's home. *Hocus Pocus* (1993) is a cult classic where three witch sisters are accidentally resurrected on Halloween, unleashing comedic mayhem in Salem. *Practical Magic* (1998) is a charming romantic comedy about two witch sisters navigating love and family curses with a blend of humor and magic." + } + ] + } + }, + "metrics": { + "unit": { + "tokens": { + "total": 277, + "completion": 109, + "prompt": 168 + } + }, + "acc": { + "costs": { + "total": 0.0 + }, + "tokens": { + "prompt": 168, + "completion": 109, + "total": 277 + } + } + }, + "meta": { + "system": "OpenAI", + "request": { + "model": "gpt-4o-mini", + "temperature": 0.2, + "headers": "None", + "streaming": false, + "base_url": "https://api.openai.com/v1/" + }, + "response": { + "model": "gpt-4o-mini-2024-07-18" + } + }, + "refs": { + "resource_id": "ffd3aa71-1690-d932-9d82-f7963d85472d", + "application_id": "0191cb41-ecf9-7112-87fa-5e4a9ce72307" + }, + "links": [ + { + "type": "testcase", + "id": "43c39569-3e04-49aa-5699-abba488eec45" + } + ] + } + } + } + ] + } + } + } + } + ] } ] } From 19a1c9d32d90f2bdc6d359b3b99ea7dae09b4fb3 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 9 Oct 2024 11:54:36 +0200 Subject: [PATCH 051/305] Fix trace now returns backward-compatible trace, Add app_id back as refs.application.id --- agenta-cli/agenta/sdk/tracing/inline.py | 256 ++++++++++++-------- agenta-cli/agenta/sdk/tracing/processors.py | 5 + agenta-cli/agenta/sdk/tracing/tracing.py | 33 ++- 3 files changed, 181 insertions(+), 113 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py index fe1a730613..b65efb245d 100644 --- a/agenta-cli/agenta/sdk/tracing/inline.py +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -1,7 +1,6 @@ ############################ ### services.shared.dtos ### -############################ - +### -------------------- ### from typing import Optional @@ -12,9 +11,6 @@ from collections import OrderedDict -## --- DISPLAY --- ## - - NOF_CHARS = 8 @@ -86,27 +82,24 @@ def __repr__(self): return _repr(self) -## --- SCOPE --- ## - - class ProjectScopeDTO(DisplayBase): project_id: UUID -## --- LIFECYCLE --- ## - - class LifecycleDTO(DisplayBase): created_at: datetime updated_at: Optional[datetime] = None updated_by_id: Optional[UUID] = None +### -------------------- ### +### services.shared.dtos ### +############################ + ################################### ### services.observability.dtos ### -################################### - +### --------------------------- ### from typing import List, Dict, Any, Union, Optional, Sequence @@ -114,8 +107,6 @@ class LifecycleDTO(DisplayBase): from datetime import datetime from uuid import UUID -## --- TIME --- ## - class TimeDTO(DisplayBase): start: datetime @@ -123,9 +114,6 @@ class TimeDTO(DisplayBase): span: int -## --- STATUS --- ## - - class StatusCode(Enum): UNSET = "UNSET" OK = "OK" @@ -138,18 +126,7 @@ class StatusDTO(DisplayBase): stacktrace: Optional[str] = None -## --- ATTRIBUTES --- ## - -AttributeValueType = Any # -""" -AttributeValueType = Union[ - str, - bool, - int, - float, - Sequence[Union[str, bool, int, float]], -] -""" +AttributeValueType = Any Attributes = Dict[str, AttributeValueType] @@ -161,9 +138,6 @@ class AttributesDTO(DisplayBase): semconv: Optional[Attributes] = None -## --- HIERARCHICAL STRUCTURE --- ## - - class TreeType(Enum): # --- VARIANTS --- # INVOCATION = "invocation" @@ -264,11 +238,8 @@ class OTelExtraDTO(DisplayBase): links: Optional[List[OTelLinkDTO]] = None -## --- ENTITIES --- ## - - -class SpanDTO(DisplayBase): # DBE - scope: ProjectScopeDTO # DBA +class SpanDTO(DisplayBase): + scope: ProjectScopeDTO lifecycle: LifecycleDTO @@ -294,8 +265,8 @@ class SpanDTO(DisplayBase): # DBE nodes: Optional[Dict[str, Union["SpanDTO", List["SpanDTO"]]]] = None -class SpanCreateDTO(DisplayBase): # DBE - scope: ProjectScopeDTO # DBA +class SpanCreateDTO(DisplayBase): + scope: ProjectScopeDTO root: RootDTO tree: TreeDTO @@ -335,12 +306,15 @@ class OTelSpanDTO(DisplayBase): parent: Optional[OTelContextDTO] = None links: Optional[List[OTelLinkDTO]] = None - -################################### -### services.observability.utils ## +### --------------------------- ### +### services.observability.dtos ### ################################### +#################################### +### services.observability.utils ### +### ---------------------------- ### + from typing import List, Dict, OrderedDict @@ -526,11 +500,14 @@ def _connect_tree_dfs( if len(parent_span.nodes) == 0: parent_span.nodes = None +### ---------------------------- ### +### services.observability.utils ### +#################################### + ######################################################## ### apis.fastapi.observability.opentelemetry.semconv ### -######################################################## - +### ------------------------------------------------ ### VERSION = "0.4.1" @@ -600,11 +577,14 @@ def _connect_tree_dfs( CODEX = {"maps": MAPS[VERSION], "keys": KEYS[VERSION]} +### ------------------------------------------------ ### +### apis.fastapi.observability.opentelemetry.semconv ### +######################################################## + ######################################## ### apis.fastapi.observability.utils ### -######################################## - +### -------------------------------- ### from typing import Optional, Union, Tuple, Any, List, Dict from uuid import UUID @@ -702,21 +682,6 @@ def _decode_key( return key.replace(f"ag.{namespace}.", "") -def _encode_value(value: Any) -> Optional[Any]: - if value is None: - return None - - if isinstance(value, (str, int, float, bool, bytes)): - return value - - if isinstance(value, dict) or isinstance(value, list): - encoded = dumps(value) - value = "@ag.type=json:" + encoded - return value - - return repr(value) - - def _decode_value( value: Any, ) -> Any: @@ -1025,14 +990,12 @@ def parse_to_agenta_span_dto( return span_dto +### -------------------------------- ### +### apis.fastapi.observability.utils ### +######################################## -################# -### THIS FILE ### -################# - - -from typing import Dict +from copy import deepcopy from opentelemetry.sdk.trace import ReadableSpan @@ -1040,62 +1003,84 @@ def parse_inline_trace( project_id: str, spans: Dict[str, ReadableSpan], ): - print("-----------------------------") - otel_span_dtos = _parse_readable_spans(spans) ############################################################ ### apis.fastapi.observability.api.otlp_collect_traces() ### - ############################################################ - - span_dtos = [ + ### ---------------------------------------------------- ### + span_dtos = [ parse_from_otel_span_dto(project_id, otel_span_dto) for otel_span_dto in otel_span_dtos ] + ### ---------------------------------------------------- ### + ### apis.fastapi.observability.api.otlp_collect_traces() ### + ############################################################ ##################################################### ### services.observability.service.ingest/query() ### - ##################################################### - + ### --------------------------------------------- ### span_idx = parse_span_dtos_to_span_idx(span_dtos) - span_id_tree = parse_span_idx_to_span_id_tree(span_idx) + ### --------------------------------------------- ### + ### services.observability.service.ingest/query() ### + ##################################################### ############################################### ### services.observability.service.ingest() ### - ############################################### - + ### --------------------------------------- ### cumulate_costs(span_id_tree, span_idx) - cumulate_tokens(span_id_tree, span_idx) - + ### --------------------------------------- ### + ### services.observability.service.ingest() ### + ############################################### + ############################################## ### services.observability.service.query() ### - ############################################## - + ### -------------------------------------- ### connect_children(span_id_tree, span_idx) - root_span_dtos = [span_dto for span_dto in span_idx.values()] + agenta_span_dtos = [parse_to_agenta_span_dto(span_dto) for span_dto in root_span_dtos] + ### -------------------------------------- ### + ### services.observability.service.query() ### + ############################################## - ############################################################ - ### apis.fastapi.observability.api.query() ### - ############################################################ + LEGACY = True + inline_trace = None - agenta_span_dtos = [parse_to_agenta_span_dto(span_dto) for span_dto in root_span_dtos] + if LEGACY: + legacy_spans = [_parse_to_legacy_span(span_dto) for span_dto in span_idx.values()] - ################# - ### THIS FILE ### - ################# + root_span = root_span_dtos[0] - _spans = [ - loads(span_dto.model_dump_json(exclude_none=True)) for span_dto in agenta_span_dtos - ] + trace_id = root_span.root.id.hex + latency = root_span.time.span + cost = root_span.metrics.get("acc", {}).get("costs", {}).get("total", 0.0) + tokens = { + "prompt_tokens": root_span.metrics.get("acc", {}).get("tokens", {}).get("prompt", 0), + "completion_tokens": root_span.metrics.get("acc", {}).get("tokens", {}).get("completion", 0), + "total_tokens": root_span.metrics.get("acc", {}).get("tokens", {}).get("total", 0), + } - [print(_span) for _span in _spans] + spans = [ + loads(span.model_dump_json(exclude_none=True)) for span in legacy_spans + ] - print("-----------------------------") + inline_trace = { + "trace_id": trace_id, + "latency": latency, + "cost": cost, + "tokens": tokens, + "spans": spans, + } + + else: + spans = [ + loads(span_dto.model_dump_json(exclude_none=True)) for span_dto in agenta_span_dtos + ] + + inline_trace = spans # turn into Agenta Model ? - return _spans + return inline_trace def _parse_readable_spans( @@ -1164,3 +1149,82 @@ def _timestamp_ns_to_datetime(timestamp_ns): ) return _datetime + + +class LlmTokens(BaseModel): + prompt_tokens: Optional[int] = 0 + completion_tokens: Optional[int] = 0 + total_tokens: Optional[int] = 0 + + +class CreateSpan(BaseModel): + id: str + app_id: str + variant_id: Optional[str] = None + variant_name: Optional[str] = None + inputs: Optional[Dict[str, Optional[Any]]] = None + internals: Optional[Dict[str, Optional[Any]]] = None + outputs: Optional[Union[str, Dict[str, Optional[Any]], List[Any]]] = None + config: Optional[Dict[str, Optional[Any]]] = None + environment: Optional[str] = None + tags: Optional[List[str]] = None + token_consumption: Optional[int] = None + name: str + parent_span_id: Optional[str] = None + attributes: Optional[Dict[str, Optional[Any]]] = None + spankind: str + status: str + user: Optional[str] = None + start_time: datetime + end_time: datetime + tokens: Optional[LlmTokens] = None + cost: Optional[float] = None + + +def _parse_to_legacy_span(span: SpanDTO) -> CreateSpan: + attributes = None + if span.otel: + attributes = span.otel.attributes + + for event in span.otel.events: + if event.name == "exception": + attributes.update(**event.attributes) + + legacy_span = CreateSpan( + id=span.node.id.hex, + spankind=span.node.type, + name=span.node.name, + # + status=span.status.code.name, + # + start_time=span.time.start, + end_time=span.time.end, + # + parent_span_id=span.parent.id.hex if span.parent else None, + # + inputs=span.data.get("inputs") if span.data else {}, + internals=span.data.get("internals") if span.data else {}, + outputs=span.data.get("outputs") if span.data else {}, + # + environment=span.meta.get("environment") if span.meta else None, + config=span.meta.get("configuration") if span.meta else None, + # + tokens=LlmTokens( + prompt_tokens=span.metrics.get("acc", {}).get("tokens", {}).get("prompt", 0.0), + completion_tokens=span.metrics.get("acc", {}).get("tokens", {}).get("completion", 0.0), + total_tokens=span.metrics.get("acc", {}).get("tokens", {}).get("total", 0.0), + ) if span.metrics else None, + cost=span.metrics.get("acc", {}).get("costs", {}).get("total", 0.0) if span.metrics else None, + # + app_id=span.refs.get("application", {}).get("id", "missing-app-id") if span.refs else "missing-app-id", + # + attributes=attributes, + # + variant_id=None, + variant_name=None, + tags=None, + token_consumption=None, + user=None, + ) + + return legacy_span diff --git a/agenta-cli/agenta/sdk/tracing/processors.py b/agenta-cli/agenta/sdk/tracing/processors.py index 4ef860d589..d40c3baa95 100644 --- a/agenta-cli/agenta/sdk/tracing/processors.py +++ b/agenta-cli/agenta/sdk/tracing/processors.py @@ -17,6 +17,7 @@ class TraceProcessor(BatchSpanProcessor): def __init__( self, span_exporter: SpanExporter, + references: Dict[str, str] = None, max_queue_size: int = None, schedule_delay_millis: float = None, max_export_batch_size: int = None, @@ -32,10 +33,14 @@ def __init__( self._registry = dict() self._exporter = span_exporter + self.references = references or dict() def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None: # ADD LINKS FROM CONTEXT, HERE + for key in self.references.keys(): + span.set_attribute(f"ag.refs.{key}", self.references[key]) + if span.context.trace_id not in self._registry: self._registry[span.context.trace_id] = dict() diff --git a/agenta-cli/agenta/sdk/tracing/tracing.py b/agenta-cli/agenta/sdk/tracing/tracing.py index db0bc691b3..61681b2bc3 100644 --- a/agenta-cli/agenta/sdk/tracing/tracing.py +++ b/agenta-cli/agenta/sdk/tracing/tracing.py @@ -42,6 +42,8 @@ def __init__( self.api_key: Optional[str] = None # HEADERS (OTLP) self.headers: Dict[str, str] = dict() + # REFERENCES + self.references: Dict[str, str] = dict() # TRACER PROVIDER self.tracer_provider: Optional[TracerProvider] = None @@ -57,7 +59,7 @@ def configure( project_id: Optional[str] = None, api_key: Optional[str] = None, # - **kwargs, + app_id: Optional[str] = None, ): # AUTHENTICATION (OTLP) # self.project_id = project_id @@ -69,19 +71,23 @@ def configure( if api_key: # self.headers.update(**{"Authorization": f"Api-Key {self.api_key}"}) self.headers.update(**{"Authorization": self.api_key}) + # REFERENCES + self.references = {"application.id": app_id} # TRACER PROVIDER self.tracer_provider = TracerProvider( resource=Resource(attributes={"service.name": "agenta-sdk"}) ) # TRACE PROCESSORS -- CONSOLE - _console = TraceProcessor(ConsoleExporter()) - self.tracer_provider.add_span_processor(_console) + # _console = TraceProcessor( + # ConsoleExporter(), + # references=self.references, + # ) + # self.tracer_provider.add_span_processor(_console) # TRACE PROCESSORS -- INLINE self.inline = TraceProcessor( - InlineExporter( - registry=self.inline_spans, - ) + InlineExporter(registry=self.inline_spans), + references=self.references, ) self.tracer_provider.add_span_processor(self.inline) # TRACE PROCESSORS -- OTLP @@ -93,10 +99,8 @@ def configure( log.info(f"Connection established.") _otlp = TraceProcessor( - OTLPExporter( - endpoint=self.otlp_url, - headers=self.headers, - ) + OTLPExporter(endpoint=self.otlp_url, headers=self.headers), + references=self.references, ) self.tracer_provider.add_span_processor(_otlp) @@ -155,14 +159,9 @@ def get_inline_trace( if not otel_spans: return {} - agenta_spans = parse_inline_trace(self.project_id, otel_spans) - - agenta_spans = { - "trace_id": trace_id, - "spans": [span for span in agenta_spans], - } + inline_trace = parse_inline_trace(self.project_id, otel_spans) - return agenta_spans + return inline_trace def get_tracer(tracing: Tracing) -> Tracer: From 11cb9aabca246f2a92980c424aed7286306e500d Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 9 Oct 2024 12:30:24 +0200 Subject: [PATCH 052/305] SDK bump prerelease --- agenta-cli/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index e21e98d233..9ce622bba5 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.24.6a1" +version = "0.25.3a1" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] From c88757773ea49395ebcaa11b837173697b60b318 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 9 Oct 2024 13:02:41 +0200 Subject: [PATCH 053/305] Fix nested foramtted strings --- agenta-cli/agenta/sdk/tracing/inline.py | 97 ++++++++++++++++++------- 1 file changed, 71 insertions(+), 26 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py index b65efb245d..a833998ed9 100644 --- a/agenta-cli/agenta/sdk/tracing/inline.py +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -60,7 +60,7 @@ def _p_ora(o, open="{", close="}", sep=": ", foo=repr): return f"[{', '.join([repr(el) for el in o])}]" elif isinstance(o, dict): o = OrderedDict(sorted(o.items())) - return f"{open}{', '.join([f"{foo(elk)}{sep}{_p_ora(elv)}" for elk, elv in o.items()])}{close}" + return f"{open}{', '.join([f'{foo(elk)}{sep}{_p_ora(elv)}' for elk, elv in o.items()])}{close}" else: if o is not None: return repr(o) @@ -92,6 +92,7 @@ class LifecycleDTO(DisplayBase): updated_by_id: Optional[UUID] = None + ### -------------------- ### ### services.shared.dtos ### ############################ @@ -306,6 +307,7 @@ class OTelSpanDTO(DisplayBase): parent: Optional[OTelContextDTO] = None links: Optional[List[OTelLinkDTO]] = None + ### --------------------------- ### ### services.observability.dtos ### ################################### @@ -429,9 +431,15 @@ def _set(span: SpanCreateDTO, tokens: dict): if tokens.get("prompt", 0.0) != 0.0: span.metrics["acc.tokens.prompt"] = tokens.get("prompt", 0.0) if tokens.get("completion", 0.0) != 0.0: - span.metrics["acc.tokens.completion"] = tokens.get("completion", 0.0) if tokens.get("completion", 0.0) != 0.0 else None + span.metrics["acc.tokens.completion"] = ( + tokens.get("completion", 0.0) + if tokens.get("completion", 0.0) != 0.0 + else None + ) if tokens.get("total", 0.0) != 0.0: - span.metrics["acc.tokens.total"] = tokens.get("total", 0.0) if tokens.get("total", 0.0) != 0.0 else None + span.metrics["acc.tokens.total"] = ( + tokens.get("total", 0.0) if tokens.get("total", 0.0) != 0.0 else None + ) _cumulate_tree_dfs(spans_id_tree, spans_idx, _get_unit, _get_acc, _acc, _set) @@ -500,6 +508,7 @@ def _connect_tree_dfs( if len(parent_span.nodes) == 0: parent_span.nodes = None + ### ---------------------------- ### ### services.observability.utils ### #################################### @@ -990,6 +999,7 @@ def parse_to_agenta_span_dto( return span_dto + ### -------------------------------- ### ### apis.fastapi.observability.utils ### ######################################## @@ -1008,7 +1018,7 @@ def parse_inline_trace( ############################################################ ### apis.fastapi.observability.api.otlp_collect_traces() ### ### ---------------------------------------------------- ### - span_dtos = [ + span_dtos = [ parse_from_otel_span_dto(project_id, otel_span_dto) for otel_span_dto in otel_span_dtos ] @@ -1033,13 +1043,15 @@ def parse_inline_trace( ### --------------------------------------- ### ### services.observability.service.ingest() ### ############################################### - + ############################################## ### services.observability.service.query() ### ### -------------------------------------- ### connect_children(span_id_tree, span_idx) root_span_dtos = [span_dto for span_dto in span_idx.values()] - agenta_span_dtos = [parse_to_agenta_span_dto(span_dto) for span_dto in root_span_dtos] + agenta_span_dtos = [ + parse_to_agenta_span_dto(span_dto) for span_dto in root_span_dtos + ] ### -------------------------------------- ### ### services.observability.service.query() ### ############################################## @@ -1048,7 +1060,9 @@ def parse_inline_trace( inline_trace = None if LEGACY: - legacy_spans = [_parse_to_legacy_span(span_dto) for span_dto in span_idx.values()] + legacy_spans = [ + _parse_to_legacy_span(span_dto) for span_dto in span_idx.values() + ] root_span = root_span_dtos[0] @@ -1056,9 +1070,15 @@ def parse_inline_trace( latency = root_span.time.span cost = root_span.metrics.get("acc", {}).get("costs", {}).get("total", 0.0) tokens = { - "prompt_tokens": root_span.metrics.get("acc", {}).get("tokens", {}).get("prompt", 0), - "completion_tokens": root_span.metrics.get("acc", {}).get("tokens", {}).get("completion", 0), - "total_tokens": root_span.metrics.get("acc", {}).get("tokens", {}).get("total", 0), + "prompt_tokens": root_span.metrics.get("acc", {}) + .get("tokens", {}) + .get("prompt", 0), + "completion_tokens": root_span.metrics.get("acc", {}) + .get("tokens", {}) + .get("completion", 0), + "total_tokens": root_span.metrics.get("acc", {}) + .get("tokens", {}) + .get("total", 0), } spans = [ @@ -1072,13 +1092,14 @@ def parse_inline_trace( "tokens": tokens, "spans": spans, } - + else: spans = [ - loads(span_dto.model_dump_json(exclude_none=True)) for span_dto in agenta_span_dtos + loads(span_dto.model_dump_json(exclude_none=True)) + for span_dto in agenta_span_dtos ] - inline_trace = spans # turn into Agenta Model ? + inline_trace = spans # turn into Agenta Model ? return inline_trace @@ -1095,7 +1116,10 @@ def _parse_readable_spans( span_id=_int_to_hex(span.get_span_context().span_id, 64), ), name=span.name, - kind=OTelSpanKind("SPAN_KIND_" + (span.kind if isinstance(span.kind, str) else span.kind.name)), + kind=OTelSpanKind( + "SPAN_KIND_" + + (span.kind if isinstance(span.kind, str) else span.kind.name) + ), start_time=_timestamp_ns_to_datetime(span.start_time), end_time=_timestamp_ns_to_datetime(span.end_time), status_code=OTelStatusCode("STATUS_CODE_" + span.status.status_code.name), @@ -1109,10 +1133,14 @@ def _parse_readable_spans( ) for event in span.events ], - parent=OTelContextDTO( - trace_id=_int_to_hex(span.parent.trace_id, 128), - span_id=_int_to_hex(span.parent.span_id, 64) - ) if span.parent else None, + parent=( + OTelContextDTO( + trace_id=_int_to_hex(span.parent.trace_id, 128), + span_id=_int_to_hex(span.parent.span_id, 64), + ) + if span.parent + else None + ), links=[ OTelLinkDTO( context=OTelContextDTO( @@ -1125,7 +1153,6 @@ def _parse_readable_spans( ], ) - otel_span_dtos.append(otel_span_dto) return otel_span_dtos @@ -1209,14 +1236,32 @@ def _parse_to_legacy_span(span: SpanDTO) -> CreateSpan: environment=span.meta.get("environment") if span.meta else None, config=span.meta.get("configuration") if span.meta else None, # - tokens=LlmTokens( - prompt_tokens=span.metrics.get("acc", {}).get("tokens", {}).get("prompt", 0.0), - completion_tokens=span.metrics.get("acc", {}).get("tokens", {}).get("completion", 0.0), - total_tokens=span.metrics.get("acc", {}).get("tokens", {}).get("total", 0.0), - ) if span.metrics else None, - cost=span.metrics.get("acc", {}).get("costs", {}).get("total", 0.0) if span.metrics else None, + tokens=( + LlmTokens( + prompt_tokens=span.metrics.get("acc", {}) + .get("tokens", {}) + .get("prompt", 0.0), + completion_tokens=span.metrics.get("acc", {}) + .get("tokens", {}) + .get("completion", 0.0), + total_tokens=span.metrics.get("acc", {}) + .get("tokens", {}) + .get("total", 0.0), + ) + if span.metrics + else None + ), + cost=( + span.metrics.get("acc", {}).get("costs", {}).get("total", 0.0) + if span.metrics + else None + ), # - app_id=span.refs.get("application", {}).get("id", "missing-app-id") if span.refs else "missing-app-id", + app_id=( + span.refs.get("application", {}).get("id", "missing-app-id") + if span.refs + else "missing-app-id" + ), # attributes=attributes, # From bb737a3888a89d2216f25c8bbaaa45cf54fb5025 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Wed, 9 Oct 2024 19:28:44 +0200 Subject: [PATCH 054/305] feat(frontend): propagated Trace header with mock data and implement next and prev button func --- .../observability/drawer/TraceHeader.tsx | 51 ++++++++++++-- .../apps/[app_id]/observability/index.tsx | 67 +++++++++++++++---- 2 files changed, 99 insertions(+), 19 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx b/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx index eda999bddc..af9067def9 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx @@ -1,10 +1,18 @@ import {JSSTheme} from "@/lib/Types" +import {AgentaRootsDTO} from "@/services/observability/types" import {DeleteOutlined} from "@ant-design/icons" import {CaretDown, CaretUp} from "@phosphor-icons/react" import {Button, Space, Tag, Typography} from "antd" -import React from "react" +import React, {useMemo} from "react" import {createUseStyles} from "react-jss" +interface TraceHeaderProps { + activeTrace: AgentaRootsDTO + selectedTraceId: string + traces: AgentaRootsDTO[] + setSelectedTraceId: (val: string) => void +} + const useStyles = createUseStyles((theme: JSSTheme) => ({ title: { fontSize: theme.fontSizeHeading5, @@ -13,20 +21,53 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ }, })) -const TraceHeader = () => { +const TraceHeader = ({ + activeTrace, + selectedTraceId, + traces, + setSelectedTraceId, +}: TraceHeaderProps) => { const classes = useStyles() + const activeTraceIndex = useMemo(() => { + return traces?.findIndex((item) => item.root.id === selectedTraceId) + }, [selectedTraceId, traces]) + + const handleNextTrace = () => { + if (traces && activeTraceIndex !== undefined && activeTraceIndex < traces.length - 1) { + setSelectedTraceId(traces[activeTraceIndex + 1].root.id) + } + } + + const handlePrevTrace = () => { + if (traces && activeTraceIndex !== undefined && activeTraceIndex > 0) { + setSelectedTraceId(traces[activeTraceIndex - 1].root.id) + } + } + return (
-
Trace - # 66c5b76d6165af12bab569e9 + # {activeTrace.root.id}
- - setSelectedTraceId("")} - expandable - headerExtra={} - mainContent={} - sideContent={} - /> + {traces && ( + ({ + onClick: () => { + setSelectedTraceId(record.root.id) + }, + })} + /> + )} + + {activeTrace && traces?.length && ( + setSelectedTraceId("")} + expandable + headerExtra={ + + } + mainContent={} + sideContent={} + /> + )} ) } From 75d8dd1a4668bb900f76a48cc751dc22e24bab26 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 10 Oct 2024 10:30:35 +0200 Subject: [PATCH 055/305] fix poetry --- agenta-backend/poetry.lock | 2679 ++++++++++++++++++++---------------- 1 file changed, 1478 insertions(+), 1201 deletions(-) diff --git a/agenta-backend/poetry.lock b/agenta-backend/poetry.lock index 1d26287807..72c0a5c0b3 100644 --- a/agenta-backend/poetry.lock +++ b/agenta-backend/poetry.lock @@ -56,115 +56,146 @@ aiohttp = ">=3.6" typing-extensions = ">=3.6.5" [[package]] -name = "aiohttp" -version = "3.9.5" -description = "Async http client/server framework (asyncio)" +name = "aiohappyeyeballs" +version = "2.4.3" +description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"}, - {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"}, - {file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"}, - {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"}, - {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"}, - {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"}, - {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"}, - {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"}, - {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"}, - {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"}, - {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"}, - {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"}, - {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"}, - {file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"}, - {file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"}, - {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"}, - {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"}, - {file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"}, - {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"}, - {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"}, - {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"}, - {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"}, - {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"}, - {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"}, - {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"}, - {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"}, - {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"}, - {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"}, - {file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"}, - {file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"}, - {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"}, - {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"}, - {file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"}, - {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"}, - {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"}, - {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"}, - {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"}, - {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"}, - {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"}, - {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"}, - {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"}, - {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"}, - {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"}, - {file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"}, - {file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"}, - {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"}, - {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"}, - {file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"}, - {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"}, - {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"}, - {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"}, - {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"}, - {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"}, - {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"}, - {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"}, - {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"}, - {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"}, - {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"}, - {file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"}, - {file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"}, - {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"}, - {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"}, - {file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"}, - {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"}, - {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"}, - {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"}, - {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"}, - {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"}, - {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"}, - {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"}, - {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"}, - {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"}, - {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"}, - {file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"}, - {file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"}, - {file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"}, + {file = "aiohappyeyeballs-2.4.3-py3-none-any.whl", hash = "sha256:8a7a83727b2756f394ab2895ea0765a0a8c475e3c71e98d43d76f22b4b435572"}, + {file = "aiohappyeyeballs-2.4.3.tar.gz", hash = "sha256:75cf88a15106a5002a8eb1dab212525c00d1f4c0fa96e551c9fbe6f09a621586"}, ] -[package.dependencies] +[[package]] +name = "aiohttp" +version = "3.10.9" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.10.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8b3fb28a9ac8f2558760d8e637dbf27aef1e8b7f1d221e8669a1074d1a266bb2"}, + {file = "aiohttp-3.10.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91aa966858593f64c8a65cdefa3d6dc8fe3c2768b159da84c1ddbbb2c01ab4ef"}, + {file = "aiohttp-3.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63649309da83277f06a15bbdc2a54fbe75efb92caa2c25bb57ca37762789c746"}, + {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3e7fabedb3fe06933f47f1538df7b3a8d78e13d7167195f51ca47ee12690373"}, + {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c070430fda1a550a1c3a4c2d7281d3b8cfc0c6715f616e40e3332201a253067"}, + {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:51d0a4901b27272ae54e42067bc4b9a90e619a690b4dc43ea5950eb3070afc32"}, + {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fec5fac7aea6c060f317f07494961236434928e6f4374e170ef50b3001e14581"}, + {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:172ad884bb61ad31ed7beed8be776eb17e7fb423f1c1be836d5cb357a096bf12"}, + {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d646fdd74c25bbdd4a055414f0fe32896c400f38ffbdfc78c68e62812a9e0257"}, + {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e86260b76786c28acf0b5fe31c8dca4c2add95098c709b11e8c35b424ebd4f5b"}, + {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d7cafc11d70fdd8801abfc2ff276744ae4cb39d8060b6b542c7e44e5f2cfc2"}, + {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fc262c3df78c8ff6020c782d9ce02e4bcffe4900ad71c0ecdad59943cba54442"}, + {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:482c85cf3d429844396d939b22bc2a03849cb9ad33344689ad1c85697bcba33a"}, + {file = "aiohttp-3.10.9-cp310-cp310-win32.whl", hash = "sha256:aeebd3061f6f1747c011e1d0b0b5f04f9f54ad1a2ca183e687e7277bef2e0da2"}, + {file = "aiohttp-3.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:fa430b871220dc62572cef9c69b41e0d70fcb9d486a4a207a5de4c1f25d82593"}, + {file = "aiohttp-3.10.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:16e6a51d8bc96b77f04a6764b4ad03eeef43baa32014fce71e882bd71302c7e4"}, + {file = "aiohttp-3.10.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8bd9125dd0cc8ebd84bff2be64b10fdba7dc6fd7be431b5eaf67723557de3a31"}, + {file = "aiohttp-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dcf354661f54e6a49193d0b5653a1b011ba856e0b7a76bda2c33e4c6892f34ea"}, + {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42775de0ca04f90c10c5c46291535ec08e9bcc4756f1b48f02a0657febe89b10"}, + {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87d1e4185c5d7187684d41ebb50c9aeaaaa06ca1875f4c57593071b0409d2444"}, + {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2695c61cf53a5d4345a43d689f37fc0f6d3a2dc520660aec27ec0f06288d1f9"}, + {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a3f063b41cc06e8d0b3fcbbfc9c05b7420f41287e0cd4f75ce0a1f3d80729e6"}, + {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d37f4718002863b82c6f391c8efd4d3a817da37030a29e2682a94d2716209de"}, + {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2746d8994ebca1bdc55a1e998feff4e94222da709623bb18f6e5cfec8ec01baf"}, + {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6f3c6648aa123bcd73d6f26607d59967b607b0da8ffcc27d418a4b59f4c98c7c"}, + {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:558b3d223fd631ad134d89adea876e7fdb4c93c849ef195049c063ada82b7d08"}, + {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4e6cb75f8ddd9c2132d00bc03c9716add57f4beff1263463724f6398b813e7eb"}, + {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:608cecd8d58d285bfd52dbca5b6251ca8d6ea567022c8a0eaae03c2589cd9af9"}, + {file = "aiohttp-3.10.9-cp311-cp311-win32.whl", hash = "sha256:36d4fba838be5f083f5490ddd281813b44d69685db910907636bc5dca6322316"}, + {file = "aiohttp-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:8be1a65487bdfc285bd5e9baf3208c2132ca92a9b4020e9f27df1b16fab998a9"}, + {file = "aiohttp-3.10.9-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4fd16b30567c5b8e167923be6e027eeae0f20cf2b8a26b98a25115f28ad48ee0"}, + {file = "aiohttp-3.10.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:40ff5b7660f903dc587ed36ef08a88d46840182d9d4b5694e7607877ced698a1"}, + {file = "aiohttp-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4edc3fd701e2b9a0d605a7b23d3de4ad23137d23fc0dbab726aa71d92f11aaaf"}, + {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e525b69ee8a92c146ae5b4da9ecd15e518df4d40003b01b454ad694a27f498b5"}, + {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5002a02c17fcfd796d20bac719981d2fca9c006aac0797eb8f430a58e9d12431"}, + {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd4ceeae2fb8cabdd1b71c82bfdd39662473d3433ec95b962200e9e752fb70d0"}, + {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6e395c3d1f773cf0651cd3559e25182eb0c03a2777b53b4575d8adc1149c6e9"}, + {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbdb8def5268f3f9cd753a265756f49228a20ed14a480d151df727808b4531dd"}, + {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f82ace0ec57c94aaf5b0e118d4366cff5889097412c75aa14b4fd5fc0c44ee3e"}, + {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6ebdc3b3714afe1b134b3bbeb5f745eed3ecbcff92ab25d80e4ef299e83a5465"}, + {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f9ca09414003c0e96a735daa1f071f7d7ed06962ef4fa29ceb6c80d06696d900"}, + {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1298b854fd31d0567cbb916091be9d3278168064fca88e70b8468875ef9ff7e7"}, + {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:60ad5b8a7452c0f5645c73d4dad7490afd6119d453d302cd5b72b678a85d6044"}, + {file = "aiohttp-3.10.9-cp312-cp312-win32.whl", hash = "sha256:1a0ee6c0d590c917f1b9629371fce5f3d3f22c317aa96fbdcce3260754d7ea21"}, + {file = "aiohttp-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:c46131c6112b534b178d4e002abe450a0a29840b61413ac25243f1291613806a"}, + {file = "aiohttp-3.10.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2bd9f3eac515c16c4360a6a00c38119333901b8590fe93c3257a9b536026594d"}, + {file = "aiohttp-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8cc0d13b4e3b1362d424ce3f4e8c79e1f7247a00d792823ffd640878abf28e56"}, + {file = "aiohttp-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ba1a599255ad6a41022e261e31bc2f6f9355a419575b391f9655c4d9e5df5ff5"}, + {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:776e9f3c9b377fcf097c4a04b241b15691e6662d850168642ff976780609303c"}, + {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8debb45545ad95b58cc16c3c1cc19ad82cffcb106db12b437885dbee265f0ab5"}, + {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2555e4949c8d8782f18ef20e9d39730d2656e218a6f1a21a4c4c0b56546a02e"}, + {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c54dc329cd44f7f7883a9f4baaefe686e8b9662e2c6c184ea15cceee587d8d69"}, + {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e709d6ac598c5416f879bb1bae3fd751366120ac3fa235a01de763537385d036"}, + {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:17c272cfe7b07a5bb0c6ad3f234e0c336fb53f3bf17840f66bd77b5815ab3d16"}, + {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c21c82df33b264216abffff9f8370f303dab65d8eee3767efbbd2734363f677"}, + {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9331dd34145ff105177855017920dde140b447049cd62bb589de320fd6ddd582"}, + {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ac3196952c673822ebed8871cf8802e17254fff2a2ed4835d9c045d9b88c5ec7"}, + {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2c33fa6e10bb7ed262e3ff03cc69d52869514f16558db0626a7c5c61dde3c29f"}, + {file = "aiohttp-3.10.9-cp313-cp313-win32.whl", hash = "sha256:a14e4b672c257a6b94fe934ee62666bacbc8e45b7876f9dd9502d0f0fe69db16"}, + {file = "aiohttp-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:a35ed3d03910785f7d9d6f5381f0c24002b2b888b298e6f941b2fc94c5055fcd"}, + {file = "aiohttp-3.10.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f392ef50e22c31fa49b5a46af7f983fa3f118f3eccb8522063bee8bfa6755f8"}, + {file = "aiohttp-3.10.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d1f5c9169e26db6a61276008582d945405b8316aae2bb198220466e68114a0f5"}, + {file = "aiohttp-3.10.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8d9d10d10ec27c0d46ddaecc3c5598c4db9ce4e6398ca872cdde0525765caa2f"}, + {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d97273a52d7f89a75b11ec386f786d3da7723d7efae3034b4dda79f6f093edc1"}, + {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d271f770b52e32236d945911b2082f9318e90ff835d45224fa9e28374303f729"}, + {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7003f33f5f7da1eb02f0446b0f8d2ccf57d253ca6c2e7a5732d25889da82b517"}, + {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6e00c8a92e7663ed2be6fcc08a2997ff06ce73c8080cd0df10cc0321a3168d7"}, + {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a61df62966ce6507aafab24e124e0c3a1cfbe23c59732987fc0fd0d71daa0b88"}, + {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:60555211a006d26e1a389222e3fab8cd379f28e0fbf7472ee55b16c6c529e3a6"}, + {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d15a29424e96fad56dc2f3abed10a89c50c099f97d2416520c7a543e8fddf066"}, + {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:a19caae0d670771ea7854ca30df76f676eb47e0fd9b2ee4392d44708f272122d"}, + {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:99f9678bf0e2b1b695e8028fedac24ab6770937932eda695815d5a6618c37e04"}, + {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2914caa46054f3b5ff910468d686742ff8cff54b8a67319d75f5d5945fd0a13d"}, + {file = "aiohttp-3.10.9-cp38-cp38-win32.whl", hash = "sha256:0bc059ecbce835630e635879f5f480a742e130d9821fbe3d2f76610a6698ee25"}, + {file = "aiohttp-3.10.9-cp38-cp38-win_amd64.whl", hash = "sha256:e883b61b75ca6efc2541fcd52a5c8ccfe288b24d97e20ac08fdf343b8ac672ea"}, + {file = "aiohttp-3.10.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fcd546782d03181b0b1d20b43d612429a90a68779659ba8045114b867971ab71"}, + {file = "aiohttp-3.10.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:85711eec2d875cd88c7eb40e734c4ca6d9ae477d6f26bd2b5bb4f7f60e41b156"}, + {file = "aiohttp-3.10.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:02d1d6610588bcd743fae827bd6f2e47e0d09b346f230824b4c6fb85c6065f9c"}, + {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3668d0c2a4d23fb136a753eba42caa2c0abbd3d9c5c87ee150a716a16c6deec1"}, + {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7c071235a47d407b0e93aa6262b49422dbe48d7d8566e1158fecc91043dd948"}, + {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac74e794e3aee92ae8f571bfeaa103a141e409863a100ab63a253b1c53b707eb"}, + {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bbf94d4a0447705b7775417ca8bb8086cc5482023a6e17cdc8f96d0b1b5aba6"}, + {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb0b2d5d51f96b6cc19e6ab46a7b684be23240426ae951dcdac9639ab111b45e"}, + {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e83dfefb4f7d285c2d6a07a22268344a97d61579b3e0dce482a5be0251d672ab"}, + {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f0a44bb40b6aaa4fb9a5c1ee07880570ecda2065433a96ccff409c9c20c1624a"}, + {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c2b627d3c8982691b06d89d31093cee158c30629fdfebe705a91814d49b554f8"}, + {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:03690541e4cc866eef79626cfa1ef4dd729c5c1408600c8cb9e12e1137eed6ab"}, + {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad3675c126f2a95bde637d162f8231cff6bc0bc9fbe31bd78075f9ff7921e322"}, + {file = "aiohttp-3.10.9-cp39-cp39-win32.whl", hash = "sha256:1321658f12b6caffafdc35cfba6c882cb014af86bef4e78c125e7e794dfb927b"}, + {file = "aiohttp-3.10.9-cp39-cp39-win_amd64.whl", hash = "sha256:9fdf5c839bf95fc67be5794c780419edb0dbef776edcfc6c2e5e2ffd5ee755fa"}, + {file = "aiohttp-3.10.9.tar.gz", hash = "sha256:143b0026a9dab07a05ad2dd9e46aa859bffdd6348ddc5967b42161168c24f857"}, +] + +[package.dependencies] +aiohappyeyeballs = ">=2.3.0" aiosignal = ">=1.1.2" async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" -yarl = ">=1.0,<2.0" +yarl = ">=1.12.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns", "brotlicffi"] +speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] [[package]] name = "aioitertools" -version = "0.11.0" +version = "0.12.0" description = "itertools and builtins for AsyncIO and mixed iterables" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "aioitertools-0.11.0-py3-none-any.whl", hash = "sha256:04b95e3dab25b449def24d7df809411c10e62aab0cbe31a50ca4e68748c43394"}, - {file = "aioitertools-0.11.0.tar.gz", hash = "sha256:42c68b8dd3a69c2bf7f2233bf7df4bb58b557bca5252ac02ed5187bbc67d6831"}, + {file = "aioitertools-0.12.0-py3-none-any.whl", hash = "sha256:fc1f5fac3d737354de8831cbba3eb04f79dd649d8f3afb4c5b114925e662a796"}, + {file = "aioitertools-0.12.0.tar.gz", hash = "sha256:c2a9055b4fbb7705f561b9d86053e8af5d10cc845d22c32008c43490b2d8dd6b"}, ] [package.dependencies] typing_extensions = {version = ">=4.0", markers = "python_version < \"3.10\""} +[package.extras] +dev = ["attribution (==1.8.0)", "black (==24.8.0)", "build (>=1.2)", "coverage (==7.6.1)", "flake8 (==7.1.1)", "flit (==3.9.0)", "mypy (==1.11.2)", "ufmt (==2.7.1)", "usort (==1.0.8.post1)"] +docs = ["sphinx (==8.0.2)", "sphinx-mdinclude (==0.6.2)"] + [[package]] name = "aiosignal" version = "1.3.1" @@ -196,13 +227,13 @@ uvloop = ["uvloop (>=0.18)"] [[package]] name = "alembic" -version = "1.13.2" +version = "1.13.3" description = "A database migration tool for SQLAlchemy." optional = false python-versions = ">=3.8" files = [ - {file = "alembic-1.13.2-py3-none-any.whl", hash = "sha256:6b8733129a6224a9a711e17c99b08462dbf7cc9670ba8f2e2ae9af860ceb1953"}, - {file = "alembic-1.13.2.tar.gz", hash = "sha256:1ff0ae32975f4fd96028c39ed9bb3c867fe3af956bd7bb37343b54c9fe7445ef"}, + {file = "alembic-1.13.3-py3-none-any.whl", hash = "sha256:908e905976d15235fae59c9ac42c4c5b75cfcefe3d27c0fbf7ae15a37715d80e"}, + {file = "alembic-1.13.3.tar.gz", hash = "sha256:203503117415561e203aa14541740643a611f641517f0209fcae63e9fa09f1a2"}, ] [package.dependencies] @@ -360,22 +391,22 @@ test = ["flake8 (>=6.1,<7.0)", "uvloop (>=0.15.3)"] [[package]] name = "attrs" -version = "23.2.0" +version = "24.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, - {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, ] [package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] -tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "autoevals" @@ -414,13 +445,13 @@ files = [ [[package]] name = "beanie" -version = "1.26.0" +version = "1.27.0" description = "Asynchronous Python ODM for MongoDB" optional = false python-versions = "<4.0,>=3.7" files = [ - {file = "beanie-1.26.0-py3-none-any.whl", hash = "sha256:b45926c01d4a899c519c665c2a5f230990717e99f7fd68172a389ca33e7693b9"}, - {file = "beanie-1.26.0.tar.gz", hash = "sha256:54016f4ec71ed0ea6ce0c7946a395090c45687f254dbbe1cf06eec608383f790"}, + {file = "beanie-1.27.0-py3-none-any.whl", hash = "sha256:2cc6762bdd59b9040dd004ecbc7d4fd5ddd22e52743915e38d1f0f92f276bcaf"}, + {file = "beanie-1.27.0.tar.gz", hash = "sha256:a5eee40f1e52214afeb8558c0823d7504856884770c3d56fc3cd5765efb87314"}, ] [package.dependencies] @@ -429,22 +460,29 @@ lazy-model = "0.2.0" motor = ">=2.5.0,<4.0.0" pydantic = ">=1.10,<3.0" toml = "*" -typing-extensions = {version = ">=4.7", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.7" [package.extras] +aws = ["motor[aws] (>=2.5.0,<4.0.0)"] +ci = ["requests", "toml", "types-requests"] doc = ["Markdown (>=3.3)", "Pygments (>=2.8.0)", "jinja2 (>=3.0.3)", "mkdocs (>=1.4)", "mkdocs-material (>=9.0)", "pydoc-markdown (>=4.8)"] +encryption = ["motor[encryption] (>=2.5.0,<4.0.0)"] +gssapi = ["motor[gssapi] (>=2.5.0,<4.0.0)"] +ocsp = ["motor[ocsp] (>=2.5.0,<4.0.0)"] queue = ["beanie-batteries-queue (>=0.2)"] +snappy = ["motor[snappy] (>=2.5.0,<4.0.0)"] test = ["asgi-lifespan (>=1.0.1)", "dnspython (>=2.1.0)", "fastapi (>=0.100)", "flake8 (>=3)", "httpx (>=0.23.0)", "pre-commit (>=2.3.0)", "pydantic-extra-types (>=2)", "pydantic-settings (>=2)", "pydantic[email]", "pyright (>=0)", "pytest (>=6.0.0)", "pytest-asyncio (>=0.21.0)", "pytest-cov (>=2.8.1)"] +zstd = ["motor[zstd] (>=2.5.0,<4.0.0)"] [[package]] name = "billiard" -version = "4.2.0" +version = "4.2.1" description = "Python multiprocessing fork with improvements and bugfixes" optional = false python-versions = ">=3.7" files = [ - {file = "billiard-4.2.0-py3-none-any.whl", hash = "sha256:07aa978b308f334ff8282bd4a746e681b3513db5c9a514cbdd810cbbdc19714d"}, - {file = "billiard-4.2.0.tar.gz", hash = "sha256:9a3c3184cb275aa17a732f93f65b20c525d3d9f253722d26a82194803ade5a2c"}, + {file = "billiard-4.2.1-py3-none-any.whl", hash = "sha256:40b59a4ac8806ba2c2369ea98d876bc6108b051c227baffd928c644d15d8f3cb"}, + {file = "billiard-4.2.1.tar.gz", hash = "sha256:12b641b0c539073fc8d3f5b8b7be998956665c4233c7c1fcd66a7e677c4fb36f"}, ] [[package]] @@ -501,13 +539,13 @@ files = [ [[package]] name = "cachetools" -version = "5.3.3" +version = "5.5.0" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, - {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, + {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, + {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, ] [[package]] @@ -568,13 +606,13 @@ zstd = ["zstandard (==0.22.0)"] [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] [[package]] @@ -658,101 +696,116 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "3.3.2" +version = "3.4.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] [[package]] @@ -919,21 +972,21 @@ files = [ [[package]] name = "dnspython" -version = "2.6.1" +version = "2.7.0" description = "DNS toolkit" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, - {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, + {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, + {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, ] [package.extras] -dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] -dnssec = ["cryptography (>=41)"] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.16.0)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "quart-trio (>=0.11.0)", "sphinx (>=7.2.0)", "sphinx-rtd-theme (>=2.0.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=43)"] doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] -doq = ["aioquic (>=0.9.25)"] -idna = ["idna (>=3.6)"] +doq = ["aioquic (>=1.0.0)"] +idna = ["idna (>=3.7)"] trio = ["trio (>=0.23)"] wmi = ["wmi (>=1.5.1)"] @@ -961,13 +1014,13 @@ websockets = ["websocket-client (>=1.3.0)"] [[package]] name = "exceptiongroup" -version = "1.2.1" +version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, - {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, ] [package.extras] @@ -1110,13 +1163,13 @@ files = [ [[package]] name = "google-auth" -version = "2.29.0" +version = "2.35.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, - {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, + {file = "google_auth-2.35.0-py2.py3-none-any.whl", hash = "sha256:25df55f327ef021de8be50bad0dfd4a916ad0de96da86cd05661c9297723ad3f"}, + {file = "google_auth-2.35.0.tar.gz", hash = "sha256:f4c64ed4e01e8e8b646ef34c018f8bf3338df0c8e37d8b3bba40e7f574a3278a"}, ] [package.dependencies] @@ -1126,76 +1179,91 @@ rsa = ">=3.1.4,<5" [package.extras] aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] -enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +enterprise-cert = ["cryptography", "pyopenssl"] pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] reauth = ["pyu2f (>=0.1.5)"] requests = ["requests (>=2.20.0,<3.0.0.dev0)"] [[package]] name = "greenlet" -version = "3.0.3" +version = "3.1.1" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" files = [ - {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, - {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, - {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, - {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, - {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, - {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, - {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, - {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, - {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, - {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, - {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, - {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, - {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, - {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, - {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, - {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, + {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, + {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, + {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, + {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, + {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, + {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, + {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, + {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af"}, + {file = "greenlet-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798"}, + {file = "greenlet-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef"}, + {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"}, + {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"}, + {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"}, + {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, + {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, + {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, + {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, ] [package.extras] @@ -1259,15 +1327,18 @@ socks = ["socksio (==1.*)"] [[package]] name = "idna" -version = "3.7" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -1279,6 +1350,88 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "jiter" +version = "0.6.1" +description = "Fast iterable JSON parser." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jiter-0.6.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d08510593cb57296851080018006dfc394070178d238b767b1879dc1013b106c"}, + {file = "jiter-0.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:adef59d5e2394ebbad13b7ed5e0306cceb1df92e2de688824232a91588e77aa7"}, + {file = "jiter-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3e02f7a27f2bcc15b7d455c9df05df8ffffcc596a2a541eeda9a3110326e7a3"}, + {file = "jiter-0.6.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed69a7971d67b08f152c17c638f0e8c2aa207e9dd3a5fcd3cba294d39b5a8d2d"}, + {file = "jiter-0.6.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2019d966e98f7c6df24b3b8363998575f47d26471bfb14aade37630fae836a1"}, + {file = "jiter-0.6.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36c0b51a285b68311e207a76c385650322734c8717d16c2eb8af75c9d69506e7"}, + {file = "jiter-0.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:220e0963b4fb507c525c8f58cde3da6b1be0bfddb7ffd6798fb8f2531226cdb1"}, + {file = "jiter-0.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aa25c7a9bf7875a141182b9c95aed487add635da01942ef7ca726e42a0c09058"}, + {file = "jiter-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e90552109ca8ccd07f47ca99c8a1509ced93920d271bb81780a973279974c5ab"}, + {file = "jiter-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:67723a011964971864e0b484b0ecfee6a14de1533cff7ffd71189e92103b38a8"}, + {file = "jiter-0.6.1-cp310-none-win32.whl", hash = "sha256:33af2b7d2bf310fdfec2da0177eab2fedab8679d1538d5b86a633ebfbbac4edd"}, + {file = "jiter-0.6.1-cp310-none-win_amd64.whl", hash = "sha256:7cea41c4c673353799906d940eee8f2d8fd1d9561d734aa921ae0f75cb9732f4"}, + {file = "jiter-0.6.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b03c24e7da7e75b170c7b2b172d9c5e463aa4b5c95696a368d52c295b3f6847f"}, + {file = "jiter-0.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:47fee1be677b25d0ef79d687e238dc6ac91a8e553e1a68d0839f38c69e0ee491"}, + {file = "jiter-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25f0d2f6e01a8a0fb0eab6d0e469058dab2be46ff3139ed2d1543475b5a1d8e7"}, + {file = "jiter-0.6.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b809e39e342c346df454b29bfcc7bca3d957f5d7b60e33dae42b0e5ec13e027"}, + {file = "jiter-0.6.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e9ac7c2f092f231f5620bef23ce2e530bd218fc046098747cc390b21b8738a7a"}, + {file = "jiter-0.6.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e51a2d80d5fe0ffb10ed2c82b6004458be4a3f2b9c7d09ed85baa2fbf033f54b"}, + {file = "jiter-0.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3343d4706a2b7140e8bd49b6c8b0a82abf9194b3f0f5925a78fc69359f8fc33c"}, + {file = "jiter-0.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82521000d18c71e41c96960cb36e915a357bc83d63a8bed63154b89d95d05ad1"}, + {file = "jiter-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3c843e7c1633470708a3987e8ce617ee2979ee18542d6eb25ae92861af3f1d62"}, + {file = "jiter-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a2e861658c3fe849efc39b06ebb98d042e4a4c51a8d7d1c3ddc3b1ea091d0784"}, + {file = "jiter-0.6.1-cp311-none-win32.whl", hash = "sha256:7d72fc86474862c9c6d1f87b921b70c362f2b7e8b2e3c798bb7d58e419a6bc0f"}, + {file = "jiter-0.6.1-cp311-none-win_amd64.whl", hash = "sha256:3e36a320634f33a07794bb15b8da995dccb94f944d298c8cfe2bd99b1b8a574a"}, + {file = "jiter-0.6.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1fad93654d5a7dcce0809aff66e883c98e2618b86656aeb2129db2cd6f26f867"}, + {file = "jiter-0.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4e6e340e8cd92edab7f6a3a904dbbc8137e7f4b347c49a27da9814015cc0420c"}, + {file = "jiter-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:691352e5653af84ed71763c3c427cff05e4d658c508172e01e9c956dfe004aba"}, + {file = "jiter-0.6.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:defee3949313c1f5b55e18be45089970cdb936eb2a0063f5020c4185db1b63c9"}, + {file = "jiter-0.6.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26d2bdd5da097e624081c6b5d416d3ee73e5b13f1703bcdadbb1881f0caa1933"}, + {file = "jiter-0.6.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18aa9d1626b61c0734b973ed7088f8a3d690d0b7f5384a5270cd04f4d9f26c86"}, + {file = "jiter-0.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a3567c8228afa5ddcce950631c6b17397ed178003dc9ee7e567c4c4dcae9fa0"}, + {file = "jiter-0.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e5c0507131c922defe3f04c527d6838932fcdfd69facebafd7d3574fa3395314"}, + {file = "jiter-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:540fcb224d7dc1bcf82f90f2ffb652df96f2851c031adca3c8741cb91877143b"}, + {file = "jiter-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e7b75436d4fa2032b2530ad989e4cb0ca74c655975e3ff49f91a1a3d7f4e1df2"}, + {file = "jiter-0.6.1-cp312-none-win32.whl", hash = "sha256:883d2ced7c21bf06874fdeecab15014c1c6d82216765ca6deef08e335fa719e0"}, + {file = "jiter-0.6.1-cp312-none-win_amd64.whl", hash = "sha256:91e63273563401aadc6c52cca64a7921c50b29372441adc104127b910e98a5b6"}, + {file = "jiter-0.6.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:852508a54fe3228432e56019da8b69208ea622a3069458252f725d634e955b31"}, + {file = "jiter-0.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f491cc69ff44e5a1e8bc6bf2b94c1f98d179e1aaf4a554493c171a5b2316b701"}, + {file = "jiter-0.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc56c8f0b2a28ad4d8047f3ae62d25d0e9ae01b99940ec0283263a04724de1f3"}, + {file = "jiter-0.6.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:51b58f7a0d9e084a43b28b23da2b09fc5e8df6aa2b6a27de43f991293cab85fd"}, + {file = "jiter-0.6.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f79ce15099154c90ef900d69c6b4c686b64dfe23b0114e0971f2fecd306ec6c"}, + {file = "jiter-0.6.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:03a025b52009f47e53ea619175d17e4ded7c035c6fbd44935cb3ada11e1fd592"}, + {file = "jiter-0.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c74a8d93718137c021d9295248a87c2f9fdc0dcafead12d2930bc459ad40f885"}, + {file = "jiter-0.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40b03b75f903975f68199fc4ec73d546150919cb7e534f3b51e727c4d6ccca5a"}, + {file = "jiter-0.6.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:825651a3f04cf92a661d22cad61fc913400e33aa89b3e3ad9a6aa9dc8a1f5a71"}, + {file = "jiter-0.6.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:928bf25eb69ddb292ab8177fe69d3fbf76c7feab5fce1c09265a7dccf25d3991"}, + {file = "jiter-0.6.1-cp313-none-win32.whl", hash = "sha256:352cd24121e80d3d053fab1cc9806258cad27c53cad99b7a3cac57cf934b12e4"}, + {file = "jiter-0.6.1-cp313-none-win_amd64.whl", hash = "sha256:be7503dd6f4bf02c2a9bacb5cc9335bc59132e7eee9d3e931b13d76fd80d7fda"}, + {file = "jiter-0.6.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:31d8e00e1fb4c277df8ab6f31a671f509ebc791a80e5c61fdc6bc8696aaa297c"}, + {file = "jiter-0.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77c296d65003cd7ee5d7b0965f6acbe6cffaf9d1fa420ea751f60ef24e85fed5"}, + {file = "jiter-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeeb0c0325ef96c12a48ea7e23e2e86fe4838e6e0a995f464cf4c79fa791ceeb"}, + {file = "jiter-0.6.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a31c6fcbe7d6c25d6f1cc6bb1cba576251d32795d09c09961174fe461a1fb5bd"}, + {file = "jiter-0.6.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59e2b37f3b9401fc9e619f4d4badcab2e8643a721838bcf695c2318a0475ae42"}, + {file = "jiter-0.6.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bae5ae4853cb9644144e9d0755854ce5108d470d31541d83f70ca7ecdc2d1637"}, + {file = "jiter-0.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9df588e9c830b72d8db1dd7d0175af6706b0904f682ea9b1ca8b46028e54d6e9"}, + {file = "jiter-0.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15f8395e835cf561c85c1adee72d899abf2733d9df72e9798e6d667c9b5c1f30"}, + {file = "jiter-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a99d4e0b5fc3b05ea732d67eb2092fe894e95a90e6e413f2ea91387e228a307"}, + {file = "jiter-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a311df1fa6be0ccd64c12abcd85458383d96e542531bafbfc0a16ff6feda588f"}, + {file = "jiter-0.6.1-cp38-none-win32.whl", hash = "sha256:81116a6c272a11347b199f0e16b6bd63f4c9d9b52bc108991397dd80d3c78aba"}, + {file = "jiter-0.6.1-cp38-none-win_amd64.whl", hash = "sha256:13f9084e3e871a7c0b6e710db54444088b1dd9fbefa54d449b630d5e73bb95d0"}, + {file = "jiter-0.6.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:f1c53615fcfec3b11527c08d19cff6bc870da567ce4e57676c059a3102d3a082"}, + {file = "jiter-0.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f791b6a4da23238c17a81f44f5b55d08a420c5692c1fda84e301a4b036744eb1"}, + {file = "jiter-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c97e90fec2da1d5f68ef121444c2c4fa72eabf3240829ad95cf6bbeca42a301"}, + {file = "jiter-0.6.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3cbc1a66b4e41511209e97a2866898733c0110b7245791ac604117b7fb3fedb7"}, + {file = "jiter-0.6.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4e85f9e12cd8418ab10e1fcf0e335ae5bb3da26c4d13a0fd9e6a17a674783b6"}, + {file = "jiter-0.6.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08be33db6dcc374c9cc19d3633af5e47961a7b10d4c61710bd39e48d52a35824"}, + {file = "jiter-0.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:677be9550004f5e010d673d3b2a2b815a8ea07a71484a57d3f85dde7f14cf132"}, + {file = "jiter-0.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e8bd065be46c2eecc328e419d6557bbc37844c88bb07b7a8d2d6c91c7c4dedc9"}, + {file = "jiter-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bd95375ce3609ec079a97c5d165afdd25693302c071ca60c7ae1cf826eb32022"}, + {file = "jiter-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db459ed22d0208940d87f614e1f0ea5a946d29a3cfef71f7e1aab59b6c6b2afb"}, + {file = "jiter-0.6.1-cp39-none-win32.whl", hash = "sha256:d71c962f0971347bd552940ab96aa42ceefcd51b88c4ced8a27398182efa8d80"}, + {file = "jiter-0.6.1-cp39-none-win_amd64.whl", hash = "sha256:d465db62d2d10b489b7e7a33027c4ae3a64374425d757e963f86df5b5f2e7fc5"}, + {file = "jiter-0.6.1.tar.gz", hash = "sha256:e19cd21221fc139fb032e4112986656cb2739e9fe6d84c13956ab30ccc7d4449"}, +] + [[package]] name = "jmespath" version = "1.0.1" @@ -1313,13 +1466,13 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- [[package]] name = "jsonschema-specifications" -version = "2023.12.1" +version = "2024.10.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, - {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, + {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, + {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, ] [package.dependencies] @@ -1327,29 +1480,30 @@ referencing = ">=0.31.0" [[package]] name = "kombu" -version = "5.3.7" +version = "5.4.2" description = "Messaging library for Python." optional = false python-versions = ">=3.8" files = [ - {file = "kombu-5.3.7-py3-none-any.whl", hash = "sha256:5634c511926309c7f9789f1433e9ed402616b56836ef9878f01bd59267b4c7a9"}, - {file = "kombu-5.3.7.tar.gz", hash = "sha256:011c4cd9a355c14a1de8d35d257314a1d2456d52b7140388561acac3cf1a97bf"}, + {file = "kombu-5.4.2-py3-none-any.whl", hash = "sha256:14212f5ccf022fc0a70453bb025a1dcc32782a588c49ea866884047d66e14763"}, + {file = "kombu-5.4.2.tar.gz", hash = "sha256:eef572dd2fd9fc614b37580e3caeafdd5af46c1eff31e7fba89138cdb406f2cf"}, ] [package.dependencies] amqp = ">=5.1.1,<6.0.0" -typing-extensions = {version = "*", markers = "python_version < \"3.10\""} -vine = "*" +typing-extensions = {version = "4.12.2", markers = "python_version < \"3.10\""} +tzdata = {version = "*", markers = "python_version >= \"3.9\""} +vine = "5.1.0" [package.extras] azureservicebus = ["azure-servicebus (>=7.10.0)"] azurestoragequeues = ["azure-identity (>=1.12.0)", "azure-storage-queue (>=12.6.0)"] confluentkafka = ["confluent-kafka (>=2.2.0)"] -consul = ["python-consul2"] +consul = ["python-consul2 (==0.1.5)"] librabbitmq = ["librabbitmq (>=2.0.0)"] mongodb = ["pymongo (>=4.1.1)"] -msgpack = ["msgpack"] -pyro = ["pyro4"] +msgpack = ["msgpack (==1.1.0)"] +pyro = ["pyro4 (==4.82)"] qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] redis = ["redis (>=4.5.2,!=4.5.5,!=5.0.2)"] slmq = ["softlayer-messaging (>=1.0.3)"] @@ -1400,105 +1554,96 @@ pydantic = ">=1.9.0" [[package]] name = "levenshtein" -version = "0.25.1" +version = "0.26.0" description = "Python extension for computing string edit distances and similarities." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "Levenshtein-0.25.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eb4d1ec9f2dcbde1757c4b7fb65b8682bc2de45b9552e201988f287548b7abdf"}, - {file = "Levenshtein-0.25.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4d9fa3affef48a7e727cdbd0d9502cd060da86f34d8b3627edd769d347570e2"}, - {file = "Levenshtein-0.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1b6cd186e58196ff8b402565317e9346b408d0c04fa0ed12ce4868c0fcb6d03"}, - {file = "Levenshtein-0.25.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82637ef5428384dd1812849dd7328992819bf0c4a20bff0a3b3ee806821af7ed"}, - {file = "Levenshtein-0.25.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e73656da6cc3e32a6e4bcd48562fcb64599ef124997f2c91f5320d7f1532c069"}, - {file = "Levenshtein-0.25.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5abff796f92cdfba69b9cbf6527afae918d0e95cbfac000bd84017f74e0bd427"}, - {file = "Levenshtein-0.25.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38827d82f2ca9cb755da6f03e686866f2f411280db005f4304272378412b4cba"}, - {file = "Levenshtein-0.25.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b989df1e3231261a87d68dfa001a2070771e178b09650f9cf99a20e3d3abc28"}, - {file = "Levenshtein-0.25.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2011d3b3897d438a2f88ef7aed7747f28739cae8538ec7c18c33dd989930c7a0"}, - {file = "Levenshtein-0.25.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6c375b33ec7acc1c6855e8ee8c7c8ac6262576ffed484ff5c556695527f49686"}, - {file = "Levenshtein-0.25.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ce0cb9dd012ef1bf4d5b9d40603e7709b6581aec5acd32fcea9b371b294ca7aa"}, - {file = "Levenshtein-0.25.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:9da9ecb81bae67d784defed7274f894011259b038ec31f2339c4958157970115"}, - {file = "Levenshtein-0.25.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3bd7be5dbe5f4a1b691f381e39512927b39d1e195bd0ad61f9bf217a25bf36c9"}, - {file = "Levenshtein-0.25.1-cp310-cp310-win32.whl", hash = "sha256:f6abb9ced98261de67eb495b95e1d2325fa42b0344ed5763f7c0f36ee2e2bdba"}, - {file = "Levenshtein-0.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:97581af3e0a6d359af85c6cf06e51f77f4d635f7109ff7f8ed7fd634d8d8c923"}, - {file = "Levenshtein-0.25.1-cp310-cp310-win_arm64.whl", hash = "sha256:9ba008f490788c6d8d5a10735fcf83559965be97e4ef0812db388a84b1cc736a"}, - {file = "Levenshtein-0.25.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f57d9cf06dac55c2d2f01f0d06e32acc074ab9a902921dc8fddccfb385053ad5"}, - {file = "Levenshtein-0.25.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:22b60c6d791f4ca67a3686b557ddb2a48de203dae5214f220f9dddaab17f44bb"}, - {file = "Levenshtein-0.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d0444ee62eccf1e6cedc7c5bc01a9face6ff70cc8afa3f3ca9340e4e16f601a4"}, - {file = "Levenshtein-0.25.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e8758be8221a274c83924bae8dd8f42041792565a3c3bdd3c10e3f9b4a5f94e"}, - {file = "Levenshtein-0.25.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:147221cfb1d03ed81d22fdd2a4c7fc2112062941b689e027a30d2b75bbced4a3"}, - {file = "Levenshtein-0.25.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a454d5bc4f4a289f5471418788517cc122fcc00d5a8aba78c54d7984840655a2"}, - {file = "Levenshtein-0.25.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c25f3778bbac78286bef2df0ca80f50517b42b951af0a5ddaec514412f79fac"}, - {file = "Levenshtein-0.25.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:181486cf465aff934694cc9a19f3898a1d28025a9a5f80fc1608217e7cd1c799"}, - {file = "Levenshtein-0.25.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b8db9f672a5d150706648b37b044dba61f36ab7216c6a121cebbb2899d7dfaa3"}, - {file = "Levenshtein-0.25.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f2a69fe5ddea586d439f9a50d0c51952982f6c0db0e3573b167aa17e6d1dfc48"}, - {file = "Levenshtein-0.25.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:3b684675a3bd35efa6997856e73f36c8a41ef62519e0267dcbeefd15e26cae71"}, - {file = "Levenshtein-0.25.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:cc707ef7edb71f6bf8339198b929ead87c022c78040e41668a4db68360129cef"}, - {file = "Levenshtein-0.25.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:41512c436b8c691326e2d07786d906cba0e92b5e3f455bf338befb302a0ca76d"}, - {file = "Levenshtein-0.25.1-cp311-cp311-win32.whl", hash = "sha256:2a3830175c01ade832ba0736091283f14a6506a06ffe8c846f66d9fbca91562f"}, - {file = "Levenshtein-0.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:9e0af4e6e023e0c8f79af1d1ca5f289094eb91201f08ad90f426d71e4ae84052"}, - {file = "Levenshtein-0.25.1-cp311-cp311-win_arm64.whl", hash = "sha256:38e5d9a1d737d7b49fa17d6a4c03a0359288154bf46dc93b29403a9dd0cd1a7d"}, - {file = "Levenshtein-0.25.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4a40fa16ecd0bf9e557db67131aabeea957f82fe3e8df342aa413994c710c34e"}, - {file = "Levenshtein-0.25.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4f7d2045d5927cffa65a0ac671c263edbfb17d880fdce2d358cd0bda9bcf2b6d"}, - {file = "Levenshtein-0.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40f96590539f9815be70e330b4d2efcce0219db31db5a22fffe99565192f5662"}, - {file = "Levenshtein-0.25.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d78512dd25b572046ff86d8903bec283c373063349f8243430866b6a9946425"}, - {file = "Levenshtein-0.25.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c161f24a1b216e8555c874c7dd70c1a0d98f783f252a16c9face920a8b8a6f3e"}, - {file = "Levenshtein-0.25.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:06ebbfd010a00490795f478d18d7fa2ffc79c9c03fc03b678081f31764d16bab"}, - {file = "Levenshtein-0.25.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa9ec0a4489ebfb25a9ec2cba064ed68d0d2485b8bc8b7203f84a7874755e0f"}, - {file = "Levenshtein-0.25.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26408938a6db7b252824a701545d50dc9cdd7a3e4c7ee70834cca17953b76ad8"}, - {file = "Levenshtein-0.25.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:330ec2faff957281f4e6a1a8c88286d1453e1d73ee273ea0f937e0c9281c2156"}, - {file = "Levenshtein-0.25.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9115d1b08626dfdea6f3955cb49ba5a578f7223205f80ead0038d6fc0442ce13"}, - {file = "Levenshtein-0.25.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:bbd602edab758e93a5c67bf0d8322f374a47765f1cdb6babaf593a64dc9633ad"}, - {file = "Levenshtein-0.25.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b930b4df32cd3aabbed0e9f0c4fdd1ea4090a5c022ba9f1ae4ab70ccf1cf897a"}, - {file = "Levenshtein-0.25.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:dd66fb51f88a3f73a802e1ff19a14978ddc9fbcb7ce3a667ca34f95ef54e0e44"}, - {file = "Levenshtein-0.25.1-cp312-cp312-win32.whl", hash = "sha256:386de94bd1937a16ae3c8f8b7dd2eff1b733994ecf56ce4d05dfdd0e776d0261"}, - {file = "Levenshtein-0.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:9ee1902153d47886c9787598a4a5c324ce7fde44d44daa34fcf3652ac0de21bc"}, - {file = "Levenshtein-0.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:b56a7e7676093c3aee50402226f4079b15bd21b5b8f1820f9d6d63fe99dc4927"}, - {file = "Levenshtein-0.25.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6b5dfdf6a0e2f35fd155d4c26b03398499c24aba7bc5db40245789c46ad35c04"}, - {file = "Levenshtein-0.25.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:355ff797f704459ddd8b95354d699d0d0642348636c92d5e67b49be4b0e6112b"}, - {file = "Levenshtein-0.25.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:933b827a3b721210fff522f3dca9572f9f374a0e88fa3a6c7ee3164406ae7794"}, - {file = "Levenshtein-0.25.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be1da669a240f272d904ab452ad0a1603452e190f4e03e886e6b3a9904152b89"}, - {file = "Levenshtein-0.25.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:265cbd78962503a26f2bea096258a3b70b279bb1a74a525c671d3ee43a190f9c"}, - {file = "Levenshtein-0.25.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63cc4d53a35e673b12b721a58b197b4a65734688fb72aa1987ce63ed612dca96"}, - {file = "Levenshtein-0.25.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75fee0c471b8799c70dad9d0d5b70f1f820249257f9617601c71b6c1b37bee92"}, - {file = "Levenshtein-0.25.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:045d6b0db124fbd37379b2b91f6d0786c2d9220e7a848e2dd31b99509a321240"}, - {file = "Levenshtein-0.25.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:db7a2e9c51ac9cc2fd5679484f1eac6e0ab2085cb181240445f7fbf10df73230"}, - {file = "Levenshtein-0.25.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c379c588aa0d93d4607db7eb225fd683263d49669b1bbe49e28c978aa6a4305d"}, - {file = "Levenshtein-0.25.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:966dd00424df7f69b78da02a29b530fbb6c1728e9002a2925ed7edf26b231924"}, - {file = "Levenshtein-0.25.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:09daa6b068709cc1e68b670a706d928ed8f0b179a26161dd04b3911d9f757525"}, - {file = "Levenshtein-0.25.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d6bed0792635081accf70a7e11cfece986f744fddf46ce26808cd8bfc067e430"}, - {file = "Levenshtein-0.25.1-cp38-cp38-win32.whl", hash = "sha256:28e7b7faf5a745a690d1b1706ab82a76bbe9fa6b729d826f0cfdd24fd7c19740"}, - {file = "Levenshtein-0.25.1-cp38-cp38-win_amd64.whl", hash = "sha256:8ca0cc9b9e07316b5904f158d5cfa340d55b4a3566ac98eaac9f087c6efb9a1a"}, - {file = "Levenshtein-0.25.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:45682cdb3ac4a5465c01b2dce483bdaa1d5dcd1a1359fab37d26165b027d3de2"}, - {file = "Levenshtein-0.25.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f8dc3e63c4cd746ec162a4cd744c6dde857e84aaf8c397daa46359c3d54e6219"}, - {file = "Levenshtein-0.25.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:01ad1eb09933a499a49923e74e05b1428ca4ef37fed32965fef23f1334a11563"}, - {file = "Levenshtein-0.25.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbb4e8c4b8b7bbe0e1aa64710b806b6c3f31d93cb14969ae2c0eff0f3a592db8"}, - {file = "Levenshtein-0.25.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48d1fe224b365975002e3e2ea947cbb91d2936a16297859b71c4abe8a39932c"}, - {file = "Levenshtein-0.25.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a164df16d876aab0a400f72aeac870ea97947ea44777c89330e9a16c7dc5cc0e"}, - {file = "Levenshtein-0.25.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:995d3bcedcf64be6ceca423f6cfe29184a36d7c4cbac199fdc9a0a5ec7196cf5"}, - {file = "Levenshtein-0.25.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdaf62d637bef6711d6f3457e2684faab53b2db2ed53c05bc0dc856464c74742"}, - {file = "Levenshtein-0.25.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:af9de3b5f8f5f3530cfd97daab9ab480d1b121ef34d8c0aa5bab0c645eae219e"}, - {file = "Levenshtein-0.25.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:78fba73c352383b356a30c4674e39f086ffef7122fa625e7550b98be2392d387"}, - {file = "Levenshtein-0.25.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:9e0df0dcea3943321398f72e330c089b5d5447318310db6f17f5421642f3ade6"}, - {file = "Levenshtein-0.25.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:387f768bb201b9bc45f0f49557e2fb9a3774d9d087457bab972162dcd4fd352b"}, - {file = "Levenshtein-0.25.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5dcf931b64311039b43495715e9b795fbd97ab44ba3dd6bf24360b15e4e87649"}, - {file = "Levenshtein-0.25.1-cp39-cp39-win32.whl", hash = "sha256:2449f8668c0bd62a2b305a5e797348984c06ac20903b38b3bab74e55671ddd51"}, - {file = "Levenshtein-0.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:28803fd6ec7b58065621f5ec0d24e44e2a7dc4842b64dcab690cb0a7ea545210"}, - {file = "Levenshtein-0.25.1-cp39-cp39-win_arm64.whl", hash = "sha256:0b074d452dff8ee86b5bdb6031aa32bb2ed3c8469a56718af5e010b9bb5124dc"}, - {file = "Levenshtein-0.25.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e9e060ef3925a68aeb12276f0e524fb1264592803d562ec0306c7c3f5c68eae0"}, - {file = "Levenshtein-0.25.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f84b84049318d44722db307c448f9dcb8d27c73525a378e901189a94889ba61"}, - {file = "Levenshtein-0.25.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07e23fdf330cb185a0c7913ca5bd73a189dfd1742eae3a82e31ed8688b191800"}, - {file = "Levenshtein-0.25.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d06958e4a81ea0f0b2b7768a2ad05bcd50a9ad04c4d521dd37d5730ff12decdc"}, - {file = "Levenshtein-0.25.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2ea7c34ec22b2fce21299b0caa6dde6bdebafcc2970e265853c9cfea8d1186da"}, - {file = "Levenshtein-0.25.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fddc0ccbdd94f57aa32e2eb3ac8310d08df2e175943dc20b3e1fc7a115850af4"}, - {file = "Levenshtein-0.25.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d52249cb3448bfe661d3d7db3a6673e835c7f37b30b0aeac499a1601bae873d"}, - {file = "Levenshtein-0.25.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8dd4c201b15f8c1e612f9074335392c8208ac147acbce09aff04e3974bf9b16"}, - {file = "Levenshtein-0.25.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23a4d95ce9d44161c7aa87ab76ad6056bc1093c461c60c097054a46dc957991f"}, - {file = "Levenshtein-0.25.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:65eea8a9c33037b23069dca4b3bc310e3c28ca53f60ec0c958d15c0952ba39fa"}, - {file = "Levenshtein-0.25.1.tar.gz", hash = "sha256:2df14471c778c75ffbd59cb64bbecfd4b0ef320ef9f80e4804764be7d5678980"}, -] - -[package.dependencies] -rapidfuzz = ">=3.8.0,<4.0.0" + {file = "levenshtein-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e5fb23bf35ef1094d00415bb2116dd862fb919adc4044436508029dafedfd687"}, + {file = "levenshtein-0.26.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea97df5fecf4195ed24aa4c245e5b6cf42527a5818bd7d0d92ba8c7425828861"}, + {file = "levenshtein-0.26.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c06ddb9149ee0f25ba334ad04ee3f782be3e64ab4bf8b9f34d6e4bf19a22880"}, + {file = "levenshtein-0.26.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf2561d9077367aa01f3c6caf6132cec84b2b9a481f64f14f35f9e976a353605"}, + {file = "levenshtein-0.26.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54200110ba27c20cb2e3bc48cf855d7d910c6704a47caefe97ced8c59ee075b4"}, + {file = "levenshtein-0.26.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed1c1354f0f1e4736c2fb6f91e3913b63e38c8873a228a299f656bdaf788e1eb"}, + {file = "levenshtein-0.26.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4ecf7d755304d2ffab2ee808100f5e87889347b572c61c97d9f4f9a318806713"}, + {file = "levenshtein-0.26.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c532571fabd8a23fa12971f432cd17ababa031dee6efbaa8fa31380876593a3e"}, + {file = "levenshtein-0.26.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:71a3afbfc7d97fd536f128084a20c2a79b5d79995c7111eced1baf4d103ea6d4"}, + {file = "levenshtein-0.26.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c49a20ddb119ab2d56e7e4d62e43602dff8c397718be4f00474723b40d626a7f"}, + {file = "levenshtein-0.26.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e8a34d41c7b392df98fce7c8353ea87cbbbf39239db141096ded00f4e1e75e6e"}, + {file = "levenshtein-0.26.0-cp310-cp310-win32.whl", hash = "sha256:a541040a01333994e5a037cb4e2c2b0e2496a2141facf63bbc9b359fe9f5b1dd"}, + {file = "levenshtein-0.26.0-cp310-cp310-win_amd64.whl", hash = "sha256:68a165bd91dab44c2b91c8278e3728f0da98cb48b1ed7f263bfdf616efcf4932"}, + {file = "levenshtein-0.26.0-cp310-cp310-win_arm64.whl", hash = "sha256:968731244a7d56354c912c054e7b6ea33c3700632ef7cab1c895b3110e86dc7b"}, + {file = "levenshtein-0.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7aabafb951b96ca6e0f981b1edb3ec81b41c010b7437758e275393768fa84453"}, + {file = "levenshtein-0.26.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cefa552c5190e912f0fe39b62a5b08597d1256f330ed2c459ba724947458282"}, + {file = "levenshtein-0.26.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a0ed02c8aaef0903b72fe0da88d9d24d7964b07dbc123997e549ac165efad8d"}, + {file = "levenshtein-0.26.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5d21d3b08ceb7e544fae04897e211e43fb3500c9b3a8e74d08468b015c7270d"}, + {file = "levenshtein-0.26.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77422c5da5cfd8455a8835329d965e24250b0f0c1398e0a6362879f00d18f27c"}, + {file = "levenshtein-0.26.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4931c8263e06edbece310b1f8e03bfcb74f211863a85058b46cdf8460a4136af"}, + {file = "levenshtein-0.26.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3e265812db8b04e6ae159751c7a82d6e0e5025223bd330fc9104a8a5beeeb7cf"}, + {file = "levenshtein-0.26.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8c58cb6c9f90f9b11d6b478e2da6ac1f0bcb5ea9608a5611088d30f782ee5920"}, + {file = "levenshtein-0.26.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:754669e9b82795cfc3ca0d70f2e715b58ff4d0f7e7f4e77fc6539543439ae22c"}, + {file = "levenshtein-0.26.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:830b1993e3e945b213a6b73ceca8b555147a6ecd7323e4959b80dee35abfc7fc"}, + {file = "levenshtein-0.26.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2b3aa9a0b844ddaeb6f5317eb4e85b5748901cf40c9a9b0d3a8bf76ef9d3cccc"}, + {file = "levenshtein-0.26.0-cp311-cp311-win32.whl", hash = "sha256:07ffd78569ca80adfd55172156faac35eb12ccd375d6d51ba4512b0346337cbf"}, + {file = "levenshtein-0.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:1bf886afed2304e0022c33ed644afb719411cce4d4af11ba5bb040f05d9f00c1"}, + {file = "levenshtein-0.26.0-cp311-cp311-win_arm64.whl", hash = "sha256:c68b6e6c74ce9056370559196177b9e514ba20611a1ce9545dcd366d8a97cc60"}, + {file = "levenshtein-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fe230c754f628f6d266b8d970f8556775fb0a9c05027f807754312af7927b2ed"}, + {file = "levenshtein-0.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a01150b33505596be83bff9f6f06f53bc1a14fd5e36e840dc134822c6ba20e00"}, + {file = "levenshtein-0.26.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:247767593ede88f3ddbd4f948ed30817cb751e25a335b35ec2332752021e4e10"}, + {file = "levenshtein-0.26.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:52a7407416723cd7163238b16b7c0eba4b30bc53f549e803e654f1df6824e0b5"}, + {file = "levenshtein-0.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0fa54bd453b8b347139b4ef940d717939c6a8966fdbcde2cf8d70316497e292"}, + {file = "levenshtein-0.26.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14c96604ce1228922094ea266d9927227ab129e939b3e5dd84fee07390b72b5f"}, + {file = "levenshtein-0.26.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6170cbbec2ffc3df86adf2f457f83a22df99b6c4e2b21c76bc9a67b23c2940d1"}, + {file = "levenshtein-0.26.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7211e700ddca453efe49bdce57c84073451667873d80037c73c1f55840d08e70"}, + {file = "levenshtein-0.26.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:2150961cff9cfcfd4b5e4c1a5c90b2d4520a7d9cb65cd070cda5601b7f099496"}, + {file = "levenshtein-0.26.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0afb8bd5bc2d686d34ed721399d78359d104c7483a740b262fd260357599e318"}, + {file = "levenshtein-0.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a68f863bef8a5301bc7ca4b99f2f8d666b5edcd30dca6234ee6cd541ca8a8f4e"}, + {file = "levenshtein-0.26.0-cp312-cp312-win32.whl", hash = "sha256:3baa2c8ebc1f3b773ffb010e03e7319098323618f919c5db2c7188e439d13602"}, + {file = "levenshtein-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad6da19613e53c288bd130fadb91937b0ba849013065ddd377568cdd90611caa"}, + {file = "levenshtein-0.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:bcbd4403c630755a5f898e6905975c1a8bb2c71a756d399f93811f82b5c0b1b3"}, + {file = "levenshtein-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:770dc2973c9c76598d68b736319c2b2b6fad1f88837119a92e9124ff2167cc49"}, + {file = "levenshtein-0.26.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb13782c15c2c0f5a74e920a9c9c26da5730830c13809bcb7a32876f1a4fb643"}, + {file = "levenshtein-0.26.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a9949dd238e8c1bd27669a1bf66a5507eed2fd486130b30b2df44cc4f238a3c"}, + {file = "levenshtein-0.26.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dafc593a6e346fec5520f5dec9d87f4894aa9d584ccad43ce9fdcc363d9e9c34"}, + {file = "levenshtein-0.26.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ce82de94989df4367ff38de2c4193bf0ea77b4e207ae60e98876922007a0f81"}, + {file = "levenshtein-0.26.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cbd4775e0e88e209cbb4f722d39d057531138dafbd07e36c4b33eb18e29494ef"}, + {file = "levenshtein-0.26.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:75e2567bf64e422843d0f67917938c0235a9292b395076dd5f8bbb731d1c3058"}, + {file = "levenshtein-0.26.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:22c40516cc830aeeb18b09bd96ec32378b7e52e201d67e86dfb7f6a2e3bf3b3d"}, + {file = "levenshtein-0.26.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c311554a220a12110d2640ee245d74f07b25cdc0acc04c625ab2bdf708f3bf84"}, + {file = "levenshtein-0.26.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0560746b2c40cdd25458cb4a065e4d7ffbb245783638f5b0173cfd135f9d6664"}, + {file = "levenshtein-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3e8e373586026d71ec2053300ef46c5268fe41a29d9f58a724487e3f060502db"}, + {file = "levenshtein-0.26.0-cp313-cp313-win32.whl", hash = "sha256:f88d05187622366b529b25b130a8fad6497d7b6189e82198ce67d3921c3e418c"}, + {file = "levenshtein-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cc288a6ab97a3ccd980d8a0465378b99eb53e32232f99279092a97578c4b2e9"}, + {file = "levenshtein-0.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:de7727a57d1241f8c2d53a9f70642f48891514789bbd4daa0f90547b4e0bf70f"}, + {file = "levenshtein-0.26.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:64bfad7270c2fc0f872f6ca2e26cc091efebfb64b7b1ae156123c2304fbcbae4"}, + {file = "levenshtein-0.26.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17c9e1d7bbde972c09e2c6578392ff0668697db8f815649594b984ed2b0b2843"}, + {file = "levenshtein-0.26.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7acff8e4335f18bb5b60cbd310957cf46c964f0b5bc1cc82fe6647c45309b9f8"}, + {file = "levenshtein-0.26.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d526636efb6e727dce5f67eb3437079f1d5d71dd2c0a2b6f48a62791a789764e"}, + {file = "levenshtein-0.26.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a492bd9e0075ed5f748ac000ffff581d7652f6cb2cf1d9ec9de3d12270dfc225"}, + {file = "levenshtein-0.26.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd953f8952c825c5d7112e9d64136afc8c7c122b9facc3ec4f9f87591a85e6d1"}, + {file = "levenshtein-0.26.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:912fe5a3bcc970ab4b97e7e83ea118cf9a98831813535efa0f499b0b4df032dd"}, + {file = "levenshtein-0.26.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d9c8ac0f4ae2de173b6ccc3d0b379d1aa4212f653badc324fb5aa868e304e847"}, + {file = "levenshtein-0.26.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:47a4d8c58114c2b9a4318dcb8e2a0dc8446ec3ec637b051f3dbe27555786f603"}, + {file = "levenshtein-0.26.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:baf0c8c09d5c55f136b62e201886c4b93b4abbdbde0475f67dd8fc278bb403f7"}, + {file = "levenshtein-0.26.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88f051a0a4eec22cd8071634d5a2ee40d9810080e3b6d1e61e4fbe2a7a7ece3d"}, + {file = "levenshtein-0.26.0-cp39-cp39-win32.whl", hash = "sha256:c2d3f8cf45deeb6d0faa9df089c0e36e457236fc66aa31e67d3e7c15336e8401"}, + {file = "levenshtein-0.26.0-cp39-cp39-win_amd64.whl", hash = "sha256:9238be73beff55e2b285ac393573274e55233e7270cb6484fa4550b775016705"}, + {file = "levenshtein-0.26.0-cp39-cp39-win_arm64.whl", hash = "sha256:abc4618610ec6fff1c894fc931bc8b57c6d1ad7b8b9acafa685e19a84f521f8b"}, + {file = "levenshtein-0.26.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ca2735baf43fb540be7d3cf24eb2fc97a395e9e85ec8fae64199f3b2ccbf5c5c"}, + {file = "levenshtein-0.26.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93a949fabc6280ab44a6252d780577715e8e71a63a295c4730ce5490006ae1f5"}, + {file = "levenshtein-0.26.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:588094f406d5cb01dbf74201f8b4fe345d2b92a789997eb5371dad754f831d5b"}, + {file = "levenshtein-0.26.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c95201c7425cdff12ed16ff0a15af52c46477935e7589d1dc6ebd24f416c9ed8"}, + {file = "levenshtein-0.26.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:651b011617937d18f671450c8294d11b5b556b09e33ac038b923ae6496e35d31"}, + {file = "levenshtein-0.26.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0576b0e817904adc621077761e71c97555ae9e698e712b0bdbe868464444200b"}, + {file = "levenshtein-0.26.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16a3f4d0bcdbd0d14e0c49844a2ac02d8acece9f6cce41ea437c1d07ac536ec1"}, + {file = "levenshtein-0.26.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e40e9a2c6482f6cce9046693844b33c492f35e57c849a0e104b03234ef3a31f6"}, + {file = "levenshtein-0.26.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8104cb5642277439c07091c160ad3b1c3d20c160db8dbf5306871f065721e5e8"}, + {file = "levenshtein-0.26.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a30d30485eec8a2efe72d639fb9c33f0991416a6ddb490d6f0eef01c9dc79b7a"}, + {file = "levenshtein-0.26.0.tar.gz", hash = "sha256:960b020d96bbd348400d6ff5c16290adee49f0ae2d42707a550a3b4f7d092abe"}, +] + +[package.dependencies] +rapidfuzz = ">=3.9.0,<4.0.0" [[package]] name = "mako" @@ -1521,232 +1666,238 @@ testing = ["pytest"] [[package]] name = "markupsafe" -version = "2.1.5" +version = "3.0.1" description = "Safely add untrusted strings to HTML/XML markup." optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:db842712984e91707437461930e6011e60b39136c7331e971952bb30465bc1a1"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ffb4a8e7d46ed96ae48805746755fadd0909fea2306f93d5d8233ba23dda12a"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67c519635a4f64e495c50e3107d9b4075aec33634272b5db1cde839e07367589"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48488d999ed50ba8d38c581d67e496f955821dc183883550a6fbc7f1aefdc170"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f31ae06f1328595d762c9a2bf29dafd8621c7d3adc130cbb46278079758779ca"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80fcbf3add8790caddfab6764bde258b5d09aefbe9169c183f88a7410f0f6dea"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3341c043c37d78cc5ae6e3e305e988532b072329639007fd408a476642a89fd6"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cb53e2a99df28eee3b5f4fea166020d3ef9116fdc5764bc5117486e6d1211b25"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-win32.whl", hash = "sha256:db15ce28e1e127a0013dfb8ac243a8e392db8c61eae113337536edb28bdc1f97"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:4ffaaac913c3f7345579db4f33b0020db693f302ca5137f106060316761beea9"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:26627785a54a947f6d7336ce5963569b5d75614619e75193bdb4e06e21d447ad"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b954093679d5750495725ea6f88409946d69cfb25ea7b4c846eef5044194f583"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:973a371a55ce9ed333a3a0f8e0bcfae9e0d637711534bcb11e130af2ab9334e7"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d98e66a24497637dd31ccab090b34392dddb1f2f811c4b4cd80c230205c074a3"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad91738f14eb8da0ff82f2acd0098b6257621410dcbd4df20aaa5b4233d75a50"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7044312a928a66a4c2a22644147bc61a199c1709712069a344a3fb5cfcf16915"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a4792d3b3a6dfafefdf8e937f14906a51bd27025a36f4b188728a73382231d91"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-win32.whl", hash = "sha256:fa7d686ed9883f3d664d39d5a8e74d3c5f63e603c2e3ff0abcba23eac6542635"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ba25a71ebf05b9bb0e2ae99f8bc08a07ee8e98c612175087112656ca0f5c8bf"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8ae369e84466aa70f3154ee23c1451fda10a8ee1b63923ce76667e3077f2b0c4"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40f1e10d51c92859765522cbd79c5c8989f40f0419614bcdc5015e7b6bf97fc5"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a4cb365cb49b750bdb60b846b0c0bc49ed62e59a76635095a179d440540c346"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee3941769bd2522fe39222206f6dd97ae83c442a94c90f2b7a25d847d40f4729"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62fada2c942702ef8952754abfc1a9f7658a4d5460fabe95ac7ec2cbe0d02abc"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c2d64fdba74ad16138300815cfdc6ab2f4647e23ced81f59e940d7d4a1469d9"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fb532dd9900381d2e8f48172ddc5a59db4c445a11b9fab40b3b786da40d3b56b"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0f84af7e813784feb4d5e4ff7db633aba6c8ca64a833f61d8e4eade234ef0c38"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-win32.whl", hash = "sha256:cbf445eb5628981a80f54087f9acdbf84f9b7d862756110d172993b9a5ae81aa"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:a10860e00ded1dd0a65b83e717af28845bb7bd16d8ace40fe5531491de76b79f"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e81c52638315ff4ac1b533d427f50bc0afc746deb949210bc85f05d4f15fd772"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:312387403cd40699ab91d50735ea7a507b788091c416dd007eac54434aee51da"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ae99f31f47d849758a687102afdd05bd3d3ff7dbab0a8f1587981b58a76152a"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c97ff7fedf56d86bae92fa0a646ce1a0ec7509a7578e1ed238731ba13aabcd1c"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7420ceda262dbb4b8d839a4ec63d61c261e4e77677ed7c66c99f4e7cb5030dd"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45d42d132cff577c92bfba536aefcfea7e26efb975bd455db4e6602f5c9f45e7"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c8817557d0de9349109acb38b9dd570b03cc5014e8aabf1cbddc6e81005becd"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a54c43d3ec4cf2a39f4387ad044221c66a376e58c0d0e971d47c475ba79c6b5"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-win32.whl", hash = "sha256:c91b394f7601438ff79a4b93d16be92f216adb57d813a78be4446fe0f6bc2d8c"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:fe32482b37b4b00c7a52a07211b479653b7fe4f22b2e481b9a9b099d8a430f2f"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:17b2aea42a7280db02ac644db1d634ad47dcc96faf38ab304fe26ba2680d359a"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:852dc840f6d7c985603e60b5deaae1d89c56cb038b577f6b5b8c808c97580f1d"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0778de17cff1acaeccc3ff30cd99a3fd5c50fc58ad3d6c0e0c4c58092b859396"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:800100d45176652ded796134277ecb13640c1a537cad3b8b53da45aa96330453"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d06b24c686a34c86c8c1fba923181eae6b10565e4d80bdd7bc1c8e2f11247aa4"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:33d1c36b90e570ba7785dacd1faaf091203d9942bc036118fab8110a401eb1a8"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:beeebf760a9c1f4c07ef6a53465e8cfa776ea6a2021eda0d0417ec41043fe984"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bbde71a705f8e9e4c3e9e33db69341d040c827c7afa6789b14c6e16776074f5a"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-win32.whl", hash = "sha256:82b5dba6eb1bcc29cc305a18a3c5365d2af06ee71b123216416f7e20d2a84e5b"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:730d86af59e0e43ce277bb83970530dd223bf7f2a838e086b50affa6ec5f9295"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4935dd7883f1d50e2ffecca0aa33dc1946a94c8f3fdafb8df5c330e48f71b132"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e9393357f19954248b00bed7c56f29a25c930593a77630c719653d51e7669c2a"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40621d60d0e58aa573b68ac5e2d6b20d44392878e0bfc159012a5787c4e35bc8"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f94190df587738280d544971500b9cafc9b950d32efcb1fba9ac10d84e6aa4e6"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6a387d61fe41cdf7ea95b38e9af11cfb1a63499af2759444b99185c4ab33f5b"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8ad4ad1429cd4f315f32ef263c1342166695fad76c100c5d979c45d5570ed58b"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e24bfe89c6ac4c31792793ad9f861b8f6dc4546ac6dc8f1c9083c7c4f2b335cd"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2a4b34a8d14649315c4bc26bbfa352663eb51d146e35eef231dd739d54a5430a"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-win32.whl", hash = "sha256:242d6860f1fd9191aef5fae22b51c5c19767f93fb9ead4d21924e0bcb17619d8"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:93e8248d650e7e9d49e8251f883eed60ecbc0e8ffd6349e18550925e31bd029b"}, + {file = "markupsafe-3.0.1.tar.gz", hash = "sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344"}, ] [[package]] name = "motor" -version = "3.4.0" +version = "3.6.0" description = "Non-blocking MongoDB driver for Tornado or asyncio" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "motor-3.4.0-py3-none-any.whl", hash = "sha256:4b1e1a0cc5116ff73be2c080a72da078f2bb719b53bc7a6bb9e9a2f7dcd421ed"}, - {file = "motor-3.4.0.tar.gz", hash = "sha256:c89b4e4eb2e711345e91c7c9b122cb68cce0e5e869ed0387dd0acb10775e3131"}, + {file = "motor-3.6.0-py3-none-any.whl", hash = "sha256:9f07ed96f1754963d4386944e1b52d403a5350c687edc60da487d66f98dbf894"}, + {file = "motor-3.6.0.tar.gz", hash = "sha256:0ef7f520213e852bf0eac306adf631aabe849227d8aec900a2612512fb9c5b8d"}, ] [package.dependencies] -pymongo = ">=4.5,<5" +pymongo = ">=4.9,<4.10" [package.extras] aws = ["pymongo[aws] (>=4.5,<5)"] +docs = ["aiohttp", "furo (==2024.8.6)", "readthedocs-sphinx-search (>=0.3,<1.0)", "sphinx (>=5.3,<8)", "sphinx-rtd-theme (>=2,<3)", "tornado"] encryption = ["pymongo[encryption] (>=4.5,<5)"] gssapi = ["pymongo[gssapi] (>=4.5,<5)"] ocsp = ["pymongo[ocsp] (>=4.5,<5)"] snappy = ["pymongo[snappy] (>=4.5,<5)"] -srv = ["pymongo[srv] (>=4.5,<5)"] -test = ["aiohttp (!=3.8.6)", "mockupdb", "motor[encryption]", "pytest (>=7)", "tornado (>=5)"] +test = ["aiohttp (>=3.8.7)", "cffi (>=1.17.0rc1)", "mockupdb", "pymongo[encryption] (>=4.5,<5)", "pytest (>=7)", "pytest-asyncio", "tornado (>=5)"] zstd = ["pymongo[zstd] (>=4.5,<5)"] [[package]] name = "multidict" -version = "6.0.5" +version = "6.1.0" description = "multidict implementation" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, - {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, - {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, - {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, - {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, - {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, - {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, - {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, - {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, - {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, - {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, - {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, - {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, - {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, - {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, - {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, -] + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, + {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, + {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, + {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, + {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, + {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, + {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, + {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, + {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, + {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, + {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, + {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, + {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, + {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, + {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} [[package]] name = "newrelic" -version = "9.10.0" +version = "9.13.0" description = "New Relic Python Agent" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ - {file = "newrelic-9.10.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:a4d4e5670082225ca7ef0ee986ef8e6588f4e530a05d43d66f9368459c0b1f18"}, - {file = "newrelic-9.10.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:f4605bc4feb114235e242dfe260b75ec85d0894f5400aa7f30e75fbbc0423b3f"}, - {file = "newrelic-9.10.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d3be6c97d007ceb142f908f5ab2444807b44dc600a0b7f3254dc685b5b03fd10"}, - {file = "newrelic-9.10.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4e573d49c1543a488d6567906a9b2cb0c748cdbf80724c322b06874f8e47c789"}, - {file = "newrelic-9.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae0515f7ab19f1a5dd14e31506420d1b86014c5e1340c2a210833248bc765dae"}, - {file = "newrelic-9.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acf5cdcafd2971933ad2f9e836284957f4a3eababe88f063cf53b1b1f67f1a16"}, - {file = "newrelic-9.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5d18236bf4a80fca4eb1db03448ed72bf8e16b84b3a4ed5fcc29bb91c2d05d54"}, - {file = "newrelic-9.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:744c815f15ec06e441c11a6c57042d2eca8c41401c11de6f47b3e105d952b9bd"}, - {file = "newrelic-9.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:524ed5bfa09d330746b45e0087765da994ca34802cce032063041e404e58414c"}, - {file = "newrelic-9.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ad9cd5459b8c620ab7a876bd5d920c3ef2943948d1262a42289d4f8d16dadab"}, - {file = "newrelic-9.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4404c649b5e6165dcdd59091092c19b292a43cc96520d5ffd718b628fb866096"}, - {file = "newrelic-9.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2576bbec0b640d9b76454dcfd5b2f03078e0bb062a7ea3952a8db7b9972c352"}, - {file = "newrelic-9.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77537a020ce84033f39210e46cc43bb3927cec3fb4b34b5c4df802e96fddaedf"}, - {file = "newrelic-9.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2236f70b8c6aa79635f2175e7315d032f3a80dfd65ad9c9ed12a921f5df4c655"}, - {file = "newrelic-9.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b8201a33caf7632b2e55e3f9687584ad6956aaf5751485cdb2bad7c428a9b400"}, - {file = "newrelic-9.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6ed4bc2c9a44dfe59958eeecf1f327f0a0fb6324b5e609515bc511944d12db74"}, - {file = "newrelic-9.10.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cc3ddb26c0615ba4e18f87453bca57f0688a43d2fcdd50e2771a77515cfc3ba"}, - {file = "newrelic-9.10.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09912303e04bee6aa1fe1c671e87b4e8e55461081a96210895828798f5ba8c3f"}, - {file = "newrelic-9.10.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:40368dca0d423efe40b210686d7018787d4365a24ee1deca136b3b7c9d850325"}, - {file = "newrelic-9.10.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56f4c309a07a2c66243b12d18056c32aa704735469741495642c31be4a1c77fa"}, - {file = "newrelic-9.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d68fc707d896dc7da8d6939bcc1f995bf9e463c2b911fc63250a10e1502a234"}, - {file = "newrelic-9.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cd462804a6ede617fb3b4b126e9083b3ee8b4ed1250f7cc12299ebacb785432"}, - {file = "newrelic-9.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ceef4fef2a5cffb69e9e1742bd18a35625ca62c3856c7016c22be68ec876753d"}, - {file = "newrelic-9.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1f11d9c17b50982fcc39de71f6592a61920ec5e5c29b9105edc9f8fb7f2480b9"}, - {file = "newrelic-9.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf6757d422954e61082715dbba4208cae17bf3720006bc337c3f87f19ede2876"}, - {file = "newrelic-9.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae84bacfdc60792bd04e681027cc5c58e6737a04c652e9be2eda84abe21f57f5"}, - {file = "newrelic-9.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:667722cf1f4ed9f6cd99f4fbe247fc2bdb941935528e14a93659ba2c651dc889"}, - {file = "newrelic-9.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0c18210648889416da3de61aa282248e012cb507ba9841511407f922fff9a52"}, - {file = "newrelic-9.10.0.tar.gz", hash = "sha256:02db25b0fd2fc835efe4a7f1c92dbc5bbb95125341aba07152041aa6a5666cda"}, + {file = "newrelic-9.13.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:52c801bd2147acfec8cbbf92adba8cab3d8d19982eceaa08409a3938d6f8524a"}, + {file = "newrelic-9.13.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:0e1e40fe4b953925f86590a74a7de2488fa37f1ba9c9f5ebcc3f765b9ba3bd1b"}, + {file = "newrelic-9.13.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:c59adb97aaa4d111a33e3f936bf85d990f8837cabe51cebb1c6128778006563f"}, + {file = "newrelic-9.13.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:0f5e404d46d6e24c920dfb0ad441e88fde633ced747f9894c1654263c9ed2c59"}, + {file = "newrelic-9.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75c73cd71005e1467a76b9108dbd355fc256e12b822b2fdc28810bde991cc92e"}, + {file = "newrelic-9.13.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd417c5873b45c8154cca92d81ab159f6c5058138ff848cba4c9e02f10ad5c61"}, + {file = "newrelic-9.13.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:273c30db450d2ae3fa0c2bce583e7d64a4cdb2b957c94bf4202bcfb30bfee411"}, + {file = "newrelic-9.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:348c298232d6323b31d65d95dc97ac55d9b0117feb713a8f1c6baa056e7f6204"}, + {file = "newrelic-9.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbfe4a0f2d34d8d9ef31cee7c73a49d3fe2b9a92129d70819058f1df736cdd38"}, + {file = "newrelic-9.13.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db3cc230970902c2548e5d747ce96d38bc009d087cf49bef4ce8679cdedc57c1"}, + {file = "newrelic-9.13.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d5fe36aae1154610d2d03cd8cdfc52b6ea3f63a0b672b14185e4e1532016f826"}, + {file = "newrelic-9.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f9ab59b30729532bab64497910c1051665269814366be8ee3fde2391032dd9f6"}, + {file = "newrelic-9.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8addb08bf7595eeb65ada2b33d9272541b6e872e519382be28690a920f4785"}, + {file = "newrelic-9.13.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:632435a5f5170dd9a72012b6c21ca62ec2e9e4b24e7d52fc4d895a359dbba652"}, + {file = "newrelic-9.13.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d720f97c844a015cd54d69e052f0956e93e45fcd33b829e8cc20356af6a0b0c4"}, + {file = "newrelic-9.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3c5cc86c88302623375e282ec17a1c55da739f2ab58ca48607f85c48a43cba33"}, + {file = "newrelic-9.13.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abc7487cff166de747da8b05b108150a6f7f2c855b3d2547cfb1f53b9330e7dd"}, + {file = "newrelic-9.13.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba2b1a0c1b31d42f5ea1a80f60e57483898faf86bb24474dbd7d0a7fad41b0"}, + {file = "newrelic-9.13.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:63636e34ab52a303ad21afd5d7f5c6418e814fcb8d3430266db4a3cfc7e19aef"}, + {file = "newrelic-9.13.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:07743279225f860444283c4dcaa7ccd02944e61ce95d666b6ab00188bb1f7f99"}, + {file = "newrelic-9.13.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14894911effd29f7bca70126ff991d4291198c6b1d9a29c9aa10d9743a685c27"}, + {file = "newrelic-9.13.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee7edcd5d364c6d2d97922aa436f5d27788a7ab9e8aa845ecaf59ddf7c66457"}, + {file = "newrelic-9.13.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:743b5ff8a2ad02989ea87334391506dbcb578436a4d8cc49e819421ca2e5f743"}, + {file = "newrelic-9.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0bd29b99dce7402635922df984c338358fbe159f0df9b153d1e913bed77210d3"}, + {file = "newrelic-9.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b70eed56613147a96a5ae4c559c42f54ea00d4d52e5679ac9b9d93c4d6e572a4"}, + {file = "newrelic-9.13.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec37f7bd9df294b17948fcfa761e0fe06301c7eaea4bbd97f4afe04dc2acbbac"}, + {file = "newrelic-9.13.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7ebbef59d94d5feaae5eab56a44df5a677b90ac75e437d48eb6b71ae7d8e6f9d"}, + {file = "newrelic-9.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49cabe8222da2515d1e6a113f9fccd8a9d41ca3678dec9592e87a01ea0005056"}, + {file = "newrelic-9.13.0.tar.gz", hash = "sha256:7405bfc65d6d983a738e756044956f06c366a234fdde0ccf7cf0d52fedfd72e4"}, ] [package.extras] @@ -1815,36 +1966,37 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] [[package]] name = "openai" -version = "1.30.5" +version = "1.51.2" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.30.5-py3-none-any.whl", hash = "sha256:2ad95e926de0d2e09cde632a9204b0a6dca4a03c2cdcc84329b01f355784355a"}, - {file = "openai-1.30.5.tar.gz", hash = "sha256:5366562eb2c5917e6116ae0391b7ae6e3acd62b0ae3f565ada32b35d8fcfa106"}, + {file = "openai-1.51.2-py3-none-any.whl", hash = "sha256:5c5954711cba931423e471c37ff22ae0fd3892be9b083eee36459865fbbb83fa"}, + {file = "openai-1.51.2.tar.gz", hash = "sha256:c6a51fac62a1ca9df85a522e462918f6bb6bc51a8897032217e453a0730123a6"}, ] [package.dependencies] anyio = ">=3.5.0,<5" distro = ">=1.7.0,<2" httpx = ">=0.23.0,<1" +jiter = ">=0.4.0,<1" pydantic = ">=1.9.0,<3" sniffio = "*" tqdm = ">4" -typing-extensions = ">=4.7,<5" +typing-extensions = ">=4.11,<5" [package.extras] datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] [[package]] name = "packaging" -version = "23.2" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] @@ -1886,36 +2038,143 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "prompt-toolkit" -version = "3.0.45" +version = "3.0.48" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.7.0" files = [ - {file = "prompt_toolkit-3.0.45-py3-none-any.whl", hash = "sha256:a29b89160e494e3ea8622b09fa5897610b437884dcdcd054fdc1308883326c2a"}, - {file = "prompt_toolkit-3.0.45.tar.gz", hash = "sha256:07c60ee4ab7b7e90824b61afa840c8f5aad2d46b3e2e10acc33d8ecc94a49089"}, + {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"}, + {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, ] [package.dependencies] wcwidth = "*" +[[package]] +name = "propcache" +version = "0.2.0" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.8" +files = [ + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336"}, + {file = "propcache-0.2.0-cp310-cp310-win32.whl", hash = "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad"}, + {file = "propcache-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b"}, + {file = "propcache-0.2.0-cp311-cp311-win32.whl", hash = "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1"}, + {file = "propcache-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348"}, + {file = "propcache-0.2.0-cp312-cp312-win32.whl", hash = "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5"}, + {file = "propcache-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544"}, + {file = "propcache-0.2.0-cp313-cp313-win32.whl", hash = "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032"}, + {file = "propcache-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed"}, + {file = "propcache-0.2.0-cp38-cp38-win32.whl", hash = "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d"}, + {file = "propcache-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798"}, + {file = "propcache-0.2.0-cp39-cp39-win32.whl", hash = "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9"}, + {file = "propcache-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df"}, + {file = "propcache-0.2.0-py3-none-any.whl", hash = "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036"}, + {file = "propcache-0.2.0.tar.gz", hash = "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70"}, +] + [[package]] name = "protobuf" -version = "4.25.4" +version = "4.25.5" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.4-cp310-abi3-win32.whl", hash = "sha256:db9fd45183e1a67722cafa5c1da3e85c6492a5383f127c86c4c4aa4845867dc4"}, - {file = "protobuf-4.25.4-cp310-abi3-win_amd64.whl", hash = "sha256:ba3d8504116a921af46499471c63a85260c1a5fc23333154a427a310e015d26d"}, - {file = "protobuf-4.25.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:eecd41bfc0e4b1bd3fa7909ed93dd14dd5567b98c941d6c1ad08fdcab3d6884b"}, - {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4c8a70fdcb995dcf6c8966cfa3a29101916f7225e9afe3ced4395359955d3835"}, - {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:3319e073562e2515c6ddc643eb92ce20809f5d8f10fead3332f71c63be6a7040"}, - {file = "protobuf-4.25.4-cp38-cp38-win32.whl", hash = "sha256:7e372cbbda66a63ebca18f8ffaa6948455dfecc4e9c1029312f6c2edcd86c4e1"}, - {file = "protobuf-4.25.4-cp38-cp38-win_amd64.whl", hash = "sha256:051e97ce9fa6067a4546e75cb14f90cf0232dcb3e3d508c448b8d0e4265b61c1"}, - {file = "protobuf-4.25.4-cp39-cp39-win32.whl", hash = "sha256:90bf6fd378494eb698805bbbe7afe6c5d12c8e17fca817a646cd6a1818c696ca"}, - {file = "protobuf-4.25.4-cp39-cp39-win_amd64.whl", hash = "sha256:ac79a48d6b99dfed2729ccccee547b34a1d3d63289c71cef056653a846a2240f"}, - {file = "protobuf-4.25.4-py3-none-any.whl", hash = "sha256:bfbebc1c8e4793cfd58589acfb8a1026be0003e852b9da7db5a4285bde996978"}, - {file = "protobuf-4.25.4.tar.gz", hash = "sha256:0dc4a62cc4052a036ee2204d26fe4d835c62827c855c8a03f29fe6da146b380d"}, + {file = "protobuf-4.25.5-cp310-abi3-win32.whl", hash = "sha256:5e61fd921603f58d2f5acb2806a929b4675f8874ff5f330b7d6f7e2e784bbcd8"}, + {file = "protobuf-4.25.5-cp310-abi3-win_amd64.whl", hash = "sha256:4be0571adcbe712b282a330c6e89eae24281344429ae95c6d85e79e84780f5ea"}, + {file = "protobuf-4.25.5-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:b2fde3d805354df675ea4c7c6338c1aecd254dfc9925e88c6d31a2bcb97eb173"}, + {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:919ad92d9b0310070f8356c24b855c98df2b8bd207ebc1c0c6fcc9ab1e007f3d"}, + {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fe14e16c22be926d3abfcb500e60cab068baf10b542b8c858fa27e098123e331"}, + {file = "protobuf-4.25.5-cp38-cp38-win32.whl", hash = "sha256:98d8d8aa50de6a2747efd9cceba361c9034050ecce3e09136f90de37ddba66e1"}, + {file = "protobuf-4.25.5-cp38-cp38-win_amd64.whl", hash = "sha256:b0234dd5a03049e4ddd94b93400b67803c823cfc405689688f59b34e0742381a"}, + {file = "protobuf-4.25.5-cp39-cp39-win32.whl", hash = "sha256:abe32aad8561aa7cc94fc7ba4fdef646e576983edb94a73381b03c53728a626f"}, + {file = "protobuf-4.25.5-cp39-cp39-win_amd64.whl", hash = "sha256:7a183f592dc80aa7c8da7ad9e55091c4ffc9497b3054452d629bb85fa27c2a45"}, + {file = "protobuf-4.25.5-py3-none-any.whl", hash = "sha256:0aebecb809cae990f8129ada5ca273d9d670b76d9bfc9b1809f0a9c02b7dbf41"}, + {file = "protobuf-4.25.5.tar.gz", hash = "sha256:7f8249476b4a9473645db7f8ab42b02fe1488cbe5fb72fddd445e0665afd8584"}, ] [[package]] @@ -2001,24 +2260,24 @@ files = [ [[package]] name = "pyasn1" -version = "0.6.0" +version = "0.6.1" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" optional = false python-versions = ">=3.8" files = [ - {file = "pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473"}, - {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"}, + {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, + {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, ] [[package]] name = "pyasn1-modules" -version = "0.4.0" +version = "0.4.1" description = "A collection of ASN.1-based protocols modules" optional = false python-versions = ">=3.8" files = [ - {file = "pyasn1_modules-0.4.0-py3-none-any.whl", hash = "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b"}, - {file = "pyasn1_modules-0.4.0.tar.gz", hash = "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6"}, + {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"}, + {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"}, ] [package.dependencies] @@ -2076,109 +2335,123 @@ files = [ [[package]] name = "pydantic" -version = "2.7.4" +version = "2.9.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"}, - {file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"}, + {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, + {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, ] [package.dependencies] -annotated-types = ">=0.4.0" -pydantic-core = "2.18.4" -typing-extensions = ">=4.6.1" +annotated-types = ">=0.6.0" +pydantic-core = "2.23.4" +typing-extensions = [ + {version = ">=4.6.1", markers = "python_version < \"3.13\""}, + {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, +] [package.extras] email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.18.4" +version = "2.23.4" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"}, - {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"}, - {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"}, - {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"}, - {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"}, - {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"}, - {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"}, - {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"}, - {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"}, - {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"}, - {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"}, - {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"}, - {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"}, - {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"}, - {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"}, - {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"}, - {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"}, - {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"}, - {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"}, - {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"}, - {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"}, - {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"}, - {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"}, - {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"}, - {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"}, - {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"}, - {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"}, - {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"}, - {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"}, - {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"}, - {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"}, - {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"}, - {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, + {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, + {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, + {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, + {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, + {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, + {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, + {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, + {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, + {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, + {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, + {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, + {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, + {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, ] [package.dependencies] @@ -2206,71 +2479,70 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] [[package]] name = "pymongo" -version = "4.7.2" +version = "4.9.2" description = "Python driver for MongoDB " optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pymongo-4.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:268d8578c0500012140c5460755ea405cbfe541ef47c81efa9d6744f0f99aeca"}, - {file = "pymongo-4.7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:827611beb6c483260d520cfa6a49662d980dfa5368a04296f65fa39e78fccea7"}, - {file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a754e366c404d19ff3f077ddeed64be31e0bb515e04f502bf11987f1baa55a16"}, - {file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c44efab10d9a3db920530f7bcb26af8f408b7273d2f0214081d3891979726328"}, - {file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35b3f0c7d49724859d4df5f0445818d525824a6cd55074c42573d9b50764df67"}, - {file = "pymongo-4.7.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e37faf298a37ffb3e0809e77fbbb0a32b6a2d18a83c59cfc2a7b794ea1136b0"}, - {file = "pymongo-4.7.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1bcd58669e56c08f1e72c5758868b5df169fe267501c949ee83c418e9df9155"}, - {file = "pymongo-4.7.2-cp310-cp310-win32.whl", hash = "sha256:c72d16fede22efe7cdd1f422e8da15760e9498024040429362886f946c10fe95"}, - {file = "pymongo-4.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:12d1fef77d25640cb78893d07ff7d2fac4c4461d8eec45bd3b9ad491a1115d6e"}, - {file = "pymongo-4.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fc5af24fcf5fc6f7f40d65446400d45dd12bea933d0299dc9e90c5b22197f1e9"}, - {file = "pymongo-4.7.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:730778b6f0964b164c187289f906bbc84cb0524df285b7a85aa355bbec43eb21"}, - {file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47a1a4832ef2f4346dcd1a10a36ade7367ad6905929ddb476459abb4fd1b98cb"}, - {file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6eab12c6385526d386543d6823b07187fefba028f0da216506e00f0e1855119"}, - {file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37e9ea81fa59ee9274457ed7d59b6c27f6f2a5fe8e26f184ecf58ea52a019cb8"}, - {file = "pymongo-4.7.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e9d9d2c0aae73aa4369bd373ac2ac59f02c46d4e56c4b6d6e250cfe85f76802"}, - {file = "pymongo-4.7.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb6e00a79dff22c9a72212ad82021b54bdb3b85f38a85f4fc466bde581d7d17a"}, - {file = "pymongo-4.7.2-cp311-cp311-win32.whl", hash = "sha256:02efd1bb3397e24ef2af45923888b41a378ce00cb3a4259c5f4fc3c70497a22f"}, - {file = "pymongo-4.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:87bb453ac3eb44db95cb6d5a616fbc906c1c00661eec7f55696253a6245beb8a"}, - {file = "pymongo-4.7.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:12c466e02133b7f8f4ff1045c6b5916215c5f7923bc83fd6e28e290cba18f9f6"}, - {file = "pymongo-4.7.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f91073049c43d14e66696970dd708d319b86ee57ef9af359294eee072abaac79"}, - {file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87032f818bf5052ab742812c715eff896621385c43f8f97cdd37d15b5d394e95"}, - {file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a87eef394039765679f75c6a47455a4030870341cb76eafc349c5944408c882"}, - {file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d275596f840018858757561840767b39272ac96436fcb54f5cac6d245393fd97"}, - {file = "pymongo-4.7.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82102e353be13f1a6769660dd88115b1da382447672ba1c2662a0fbe3df1d861"}, - {file = "pymongo-4.7.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:194065c9d445017b3c82fb85f89aa2055464a080bde604010dc8eb932a6b3c95"}, - {file = "pymongo-4.7.2-cp312-cp312-win32.whl", hash = "sha256:db4380d1e69fdad1044a4b8f3bb105200542c49a0dde93452d938ff9db1d6d29"}, - {file = "pymongo-4.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:fadc6e8db7707c861ebe25b13ad6aca19ea4d2c56bf04a26691f46c23dadf6e4"}, - {file = "pymongo-4.7.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2cb77d09bd012cb4b30636e7e38d00b5f9be5eb521c364bde66490c45ee6c4b4"}, - {file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56bf8b706946952acdea0fe478f8e44f1ed101c4b87f046859e6c3abe6c0a9f4"}, - {file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcf337d1b252405779d9c79978d6ca15eab3cdaa2f44c100a79221bddad97c8a"}, - {file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ffd1519edbe311df73c74ec338de7d294af535b2748191c866ea3a7c484cd15"}, - {file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d59776f435564159196d971aa89422ead878174aff8fe18e06d9a0bc6d648c"}, - {file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:347c49cf7f0ba49ea87c1a5a1984187ecc5516b7c753f31938bf7b37462824fd"}, - {file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:84bc00200c3cbb6c98a2bb964c9e8284b641e4a33cf10c802390552575ee21de"}, - {file = "pymongo-4.7.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fcaf8c911cb29316a02356f89dbc0e0dfcc6a712ace217b6b543805690d2aefd"}, - {file = "pymongo-4.7.2-cp37-cp37m-win32.whl", hash = "sha256:b48a5650ee5320d59f6d570bd99a8d5c58ac6f297a4e9090535f6561469ac32e"}, - {file = "pymongo-4.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:5239ef7e749f1326ea7564428bf861d5250aa39d7f26d612741b1b1273227062"}, - {file = "pymongo-4.7.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2dcf608d35644e8d276d61bf40a93339d8d66a0e5f3e3f75b2c155a421a1b71"}, - {file = "pymongo-4.7.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:25eeb2c18ede63891cbd617943dd9e6b9cbccc54f276e0b2e693a0cc40f243c5"}, - {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9349f0bb17a31371d4cacb64b306e4ca90413a3ad1fffe73ac7cd495570d94b5"}, - {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffd4d7cb2e6c6e100e2b39606d38a9ffc934e18593dc9bb326196afc7d93ce3d"}, - {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a8bd37f5dabc86efceb8d8cbff5969256523d42d08088f098753dba15f3b37a"}, - {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c78f156edc59b905c80c9003e022e1a764c54fd40ac4fea05b0764f829790e2"}, - {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d892fb91e81cccb83f507cdb2ea0aa026ec3ced7f12a1d60f6a5bf0f20f9c1f"}, - {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:87832d6076c2c82f42870157414fd876facbb6554d2faf271ffe7f8f30ce7bed"}, - {file = "pymongo-4.7.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ce1a374ea0e49808e0380ffc64284c0ce0f12bd21042b4bef1af3eb7bdf49054"}, - {file = "pymongo-4.7.2-cp38-cp38-win32.whl", hash = "sha256:eb0642e5f0dd7e86bb358749cc278e70b911e617f519989d346f742dc9520dfb"}, - {file = "pymongo-4.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:4bdb5ffe1cd3728c9479671a067ef44dacafc3743741d4dc700c377c4231356f"}, - {file = "pymongo-4.7.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:743552033c63f0afdb56b9189ab04b5c1dbffd7310cf7156ab98eebcecf24621"}, - {file = "pymongo-4.7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5239776633f7578b81207e5646245415a5a95f6ae5ef5dff8e7c2357e6264bfc"}, - {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:727ad07952c155cd20045f2ce91143c7dc4fb01a5b4e8012905a89a7da554b0c"}, - {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9385654f01a90f73827af4db90c290a1519f7d9102ba43286e187b373e9a78e9"}, - {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d833651f1ba938bb7501f13e326b96cfbb7d98867b2d545ca6d69c7664903e0"}, - {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf17ea9cea14d59b0527403dd7106362917ced7c4ec936c4ba22bd36c912c8e0"}, - {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cecd2df037249d1c74f0af86fb5b766104a5012becac6ff63d85d1de53ba8b98"}, - {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65b4c00dedbd333698b83cd2095a639a6f0d7c4e2a617988f6c65fb46711f028"}, - {file = "pymongo-4.7.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d9b6cbc037108ff1a0a867e7670d8513c37f9bcd9ee3d2464411bfabf70ca002"}, - {file = "pymongo-4.7.2-cp39-cp39-win32.whl", hash = "sha256:cf28430ec1924af1bffed37b69a812339084697fd3f3e781074a0148e6475803"}, - {file = "pymongo-4.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:e004527ea42a6b99a8b8d5b42b42762c3bdf80f88fbdb5c3a9d47f3808495b86"}, - {file = "pymongo-4.7.2.tar.gz", hash = "sha256:9024e1661c6e40acf468177bf90ce924d1bc681d2b244adda3ed7b2f4c4d17d7"}, + {file = "pymongo-4.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab8d54529feb6e29035ba8f0570c99ad36424bc26486c238ad7ce28597bc43c8"}, + {file = "pymongo-4.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f928bdc152a995cbd0b563fab201b2df873846d11f7a41d1f8cc8a01b35591ab"}, + {file = "pymongo-4.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6e7251d59fa3dcbb1399a71a3aec63768cebc6b22180b671601c2195fe1f90a"}, + {file = "pymongo-4.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e759ed0459e7264a11b6896016f616341a8e4c6ab7f71ae651bd21ffc7e9524"}, + {file = "pymongo-4.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f3fc60f242191840ccf02b898bc615b5141fbb70064f38f7e60fcaa35d3b5efd"}, + {file = "pymongo-4.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c798351666ac97a0ddaa823689061c3af949c2d6acf7fb2d9ab0a7f465ced79"}, + {file = "pymongo-4.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aac78b5fdd49ed8cae49adf76befacb02293a23b412676775c4715148e166d85"}, + {file = "pymongo-4.9.2-cp310-cp310-win32.whl", hash = "sha256:bf77bf175c315e299a91332c2bbebc097c4d4fcc8713e513a9861684aa39023a"}, + {file = "pymongo-4.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:c42b5aad8971256365bfd0a545fb1c7a199c93db80decd298ea2f987419e2a6d"}, + {file = "pymongo-4.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:99e40f44877b32bf4b3c46ceed2228f08c222cf7dec8a4366dd192a1429143fa"}, + {file = "pymongo-4.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6f6834d575ed87edc7dfcab4501d961b6a423b3839edd29ecb1382eee7736777"}, + {file = "pymongo-4.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3010018f5672e5b7e8d096dea9f1ea6545b05345ff0eb1754f6ee63785550773"}, + {file = "pymongo-4.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69394ee9f0ce38ff71266bad01b7e045cd75e58500ebad5d72187cbabf2e652a"}, + {file = "pymongo-4.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87b18094100f21615d9db99c255dcd9e93e476f10fb03c1d3632cf4b82d201d2"}, + {file = "pymongo-4.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3039e093d28376d6a54bdaa963ca12230c8a53d7b19c8e6368e19bcfbd004176"}, + {file = "pymongo-4.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ab42d9ee93fe6b90020c42cba5bfb43a2b4660951225d137835efc21940da48"}, + {file = "pymongo-4.9.2-cp311-cp311-win32.whl", hash = "sha256:a663ca60e187a248d370c58961e40f5463077d2b43831eb92120ea28a79ecf96"}, + {file = "pymongo-4.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:24e7b6887bbfefd05afed26a99a2c69459e2daa351a43a410de0d6c0ee3cce4e"}, + {file = "pymongo-4.9.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8083bbe8cb10bb33dca4d93f8223dd8d848215250bb73867374650bac5fe69e1"}, + {file = "pymongo-4.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a1b8c636bf557c7166e3799bbf1120806ca39e3f06615b141c88d9c9ceae4d8c"}, + {file = "pymongo-4.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8aac5dce28454f47576063fbad31ea9789bba67cab86c95788f97aafd810e65b"}, + {file = "pymongo-4.9.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1d5e7123af1fddf15b2b53e58f20bf5242884e671bcc3860f5e954fe13aeddd"}, + {file = "pymongo-4.9.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe97c847b56d61e533a7af0334193d6b28375b9189effce93129c7e4733794a9"}, + {file = "pymongo-4.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96ad54433a996e2d1985a9cd8fc82538ca8747c95caae2daf453600cc8c317f9"}, + {file = "pymongo-4.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:98b9cade40f5b13e04492a42ae215c3721099be1014ddfe0fbd23f27e4f62c0c"}, + {file = "pymongo-4.9.2-cp312-cp312-win32.whl", hash = "sha256:dde6068ae7c62ea8ee2c5701f78c6a75618cada7e11f03893687df87709558de"}, + {file = "pymongo-4.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:e1ab6cd7cd2d38ffc7ccdc79fdc166c7a91a63f844a96e3e6b2079c054391c68"}, + {file = "pymongo-4.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1ad79d6a74f439a068caf9a1e2daeabc20bf895263435484bbd49e90fbea7809"}, + {file = "pymongo-4.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:877699e21703717507cbbea23e75b419f81a513b50b65531e1698df08b2d7094"}, + {file = "pymongo-4.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc9322ce7cf116458a637ac10517b0c5926a8211202be6dbdc51dab4d4a9afc8"}, + {file = "pymongo-4.9.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cca029f46acf475504eedb33c7839f030c4bc4f946dcba12d9a954cc48850b79"}, + {file = "pymongo-4.9.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c8c861e77527eec5a4b7363c16030dd0374670b620b08a5300f97594bbf5a40"}, + {file = "pymongo-4.9.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fc70326ae71b3c7b8d6af82f46bb71dafdba3c8f335b29382ae9cf263ef3a5c"}, + {file = "pymongo-4.9.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba9d2f6df977fee24437f82f7412460b0628cd6b961c4235c9cff71577a5b61f"}, + {file = "pymongo-4.9.2-cp313-cp313-win32.whl", hash = "sha256:b3254769e708bc4aa634745c262081d13c841a80038eff3afd15631540a1d227"}, + {file = "pymongo-4.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:169b85728cc17800344ba17d736375f400ef47c9fbb4c42910c4b3e7c0247382"}, + {file = "pymongo-4.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3f28afd783be3cebef1235a45340589169d7774cd9909ba0249e2f851ff511d"}, + {file = "pymongo-4.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7a0b2e7fedc5911cd44590b5fd8e3714029f378f37f3c0c2043f67150b588d4a"}, + {file = "pymongo-4.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5af264b9a973859123e3129d131d7246f57659304400e3e6b35ed6eaf099854d"}, + {file = "pymongo-4.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65c6b2e2a6db38f49433021dda0802ad081118224b2264500ef03a2d82ae26a7"}, + {file = "pymongo-4.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:410ea165f2f819118eed764c5faa35fa71aeff5ce8b5046af99ed158a5661e9e"}, + {file = "pymongo-4.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c3c71337d4c923f719cb56253af9244e90353a2454088ee4f184bfb0dd446a4"}, + {file = "pymongo-4.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:77528a2b928fe3f1f655cefa195e6718ab1ccd1a456aba486d76318e526a7fac"}, + {file = "pymongo-4.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fdbd558d90b55d7c39c096a79f8a725f1f02b658211924ab98dbc03ecad01095"}, + {file = "pymongo-4.9.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e3ff4201ea707f57bf381f61df0e9cd6e896627a59f98a5d1c4a1bd14a2544cb"}, + {file = "pymongo-4.9.2-cp38-cp38-win32.whl", hash = "sha256:ae227bba43e2e6fc8c3440a70b3b8f9ab2b0eb0906d0d2cf814dd9490c572e2a"}, + {file = "pymongo-4.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:a92c96886048d3ebae62dbcfc775c7f2b965270160e3cb6aab4e06750e030b05"}, + {file = "pymongo-4.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e54e2c6f1dec45c57a587b4c13c16666d5f7c031a642ae177140d1e0551a947e"}, + {file = "pymongo-4.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a49d9292f22a0395c0fd2822a06e385910f1f902c3a9feafc1d0bfc27cd2df6b"}, + {file = "pymongo-4.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80a1ee9b72eebd96619ebe0beb718a5bcf2a70f464edf315f97b9315ed6854a9"}, + {file = "pymongo-4.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea9c47f86a322280381e9ddba7491e664ea80bf75df247ea2346faf7626e4e4c"}, + {file = "pymongo-4.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf963104dfd7235bebc44cef40b4b12c6638bb03b3a828cb495498e286b6edd0"}, + {file = "pymongo-4.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f13330bdf4a57ef70bdd6282721547ec464f773203be47bac1efc4abd74a9190"}, + {file = "pymongo-4.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fb10d7069f1e7d7d6a458b1c5e9d1454be6eca2d9885bec25c1202e22c88d2a"}, + {file = "pymongo-4.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cd832de5df92caa68ee66c872708951d7e0c1f7b289b74189f2ccf1832c56dda"}, + {file = "pymongo-4.9.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3f55efe0f77198c055800e605268bfd77a3f0223d1a80b55b771d0c350bc3ade"}, + {file = "pymongo-4.9.2-cp39-cp39-win32.whl", hash = "sha256:f2f43e5d6e739aa78c7053bdf351453c0e53d7667a3cac73255c2169631e052a"}, + {file = "pymongo-4.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:31c35d3dac5a1b0f65b3da2a19dc7fb88271c86329c75cfea775d5381ade6c06"}, + {file = "pymongo-4.9.2.tar.gz", hash = "sha256:3e63535946f5df7848307b9031aa921f82bb0cbe45f9b0c3296f2173f9283eb0"}, ] [package.dependencies] @@ -2278,11 +2550,12 @@ dnspython = ">=1.16.0,<3.0.0" [package.extras] aws = ["pymongo-auth-aws (>=1.1.0,<2.0.0)"] -encryption = ["certifi", "pymongo-auth-aws (>=1.1.0,<2.0.0)", "pymongocrypt (>=1.6.0,<2.0.0)"] +docs = ["furo (==2023.9.10)", "readthedocs-sphinx-search (>=0.3,<1.0)", "sphinx (>=5.3,<8)", "sphinx-autobuild (>=2020.9.1)", "sphinx-rtd-theme (>=2,<3)", "sphinxcontrib-shellcheck (>=1,<2)"] +encryption = ["certifi", "pymongo-auth-aws (>=1.1.0,<2.0.0)", "pymongocrypt (>=1.10.0,<2.0.0)"] gssapi = ["pykerberos", "winkerberos (>=0.5.0)"] ocsp = ["certifi", "cryptography (>=2.5)", "pyopenssl (>=17.2.0)", "requests (<3.0.0)", "service-identity (>=18.1.0)"] snappy = ["python-snappy"] -test = ["pytest (>=7)"] +test = ["pytest (>=8.2)", "pytest-asyncio (>=0.24.0)"] zstd = ["zstandard"] [[package]] @@ -2394,191 +2667,192 @@ files = [ [[package]] name = "pywin32" -version = "306" +version = "307" description = "Python for Window Extensions" optional = false python-versions = "*" files = [ - {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, - {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, - {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, - {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, - {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, - {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, - {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, - {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, - {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, - {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, - {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, - {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, - {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, - {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, + {file = "pywin32-307-cp310-cp310-win32.whl", hash = "sha256:f8f25d893c1e1ce2d685ef6d0a481e87c6f510d0f3f117932781f412e0eba31b"}, + {file = "pywin32-307-cp310-cp310-win_amd64.whl", hash = "sha256:36e650c5e5e6b29b5d317385b02d20803ddbac5d1031e1f88d20d76676dd103d"}, + {file = "pywin32-307-cp310-cp310-win_arm64.whl", hash = "sha256:0c12d61e0274e0c62acee79e3e503c312426ddd0e8d4899c626cddc1cafe0ff4"}, + {file = "pywin32-307-cp311-cp311-win32.whl", hash = "sha256:fec5d27cc893178fab299de911b8e4d12c5954e1baf83e8a664311e56a272b75"}, + {file = "pywin32-307-cp311-cp311-win_amd64.whl", hash = "sha256:987a86971753ed7fdd52a7fb5747aba955b2c7fbbc3d8b76ec850358c1cc28c3"}, + {file = "pywin32-307-cp311-cp311-win_arm64.whl", hash = "sha256:fd436897c186a2e693cd0437386ed79f989f4d13d6f353f8787ecbb0ae719398"}, + {file = "pywin32-307-cp312-cp312-win32.whl", hash = "sha256:07649ec6b01712f36debf39fc94f3d696a46579e852f60157a729ac039df0815"}, + {file = "pywin32-307-cp312-cp312-win_amd64.whl", hash = "sha256:00d047992bb5dcf79f8b9b7c81f72e0130f9fe4b22df613f755ab1cc021d8347"}, + {file = "pywin32-307-cp312-cp312-win_arm64.whl", hash = "sha256:b53658acbfc6a8241d72cc09e9d1d666be4e6c99376bc59e26cdb6223c4554d2"}, + {file = "pywin32-307-cp313-cp313-win32.whl", hash = "sha256:ea4d56e48dc1ab2aa0a5e3c0741ad6e926529510516db7a3b6981a1ae74405e5"}, + {file = "pywin32-307-cp313-cp313-win_amd64.whl", hash = "sha256:576d09813eaf4c8168d0bfd66fb7cb3b15a61041cf41598c2db4a4583bf832d2"}, + {file = "pywin32-307-cp313-cp313-win_arm64.whl", hash = "sha256:b30c9bdbffda6a260beb2919f918daced23d32c79109412c2085cbc513338a0a"}, + {file = "pywin32-307-cp37-cp37m-win32.whl", hash = "sha256:5101472f5180c647d4525a0ed289ec723a26231550dbfd369ec19d5faf60e511"}, + {file = "pywin32-307-cp37-cp37m-win_amd64.whl", hash = "sha256:05de55a7c110478dc4b202230e98af5e0720855360d2b31a44bb4e296d795fba"}, + {file = "pywin32-307-cp38-cp38-win32.whl", hash = "sha256:13d059fb7f10792542082f5731d5d3d9645320fc38814759313e5ee97c3fac01"}, + {file = "pywin32-307-cp38-cp38-win_amd64.whl", hash = "sha256:7e0b2f93769d450a98ac7a31a087e07b126b6d571e8b4386a5762eb85325270b"}, + {file = "pywin32-307-cp39-cp39-win32.whl", hash = "sha256:55ee87f2f8c294e72ad9d4261ca423022310a6e79fb314a8ca76ab3f493854c6"}, + {file = "pywin32-307-cp39-cp39-win_amd64.whl", hash = "sha256:e9d5202922e74985b037c9ef46778335c102b74b95cec70f629453dbe7235d87"}, ] [[package]] name = "pyyaml" -version = "6.0.1" +version = "6.0.2" description = "YAML parser and emitter for Python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] [[package]] name = "rapidfuzz" -version = "3.9.5" +version = "3.10.0" description = "rapid fuzzy string matching" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "rapidfuzz-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7659058863d84a2c36c5a76c28bc8713d33eab03e677e67260d9e1cca43fc3bb"}, - {file = "rapidfuzz-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:802a018776bd3cb7c5d23ba38ebbb1663a9f742be1d58e73b62d8c7cace6e607"}, - {file = "rapidfuzz-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da71e8fdb0d1a21f4b58b2c84bcbc2b89a472c073c5f7bdb9339f4cb3122c0e3"}, - {file = "rapidfuzz-3.9.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9433cb12731167b358fbcff9828d2294429986a03222031f6d14308eb643c77"}, - {file = "rapidfuzz-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e33e1d185206730b916b3e7d9bce1941c65b2a1488cdd0457ae21be385a7912"}, - {file = "rapidfuzz-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:758719e9613c47a274768f1926460955223fe0a03e7eda264f2b78b1b97a4743"}, - {file = "rapidfuzz-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7981cc6240d01d4480795d758ea2ee748257771f68127d630045e58fe1b5545a"}, - {file = "rapidfuzz-3.9.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b6cdca86120c3f9aa069f8d4e1c5422e92f833d705d719a2ba7082412f4c933b"}, - {file = "rapidfuzz-3.9.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ffa533acb1a9dcb6e26c4467fdc1347995fb168ec9f794b97545f6b72dee733c"}, - {file = "rapidfuzz-3.9.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:13eeaeb0d5fe00fa99336f73fb5ab65c46109c7121cf87659b9601908b8b6178"}, - {file = "rapidfuzz-3.9.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:d7b1922b1403ccb3583218e8cd931b08e04c5442ca03dbaf6ea4fcf574ee2b24"}, - {file = "rapidfuzz-3.9.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b0189f691cea4dc9fe074ea6b97da30a91d0882fa69724b4b34b51d2c1983473"}, - {file = "rapidfuzz-3.9.5-cp310-cp310-win32.whl", hash = "sha256:72e466e5de12a327a09ed6d0116f024759b5146b335645c32241da84134a7f34"}, - {file = "rapidfuzz-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:345011cfcafaa3674c46673baad67d2394eb58285530d8333e65c3c9a143b4f4"}, - {file = "rapidfuzz-3.9.5-cp310-cp310-win_arm64.whl", hash = "sha256:5dc19c8222475e4f7f528b94d2fa28e7979355c5cf7c6e73902d2abb2be96522"}, - {file = "rapidfuzz-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c741972d64031535cfd76d89cf47259e590e822353be57ec2f5d56758c98296"}, - {file = "rapidfuzz-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7452d079800cf70a7314f73044f03cbcbd90a651d9dec39443d2a8a2b63ab53"}, - {file = "rapidfuzz-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f06f163a0341bad162e972590b73e17f9cea2ed8ee27b193875ccbc3dd6eca2f"}, - {file = "rapidfuzz-3.9.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:529e2cf441746bd492f6c36a38bf9fa6a418df95b9c003f8e92a55d8a979bd9c"}, - {file = "rapidfuzz-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9811a741aa1350ad36689d675ded8b34e423e68b396bd30bff751a9c582f586e"}, - {file = "rapidfuzz-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e36c4640a789b8c922b69a548968939d1c0433fa7aac83cb08e1334d4e5d7de"}, - {file = "rapidfuzz-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53fb2f32f14c921d2f673c5b7cd58d4cc626c574a28c0791f283880d8e57022c"}, - {file = "rapidfuzz-3.9.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:031806eb035a6f09f4ff23b9d971d50b30b5e93aa3ee620c920bee1dc32827e7"}, - {file = "rapidfuzz-3.9.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f6dbe1df0b9334e3cf07445d810c81734ae23d137b5efc69e1d676ff55691351"}, - {file = "rapidfuzz-3.9.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:24345826b50aafcea26e2e4be5c103d96fe9d7fc549ac9190641300290958f3b"}, - {file = "rapidfuzz-3.9.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bfd3b66ee1f0ebb40c672a7a7e5bda00fb763fa9bca082058084175151f8e685"}, - {file = "rapidfuzz-3.9.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a6f1df5b0e602e94199cccb5e241bbc2319644003e34f077741ebf48aea7ed1a"}, - {file = "rapidfuzz-3.9.5-cp311-cp311-win32.whl", hash = "sha256:f080d6709f51a8335e73826b96af9b4e3657631eca6c69e1ac501868dcc84b7f"}, - {file = "rapidfuzz-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:bf9ed6988da6a2c1f8df367cb5d6be26a3d8543646c8eba79741ac9e764fbc59"}, - {file = "rapidfuzz-3.9.5-cp311-cp311-win_arm64.whl", hash = "sha256:599714790dfac0a23a473134e6677d0a103690a4e21ba189cfc826e322cdc8d5"}, - {file = "rapidfuzz-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9729852038fb2de096e249899f8a9bee90fb1f92e10b6ccc539d5bb798c703bc"}, - {file = "rapidfuzz-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9dc39435476fb3b3b3c24ab2c08c726056b2b487aa7ee450aee698b808c808ac"}, - {file = "rapidfuzz-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6ceea632b0eb97dac54411c29feb190054e91fd0571f585b56e4a9159c55ab0"}, - {file = "rapidfuzz-3.9.5-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cadd66e6ef9901909dc1b11db91048f1bf4613ba7d773386f922e28b1e1df4da"}, - {file = "rapidfuzz-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63e34fb3586431589a5e1cd7fc61c6f057576c6c6804c1c673bac3de0516dee7"}, - {file = "rapidfuzz-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:181073256faec68e6b8ab3329a36cfa1360f7906aa70d9aee4a39cb70889f73f"}, - {file = "rapidfuzz-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8419c18bbbd67058ca1312f35acda2e4e4592650f105cfd166569a2ebccd01f1"}, - {file = "rapidfuzz-3.9.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:191d1057cca56641f7b919fe712cb7e48cd226342e097a78136127f8bde32caa"}, - {file = "rapidfuzz-3.9.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fe5a11eefd0ae90d32d9ff706a894498b4efb4b0c263ad9d1e6401050863504d"}, - {file = "rapidfuzz-3.9.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e1b024d9d69bb83e125adee4162991f2764f16acc3fb1ed0f0fc1ad5aeb7e394"}, - {file = "rapidfuzz-3.9.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7d5a34b8388ae99bdbd5a3646f45ac318f4c870105bdbe42a2f4c85e5b347761"}, - {file = "rapidfuzz-3.9.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0e09abc0d397019bba61c8e6dfe2ec863d4dfb1762f51c9197ce0af5d5fd9adb"}, - {file = "rapidfuzz-3.9.5-cp312-cp312-win32.whl", hash = "sha256:e3c4be3057472c79ba6f4eab35daa9f12908cb697c472d05fbbd47949a87aec6"}, - {file = "rapidfuzz-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:0d9fdb74df87018dd4146f3d00df9fca2c27f060936a9e8d3015e7bfb9cb69e4"}, - {file = "rapidfuzz-3.9.5-cp312-cp312-win_arm64.whl", hash = "sha256:491d3d425b5fe3f61f3b9a70abfd498ce9139d94956db7a8551e537e017c0e57"}, - {file = "rapidfuzz-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:518dec750a30f115ba1299ef2547cf468a69f310581a030c8a875257de747c5f"}, - {file = "rapidfuzz-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:252dc3d1c3d613b8db1b59d13381937e420c99f8a351ffa0e78c2f54746e107f"}, - {file = "rapidfuzz-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd17688b75b6fa983e8586cad30f36eb9736b860946cc8b633b9442c9481831"}, - {file = "rapidfuzz-3.9.5-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8032492021b0aa55a623d6f6e739a5d4aaabc32af379c2a5656bf1e9e178bf1"}, - {file = "rapidfuzz-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73362eb1c3d02f32e4c7f0d77eb284e9a13f278cff224f71e8f60e2aff5b6a5d"}, - {file = "rapidfuzz-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a42d1f7b8988f50013e703ed27b5e216ef8a725b2f4ac53754ad0476020b26f4"}, - {file = "rapidfuzz-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4f2e985172bb76c9179e11fb67d9c9ecbee4933740eca2977797094df02498d"}, - {file = "rapidfuzz-3.9.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8e943c5cbd10e15369be1f371ef303cb413c1008f64d93bd13762ea06ca84d59"}, - {file = "rapidfuzz-3.9.5-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:0d34b0e8e29f80cb2ac8afe8fb7b01a542b136ffbf7e2b9983d11bce49398f68"}, - {file = "rapidfuzz-3.9.5-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:62b8f9f58e9dffaa86fef84db2705457a58e191a962124f2b815026ba79d9aba"}, - {file = "rapidfuzz-3.9.5-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:ebf682bdb0f01b6b1f9a9ffe918aa3ac84fbdadb998ffbfcd5f9b12bd280170f"}, - {file = "rapidfuzz-3.9.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3ed0c17e5b6fdd2ac3230bdefa908579971377c36aa4a2f132700fa8145040db"}, - {file = "rapidfuzz-3.9.5-cp38-cp38-win32.whl", hash = "sha256:ac460d89b9759e37eef23fed05184179654882a241f6b2363df194f8940cc55f"}, - {file = "rapidfuzz-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:cf9aceb4227fd09f9a20e505f78487b2089d6420ce232d288522ea0a78b986b9"}, - {file = "rapidfuzz-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:14587df847d0d50bd10cde0a198b5d64eedb7484c72b825f5c2ead6e6ff16eee"}, - {file = "rapidfuzz-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fd94d952299ec73ea63a0fa4b699a2750785b6bb82aa56fd886d9023b86f90ab"}, - {file = "rapidfuzz-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:733bf3d7876bf6d8167e6436f99d6ea16a218ec2c8eb9da6048f20b9cc8733e2"}, - {file = "rapidfuzz-3.9.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb28f2b7173ed3678b4630b0c8b21503087d1cd082bae200dc2519ca38b26686"}, - {file = "rapidfuzz-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80a4c8a2c5ae4b133fec6b5db1af9a4126ffa6eca18a558fe5b6ab8e330d3d78"}, - {file = "rapidfuzz-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5feb75e905281e5c669e21c98d594acc3b222a8694d9342f17df988766d83748"}, - {file = "rapidfuzz-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d047b01637a31d9bf776b66438f574fd1db856ad14cf296c1f48bb6bef8a5aff"}, - {file = "rapidfuzz-3.9.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d9e0a656274ac75ec24499a06c0bc5eee67bcd8276c6061da7c05d549f1b1a61"}, - {file = "rapidfuzz-3.9.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:16c982dd3cdd33cf4aac91027a263a081d1a8050dc33a27470367a391a8d1576"}, - {file = "rapidfuzz-3.9.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:9a0c878d0980508e90e973a9cbfb591acc370085f2301c6aacadbd8362d52a36"}, - {file = "rapidfuzz-3.9.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1d9bcfec5efd55b6268328cccd12956d833582d8da6385231a5c6c6201a1156a"}, - {file = "rapidfuzz-3.9.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8171fc6e4645e636161a9ef5b44b20605adbefe23cd990b68d72cae0b9c12509"}, - {file = "rapidfuzz-3.9.5-cp39-cp39-win32.whl", hash = "sha256:35088e759b083398ab3c4154517476e116653b7403604677af9a894179f1042f"}, - {file = "rapidfuzz-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:6d8cc7e6e5c6fbcacdfe3cf7a86b60dcaf216216d86e6879ff52d488e5b11e27"}, - {file = "rapidfuzz-3.9.5-cp39-cp39-win_arm64.whl", hash = "sha256:506547889f18db0acca787ffb9f287757cbfe9f0fadddd4e07c64ce0bd924e13"}, - {file = "rapidfuzz-3.9.5-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f4e0122603af2119579e9f94e172c6e460860fdcdb713164332c1951c13df999"}, - {file = "rapidfuzz-3.9.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:e46cd486289d1d8e3dab779c725f5dde77b286185d32e7b874bfc3d161e3a927"}, - {file = "rapidfuzz-3.9.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e2c0c8bbe4f4525009e3ad9b94a39cdff5d6378233e754d0b13c29cdfaa75fc"}, - {file = "rapidfuzz-3.9.5-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb47513a17c935f6ee606dcae0ea9d20a3fb0fe9ca597758472ea08be62dc54"}, - {file = "rapidfuzz-3.9.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:976ed1105a76935b6a4d2bbc7d577be1b97b43997bcec2f29a0ab48ff6f5d6b1"}, - {file = "rapidfuzz-3.9.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9cf2028edb9ccd21d1d5aaacef2fe3e14bee4343df1c2c0f7373ef6e81013bef"}, - {file = "rapidfuzz-3.9.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:926701c8e61319ee2e4888619143f58ddcc0e3e886668269b8e053f2d68c1e92"}, - {file = "rapidfuzz-3.9.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:99eaa8dd8a44664813e0bef014775993ef75a134a863bc54cd855a60622203fd"}, - {file = "rapidfuzz-3.9.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7508ef727ef4891141dd3ac7a39a2327384ece070521ac9c58f06c27d57c72d5"}, - {file = "rapidfuzz-3.9.5-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f33d05db5bba1d076446c51347a6d93ff24d8f9d01b0b8b15ca8ec8b1ef382"}, - {file = "rapidfuzz-3.9.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7252666b85c931d51a59d5308bb6827a67434917ef510747d3ce7e88ec17e7f2"}, - {file = "rapidfuzz-3.9.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d26f7299e2872d18fb7df1bc043e53aa94fc5a4a2a6a9537ad8707579fcb1668"}, - {file = "rapidfuzz-3.9.5-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2b17ecc17322b659962234799e90054e420911b8ca510a7869c2f4419f9f3ecb"}, - {file = "rapidfuzz-3.9.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f3e037b9ec621dec0157d81566e7d47a91405e379335cf8f4ed3c20d61db91d8"}, - {file = "rapidfuzz-3.9.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42c4d1ba2647c8d2a82313c4dde332de750c936b94f016308339e762c2e5e53d"}, - {file = "rapidfuzz-3.9.5-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:876e663b11d9067e1096ea76a2de87227c7b513aff2b60667b20417da74183e4"}, - {file = "rapidfuzz-3.9.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adee55488490375c1604b878fbc1eb1a51fe5e6f5bd05047df2f8c6505a48728"}, - {file = "rapidfuzz-3.9.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:abb1ac683671000bd4ec215a494aba687d75a198db72188408154a19ea313ff4"}, - {file = "rapidfuzz-3.9.5.tar.gz", hash = "sha256:257f2406a671371bafd99a2a2c57f991783446bc2176b93a83d1d833e35d36df"}, + {file = "rapidfuzz-3.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:884453860de029380dded8f3c1918af2d8eb5adf8010261645c7e5c88c2b5428"}, + {file = "rapidfuzz-3.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718c9bd369288aca5fa929df6dbf66fdbe9768d90940a940c0b5cdc96ade4309"}, + {file = "rapidfuzz-3.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a68e3724b7dab761c01816aaa64b0903734d999d5589daf97c14ef5cc0629a8e"}, + {file = "rapidfuzz-3.10.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1af60988d47534246d9525f77288fdd9de652608a4842815d9018570b959acc6"}, + {file = "rapidfuzz-3.10.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3084161fc3e963056232ef8d937449a2943852e07101f5a136c8f3cfa4119217"}, + {file = "rapidfuzz-3.10.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cd67d3d017296d98ff505529104299f78433e4b8af31b55003d901a62bbebe9"}, + {file = "rapidfuzz-3.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b11a127ac590fc991e8a02c2d7e1ac86e8141c92f78546f18b5c904064a0552c"}, + {file = "rapidfuzz-3.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aadce42147fc09dcef1afa892485311e824c050352e1aa6e47f56b9b27af4cf0"}, + {file = "rapidfuzz-3.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b54853c2371bf0e38d67da379519deb6fbe70055efb32f6607081641af3dc752"}, + {file = "rapidfuzz-3.10.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ce19887268e90ee81a3957eef5e46a70ecc000713796639f83828b950343f49e"}, + {file = "rapidfuzz-3.10.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f39a2a5ded23b9b9194ec45740dce57177b80f86c6d8eba953d3ff1a25c97766"}, + {file = "rapidfuzz-3.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0ec338d5f4ad8d9339a88a08db5c23e7f7a52c2b2a10510c48a0cef1fb3f0ddc"}, + {file = "rapidfuzz-3.10.0-cp310-cp310-win32.whl", hash = "sha256:56fd15ea8f4c948864fa5ebd9261c67cf7b89a1c517a0caef4df75446a7af18c"}, + {file = "rapidfuzz-3.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:43dfc5e733808962a822ff6d9c29f3039a3cfb3620706f5953e17cfe4496724c"}, + {file = "rapidfuzz-3.10.0-cp310-cp310-win_arm64.whl", hash = "sha256:ae7966f205b5a7fde93b44ca8fed37c1c8539328d7f179b1197de34eceaceb5f"}, + {file = "rapidfuzz-3.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bb0013795b40db5cf361e6f21ee7cda09627cf294977149b50e217d7fe9a2f03"}, + {file = "rapidfuzz-3.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:69ef5b363afff7150a1fbe788007e307b9802a2eb6ad92ed51ab94e6ad2674c6"}, + {file = "rapidfuzz-3.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c582c46b1bb0b19f1a5f4c1312f1b640c21d78c371a6615c34025b16ee56369b"}, + {file = "rapidfuzz-3.10.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:288f6f6e7410cacb115fb851f3f18bf0e4231eb3f6cb5bd1cec0e7b25c4d039d"}, + {file = "rapidfuzz-3.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9e29a13d2fd9be3e7d8c26c7ef4ba60b5bc7efbc9dbdf24454c7e9ebba31768"}, + {file = "rapidfuzz-3.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea2da0459b951ee461bd4e02b8904890bd1c4263999d291c5cd01e6620177ad4"}, + {file = "rapidfuzz-3.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:457827ba82261aa2ae6ac06a46d0043ab12ba7216b82d87ae1434ec0f29736d6"}, + {file = "rapidfuzz-3.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5d350864269d56f51ab81ab750c9259ae5cad3152c0680baef143dcec92206a1"}, + {file = "rapidfuzz-3.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a9b8f51e08c3f983d857c3889930af9ddecc768453822076683664772d87e374"}, + {file = "rapidfuzz-3.10.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7f3a6aa6e70fc27e4ff5c479f13cc9fc26a56347610f5f8b50396a0d344c5f55"}, + {file = "rapidfuzz-3.10.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:803f255f10d63420979b1909ef976e7d30dec42025c9b067fc1d2040cc365a7e"}, + {file = "rapidfuzz-3.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2026651761bf83a0f31495cc0f70840d5c0d54388f41316e3f9cb51bd85e49a5"}, + {file = "rapidfuzz-3.10.0-cp311-cp311-win32.whl", hash = "sha256:4df75b3ebbb8cfdb9bf8b213b168620b88fd92d0c16a8bc9f9234630b282db59"}, + {file = "rapidfuzz-3.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:f9f0bbfb6787b97c51516f3ccf97737d504db5d239ad44527673b81f598b84ab"}, + {file = "rapidfuzz-3.10.0-cp311-cp311-win_arm64.whl", hash = "sha256:10fdad800441b9c97d471a937ba7d42625f1b530db05e572f1cb7d401d95c893"}, + {file = "rapidfuzz-3.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7dc87073ba3a40dd65591a2100aa71602107443bf10770579ff9c8a3242edb94"}, + {file = "rapidfuzz-3.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a425a0a868cf8e9c6e93e1cda4b758cdfd314bb9a4fc916c5742c934e3613480"}, + {file = "rapidfuzz-3.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a86d5d1d75e61df060c1e56596b6b0a4422a929dff19cc3dbfd5eee762c86b61"}, + {file = "rapidfuzz-3.10.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34f213d59219a9c3ca14e94a825f585811a68ac56b4118b4dc388b5b14afc108"}, + {file = "rapidfuzz-3.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96ad46f5f56f70fab2be9e5f3165a21be58d633b90bf6e67fc52a856695e4bcf"}, + {file = "rapidfuzz-3.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9178277f72d144a6c7704d7ae7fa15b7b86f0f0796f0e1049c7b4ef748a662ef"}, + {file = "rapidfuzz-3.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76a35e9e19a7c883c422ffa378e9a04bc98cb3b29648c5831596401298ee51e6"}, + {file = "rapidfuzz-3.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a6405d34c394c65e4f73a1d300c001f304f08e529d2ed6413b46ee3037956eb"}, + {file = "rapidfuzz-3.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bd393683129f446a75d8634306aed7e377627098a1286ff3af2a4f1736742820"}, + {file = "rapidfuzz-3.10.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b0445fa9880ead81f5a7d0efc0b9c977a947d8052c43519aceeaf56eabaf6843"}, + {file = "rapidfuzz-3.10.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c50bc308fa29767ed8f53a8d33b7633a9e14718ced038ed89d41b886e301da32"}, + {file = "rapidfuzz-3.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e89605afebbd2d4b045bccfdc12a14b16fe8ccbae05f64b4b4c64a97dad1c891"}, + {file = "rapidfuzz-3.10.0-cp312-cp312-win32.whl", hash = "sha256:2db9187f3acf3cd33424ecdbaad75414c298ecd1513470df7bda885dcb68cc15"}, + {file = "rapidfuzz-3.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:50e3d0c72ea15391ba9531ead7f2068a67c5b18a6a365fef3127583aaadd1725"}, + {file = "rapidfuzz-3.10.0-cp312-cp312-win_arm64.whl", hash = "sha256:9eac95b4278bd53115903d89118a2c908398ee8bdfd977ae844f1bd2b02b917c"}, + {file = "rapidfuzz-3.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fe5231e8afd069c742ac5b4f96344a0fe4aff52df8e53ef87faebf77f827822c"}, + {file = "rapidfuzz-3.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:886882367dbc985f5736356105798f2ae6e794e671fc605476cbe2e73838a9bb"}, + {file = "rapidfuzz-3.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b33e13e537e3afd1627d421a142a12bbbe601543558a391a6fae593356842f6e"}, + {file = "rapidfuzz-3.10.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:094c26116d55bf9c53abd840d08422f20da78ec4c4723e5024322321caedca48"}, + {file = "rapidfuzz-3.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:545fc04f2d592e4350f59deb0818886c1b444ffba3bec535b4fbb97191aaf769"}, + {file = "rapidfuzz-3.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:916a6abf3632e592b937c3d04c00a6efadd8fd30539cdcd4e6e4d92be7ca5d90"}, + {file = "rapidfuzz-3.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb6ec40cef63b1922083d33bfef2f91fc0b0bc07b5b09bfee0b0f1717d558292"}, + {file = "rapidfuzz-3.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c77a7330dd15c7eb5fd3631dc646fc96327f98db8181138766bd14d3e905f0ba"}, + {file = "rapidfuzz-3.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:949b5e9eeaa4ecb4c7e9c2a4689dddce60929dd1ff9c76a889cdbabe8bbf2171"}, + {file = "rapidfuzz-3.10.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b5363932a5aab67010ae1a6205c567d1ef256fb333bc23c27582481606be480c"}, + {file = "rapidfuzz-3.10.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5dd6eec15b13329abe66cc241b484002ecb0e17d694491c944a22410a6a9e5e2"}, + {file = "rapidfuzz-3.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79e7f98525b60b3c14524e0a4e1fedf7654657b6e02eb25f1be897ab097706f3"}, + {file = "rapidfuzz-3.10.0-cp313-cp313-win32.whl", hash = "sha256:d29d1b9857c65f8cb3a29270732e1591b9bacf89de9d13fa764f79f07d8f1fd2"}, + {file = "rapidfuzz-3.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:fa9720e56663cc3649d62b4b5f3145e94b8f5611e8a8e1b46507777249d46aad"}, + {file = "rapidfuzz-3.10.0-cp313-cp313-win_arm64.whl", hash = "sha256:eda4c661e68dddd56c8fbfe1ca35e40dd2afd973f7ebb1605f4d151edc63dff8"}, + {file = "rapidfuzz-3.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cffbc50e0767396ed483900900dd58ce4351bc0d40e64bced8694bd41864cc71"}, + {file = "rapidfuzz-3.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c038b9939da3035afb6cb2f465f18163e8f070aba0482923ecff9443def67178"}, + {file = "rapidfuzz-3.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca366c2e2a54e2f663f4529b189fdeb6e14d419b1c78b754ec1744f3c01070d4"}, + {file = "rapidfuzz-3.10.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c4c82b1689b23b1b5e6a603164ed2be41b6f6de292a698b98ba2381e889eb9d"}, + {file = "rapidfuzz-3.10.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98f6ebe28831a482981ecfeedc8237047878424ad0c1add2c7f366ba44a20452"}, + {file = "rapidfuzz-3.10.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4bd1a7676ee2a4c8e2f7f2550bece994f9f89e58afb96088964145a83af7408b"}, + {file = "rapidfuzz-3.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec9139baa3f85b65adc700eafa03ed04995ca8533dd56c924f0e458ffec044ab"}, + {file = "rapidfuzz-3.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:26de93e6495078b6af4c4d93a42ca067b16cc0e95699526c82ab7d1025b4d3bf"}, + {file = "rapidfuzz-3.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f3a0bda83c18195c361b5500377d0767749f128564ca95b42c8849fd475bb327"}, + {file = "rapidfuzz-3.10.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:63e4c175cbce8c3adc22dca5e6154588ae673f6c55374d156f3dac732c88d7de"}, + {file = "rapidfuzz-3.10.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4dd3d8443970eaa02ab5ae45ce584b061f2799cd9f7e875190e2617440c1f9d4"}, + {file = "rapidfuzz-3.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e5ddb2388610799fc46abe389600625058f2a73867e63e20107c5ad5ffa57c47"}, + {file = "rapidfuzz-3.10.0-cp39-cp39-win32.whl", hash = "sha256:2e9be5d05cd960914024412b5406fb75a82f8562f45912ff86255acbfdbfb78e"}, + {file = "rapidfuzz-3.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:47aca565a39c9a6067927871973ca827023e8b65ba6c5747f4c228c8d7ddc04f"}, + {file = "rapidfuzz-3.10.0-cp39-cp39-win_arm64.whl", hash = "sha256:b0732343cdc4273b5921268026dd7266f75466eb21873cb7635a200d9d9c3fac"}, + {file = "rapidfuzz-3.10.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f744b5eb1469bf92dd143d36570d2bdbbdc88fe5cb0b5405e53dd34f479cbd8a"}, + {file = "rapidfuzz-3.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b67cc21a14327a0eb0f47bc3d7e59ec08031c7c55220ece672f9476e7a8068d3"}, + {file = "rapidfuzz-3.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fe5783676f0afba4a522c80b15e99dbf4e393c149ab610308a8ef1f04c6bcc8"}, + {file = "rapidfuzz-3.10.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4688862f957c8629d557d084f20b2d803f8738b6c4066802a0b1cc472e088d9"}, + {file = "rapidfuzz-3.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20bd153aacc244e4c907d772c703fea82754c4db14f8aa64d75ff81b7b8ab92d"}, + {file = "rapidfuzz-3.10.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:50484d563f8bfa723c74c944b0bb15b9e054db9c889348c8c307abcbee75ab92"}, + {file = "rapidfuzz-3.10.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5897242d455461f2c5b82d7397b29341fd11e85bf3608a522177071044784ee8"}, + {file = "rapidfuzz-3.10.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:116c71a81e046ba56551d8ab68067ca7034d94b617545316d460a452c5c3c289"}, + {file = "rapidfuzz-3.10.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0a547e4350d1fa32624d3eab51eff8cf329f4cae110b4ea0402486b1da8be40"}, + {file = "rapidfuzz-3.10.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:399b9b79ccfcf50ca3bad7692bc098bb8eade88d7d5e15773b7f866c91156d0c"}, + {file = "rapidfuzz-3.10.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7947a425d1be3e744707ee58c6cb318b93a56e08f080722dcc0347e0b7a1bb9a"}, + {file = "rapidfuzz-3.10.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:94c48b4a2a4b1d22246f48e2b11cae01ec7d23f0c9123f8bb822839ad79d0a88"}, + {file = "rapidfuzz-3.10.0.tar.gz", hash = "sha256:6b62af27e65bb39276a66533655a2fa3c60a487b03935721c45b7809527979be"}, ] [package.extras] -full = ["numpy"] +all = ["numpy"] [[package]] name = "redis" @@ -2683,114 +2957,114 @@ test = ["pytest", "pytest-mock"] [[package]] name = "rpds-py" -version = "0.19.1" +version = "0.20.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.19.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:aaf71f95b21f9dc708123335df22e5a2fef6307e3e6f9ed773b2e0938cc4d491"}, - {file = "rpds_py-0.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca0dda0c5715efe2ab35bb83f813f681ebcd2840d8b1b92bfc6fe3ab382fae4a"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81db2e7282cc0487f500d4db203edc57da81acde9e35f061d69ed983228ffe3b"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1a8dfa125b60ec00c7c9baef945bb04abf8ac772d8ebefd79dae2a5f316d7850"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:271accf41b02687cef26367c775ab220372ee0f4925591c6796e7c148c50cab5"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9bc4161bd3b970cd6a6fcda70583ad4afd10f2750609fb1f3ca9505050d4ef3"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0cf2a0dbb5987da4bd92a7ca727eadb225581dd9681365beba9accbe5308f7d"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b5e28e56143750808c1c79c70a16519e9bc0a68b623197b96292b21b62d6055c"}, - {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c7af6f7b80f687b33a4cdb0a785a5d4de1fb027a44c9a049d8eb67d5bfe8a687"}, - {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e429fc517a1c5e2a70d576077231538a98d59a45dfc552d1ac45a132844e6dfb"}, - {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d2dbd8f4990d4788cb122f63bf000357533f34860d269c1a8e90ae362090ff3a"}, - {file = "rpds_py-0.19.1-cp310-none-win32.whl", hash = "sha256:e0f9d268b19e8f61bf42a1da48276bcd05f7ab5560311f541d22557f8227b866"}, - {file = "rpds_py-0.19.1-cp310-none-win_amd64.whl", hash = "sha256:df7c841813f6265e636fe548a49664c77af31ddfa0085515326342a751a6ba51"}, - {file = "rpds_py-0.19.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:902cf4739458852fe917104365ec0efbea7d29a15e4276c96a8d33e6ed8ec137"}, - {file = "rpds_py-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3d73022990ab0c8b172cce57c69fd9a89c24fd473a5e79cbce92df87e3d9c48"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3837c63dd6918a24de6c526277910e3766d8c2b1627c500b155f3eecad8fad65"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cdb7eb3cf3deb3dd9e7b8749323b5d970052711f9e1e9f36364163627f96da58"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26ab43b6d65d25b1a333c8d1b1c2f8399385ff683a35ab5e274ba7b8bb7dc61c"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75130df05aae7a7ac171b3b5b24714cffeabd054ad2ebc18870b3aa4526eba23"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c34f751bf67cab69638564eee34023909380ba3e0d8ee7f6fe473079bf93f09b"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2671cb47e50a97f419a02cd1e0c339b31de017b033186358db92f4d8e2e17d8"}, - {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c73254c256081704dba0a333457e2fb815364018788f9b501efe7c5e0ada401"}, - {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4383beb4a29935b8fa28aca8fa84c956bf545cb0c46307b091b8d312a9150e6a"}, - {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dbceedcf4a9329cc665452db1aaf0845b85c666e4885b92ee0cddb1dbf7e052a"}, - {file = "rpds_py-0.19.1-cp311-none-win32.whl", hash = "sha256:f0a6d4a93d2a05daec7cb885157c97bbb0be4da739d6f9dfb02e101eb40921cd"}, - {file = "rpds_py-0.19.1-cp311-none-win_amd64.whl", hash = "sha256:c149a652aeac4902ecff2dd93c3b2681c608bd5208c793c4a99404b3e1afc87c"}, - {file = "rpds_py-0.19.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:56313be667a837ff1ea3508cebb1ef6681d418fa2913a0635386cf29cff35165"}, - {file = "rpds_py-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d1d7539043b2b31307f2c6c72957a97c839a88b2629a348ebabe5aa8b626d6b"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e1dc59a5e7bc7f44bd0c048681f5e05356e479c50be4f2c1a7089103f1621d5"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8f78398e67a7227aefa95f876481485403eb974b29e9dc38b307bb6eb2315ea"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef07a0a1d254eeb16455d839cef6e8c2ed127f47f014bbda64a58b5482b6c836"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8124101e92c56827bebef084ff106e8ea11c743256149a95b9fd860d3a4f331f"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08ce9c95a0b093b7aec75676b356a27879901488abc27e9d029273d280438505"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b02dd77a2de6e49078c8937aadabe933ceac04b41c5dde5eca13a69f3cf144e"}, - {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4dd02e29c8cbed21a1875330b07246b71121a1c08e29f0ee3db5b4cfe16980c4"}, - {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9c7042488165f7251dc7894cd533a875d2875af6d3b0e09eda9c4b334627ad1c"}, - {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f809a17cc78bd331e137caa25262b507225854073fd319e987bd216bed911b7c"}, - {file = "rpds_py-0.19.1-cp312-none-win32.whl", hash = "sha256:3ddab996807c6b4227967fe1587febade4e48ac47bb0e2d3e7858bc621b1cace"}, - {file = "rpds_py-0.19.1-cp312-none-win_amd64.whl", hash = "sha256:32e0db3d6e4f45601b58e4ac75c6f24afbf99818c647cc2066f3e4b192dabb1f"}, - {file = "rpds_py-0.19.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:747251e428406b05fc86fee3904ee19550c4d2d19258cef274e2151f31ae9d38"}, - {file = "rpds_py-0.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dc733d35f861f8d78abfaf54035461e10423422999b360966bf1c443cbc42705"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbda75f245caecff8faa7e32ee94dfaa8312a3367397975527f29654cd17a6ed"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd04d8cab16cab5b0a9ffc7d10f0779cf1120ab16c3925404428f74a0a43205a"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2d66eb41ffca6cc3c91d8387509d27ba73ad28371ef90255c50cb51f8953301"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdf4890cda3b59170009d012fca3294c00140e7f2abe1910e6a730809d0f3f9b"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1fa67ef839bad3815124f5f57e48cd50ff392f4911a9f3cf449d66fa3df62a5"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b82c9514c6d74b89a370c4060bdb80d2299bc6857e462e4a215b4ef7aa7b090e"}, - {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c7b07959866a6afb019abb9564d8a55046feb7a84506c74a6f197cbcdf8a208e"}, - {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4f580ae79d0b861dfd912494ab9d477bea535bfb4756a2269130b6607a21802e"}, - {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c6d20c8896c00775e6f62d8373aba32956aa0b850d02b5ec493f486c88e12859"}, - {file = "rpds_py-0.19.1-cp313-none-win32.whl", hash = "sha256:afedc35fe4b9e30ab240b208bb9dc8938cb4afe9187589e8d8d085e1aacb8309"}, - {file = "rpds_py-0.19.1-cp313-none-win_amd64.whl", hash = "sha256:1d4af2eb520d759f48f1073ad3caef997d1bfd910dc34e41261a595d3f038a94"}, - {file = "rpds_py-0.19.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:34bca66e2e3eabc8a19e9afe0d3e77789733c702c7c43cd008e953d5d1463fde"}, - {file = "rpds_py-0.19.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:24f8ae92c7fae7c28d0fae9b52829235df83f34847aa8160a47eb229d9666c7b"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71157f9db7f6bc6599a852852f3389343bea34315b4e6f109e5cbc97c1fb2963"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d494887d40dc4dd0d5a71e9d07324e5c09c4383d93942d391727e7a40ff810b"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b3661e6d4ba63a094138032c1356d557de5b3ea6fd3cca62a195f623e381c76"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97fbb77eaeb97591efdc654b8b5f3ccc066406ccfb3175b41382f221ecc216e8"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cc4bc73e53af8e7a42c8fd7923bbe35babacfa7394ae9240b3430b5dcf16b2a"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:35af5e4d5448fa179fd7fff0bba0fba51f876cd55212f96c8bbcecc5c684ae5c"}, - {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3511f6baf8438326e351097cecd137eb45c5f019944fe0fd0ae2fea2fd26be39"}, - {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:57863d16187995c10fe9cf911b897ed443ac68189179541734502353af33e693"}, - {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9e318e6786b1e750a62f90c6f7fa8b542102bdcf97c7c4de2a48b50b61bd36ec"}, - {file = "rpds_py-0.19.1-cp38-none-win32.whl", hash = "sha256:53dbc35808c6faa2ce3e48571f8f74ef70802218554884787b86a30947842a14"}, - {file = "rpds_py-0.19.1-cp38-none-win_amd64.whl", hash = "sha256:8df1c283e57c9cb4d271fdc1875f4a58a143a2d1698eb0d6b7c0d7d5f49c53a1"}, - {file = "rpds_py-0.19.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e76c902d229a3aa9d5ceb813e1cbcc69bf5bda44c80d574ff1ac1fa3136dea71"}, - {file = "rpds_py-0.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de1f7cd5b6b351e1afd7568bdab94934d656abe273d66cda0ceea43bbc02a0c2"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24fc5a84777cb61692d17988989690d6f34f7f95968ac81398d67c0d0994a897"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:74129d5ffc4cde992d89d345f7f7d6758320e5d44a369d74d83493429dad2de5"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e360188b72f8080fefa3adfdcf3618604cc8173651c9754f189fece068d2a45"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13e6d4840897d4e4e6b2aa1443e3a8eca92b0402182aafc5f4ca1f5e24f9270a"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f09529d2332264a902688031a83c19de8fda5eb5881e44233286b9c9ec91856d"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0d4b52811dcbc1aba08fd88d475f75b4f6db0984ba12275d9bed1a04b2cae9b5"}, - {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dd635c2c4043222d80d80ca1ac4530a633102a9f2ad12252183bcf338c1b9474"}, - {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f35b34a5184d5e0cc360b61664c1c06e866aab077b5a7c538a3e20c8fcdbf90b"}, - {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d4ec0046facab83012d821b33cead742a35b54575c4edfb7ed7445f63441835f"}, - {file = "rpds_py-0.19.1-cp39-none-win32.whl", hash = "sha256:f5b8353ea1a4d7dfb59a7f45c04df66ecfd363bb5b35f33b11ea579111d4655f"}, - {file = "rpds_py-0.19.1-cp39-none-win_amd64.whl", hash = "sha256:1fb93d3486f793d54a094e2bfd9cd97031f63fcb5bc18faeb3dd4b49a1c06523"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7d5c7e32f3ee42f77d8ff1a10384b5cdcc2d37035e2e3320ded909aa192d32c3"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:89cc8921a4a5028d6dd388c399fcd2eef232e7040345af3d5b16c04b91cf3c7e"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca34e913d27401bda2a6f390d0614049f5a95b3b11cd8eff80fe4ec340a1208"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5953391af1405f968eb5701ebbb577ebc5ced8d0041406f9052638bafe52209d"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:840e18c38098221ea6201f091fc5d4de6128961d2930fbbc96806fb43f69aec1"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d8b735c4d162dc7d86a9cf3d717f14b6c73637a1f9cd57fe7e61002d9cb1972"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce757c7c90d35719b38fa3d4ca55654a76a40716ee299b0865f2de21c146801c"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a9421b23c85f361a133aa7c5e8ec757668f70343f4ed8fdb5a4a14abd5437244"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3b823be829407393d84ee56dc849dbe3b31b6a326f388e171555b262e8456cc1"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:5e58b61dcbb483a442c6239c3836696b79f2cd8e7eec11e12155d3f6f2d886d1"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39d67896f7235b2c886fb1ee77b1491b77049dcef6fbf0f401e7b4cbed86bbd4"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8b32cd4ab6db50c875001ba4f5a6b30c0f42151aa1fbf9c2e7e3674893fb1dc4"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1c32e41de995f39b6b315d66c27dea3ef7f7c937c06caab4c6a79a5e09e2c415"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1a129c02b42d46758c87faeea21a9f574e1c858b9f358b6dd0bbd71d17713175"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:346557f5b1d8fd9966059b7a748fd79ac59f5752cd0e9498d6a40e3ac1c1875f"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31e450840f2f27699d014cfc8865cc747184286b26d945bcea6042bb6aa4d26e"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01227f8b3e6c8961490d869aa65c99653df80d2f0a7fde8c64ebddab2b9b02fd"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69084fd29bfeff14816666c93a466e85414fe6b7d236cfc108a9c11afa6f7301"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d2b88efe65544a7d5121b0c3b003ebba92bfede2ea3577ce548b69c5235185"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ea961a674172ed2235d990d7edf85d15d8dfa23ab8575e48306371c070cda67"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:5beffdbe766cfe4fb04f30644d822a1080b5359df7db3a63d30fa928375b2720"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:720f3108fb1bfa32e51db58b832898372eb5891e8472a8093008010911e324c5"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:c2087dbb76a87ec2c619253e021e4fb20d1a72580feeaa6892b0b3d955175a71"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ddd50f18ebc05ec29a0d9271e9dbe93997536da3546677f8ca00b76d477680c"}, - {file = "rpds_py-0.19.1.tar.gz", hash = "sha256:31dd5794837f00b46f4096aa8ccaa5972f73a938982e32ed817bb520c465e520"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, + {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, + {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, + {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, + {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, + {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, + {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, + {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, + {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, + {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, + {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, + {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, + {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, + {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, ] [[package]] @@ -2809,13 +3083,13 @@ pyasn1 = ">=0.1.3" [[package]] name = "s3transfer" -version = "0.10.1" +version = "0.10.3" description = "An Amazon S3 Transfer Manager" optional = false -python-versions = ">= 3.8" +python-versions = ">=3.8" files = [ - {file = "s3transfer-0.10.1-py3-none-any.whl", hash = "sha256:ceb252b11bcf87080fb7850a224fb6e05c8a776bab8f2b64b7f25b969464839d"}, - {file = "s3transfer-0.10.1.tar.gz", hash = "sha256:5683916b4c724f799e600f41dd9e10a9ff19871bf87623cc8f491cb4f5fa0a19"}, + {file = "s3transfer-0.10.3-py3-none-any.whl", hash = "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d"}, + {file = "s3transfer-0.10.3.tar.gz", hash = "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c"}, ] [package.dependencies] @@ -2841,13 +3115,13 @@ starkbank-ecdsa = ">=2.0.1" [[package]] name = "sentry-sdk" -version = "1.45.0" +version = "1.45.1" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = "*" files = [ - {file = "sentry-sdk-1.45.0.tar.gz", hash = "sha256:509aa9678c0512344ca886281766c2e538682f8acfa50fd8d405f8c417ad0625"}, - {file = "sentry_sdk-1.45.0-py2.py3-none-any.whl", hash = "sha256:1ce29e30240cc289a027011103a8c83885b15ef2f316a60bcc7c5300afa144f1"}, + {file = "sentry_sdk-1.45.1-py2.py3-none-any.whl", hash = "sha256:608887855ccfe39032bfd03936e3a1c4f4fc99b3a4ac49ced54a4220de61c9c1"}, + {file = "sentry_sdk-1.45.1.tar.gz", hash = "sha256:a16c997c0f4e3df63c0fc5e4207ccb1ab37900433e0f72fef88315d317829a26"}, ] [package.dependencies] @@ -2911,64 +3185,64 @@ files = [ [[package]] name = "sqlalchemy" -version = "2.0.30" +version = "2.0.35" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b48154678e76445c7ded1896715ce05319f74b1e73cf82d4f8b59b46e9c0ddc"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2753743c2afd061bb95a61a51bbb6a1a11ac1c44292fad898f10c9839a7f75b2"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7bfc726d167f425d4c16269a9a10fe8630ff6d14b683d588044dcef2d0f6be7"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f61ada6979223013d9ab83a3ed003ded6959eae37d0d685db2c147e9143797"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a365eda439b7a00732638f11072907c1bc8e351c7665e7e5da91b169af794af"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bba002a9447b291548e8d66fd8c96a6a7ed4f2def0bb155f4f0a1309fd2735d5"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-win32.whl", hash = "sha256:0138c5c16be3600923fa2169532205d18891b28afa817cb49b50e08f62198bb8"}, - {file = "SQLAlchemy-2.0.30-cp310-cp310-win_amd64.whl", hash = "sha256:99650e9f4cf3ad0d409fed3eec4f071fadd032e9a5edc7270cd646a26446feeb"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:955991a09f0992c68a499791a753523f50f71a6885531568404fa0f231832aa0"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f69e4c756ee2686767eb80f94c0125c8b0a0b87ede03eacc5c8ae3b54b99dc46"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c9db1ce00e59e8dd09d7bae852a9add716efdc070a3e2068377e6ff0d6fdaa"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1429a4b0f709f19ff3b0cf13675b2b9bfa8a7e79990003207a011c0db880a13"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:efedba7e13aa9a6c8407c48facfdfa108a5a4128e35f4c68f20c3407e4376aa9"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16863e2b132b761891d6c49f0a0f70030e0bcac4fd208117f6b7e053e68668d0"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-win32.whl", hash = "sha256:2ecabd9ccaa6e914e3dbb2aa46b76dede7eadc8cbf1b8083c94d936bcd5ffb49"}, - {file = "SQLAlchemy-2.0.30-cp311-cp311-win_amd64.whl", hash = "sha256:0b3f4c438e37d22b83e640f825ef0f37b95db9aa2d68203f2c9549375d0b2260"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5a79d65395ac5e6b0c2890935bad892eabb911c4aa8e8015067ddb37eea3d56c"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a5baf9267b752390252889f0c802ea13b52dfee5e369527da229189b8bd592e"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cb5a646930c5123f8461f6468901573f334c2c63c795b9af350063a736d0134"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296230899df0b77dec4eb799bcea6fbe39a43707ce7bb166519c97b583cfcab3"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c62d401223f468eb4da32627bffc0c78ed516b03bb8a34a58be54d618b74d472"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3b69e934f0f2b677ec111b4d83f92dc1a3210a779f69bf905273192cf4ed433e"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-win32.whl", hash = "sha256:77d2edb1f54aff37e3318f611637171e8ec71472f1fdc7348b41dcb226f93d90"}, - {file = "SQLAlchemy-2.0.30-cp312-cp312-win_amd64.whl", hash = "sha256:b6c7ec2b1f4969fc19b65b7059ed00497e25f54069407a8701091beb69e591a5"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a8e3b0a7e09e94be7510d1661339d6b52daf202ed2f5b1f9f48ea34ee6f2d57"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b60203c63e8f984df92035610c5fb76d941254cf5d19751faab7d33b21e5ddc0"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1dc3eabd8c0232ee8387fbe03e0a62220a6f089e278b1f0aaf5e2d6210741ad"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:40ad017c672c00b9b663fcfcd5f0864a0a97828e2ee7ab0c140dc84058d194cf"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e42203d8d20dc704604862977b1470a122e4892791fe3ed165f041e4bf447a1b"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-win32.whl", hash = "sha256:2a4f4da89c74435f2bc61878cd08f3646b699e7d2eba97144030d1be44e27584"}, - {file = "SQLAlchemy-2.0.30-cp37-cp37m-win_amd64.whl", hash = "sha256:b6bf767d14b77f6a18b6982cbbf29d71bede087edae495d11ab358280f304d8e"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc0c53579650a891f9b83fa3cecd4e00218e071d0ba00c4890f5be0c34887ed3"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:311710f9a2ee235f1403537b10c7687214bb1f2b9ebb52702c5aa4a77f0b3af7"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:408f8b0e2c04677e9c93f40eef3ab22f550fecb3011b187f66a096395ff3d9fd"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37a4b4fb0dd4d2669070fb05b8b8824afd0af57587393015baee1cf9890242d9"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a943d297126c9230719c27fcbbeab57ecd5d15b0bd6bfd26e91bfcfe64220621"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a089e218654e740a41388893e090d2e2c22c29028c9d1353feb38638820bbeb"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-win32.whl", hash = "sha256:fa561138a64f949f3e889eb9ab8c58e1504ab351d6cf55259dc4c248eaa19da6"}, - {file = "SQLAlchemy-2.0.30-cp38-cp38-win_amd64.whl", hash = "sha256:7d74336c65705b986d12a7e337ba27ab2b9d819993851b140efdf029248e818e"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae8c62fe2480dd61c532ccafdbce9b29dacc126fe8be0d9a927ca3e699b9491a"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2383146973a15435e4717f94c7509982770e3e54974c71f76500a0136f22810b"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8409de825f2c3b62ab15788635ccaec0c881c3f12a8af2b12ae4910a0a9aeef6"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0094c5dc698a5f78d3d1539853e8ecec02516b62b8223c970c86d44e7a80f6c7"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:edc16a50f5e1b7a06a2dcc1f2205b0b961074c123ed17ebda726f376a5ab0953"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f7703c2010355dd28f53deb644a05fc30f796bd8598b43f0ba678878780b6e4c"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-win32.whl", hash = "sha256:1f9a727312ff6ad5248a4367358e2cf7e625e98b1028b1d7ab7b806b7d757513"}, - {file = "SQLAlchemy-2.0.30-cp39-cp39-win_amd64.whl", hash = "sha256:a0ef36b28534f2a5771191be6edb44cc2673c7b2edf6deac6562400288664221"}, - {file = "SQLAlchemy-2.0.30-py3-none-any.whl", hash = "sha256:7108d569d3990c71e26a42f60474b4c02c8586c4681af5fd67e51a044fdea86a"}, - {file = "SQLAlchemy-2.0.30.tar.gz", hash = "sha256:2b1708916730f4830bc69d6f49d37f7698b5bd7530aca7f04f785f8849e95255"}, -] - -[package.dependencies] -greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} + {file = "SQLAlchemy-2.0.35-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:67219632be22f14750f0d1c70e62f204ba69d28f62fd6432ba05ab295853de9b"}, + {file = "SQLAlchemy-2.0.35-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4668bd8faf7e5b71c0319407b608f278f279668f358857dbfd10ef1954ac9f90"}, + {file = "SQLAlchemy-2.0.35-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb8bea573863762bbf45d1e13f87c2d2fd32cee2dbd50d050f83f87429c9e1ea"}, + {file = "SQLAlchemy-2.0.35-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f552023710d4b93d8fb29a91fadf97de89c5926c6bd758897875435f2a939f33"}, + {file = "SQLAlchemy-2.0.35-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:016b2e665f778f13d3c438651dd4de244214b527a275e0acf1d44c05bc6026a9"}, + {file = "SQLAlchemy-2.0.35-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7befc148de64b6060937231cbff8d01ccf0bfd75aa26383ffdf8d82b12ec04ff"}, + {file = "SQLAlchemy-2.0.35-cp310-cp310-win32.whl", hash = "sha256:22b83aed390e3099584b839b93f80a0f4a95ee7f48270c97c90acd40ee646f0b"}, + {file = "SQLAlchemy-2.0.35-cp310-cp310-win_amd64.whl", hash = "sha256:a29762cd3d116585278ffb2e5b8cc311fb095ea278b96feef28d0b423154858e"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e21f66748ab725ade40fa7af8ec8b5019c68ab00b929f6643e1b1af461eddb60"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8a6219108a15fc6d24de499d0d515c7235c617b2540d97116b663dade1a54d62"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:042622a5306c23b972192283f4e22372da3b8ddf5f7aac1cc5d9c9b222ab3ff6"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:627dee0c280eea91aed87b20a1f849e9ae2fe719d52cbf847c0e0ea34464b3f7"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4fdcd72a789c1c31ed242fd8c1bcd9ea186a98ee8e5408a50e610edfef980d71"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:89b64cd8898a3a6f642db4eb7b26d1b28a497d4022eccd7717ca066823e9fb01"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-win32.whl", hash = "sha256:6a93c5a0dfe8d34951e8a6f499a9479ffb9258123551fa007fc708ae2ac2bc5e"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-win_amd64.whl", hash = "sha256:c68fe3fcde03920c46697585620135b4ecfdfc1ed23e75cc2c2ae9f8502c10b8"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:eb60b026d8ad0c97917cb81d3662d0b39b8ff1335e3fabb24984c6acd0c900a2"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6921ee01caf375363be5e9ae70d08ce7ca9d7e0e8983183080211a062d299468"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8cdf1a0dbe5ced887a9b127da4ffd7354e9c1a3b9bb330dce84df6b70ccb3a8d"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93a71c8601e823236ac0e5d087e4f397874a421017b3318fd92c0b14acf2b6db"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e04b622bb8a88f10e439084486f2f6349bf4d50605ac3e445869c7ea5cf0fa8c"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1b56961e2d31389aaadf4906d453859f35302b4eb818d34a26fab72596076bb8"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-win32.whl", hash = "sha256:0f9f3f9a3763b9c4deb8c5d09c4cc52ffe49f9876af41cc1b2ad0138878453cf"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-win_amd64.whl", hash = "sha256:25b0f63e7fcc2a6290cb5f7f5b4fc4047843504983a28856ce9b35d8f7de03cc"}, + {file = "SQLAlchemy-2.0.35-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f021d334f2ca692523aaf7bbf7592ceff70c8594fad853416a81d66b35e3abf9"}, + {file = "SQLAlchemy-2.0.35-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05c3f58cf91683102f2f0265c0db3bd3892e9eedabe059720492dbaa4f922da1"}, + {file = "SQLAlchemy-2.0.35-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:032d979ce77a6c2432653322ba4cbeabf5a6837f704d16fa38b5a05d8e21fa00"}, + {file = "SQLAlchemy-2.0.35-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:2e795c2f7d7249b75bb5f479b432a51b59041580d20599d4e112b5f2046437a3"}, + {file = "SQLAlchemy-2.0.35-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:cc32b2990fc34380ec2f6195f33a76b6cdaa9eecf09f0c9404b74fc120aef36f"}, + {file = "SQLAlchemy-2.0.35-cp37-cp37m-win32.whl", hash = "sha256:9509c4123491d0e63fb5e16199e09f8e262066e58903e84615c301dde8fa2e87"}, + {file = "SQLAlchemy-2.0.35-cp37-cp37m-win_amd64.whl", hash = "sha256:3655af10ebcc0f1e4e06c5900bb33e080d6a1fa4228f502121f28a3b1753cde5"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4c31943b61ed8fdd63dfd12ccc919f2bf95eefca133767db6fbbd15da62078ec"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a62dd5d7cc8626a3634208df458c5fe4f21200d96a74d122c83bc2015b333bc1"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0630774b0977804fba4b6bbea6852ab56c14965a2b0c7fc7282c5f7d90a1ae72"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d625eddf7efeba2abfd9c014a22c0f6b3796e0ffb48f5d5ab106568ef01ff5a"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ada603db10bb865bbe591939de854faf2c60f43c9b763e90f653224138f910d9"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c41411e192f8d3ea39ea70e0fae48762cd11a2244e03751a98bd3c0ca9a4e936"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-win32.whl", hash = "sha256:d299797d75cd747e7797b1b41817111406b8b10a4f88b6e8fe5b5e59598b43b0"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-win_amd64.whl", hash = "sha256:0375a141e1c0878103eb3d719eb6d5aa444b490c96f3fedab8471c7f6ffe70ee"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ccae5de2a0140d8be6838c331604f91d6fafd0735dbdcee1ac78fc8fbaba76b4"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2a275a806f73e849e1c309ac11108ea1a14cd7058577aba962cd7190e27c9e3c"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:732e026240cdd1c1b2e3ac515c7a23820430ed94292ce33806a95869c46bd139"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:890da8cd1941fa3dab28c5bac3b9da8502e7e366f895b3b8e500896f12f94d11"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0d8326269dbf944b9201911b0d9f3dc524d64779a07518199a58384c3d37a44"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b76d63495b0508ab9fc23f8152bac63205d2a704cd009a2b0722f4c8e0cba8e0"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-win32.whl", hash = "sha256:69683e02e8a9de37f17985905a5eca18ad651bf592314b4d3d799029797d0eb3"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-win_amd64.whl", hash = "sha256:aee110e4ef3c528f3abbc3c2018c121e708938adeeff9006428dd7c8555e9b3f"}, + {file = "SQLAlchemy-2.0.35-py3-none-any.whl", hash = "sha256:2ab3f0336c0387662ce6221ad30ab3a5e6499aab01b9790879b6578fd9b8faa1"}, + {file = "sqlalchemy-2.0.35.tar.gz", hash = "sha256:e11d7ea4d24f0a262bccf9a7cd6284c976c5369dac21db237cff59586045ab9f"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} typing-extensions = ">=4.6.0" [package.extras] @@ -3043,13 +3317,13 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7 [[package]] name = "supertokens-python" -version = "0.24.2" +version = "0.24.3" description = "SuperTokens SDK for Python" optional = false python-versions = ">=3.7" files = [ - {file = "supertokens_python-0.24.2-py3-none-any.whl", hash = "sha256:a0454806e69006a1aeb8f7436a70671f81cebb82762bebb7923442bddc507bd4"}, - {file = "supertokens_python-0.24.2.tar.gz", hash = "sha256:5873f6624ff69914124d3b092cb21fea76cc5a58fb3f3476721d1d06c62e9bf2"}, + {file = "supertokens_python-0.24.3-py3-none-any.whl", hash = "sha256:e3d539d91e10126eeb5da173e90a1c47fe711a6324d21feffba4626981ccc7e6"}, + {file = "supertokens_python-0.24.3.tar.gz", hash = "sha256:3aa10bee90f41d0bc99acd2e036cdbd54064117346934c80efc53eaaf4d128ca"}, ] [package.dependencies] @@ -3102,24 +3376,24 @@ files = [ [[package]] name = "tomli" -version = "2.0.1" +version = "2.0.2" description = "A lil' TOML parser" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, + {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, + {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, ] [[package]] name = "tqdm" -version = "4.66.4" +version = "4.66.5" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"}, - {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"}, + {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, + {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, ] [package.dependencies] @@ -3149,35 +3423,35 @@ requests = ">=2.0.0" [[package]] name = "typing-extensions" -version = "4.12.0" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"}, - {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "tzdata" -version = "2024.1" +version = "2024.2" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, ] [[package]] name = "urllib3" -version = "1.26.18" +version = "1.26.20" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "urllib3-1.26.18-py2.py3-none-any.whl", hash = "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07"}, - {file = "urllib3-1.26.18.tar.gz", hash = "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0"}, + {file = "urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e"}, + {file = "urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32"}, ] [package.extras] @@ -3456,108 +3730,111 @@ files = [ [[package]] name = "yarl" -version = "1.9.4" +version = "1.14.0" description = "Yet another URL library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, - {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, - {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, - {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, - {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, - {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, - {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, - {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, - {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, - {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, - {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, - {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, - {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, - {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, - {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, - {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, - {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, - {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, + {file = "yarl-1.14.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1bfc25aa6a7c99cf86564210f79a0b7d4484159c67e01232b116e445b3036547"}, + {file = "yarl-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0cf21f46a15d445417de8fc89f2568852cf57fe8ca1ab3d19ddb24d45c0383ae"}, + {file = "yarl-1.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1dda53508df0de87b6e6b0a52d6718ff6c62a5aca8f5552748404963df639269"}, + {file = "yarl-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:587c3cc59bc148a9b1c07a019346eda2549bc9f468acd2f9824d185749acf0a6"}, + {file = "yarl-1.14.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3007a5b75cb50140708420fe688c393e71139324df599434633019314ceb8b59"}, + {file = "yarl-1.14.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:06ff23462398333c78b6f4f8d3d70410d657a471c2c5bbe6086133be43fc8f1a"}, + {file = "yarl-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:689a99a42ee4583fcb0d3a67a0204664aa1539684aed72bdafcbd505197a91c4"}, + {file = "yarl-1.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0547ab1e9345dc468cac8368d88ea4c5bd473ebc1d8d755347d7401982b5dd8"}, + {file = "yarl-1.14.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:742aef0a99844faaac200564ea6f5e08facb285d37ea18bd1a5acf2771f3255a"}, + {file = "yarl-1.14.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:176110bff341b6730f64a1eb3a7070e12b373cf1c910a9337e7c3240497db76f"}, + {file = "yarl-1.14.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46a9772a1efa93f9cd170ad33101c1817c77e0e9914d4fe33e2da299d7cf0f9b"}, + {file = "yarl-1.14.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ee2c68e4f2dd1b1c15b849ba1c96fac105fca6ffdb7c1e8be51da6fabbdeafb9"}, + {file = "yarl-1.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:047b258e00b99091b6f90355521f026238c63bd76dcf996d93527bb13320eefd"}, + {file = "yarl-1.14.0-cp310-cp310-win32.whl", hash = "sha256:0aa92e3e30a04f9462a25077db689c4ac5ea9ab6cc68a2e563881b987d42f16d"}, + {file = "yarl-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:d9baec588f015d0ee564057aa7574313c53a530662ffad930b7886becc85abdf"}, + {file = "yarl-1.14.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:07f9eaf57719d6721ab15805d85f4b01a5b509a0868d7320134371bcb652152d"}, + {file = "yarl-1.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c14b504a74e58e2deb0378b3eca10f3d076635c100f45b113c18c770b4a47a50"}, + {file = "yarl-1.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:16a682a127930f3fc4e42583becca6049e1d7214bcad23520c590edd741d2114"}, + {file = "yarl-1.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73bedd2be05f48af19f0f2e9e1353921ce0c83f4a1c9e8556ecdcf1f1eae4892"}, + {file = "yarl-1.14.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3ab950f8814f3b7b5e3eebc117986f817ec933676f68f0a6c5b2137dd7c9c69"}, + {file = "yarl-1.14.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b693c63e7e64b524f54aa4888403c680342d1ad0d97be1707c531584d6aeeb4f"}, + {file = "yarl-1.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85cb3e40eaa98489f1e2e8b29f5ad02ee1ee40d6ce6b88d50cf0f205de1d9d2c"}, + {file = "yarl-1.14.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f24f08b6c9b9818fd80612c97857d28f9779f0d1211653ece9844fc7b414df2"}, + {file = "yarl-1.14.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:29a84a46ec3ebae7a1c024c055612b11e9363a8a23238b3e905552d77a2bc51b"}, + {file = "yarl-1.14.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5cd5dad8366e0168e0fd23d10705a603790484a6dbb9eb272b33673b8f2cce72"}, + {file = "yarl-1.14.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a152751af7ef7b5d5fa6d215756e508dd05eb07d0cf2ba51f3e740076aa74373"}, + {file = "yarl-1.14.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:3d569f877ed9a708e4c71a2d13d2940cb0791da309f70bd970ac1a5c088a0a92"}, + {file = "yarl-1.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6a615cad11ec3428020fb3c5a88d85ce1b5c69fd66e9fcb91a7daa5e855325dd"}, + {file = "yarl-1.14.0-cp311-cp311-win32.whl", hash = "sha256:bab03192091681d54e8225c53f270b0517637915d9297028409a2a5114ff4634"}, + {file = "yarl-1.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:985623575e5c4ea763056ffe0e2d63836f771a8c294b3de06d09480538316b13"}, + {file = "yarl-1.14.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fc2c80bc87fba076e6cbb926216c27fba274dae7100a7b9a0983b53132dd99f2"}, + {file = "yarl-1.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:55c144d363ad4626ca744556c049c94e2b95096041ac87098bb363dcc8635e8d"}, + {file = "yarl-1.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b03384eed107dbeb5f625a99dc3a7de8be04fc8480c9ad42fccbc73434170b20"}, + {file = "yarl-1.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f72a0d746d38cb299b79ce3d4d60ba0892c84bbc905d0d49c13df5bace1b65f8"}, + {file = "yarl-1.14.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8648180b34faaea4aa5b5ca7e871d9eb1277033fa439693855cf0ea9195f85f1"}, + {file = "yarl-1.14.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9557c9322aaa33174d285b0c1961fb32499d65ad1866155b7845edc876c3c835"}, + {file = "yarl-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f50eb3837012a937a2b649ec872b66ba9541ad9d6f103ddcafb8231cfcafd22"}, + {file = "yarl-1.14.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8892fa575ac9b1b25fae7b221bc4792a273877b9b56a99ee2d8d03eeb3dbb1d2"}, + {file = "yarl-1.14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e6a2c5c5bb2556dfbfffffc2bcfb9c235fd2b566d5006dfb2a37afc7e3278a07"}, + {file = "yarl-1.14.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ab3abc0b78a5dfaa4795a6afbe7b282b6aa88d81cf8c1bb5e394993d7cae3457"}, + {file = "yarl-1.14.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:47eede5d11d669ab3759b63afb70d28d5328c14744b8edba3323e27dc52d298d"}, + {file = "yarl-1.14.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fe4d2536c827f508348d7b40c08767e8c7071614250927233bf0c92170451c0a"}, + {file = "yarl-1.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0fd7b941dd1b00b5f0acb97455fea2c4b7aac2dd31ea43fb9d155e9bc7b78664"}, + {file = "yarl-1.14.0-cp312-cp312-win32.whl", hash = "sha256:99ff3744f5fe48288be6bc402533b38e89749623a43208e1d57091fc96b783b9"}, + {file = "yarl-1.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:1ca3894e9e9f72da93544f64988d9c052254a338a9f855165f37f51edb6591de"}, + {file = "yarl-1.14.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5d02d700705d67e09e1f57681f758f0b9d4412eeb70b2eb8d96ca6200b486db3"}, + {file = "yarl-1.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:30600ba5db60f7c0820ef38a2568bb7379e1418ecc947a0f76fd8b2ff4257a97"}, + {file = "yarl-1.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e85d86527baebb41a214cc3b45c17177177d900a2ad5783dbe6f291642d4906f"}, + {file = "yarl-1.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37001e5d4621cef710c8dc1429ca04e189e572f128ab12312eab4e04cf007132"}, + {file = "yarl-1.14.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4f4547944d4f5cfcdc03f3f097d6f05bbbc915eaaf80a2ee120d0e756de377d"}, + {file = "yarl-1.14.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75ff4c819757f9bdb35de049a509814d6ce851fe26f06eb95a392a5640052482"}, + {file = "yarl-1.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68ac1a09392ed6e3fd14be880d39b951d7b981fd135416db7d18a6208c536561"}, + {file = "yarl-1.14.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96952f642ac69075e44c7d0284528938fdff39422a1d90d3e45ce40b72e5e2d9"}, + {file = "yarl-1.14.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a56fbe3d7f3bce1d060ea18d2413a2ca9ca814eea7cedc4d247b5f338d54844e"}, + {file = "yarl-1.14.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7e2637d75e92763d1322cb5041573279ec43a80c0f7fbbd2d64f5aee98447b17"}, + {file = "yarl-1.14.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9abe80ae2c9d37c17599557b712e6515f4100a80efb2cda15f5f070306477cd2"}, + {file = "yarl-1.14.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:217a782020b875538eebf3948fac3a7f9bbbd0fd9bf8538f7c2ad7489e80f4e8"}, + {file = "yarl-1.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9cfef3f14f75bf6aba73a76caf61f9d00865912a04a4393c468a7ce0981b519"}, + {file = "yarl-1.14.0-cp313-cp313-win32.whl", hash = "sha256:d8361c7d04e6a264481f0b802e395f647cd3f8bbe27acfa7c12049efea675bd1"}, + {file = "yarl-1.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:bc24f968b82455f336b79bf37dbb243b7d76cd40897489888d663d4e028f5069"}, + {file = "yarl-1.14.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:91d875f75fabf76b3018c5f196bf3d308ed2b49ddcb46c1576d6b075754a1393"}, + {file = "yarl-1.14.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4009def9be3a7e5175db20aa2d7307ecd00bbf50f7f0f989300710eee1d0b0b9"}, + {file = "yarl-1.14.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:582cedde49603f139be572252a318b30dc41039bc0b8165f070f279e5d12187f"}, + {file = "yarl-1.14.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbd9ff43a04f8ffe8a959a944c2dca10d22f5f99fc6a459f49c3ebfb409309d9"}, + {file = "yarl-1.14.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9f805e37ed16cc212fdc538a608422d7517e7faf539bedea4fe69425bc55d76"}, + {file = "yarl-1.14.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95e16e9eaa2d7f5d87421b8fe694dd71606aa61d74b824c8d17fc85cc51983d1"}, + {file = "yarl-1.14.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:816d24f584edefcc5ca63428f0b38fee00b39fe64e3c5e558f895a18983efe96"}, + {file = "yarl-1.14.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd2660c01367eb3ef081b8fa0a5da7fe767f9427aa82023a961a5f28f0d4af6c"}, + {file = "yarl-1.14.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:94b2bb9bcfd5be9d27004ea4398fb640373dd0c1a9e219084f42c08f77a720ab"}, + {file = "yarl-1.14.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c2089a9afef887664115f7fa6d3c0edd6454adaca5488dba836ca91f60401075"}, + {file = "yarl-1.14.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2192f718db4a8509f63dd6d950f143279211fa7e6a2c612edc17d85bf043d36e"}, + {file = "yarl-1.14.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:8385ab36bf812e9d37cf7613999a87715f27ef67a53f0687d28c44b819df7cb0"}, + {file = "yarl-1.14.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b4c1ecba93e7826dc71ddba75fb7740cdb52e7bd0be9f03136b83f54e6a1f511"}, + {file = "yarl-1.14.0-cp38-cp38-win32.whl", hash = "sha256:e749af6c912a7bb441d105c50c1a3da720474e8acb91c89350080dd600228f0e"}, + {file = "yarl-1.14.0-cp38-cp38-win_amd64.whl", hash = "sha256:147e36331f6f63e08a14640acf12369e041e0751bb70d9362df68c2d9dcf0c87"}, + {file = "yarl-1.14.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a9f917966d27f7ce30039fe8d900f913c5304134096554fd9bea0774bcda6d1"}, + {file = "yarl-1.14.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a2f8fb7f944bcdfecd4e8d855f84c703804a594da5123dd206f75036e536d4d"}, + {file = "yarl-1.14.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f4e475f29a9122f908d0f1f706e1f2fc3656536ffd21014ff8a6f2e1b14d1d8"}, + {file = "yarl-1.14.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8089d4634d8fa2b1806ce44fefa4979b1ab2c12c0bc7ef3dfa45c8a374811348"}, + {file = "yarl-1.14.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b16f6c75cffc2dc0616ea295abb0e1967601bd1fb1e0af6a1de1c6c887f3439"}, + {file = "yarl-1.14.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498b3c55087b9d762636bca9b45f60d37e51d24341786dc01b81253f9552a607"}, + {file = "yarl-1.14.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3f8bfc1db82589ef965ed234b87de30d140db8b6dc50ada9e33951ccd8ec07a"}, + {file = "yarl-1.14.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:625f207b1799e95e7c823f42f473c1e9dbfb6192bd56bba8695656d92be4535f"}, + {file = "yarl-1.14.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:781e2495e408a81e4eaeedeb41ba32b63b1980dddf8b60dbbeff6036bcd35049"}, + {file = "yarl-1.14.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:659603d26d40dd4463200df9bfbc339fbfaed3fe32e5c432fe1dc2b5d4aa94b4"}, + {file = "yarl-1.14.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4e0d45ebf975634468682c8bec021618b3ad52c37619e5c938f8f831fa1ac5c0"}, + {file = "yarl-1.14.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a2e4725a08cb2b4794db09e350c86dee18202bb8286527210e13a1514dc9a59a"}, + {file = "yarl-1.14.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:19268b4fec1d7760134f2de46ef2608c2920134fb1fa61e451f679e41356dc55"}, + {file = "yarl-1.14.0-cp39-cp39-win32.whl", hash = "sha256:337912bcdcf193ade64b9aae5a4017a0a1950caf8ca140362e361543c6773f21"}, + {file = "yarl-1.14.0-cp39-cp39-win_amd64.whl", hash = "sha256:b6d0147574ce2e7b812c989e50fa72bbc5338045411a836bd066ce5fc8ac0bce"}, + {file = "yarl-1.14.0-py3-none-any.whl", hash = "sha256:c8ed4034f0765f8861620c1f2f2364d2e58520ea288497084dae880424fc0d9f"}, + {file = "yarl-1.14.0.tar.gz", hash = "sha256:88c7d9d58aab0724b979ab5617330acb1c7030b79379c8138c1c8c94e121d1b3"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" +propcache = ">=0.2.0" [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "8af5a2fcfe8b30f6c8b36f6ef1e10e22ddca741515d817af923f51aca6affa83" \ No newline at end of file +content-hash = "70a25dabadc35bc8f4015a8d01a969c0d33087889c222d81ac6ebf7d96f06bdf" From 8e751c1dd813f65edc971a51a9458a92597a06cc Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 10 Oct 2024 10:32:15 +0200 Subject: [PATCH 056/305] black CLI --- agenta-cli/agenta/sdk/decorators/tracing.py | 4 +++- agenta-cli/agenta/sdk/tracing/inline.py | 6 ------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 83c8ec1ded..ac88af0d84 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -63,7 +63,9 @@ def redact( not in ( ignore if isinstance(ignore, list) - else io.keys() if ignore is True else [] + else io.keys() + if ignore is True + else [] ) } diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py index a833998ed9..9d9a36a486 100644 --- a/agenta-cli/agenta/sdk/tracing/inline.py +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -323,7 +323,6 @@ class OTelSpanDTO(DisplayBase): def parse_span_dtos_to_span_idx( span_dtos: List[SpanCreateDTO], ) -> Dict[str, SpanCreateDTO]: - span_idx = {span_dto.node.id: span_dto for span_dto in span_dtos} return span_idx @@ -332,7 +331,6 @@ def parse_span_dtos_to_span_idx( def parse_span_idx_to_span_id_tree( span_idx: Dict[str, SpanCreateDTO], ) -> OrderedDict: - span_id_tree = OrderedDict() index = {} @@ -354,7 +352,6 @@ def cumulate_costs( spans_id_tree: OrderedDict, spans_idx: Dict[str, SpanCreateDTO], ) -> None: - def _get_unit(span: SpanCreateDTO): if span.metrics is not None: return span.metrics.get("unit.costs.total", 0.0) @@ -384,7 +381,6 @@ def cumulate_tokens( spans_id_tree: OrderedDict, spans_idx: Dict[str, dict], ) -> None: - def _get_unit(span: SpanCreateDTO): _tokens = { "prompt": 0.0, @@ -640,7 +636,6 @@ def _unmarshal_attributes( level = unmarshalled for i, part in enumerate(keys[:-1]): - if part.isdigit(): part = int(part) @@ -812,7 +807,6 @@ def _parse_from_links( def _parse_from_attributes( otel_span_dto: OTelSpanDTO, ) -> Tuple[dict, dict, dict, dict, dict]: - # DATA _data = _get_attributes(otel_span_dto.attributes, "data") From 424244b4029f381d7327e91178c42647a8ee709f Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 11 Oct 2024 12:36:51 +0200 Subject: [PATCH 057/305] Add observability to OSS, swap engine/session, Clean up imports and env vars --- agenta-backend/agenta_backend/__init__.py | 4 +- .../agenta_backend/apis/__init__.py | 0 .../agenta_backend/apis/fastapi/__init__.py | 0 .../apis/fastapi/observability/__init__.py | 0 .../apis/fastapi/observability/models.py | 59 ++ .../observability/opentelemetry/__init__.py | 0 .../observability/opentelemetry/otlp.py | 222 +++++ .../observability/opentelemetry/semconv.py | 67 ++ .../observability/opentelemetry/traces.proto | 416 +++++++++ .../opentelemetry/traces_proto.py | 57 ++ .../apis/fastapi/observability/router.py | 273 ++++++ .../apis/fastapi/observability/utils.py | 824 ++++++++++++++++++ .../apis/fastapi/shared/__init__.py | 0 .../apis/fastapi/shared/models.py | 83 ++ .../apis/fastapi/shared/utils.py | 26 + .../agenta_backend/core/__init__.py | 0 .../core/observability/__init__.py | 0 .../agenta_backend/core/observability/dtos.py | 353 ++++++++ .../core/observability/interfaces.py | 73 ++ .../core/observability/service.py | 126 +++ .../core/observability/utils.py | 192 ++++ .../agenta_backend/core/shared/__init__.py | 0 .../agenta_backend/core/shared/dtos.py | 98 +++ agenta-backend/agenta_backend/dbs/__init__.py | 0 .../agenta_backend/dbs/postgres/__init__.py | 0 .../dbs/postgres/observability/__init__.py | 0 .../dbs/postgres/observability/dao.py | 396 +++++++++ .../dbs/postgres/observability/dbas.py | 109 +++ .../dbs/postgres/observability/dbes.py | 32 + .../dbs/postgres/observability/mappings.py | 167 ++++ .../dbs/postgres/shared/__init__.py | 0 .../dbs/postgres/shared/base.py | 5 + .../dbs/postgres/shared/config.py | 5 + .../dbs/postgres/shared/dbas.py | 101 +++ .../dbs/postgres/shared/engine.py | 79 ++ agenta-backend/agenta_backend/main.py | 9 + .../migrations/mongo_to_postgres/utils.py | 8 +- .../agenta_backend/migrations/postgres/env.py | 9 +- .../b7af1b4dc016_add_observability_in_oss.py | 103 +++ .../d0dca996e123_add_hstore_extension.py | 19 + .../agenta_backend/models/db_models.py | 2 +- .../agenta_backend/services/db_manager.py | 212 ++--- .../services/evaluation_service.py | 5 - .../agenta_backend/services/user_service.py | 7 +- agenta-backend/agenta_backend/tests/engine.py | 15 +- agenta-backend/agenta_backend/tests/models.py | 76 ++ .../tests/variants_main_router/conftest.py | 16 +- .../test_app_variant_router.py | 13 +- .../test_variant_evaluators_router.py | 29 +- .../test_variant_testset_router.py | 12 +- .../test_variant_versioning_deployment.py | 7 +- .../test_user_profile.py | 7 +- 52 files changed, 4144 insertions(+), 172 deletions(-) create mode 100644 agenta-backend/agenta_backend/apis/__init__.py create mode 100644 agenta-backend/agenta_backend/apis/fastapi/__init__.py create mode 100644 agenta-backend/agenta_backend/apis/fastapi/observability/__init__.py create mode 100644 agenta-backend/agenta_backend/apis/fastapi/observability/models.py create mode 100644 agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/__init__.py create mode 100644 agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py create mode 100644 agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py create mode 100644 agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/traces.proto create mode 100644 agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/traces_proto.py create mode 100644 agenta-backend/agenta_backend/apis/fastapi/observability/router.py create mode 100644 agenta-backend/agenta_backend/apis/fastapi/observability/utils.py create mode 100644 agenta-backend/agenta_backend/apis/fastapi/shared/__init__.py create mode 100644 agenta-backend/agenta_backend/apis/fastapi/shared/models.py create mode 100644 agenta-backend/agenta_backend/apis/fastapi/shared/utils.py create mode 100644 agenta-backend/agenta_backend/core/__init__.py create mode 100644 agenta-backend/agenta_backend/core/observability/__init__.py create mode 100644 agenta-backend/agenta_backend/core/observability/dtos.py create mode 100644 agenta-backend/agenta_backend/core/observability/interfaces.py create mode 100644 agenta-backend/agenta_backend/core/observability/service.py create mode 100644 agenta-backend/agenta_backend/core/observability/utils.py create mode 100644 agenta-backend/agenta_backend/core/shared/__init__.py create mode 100644 agenta-backend/agenta_backend/core/shared/dtos.py create mode 100644 agenta-backend/agenta_backend/dbs/__init__.py create mode 100644 agenta-backend/agenta_backend/dbs/postgres/__init__.py create mode 100644 agenta-backend/agenta_backend/dbs/postgres/observability/__init__.py create mode 100644 agenta-backend/agenta_backend/dbs/postgres/observability/dao.py create mode 100644 agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py create mode 100644 agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py create mode 100644 agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py create mode 100644 agenta-backend/agenta_backend/dbs/postgres/shared/__init__.py create mode 100644 agenta-backend/agenta_backend/dbs/postgres/shared/base.py create mode 100644 agenta-backend/agenta_backend/dbs/postgres/shared/config.py create mode 100644 agenta-backend/agenta_backend/dbs/postgres/shared/dbas.py create mode 100644 agenta-backend/agenta_backend/dbs/postgres/shared/engine.py create mode 100644 agenta-backend/agenta_backend/migrations/postgres/versions/b7af1b4dc016_add_observability_in_oss.py create mode 100644 agenta-backend/agenta_backend/migrations/postgres/versions/d0dca996e123_add_hstore_extension.py create mode 100644 agenta-backend/agenta_backend/tests/models.py diff --git a/agenta-backend/agenta_backend/__init__.py b/agenta-backend/agenta_backend/__init__.py index ff650b4015..4077714df4 100644 --- a/agenta-backend/agenta_backend/__init__.py +++ b/agenta-backend/agenta_backend/__init__.py @@ -1,6 +1,6 @@ import os -if os.environ["FEATURE_FLAG"] in ["cloud", "cloud-dev"]: +if os.environ.get("FEATURE_FLAG") in ["cloud", "cloud-dev"]: import agenta_backend.cloud.__init__ -if os.environ["FEATURE_FLAG"] in ["ee"]: +if os.environ.get("FEATURE_FLAG") in ["ee"]: import agenta_backend.ee.__init__ diff --git a/agenta-backend/agenta_backend/apis/__init__.py b/agenta-backend/agenta_backend/apis/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/agenta-backend/agenta_backend/apis/fastapi/__init__.py b/agenta-backend/agenta_backend/apis/fastapi/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/__init__.py b/agenta-backend/agenta_backend/apis/fastapi/observability/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/models.py b/agenta-backend/agenta_backend/apis/fastapi/observability/models.py new file mode 100644 index 0000000000..7d4145ed75 --- /dev/null +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/models.py @@ -0,0 +1,59 @@ +from typing import List, Dict + +from agenta_backend.apis.fastapi.shared.models import DisplayBase, VersionedModel + +from agenta_backend.core.observability.dtos import ( + OTelSpanDTO, + SpanDTO, + TreeDTO, + RootDTO, +) + + +class CollectStatusResponse(VersionedModel): + status: str + pass + + +class OTelSpansResponse(VersionedModel): + spans: List[OTelSpanDTO] + + +class AgentaNodeDTO(SpanDTO): + pass + + +class AgentaNodesDTO(DisplayBase): + nodes: Dict[str, AgentaNodeDTO] + + +class AgentaTreeDTO(DisplayBase): + tree: TreeDTO + + nodes: Dict[str, AgentaNodeDTO] + + +class AgentaTreesDTO(DisplayBase): + trees: List[AgentaTreeDTO] # -> Dict with tree.name ? + + +class AgentaRootDTO(DisplayBase): + root: RootDTO + + trees: List[AgentaTreeDTO] # -> Dict with tree.name ? + + +class AgentaRootsDTO(DisplayBase): + roots: List[AgentaRootDTO] # -> Dict with root.name ? root.id ? + + +class AgentaNodesResponse(VersionedModel, AgentaNodesDTO): + pass + + +class AgentaTreesResponse(VersionedModel, AgentaTreesDTO): + pass + + +class AgentaRootsResponse(VersionedModel, AgentaRootsDTO): + pass diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/__init__.py b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py new file mode 100644 index 0000000000..3060e20639 --- /dev/null +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py @@ -0,0 +1,222 @@ +from typing import List +from json import dumps +from uuid import UUID +from hashlib import shake_128 +from datetime import datetime +from collections import OrderedDict + +import agenta_backend.apis.fastapi.observability.opentelemetry.traces_proto as Trace_Proto + +from google.protobuf.json_format import MessageToDict + +from agenta_backend.core.observability.dtos import ( + OTelSpanDTO, + OTelContextDTO, + OTelEventDTO, + OTelLinkDTO, +) + + +RESOURCE_ID_SIZE = 16 # 64-bit int +TRACE_ID_SIZE = 32 # 128-bit int +SPAN_ID_SIZE = 16 # 64-bit int + +SPAN_KINDS = [ + "SPAN_KIND_UNSPECIFIED", + "SPAN_KIND_INTERNAL", + "SPAN_KIND_SERVER", + "SPAN_KIND_CLIENT", + "SPAN_KIND_PRODUCER", + "SPAN_KIND_CONSUMER", +] + +SPAN_STATUS_CODES = [ + "STATUS_CODE_UNSET", + "STATUS_CODE_OK", + "STATUS_CODE_ERROR", +] + + +def _parse_attribute( + attribute, +): + key = attribute.key + raw_value = attribute.value + value_type = list(MessageToDict(raw_value).keys())[0].replace("V", "_v") + clean_value = getattr(raw_value, value_type) + + return (key, clean_value) + + +def _parse_timestamp( + timestamp_ns: int, +) -> str: + return datetime.fromtimestamp( + timestamp_ns / 1_000_000_000, + ).isoformat( + timespec="microseconds", + ) + + +LJUST = 20 + + +def parse_otlp_stream( + otlp_stream: bytes, +) -> List[OTelSpanDTO]: + + # otlp_stream = b"\n\x94$\n6\n\x18\n\x0cservice.name\x12\x08\n\x06agenta\n\x1a\n\x0fservice.version\x12\x07\n\x050.1.0\x12\x95\x1d\n1\n'opentelemetry.instrumentation.openai.v1\x12\x060.30.0\x12\xa3\x04\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\xd9N#YhoL\n\"\x08\x1dh\x04X\xa0\xf4\xf8P*\x11openai.embeddings0\x039\x00\x90\xa2\tWP\xfa\x17A\x08\xb2 WP\xfa\x17J\x1f\n\x10llm.request.type\x12\x0b\n\tembeddingJ9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307J\x19\n\rgen_ai.system\x12\x08\n\x06OpenAIJ0\n\x14gen_ai.request.model\x12\x18\n\x16text-embedding-ada-002J\x15\n\x0bllm.headers\x12\x06\n\x04NoneJ\x16\n\x10llm.is_streaming\x12\x02\x10\x00JI\n\x17gen_ai.prompt.0.content\x12.\n,Movies about witches in the genre of comedy.J6\n\x16gen_ai.openai.api_base\x12\x1c\n\x1ahttps://api.openai.com/v1/J1\n\x15gen_ai.response.model\x12\x18\n\x16text-embedding-ada-002J\x1c\n\x16llm.usage.total_tokens\x12\x02\x18\tJ \n\x1agen_ai.usage.prompt_tokens\x12\x02\x18\tz\x00\x85\x01\x00\x01\x00\x00\x12\xb9\x18\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\x1b\x05'\x8e\xb4\xaar\xda\"\x08\xac\t\x88\x86\x96\x87\xce\xb4*\x0bopenai.chat0\x039\x10h\x16&WP\xfa\x17A\x10{0qWP\xfa\x17J\x1a\n\x10llm.request.type\x12\x06\n\x04chatJ9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307J\x19\n\rgen_ai.system\x12\x08\n\x06OpenAIJ'\n\x14gen_ai.request.model\x12\x0f\n\rgpt-3.5-turboJ'\n\x1agen_ai.request.temperature\x12\t!\x9a\x99\x99\x99\x99\x99\xe9?J\x15\n\x0bllm.headers\x12\x06\n\x04NoneJ\x16\n\x10llm.is_streaming\x12\x02\x10\x00J6\n\x16gen_ai.openai.api_base\x12\x1c\n\x1ahttps://api.openai.com/v1/J \n\x14gen_ai.prompt.0.role\x12\x08\n\x06systemJ\x84\x11\n\x17gen_ai.prompt.0.content\x12\xe8\x10\n\xe5\x10Given the following list of suggested movies:\n\nThe Craft (1996) in ['Drama', 'Fantasy', 'Horror']: A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.\nWicked Stepmother (1989) in ['Comedy', 'Fantasy']: A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...\nOz the Great and Powerful (2013) in ['Adventure', 'Family', 'Fantasy']: A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.\nInto the Woods (2014) in ['Adventure', 'Fantasy', 'Musical']: A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.\nSnow White: A Tale of Terror (1997) in ['Fantasy', 'Horror']: In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.\nBedknobs and Broomsticks (1971) in ['Adventure', 'Family', 'Fantasy']: An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.\nMy Neighbor Totoro (1988) in ['Animation', 'Family', 'Fantasy']: When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.\nHocus Pocus (1993) in ['Comedy', 'Family', 'Fantasy']: After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.\nPractical Magic (1998) in ['Comedy', 'Fantasy', 'Romance']: The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love.J\x1e\n\x14gen_ai.prompt.1.role\x12\x06\n\x04userJ]\n\x17gen_ai.prompt.1.content\x12B\n@Provide a list of 3 movies about witches in the genre of comedy.J-\n\x15gen_ai.response.model\x12\x14\n\x12gpt-3.5-turbo-0125J\x1d\n\x16llm.usage.total_tokens\x12\x03\x18\xc2\x04J$\n\x1egen_ai.usage.completion_tokens\x12\x02\x18*J!\n\x1agen_ai.usage.prompt_tokens\x12\x03\x18\x98\x04J+\n!gen_ai.completion.0.finish_reason\x12\x06\n\x04stopJ'\n\x18gen_ai.completion.0.role\x12\x0b\n\tassistantJ\xa7\x01\n\x1bgen_ai.completion.0.content\x12\x87\x01\n\x84\x01Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989)\n2. Hocus Pocus (1993)\n3. Practical Magic (1998)z\x00\x85\x01\x00\x01\x00\x00\x12\xc1\x06\n\x0f\n\ragenta.tracer\x12\x86\x01\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\x1dh\x04X\xa0\xf4\xf8P\"\x08\xac\xb2\x8e\xff\xb2\xe8\xea\x14*\x05embed0\x019@\xc1\x9f\tWP\xfa\x17A\xb0H- WP\xfa\x17J9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307z\x02\x18\x01\x85\x01\x00\x01\x00\x00\x12\x87\x01\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\xcaD;\xdf \xbb\x13|\"\x08\xac\xb2\x8e\xff\xb2\xe8\xea\x14*\x06search0\x019`\xf0/ WP\xfa\x17A\xc0\x16\x03&WP\xfa\x17J9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307z\x02\x18\x01\x85\x01\x00\x01\x00\x00\x12\x8a\x01\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\xac\xb2\x8e\xff\xb2\xe8\xea\x14\"\x08\x9f\xad5\xa6\\\xc9\xf9\xeb*\tretriever0\x019\xc8o\x9b\tWP\xfa\x17A\xd8w\x07&WP\xfa\x17J9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307z\x02\x18\x01\x85\x01\x00\x01\x00\x00\x12\x85\x01\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\xac\t\x88\x86\x96\x87\xce\xb4\"\x08\xf8T\xa3\xe9\x07;\x90\x86*\x04chat0\x019\x08\xbd\x0f&WP\xfa\x17Ax\xfe:qWP\xfa\x17J9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307z\x02\x18\x01\x85\x01\x00\x01\x00\x00\x12\x89\x01\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\xf8T\xa3\xe9\x07;\x90\x86\"\x08\x9f\xad5\xa6\\\xc9\xf9\xeb*\x08reporter0\x019\xb8\x17\n&WP\xfa\x17A0\x81 0 else None + # print("s_events".ljust(LJUST), s_events) + + s_links = [ + OTelLinkDTO( + context=OTelContextDTO( + trace_id="0x" + link.trace_id.hex(), + span_id="0x" + link.span_id.hex(), + ), + attributes={ + k: v + for k, v in [ + _parse_attribute(attribute) + for attribute in link.attributes + ] + }, + ) + for link in span.links + ] + s_links = s_links if len(s_links) > 0 else None + # print("s_links".ljust(LJUST), s_links) + + otel_span_dto = OTelSpanDTO( + context=s_context, + name=s_name, + kind=s_kind, + start_time=s_start_time, + end_time=s_end_time, + status_code=s_status_code, + status_message=s_status_message, + attributes=s_attributes, + events=s_events, + parent=p_context, + links=s_links, + ) + + otel_span_dtos.append(otel_span_dto) + + return otel_span_dtos diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py new file mode 100644 index 0000000000..a0344a4dcb --- /dev/null +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py @@ -0,0 +1,67 @@ +VERSION = "0.4.1" + +V_0_4_1_ATTRIBUTES_EXACT = [ + ("gen_ai.system", "ag.meta.system"), + ("gen_ai.request.base_url", "ag.meta.request.base_url"), + ("gen_ai.request.endpoint", "ag.meta.request.endpoint"), + ("gen_ai.request.headers", "ag.meta.request.headers"), + ("gen_ai.request.type", "ag.type.node"), + ("gen_ai.request.streaming", "ag.meta.request.streaming"), + ("gen_ai.request.model", "ag.meta.request.model"), + ("gen_ai.request.max_tokens", "ag.meta.request.max_tokens"), + ("gen_ai.request.temperature", "ag.meta.request.temperature"), + ("gen_ai.request.top_p", "ag.meta.request.top_p"), + ("gen_ai.response.model", "ag.meta.response.model"), + ("gen_ai.usage.prompt_tokens", "ag.metrics.unit.tokens.prompt"), + ("gen_ai.usage.completion_tokens", "ag.metrics.unit.tokens.completion"), + ("gen_ai.usage.total_tokens", "ag.metrics.unit.tokens.total"), + ("llm.headers", "ag.meta.request.headers"), + ("llm.request.type", "ag.type.node"), + ("llm.top_k", "ag.meta.request.top_k"), + ("llm.is_streaming", "ag.meta.request.streaming"), + ("llm.usage.total_tokens", "ag.metrics.unit.tokens.total"), + ("gen_ai.openai.api_base", "ag.meta.request.base_url"), + ("db.system", "ag.meta.system"), + ("db.vector.query.top_k", "ag.meta.request.top_k"), + ("pinecone.query.top_k", "ag.meta.request.top_k"), + ("traceloop.span.kind", "ag.type.node"), +] +V_0_4_1_ATTRIBUTES_PREFIX = [ + ("gen_ai.prompt", "ag.data.inputs.prompt"), + ("gen_ai.completion", "ag.data.outputs.completion"), +] + +V_0_4_1_MAPS = { + "attributes": { + "exact": { + "from": {otel: agenta for otel, agenta in V_0_4_1_ATTRIBUTES_EXACT[::-1]}, + "to": {agenta: otel for otel, agenta in V_0_4_1_ATTRIBUTES_EXACT[::-1]}, + }, + "prefix": { + "from": {otel: agenta for otel, agenta in V_0_4_1_ATTRIBUTES_PREFIX[::-1]}, + "to": {agenta: otel for otel, agenta in V_0_4_1_ATTRIBUTES_PREFIX[::-1]}, + }, + }, +} +V_0_4_1_KEYS = { + "attributes": { + "exact": { + "from": list(V_0_4_1_MAPS["attributes"]["exact"]["from"].keys()), + "to": list(V_0_4_1_MAPS["attributes"]["exact"]["to"].keys()), + }, + "prefix": { + "from": list(V_0_4_1_MAPS["attributes"]["prefix"]["from"].keys()), + "to": list(V_0_4_1_MAPS["attributes"]["prefix"]["to"].keys()), + }, + }, +} + + +MAPS = { + "0.4.1": V_0_4_1_MAPS, # LATEST +} +KEYS = { + "0.4.1": V_0_4_1_KEYS, # LATEST +} + +CODEX = {"maps": MAPS[VERSION], "keys": KEYS[VERSION]} diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/traces.proto b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/traces.proto new file mode 100644 index 0000000000..667579e51c --- /dev/null +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/traces.proto @@ -0,0 +1,416 @@ +// Copyright 2019, OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package opentelemetry.proto.trace.v1; + +// AnyValue is used to represent any type of attribute value. AnyValue may contain a +// primitive value such as a string or integer or it may contain an arbitrary nested +// object containing arrays, key-value lists and primitives. +message AnyValue { + // The value is one of the listed fields. It is valid for all values to be unspecified + // in which case this AnyValue is considered to be "empty". + oneof value { + string string_value = 1; + bool bool_value = 2; + int64 int_value = 3; + double double_value = 4; + ArrayValue array_value = 5; + KeyValueList kvlist_value = 6; + bytes bytes_value = 7; + } +} + +// ArrayValue is a list of AnyValue messages. We need ArrayValue as a message +// since oneof in AnyValue does not allow repeated fields. +message ArrayValue { + // Array of values. The array may be empty (contain 0 elements). + repeated AnyValue values = 1; +} + +// KeyValueList is a list of KeyValue messages. We need KeyValueList as a message +// since `oneof` in AnyValue does not allow repeated fields. Everywhere else where we need +// a list of KeyValue messages (e.g. in Span) we use `repeated KeyValue` directly to +// avoid unnecessary extra wrapping (which slows down the protocol). The 2 approaches +// are semantically equivalent. +message KeyValueList { + // A collection of key/value pairs of key-value pairs. The list may be empty (may + // contain 0 elements). + // The keys MUST be unique (it is not allowed to have more than one + // value with the same key). + repeated KeyValue values = 1; +} + +// KeyValue is a key-value pair that is used to store Span attributes, Link +// attributes, etc. +message KeyValue { + string key = 1; + AnyValue value = 2; +} + +// InstrumentationScope is a message representing the instrumentation scope information +// such as the fully qualified name and version. +message InstrumentationScope { + // An empty instrumentation scope name means the name is unknown. + string name = 1; + string version = 2; + + // Additional attributes that describe the scope. [Optional]. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated KeyValue attributes = 3; + uint32 dropped_attributes_count = 4; +} + +// Resource information. +message Resource { + // Set of attributes that describe the resource. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated KeyValue attributes = 1; + + // dropped_attributes_count is the number of dropped attributes. If the value is 0, then + // no attributes were dropped. + uint32 dropped_attributes_count = 2; +} + +// TracesData represents the traces data that can be stored in a persistent storage, +// OR can be embedded by other protocols that transfer OTLP traces data but do +// not implement the OTLP protocol. +// +// The main difference between this message and collector protocol is that +// in this message there will not be any "control" or "metadata" specific to +// OTLP protocol. +// +// When new fields are added into this message, the OTLP request MUST be updated +// as well. +message TracesData { + // An array of ResourceSpans. + // For data coming from a single resource this array will typically contain + // one element. Intermediary nodes that receive data from multiple origins + // typically batch the data before forwarding further and in that case this + // array will contain multiple elements. + repeated ResourceSpans resource_spans = 1; +} + +// A collection of ScopeSpans from a Resource. +message ResourceSpans { + reserved 1000; + + // The resource for the spans in this message. + // If this field is not set then no resource info is known. + Resource resource = 1; + + // A list of ScopeSpans that originate from a resource. + repeated ScopeSpans scope_spans = 2; + + // The Schema URL, if known. This is the identifier of the Schema that the resource data + // is recorded in. To learn more about Schema URL see + // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + // This schema_url applies to the data in the "resource" field. It does not apply + // to the data in the "scope_spans" field which have their own schema_url field. + string schema_url = 3; +} + +// A collection of Spans produced by an InstrumentationScope. +message ScopeSpans { + // The instrumentation scope information for the spans in this message. + // Semantically when InstrumentationScope isn't set, it is equivalent with + // an empty instrumentation scope name (unknown). + InstrumentationScope scope = 1; + + // A list of Spans that originate from an instrumentation scope. + repeated Span spans = 2; + + // The Schema URL, if known. This is the identifier of the Schema that the span data + // is recorded in. To learn more about Schema URL see + // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + // This schema_url applies to all spans and span events in the "spans" field. + string schema_url = 3; +} + +// A Span represents a single operation performed by a single component of the system. +// +// The next available field id is 17. +message Span { + // A unique identifier for a trace. All spans from the same trace share + // the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR + // of length other than 16 bytes is considered invalid (empty string in OTLP/JSON + // is zero-length and thus is also invalid). + // + // This field is required. + bytes trace_id = 1; + + // A unique identifier for a span within a trace, assigned when the span + // is created. The ID is an 8-byte array. An ID with all zeroes OR of length + // other than 8 bytes is considered invalid (empty string in OTLP/JSON + // is zero-length and thus is also invalid). + // + // This field is required. + bytes span_id = 2; + + // trace_state conveys information about request position in multiple distributed tracing graphs. + // It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header + // See also https://github.com/w3c/distributed-tracing for more details about this field. + string trace_state = 3; + + // The `span_id` of this span's parent span. If this is a root span, then this + // field must be empty. The ID is an 8-byte array. + bytes parent_span_id = 4; + + // Flags, a bit field. + // + // Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace + // Context specification. To read the 8-bit W3C trace flag, use + // `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. + // + // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. + // + // Bits 8 and 9 represent the 3 states of whether a span's parent + // is remote. The states are (unknown, is not remote, is remote). + // To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. + // To read whether the span is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. + // + // When creating span messages, if the message is logically forwarded from another source + // with an equivalent flags fields (i.e., usually another OTLP span message), the field SHOULD + // be copied as-is. If creating from a source that does not have an equivalent flags field + // (such as a runtime representation of an OpenTelemetry span), the high 22 bits MUST + // be set to zero. + // Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. + // + // [Optional]. + fixed32 flags = 16; + + // A description of the span's operation. + // + // For example, the name can be a qualified method name or a file name + // and a line number where the operation is called. A best practice is to use + // the same display name at the same call point in an application. + // This makes it easier to correlate spans in different traces. + // + // This field is semantically required to be set to non-empty string. + // Empty value is equivalent to an unknown span name. + // + // This field is required. + string name = 5; + + // SpanKind is the type of span. Can be used to specify additional relationships between spans + // in addition to a parent/child relationship. + enum SpanKind { + // Unspecified. Do NOT use as default. + // Implementations MAY assume SpanKind to be INTERNAL when receiving UNSPECIFIED. + SPAN_KIND_UNSPECIFIED = 0; + + // Indicates that the span represents an internal operation within an application, + // as opposed to an operation happening at the boundaries. Default value. + SPAN_KIND_INTERNAL = 1; + + // Indicates that the span covers server-side handling of an RPC or other + // remote network request. + SPAN_KIND_SERVER = 2; + + // Indicates that the span describes a request to some remote service. + SPAN_KIND_CLIENT = 3; + + // Indicates that the span describes a producer sending a message to a broker. + // Unlike CLIENT and SERVER, there is often no direct critical path latency relationship + // between producer and consumer spans. A PRODUCER span ends when the message was accepted + // by the broker while the logical processing of the message might span a much longer time. + SPAN_KIND_PRODUCER = 4; + + // Indicates that the span describes consumer receiving a message from a broker. + // Like the PRODUCER kind, there is often no direct critical path latency relationship + // between producer and consumer spans. + SPAN_KIND_CONSUMER = 5; + } + + // Distinguishes between spans generated in a particular context. For example, + // two spans with the same name may be distinguished using `CLIENT` (caller) + // and `SERVER` (callee) to identify queueing latency associated with the span. + SpanKind kind = 6; + + // start_time_unix_nano is the start time of the span. On the client side, this is the time + // kept by the local machine where the span execution starts. On the server side, this + // is the time when the server's application handler starts running. + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + // + // This field is semantically required and it is expected that end_time >= start_time. + fixed64 start_time_unix_nano = 7; + + // end_time_unix_nano is the end time of the span. On the client side, this is the time + // kept by the local machine where the span execution ends. On the server side, this + // is the time when the server application handler stops running. + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + // + // This field is semantically required and it is expected that end_time >= start_time. + fixed64 end_time_unix_nano = 8; + + // attributes is a collection of key/value pairs. Note, global attributes + // like server name can be set using the resource API. Examples of attributes: + // + // "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" + // "/http/server_latency": 300 + // "example.com/myattribute": true + // "example.com/score": 10.239 + // + // The OpenTelemetry API specification further restricts the allowed value types: + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/README.md#attribute + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated KeyValue attributes = 9; + + // dropped_attributes_count is the number of attributes that were discarded. Attributes + // can be discarded because their keys are too long or because there are too many + // attributes. If this value is 0, then no attributes were dropped. + uint32 dropped_attributes_count = 10; + + // Event is a time-stamped annotation of the span, consisting of user-supplied + // text description and key-value pairs. + message Event { + // time_unix_nano is the time the event occurred. + fixed64 time_unix_nano = 1; + + // name of the event. + // This field is semantically required to be set to non-empty string. + string name = 2; + + // attributes is a collection of attribute key/value pairs on the event. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated KeyValue attributes = 3; + + // dropped_attributes_count is the number of dropped attributes. If the value is 0, + // then no attributes were dropped. + uint32 dropped_attributes_count = 4; + } + + // events is a collection of Event items. + repeated Event events = 11; + + // dropped_events_count is the number of dropped events. If the value is 0, then no + // events were dropped. + uint32 dropped_events_count = 12; + + // A pointer from the current span to another span in the same trace or in a + // different trace. For example, this can be used in batching operations, + // where a single batch handler processes multiple requests from different + // traces or when the handler receives a request from a different project. + message Link { + // A unique identifier of a trace that this linked span is part of. The ID is a + // 16-byte array. + bytes trace_id = 1; + + // A unique identifier for the linked span. The ID is an 8-byte array. + bytes span_id = 2; + + // The trace_state associated with the link. + string trace_state = 3; + + // attributes is a collection of attribute key/value pairs on the link. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + repeated KeyValue attributes = 4; + + // dropped_attributes_count is the number of dropped attributes. If the value is 0, + // then no attributes were dropped. + uint32 dropped_attributes_count = 5; + + // Flags, a bit field. + // + // Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace + // Context specification. To read the 8-bit W3C trace flag, use + // `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. + // + // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. + // + // Bits 8 and 9 represent the 3 states of whether the link is remote. + // The states are (unknown, is not remote, is remote). + // To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. + // To read whether the link is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. + // + // Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. + // When creating new spans, bits 10-31 (most-significant 22-bits) MUST be zero. + // + // [Optional]. + fixed32 flags = 6; + } + + // links is a collection of Links, which are references from this span to a span + // in the same or different trace. + repeated Link links = 13; + + // dropped_links_count is the number of dropped links after the maximum size was + // enforced. If this value is 0, then no links were dropped. + uint32 dropped_links_count = 14; + + // An optional final status for this span. Semantically when Status isn't set, it means + // span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0). + Status status = 15; +} + +// The Status type defines a logical error model that is suitable for different +// programming environments, including REST APIs and RPC APIs. +message Status { + reserved 1; + + // A developer-facing human readable error message. + string message = 2; + + // For the semantics of status codes see + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#set-status + enum StatusCode { + // The default status. + STATUS_CODE_UNSET = 0; + // The Span has been validated by an Application developer or Operator to + // have completed successfully. + STATUS_CODE_OK = 1; + // The Span contains an error. + STATUS_CODE_ERROR = 2; + }; + + // The status code. + StatusCode code = 3; +} + +// SpanFlags represents constants used to interpret the +// Span.flags field, which is protobuf 'fixed32' type and is to +// be used as bit-fields. Each non-zero value defined in this enum is +// a bit-mask. To extract the bit-field, for example, use an +// expression like: +// +// (span.flags & SPAN_FLAGS_TRACE_FLAGS_MASK) +// +// See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. +// +// Note that Span flags were introduced in version 1.1 of the +// OpenTelemetry protocol. Older Span producers do not set this +// field, consequently consumers should not rely on the absence of a +// particular flag bit to indicate the presence of a particular feature. +enum SpanFlags { + // The zero value for the enum. Should not be used for comparisons. + // Instead use bitwise "and" with the appropriate mask as shown above. + SPAN_FLAGS_DO_NOT_USE = 0; + + // Bits 0-7 are used for trace flags. + SPAN_FLAGS_TRACE_FLAGS_MASK = 0x000000FF; + + // Bits 8 and 9 are used to indicate that the parent span or link span is remote. + // Bit 8 (`HAS_IS_REMOTE`) indicates whether the value is known. + // Bit 9 (`IS_REMOTE`) indicates whether the span or link is remote. + SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK = 0x00000100; + SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK = 0x00000200; + + // Bits 10-31 are reserved for future use. +} \ No newline at end of file diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/traces_proto.py b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/traces_proto.py new file mode 100644 index 0000000000..899bf0765c --- /dev/null +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/traces_proto.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: traces.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x0btrace.proto\x12\x1copentelemetry.proto.trace.v1"\x8a\x02\n\x08\x41nyValue\x12\x16\n\x0cstring_value\x18\x01 \x01(\tH\x00\x12\x14\n\nbool_value\x18\x02 \x01(\x08H\x00\x12\x13\n\tint_value\x18\x03 \x01(\x03H\x00\x12\x16\n\x0c\x64ouble_value\x18\x04 \x01(\x01H\x00\x12?\n\x0b\x61rray_value\x18\x05 \x01(\x0b\x32(.opentelemetry.proto.trace.v1.ArrayValueH\x00\x12\x42\n\x0ckvlist_value\x18\x06 \x01(\x0b\x32*.opentelemetry.proto.trace.v1.KeyValueListH\x00\x12\x15\n\x0b\x62ytes_value\x18\x07 \x01(\x0cH\x00\x42\x07\n\x05value"D\n\nArrayValue\x12\x36\n\x06values\x18\x01 \x03(\x0b\x32&.opentelemetry.proto.trace.v1.AnyValue"F\n\x0cKeyValueList\x12\x36\n\x06values\x18\x01 \x03(\x0b\x32&.opentelemetry.proto.trace.v1.KeyValue"N\n\x08KeyValue\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x35\n\x05value\x18\x02 \x01(\x0b\x32&.opentelemetry.proto.trace.v1.AnyValue"\x93\x01\n\x14InstrumentationScope\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12:\n\nattributes\x18\x03 \x03(\x0b\x32&.opentelemetry.proto.trace.v1.KeyValue\x12 \n\x18\x64ropped_attributes_count\x18\x04 \x01(\r"h\n\x08Resource\x12:\n\nattributes\x18\x01 \x03(\x0b\x32&.opentelemetry.proto.trace.v1.KeyValue\x12 \n\x18\x64ropped_attributes_count\x18\x02 \x01(\r"Q\n\nTracesData\x12\x43\n\x0eresource_spans\x18\x01 \x03(\x0b\x32+.opentelemetry.proto.trace.v1.ResourceSpans"\xa4\x01\n\rResourceSpans\x12\x38\n\x08resource\x18\x01 \x01(\x0b\x32&.opentelemetry.proto.trace.v1.Resource\x12=\n\x0bscope_spans\x18\x02 \x03(\x0b\x32(.opentelemetry.proto.trace.v1.ScopeSpans\x12\x12\n\nschema_url\x18\x03 \x01(\tJ\x06\x08\xe8\x07\x10\xe9\x07"\x96\x01\n\nScopeSpans\x12\x41\n\x05scope\x18\x01 \x01(\x0b\x32\x32.opentelemetry.proto.trace.v1.InstrumentationScope\x12\x31\n\x05spans\x18\x02 \x03(\x0b\x32".opentelemetry.proto.trace.v1.Span\x12\x12\n\nschema_url\x18\x03 \x01(\t"\x81\x08\n\x04Span\x12\x10\n\x08trace_id\x18\x01 \x01(\x0c\x12\x0f\n\x07span_id\x18\x02 \x01(\x0c\x12\x13\n\x0btrace_state\x18\x03 \x01(\t\x12\x16\n\x0eparent_span_id\x18\x04 \x01(\x0c\x12\r\n\x05\x66lags\x18\x10 \x01(\x07\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x39\n\x04kind\x18\x06 \x01(\x0e\x32+.opentelemetry.proto.trace.v1.Span.SpanKind\x12\x1c\n\x14start_time_unix_nano\x18\x07 \x01(\x06\x12\x1a\n\x12\x65nd_time_unix_nano\x18\x08 \x01(\x06\x12:\n\nattributes\x18\t \x03(\x0b\x32&.opentelemetry.proto.trace.v1.KeyValue\x12 \n\x18\x64ropped_attributes_count\x18\n \x01(\r\x12\x38\n\x06\x65vents\x18\x0b \x03(\x0b\x32(.opentelemetry.proto.trace.v1.Span.Event\x12\x1c\n\x14\x64ropped_events_count\x18\x0c \x01(\r\x12\x36\n\x05links\x18\r \x03(\x0b\x32\'.opentelemetry.proto.trace.v1.Span.Link\x12\x1b\n\x13\x64ropped_links_count\x18\x0e \x01(\r\x12\x34\n\x06status\x18\x0f \x01(\x0b\x32$.opentelemetry.proto.trace.v1.Status\x1a\x8b\x01\n\x05\x45vent\x12\x16\n\x0etime_unix_nano\x18\x01 \x01(\x06\x12\x0c\n\x04name\x18\x02 \x01(\t\x12:\n\nattributes\x18\x03 \x03(\x0b\x32&.opentelemetry.proto.trace.v1.KeyValue\x12 \n\x18\x64ropped_attributes_count\x18\x04 \x01(\r\x1a\xab\x01\n\x04Link\x12\x10\n\x08trace_id\x18\x01 \x01(\x0c\x12\x0f\n\x07span_id\x18\x02 \x01(\x0c\x12\x13\n\x0btrace_state\x18\x03 \x01(\t\x12:\n\nattributes\x18\x04 \x03(\x0b\x32&.opentelemetry.proto.trace.v1.KeyValue\x12 \n\x18\x64ropped_attributes_count\x18\x05 \x01(\r\x12\r\n\x05\x66lags\x18\x06 \x01(\x07"\x99\x01\n\x08SpanKind\x12\x19\n\x15SPAN_KIND_UNSPECIFIED\x10\x00\x12\x16\n\x12SPAN_KIND_INTERNAL\x10\x01\x12\x14\n\x10SPAN_KIND_SERVER\x10\x02\x12\x14\n\x10SPAN_KIND_CLIENT\x10\x03\x12\x16\n\x12SPAN_KIND_PRODUCER\x10\x04\x12\x16\n\x12SPAN_KIND_CONSUMER\x10\x05"\xae\x01\n\x06Status\x12\x0f\n\x07message\x18\x02 \x01(\t\x12=\n\x04\x63ode\x18\x03 \x01(\x0e\x32/.opentelemetry.proto.trace.v1.Status.StatusCode"N\n\nStatusCode\x12\x15\n\x11STATUS_CODE_UNSET\x10\x00\x12\x12\n\x0eSTATUS_CODE_OK\x10\x01\x12\x15\n\x11STATUS_CODE_ERROR\x10\x02J\x04\x08\x01\x10\x02*\x9c\x01\n\tSpanFlags\x12\x19\n\x15SPAN_FLAGS_DO_NOT_USE\x10\x00\x12 \n\x1bSPAN_FLAGS_TRACE_FLAGS_MASK\x10\xff\x01\x12*\n%SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK\x10\x80\x02\x12&\n!SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK\x10\x80\x04\x62\x06proto3' +) + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "trace_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals["_SPANFLAGS"]._serialized_start = 2401 + _globals["_SPANFLAGS"]._serialized_end = 2557 + _globals["_ANYVALUE"]._serialized_start = 46 + _globals["_ANYVALUE"]._serialized_end = 312 + _globals["_ARRAYVALUE"]._serialized_start = 314 + _globals["_ARRAYVALUE"]._serialized_end = 382 + _globals["_KEYVALUELIST"]._serialized_start = 384 + _globals["_KEYVALUELIST"]._serialized_end = 454 + _globals["_KEYVALUE"]._serialized_start = 456 + _globals["_KEYVALUE"]._serialized_end = 534 + _globals["_INSTRUMENTATIONSCOPE"]._serialized_start = 537 + _globals["_INSTRUMENTATIONSCOPE"]._serialized_end = 684 + _globals["_RESOURCE"]._serialized_start = 686 + _globals["_RESOURCE"]._serialized_end = 790 + _globals["_TRACESDATA"]._serialized_start = 792 + _globals["_TRACESDATA"]._serialized_end = 873 + _globals["_RESOURCESPANS"]._serialized_start = 876 + _globals["_RESOURCESPANS"]._serialized_end = 1040 + _globals["_SCOPESPANS"]._serialized_start = 1043 + _globals["_SCOPESPANS"]._serialized_end = 1193 + _globals["_SPAN"]._serialized_start = 1196 + _globals["_SPAN"]._serialized_end = 2221 + _globals["_SPAN_EVENT"]._serialized_start = 1752 + _globals["_SPAN_EVENT"]._serialized_end = 1891 + _globals["_SPAN_LINK"]._serialized_start = 1894 + _globals["_SPAN_LINK"]._serialized_end = 2065 + _globals["_SPAN_SPANKIND"]._serialized_start = 2068 + _globals["_SPAN_SPANKIND"]._serialized_end = 2221 + _globals["_STATUS"]._serialized_start = 2224 + _globals["_STATUS"]._serialized_end = 2398 + _globals["_STATUS_STATUSCODE"]._serialized_start = 2314 + _globals["_STATUS_STATUSCODE"]._serialized_end = 2392 +# @@protoc_insertion_point(module_scope) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py new file mode 100644 index 0000000000..50df5f308f --- /dev/null +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py @@ -0,0 +1,273 @@ +from typing import Dict, List, Union + +from fastapi import ( + APIRouter, + Request, + Depends, + Query, + status, + BackgroundTasks, + HTTPException, +) + +from agenta_backend.core.observability.service import ObservabilityService +from agenta_backend.core.observability.dtos import QueryDTO + +from agenta_backend.apis.fastapi.shared.utils import handle_exceptions +from agenta_backend.apis.fastapi.observability.opentelemetry.otlp import ( + parse_otlp_stream, +) +from agenta_backend.apis.fastapi.observability.utils import ( + parse_query_dto, + parse_from_otel_span_dto, + parse_to_otel_span_dto, + parse_to_agenta_span_dto, +) +from agenta_backend.apis.fastapi.observability.models import ( + CollectStatusResponse, + OTelSpansResponse, + AgentaNodesResponse, + AgentaTreesResponse, + AgentaRootsResponse, + AgentaNodeDTO, + AgentaTreeDTO, + AgentaRootDTO, + SpanDTO, + TreeDTO, + RootDTO, +) + +VERSION = "1.0.0" + + +class ObservabilityRouter: + VERSION = "1.0.0" + + def __init__( + self, + observability_service: ObservabilityService, + ): + self.service = observability_service + + self.router = APIRouter() + + ### STATUS + + self.router.add_api_route( + "/traces", + self.otlp_status, + methods=["GET"], + operation_id="otlp_status", + summary="Status of OTLP endpoint", + status_code=status.HTTP_200_OK, + response_model=CollectStatusResponse, + ) + + ### QUERIES + + self.router.add_api_route( + "/{project_id}/traces", + self.query_traces, + methods=["GET"], + operation_id="query_traces", + summary="Query traces, with optional grouping, filtering, (sorting,) and pagination.", + status_code=status.HTTP_200_OK, + response_model=Union[ + OTelSpansResponse, + AgentaNodesResponse, + AgentaTreesResponse, + AgentaRootsResponse, + ], + response_model_exclude_none=True, + ) + + ### MUTATIONS + + self.router.add_api_route( + "/traces", + self.otlp_collect_traces, + methods=["POST"], + operation_id="otlp_collect_traces", + summary="Collect traces via OTLP", + status_code=status.HTTP_202_ACCEPTED, + response_model=CollectStatusResponse, + ) + + ### STATUS + + @handle_exceptions() + async def otlp_status(self): + """ + Status of OTLP endpoint. + """ + + return CollectStatusResponse(version=self.VERSION, status="ready") + + ### QUERIES + + @handle_exceptions() + async def query_traces( + self, + project_id: str, + query_dto: QueryDTO = Depends(parse_query_dto), + format: str = Query("agenta"), + ): + """ + Query traces, with optional grouping, filtering, (sorting,) and pagination. + """ + + ### TODO -------------------------------------- # + # - [x] Allows for scoping by: + # - [x] 'project_id' + # - [x] Allows for grouping by: + # - [x] 'scenario' i.e. root + # - [x] 'trace' i.e. tree + # - [x] 'span' i.e. node + # - [x] Allows for windowing by: + # - [x] 'earliest' and 'latest' + # - [x] Allows for filtering by: + # - [x] nested conditions + # - [x] single conditions + # - [x] Allows for pagination by: + # - [x] 'page' and 'size' + # - [x] Allows for formatting by: + # - [x] 'custom' + # - [x] 'opentelemetry' + # All using: + # - [x] query params + # - [x] sane defaults + # - [x] error handling + # - [x] clean DTO types + # - [x] clean DBA types + # - [x] semconv mapping + ### ------------------------------------------- # + + ##### ADD FETCH ONE (BY PID / NID) WITH FORMAT + + if ( + format == "opentelemetry" + and query_dto.grouping + and query_dto.grouping.focus.value != "node" + ): + raise HTTPException( + status_code=400, + detail="Grouping is not supported in OpenTelemetry format.", + ) + + span_dtos = await self.service.query( + project_id=project_id, + query_dto=query_dto, + ) + + spans = [] + + if format == "opentelemetry": + spans = [parse_to_otel_span_dto(span_dto) for span_dto in span_dtos] + + return OTelSpansResponse( + version=self.VERSION, + spans=spans, + ) + + elif format == "agenta": + spans = [parse_to_agenta_span_dto(span_dto) for span_dto in span_dtos] + + if query_dto.grouping: + if query_dto.grouping.focus.value in ["tree", "root"]: + _nodes_by_tree: Dict[str, List[AgentaNodeDTO]] = dict() + _types_by_tree: Dict[str, str] = dict() + + for span in spans: + if span.tree.id not in _nodes_by_tree: + _nodes_by_tree[span.tree.id] = list() + _types_by_tree[span.tree.id] = None + + _nodes_by_tree[span.tree.id].append( + AgentaNodeDTO(**span.model_dump()) + ) + _types_by_tree[span.tree.id] = span.tree.type + + if query_dto.grouping.focus.value == "tree": + return AgentaTreesResponse( + version=VERSION, + trees=[ + AgentaTreeDTO( + tree=TreeDTO( + id=tree_id, + type=_types_by_tree[tree_id], + ), + nodes={ + span.node.name: AgentaNodeDTO( + **span.model_dump() + ) + for span in nodes + }, + ) + for tree_id, nodes in _nodes_by_tree.items() + ], + ) + + _nodes_by_root: Dict[str, List[AgentaTreeDTO]] = dict() + _types_by_root: Dict[str, str] = dict() + + for tree_id, nodes in _nodes_by_tree.items(): + if nodes[0].root.id not in _nodes_by_root: + _nodes_by_root[nodes[0].root.id] = list() + _types_by_root[nodes[0].root.id] = None + + _nodes_by_root[nodes[0].root.id].append( + AgentaTreeDTO( + version=VERSION, + tree=TreeDTO( + id=tree_id, + type=_types_by_tree[tree_id], + ), + nodes={ + span.node.name: AgentaNodeDTO(**span.model_dump()) + for span in nodes + }, + ) + ) + + return AgentaRootsResponse( + version=VERSION, + roots=[ + AgentaRootDTO( + root=RootDTO(id=root_id), + trees=trees, + ) + for root_id, trees in _nodes_by_root.items() + ], + ) + + return AgentaNodesResponse( + version=VERSION, + nodes={ + span.node.name: AgentaNodeDTO(**span.model_dump()) for span in spans + }, + ) + + ### MUTATIONS + + @handle_exceptions() + async def otlp_collect_traces( + self, + request: Request, + background_tasks: BackgroundTasks, + ): + """ + Collect traces via OTLP. + """ + + project_id = request.headers.get("AG-PROJECT-ID") + + otel_span_dtos = parse_otlp_stream(await request.body()) + + span_dtos = [ + parse_from_otel_span_dto(project_id, otel_span_dto) + for otel_span_dto in otel_span_dtos + ] + + background_tasks.add_task(self.service.ingest, span_dtos=span_dtos) + + return CollectStatusResponse(version=self.VERSION, status="processing") diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py new file mode 100644 index 0000000000..d25497634e --- /dev/null +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py @@ -0,0 +1,824 @@ +from typing import Optional, Union, Tuple, Any, List, Dict +from uuid import UUID +from collections import OrderedDict +from json import loads, JSONDecodeError, dumps +from copy import copy + +from fastapi import Query, HTTPException + +from agenta_backend.apis.fastapi.observability.opentelemetry.semconv import CODEX + +from agenta_backend.core.shared.dtos import ProjectScopeDTO +from agenta_backend.core.observability.dtos import SpanCreateDTO, SpanDTO +from agenta_backend.core.observability.dtos import ( + TimeDTO, + StatusDTO, + RootDTO, + TreeDTO, + NodeDTO, + ParentDTO, + LinkDTO, + OTelExtraDTO, + OTelSpanDTO, + Attributes, + OTelContextDTO, + OTelLinkDTO, +) +from agenta_backend.core.observability.dtos import ( + ScopingDTO, + WindowingDTO, + FilteringDTO, + ConditionDTO, + TextOptionsDTO, + GroupingDTO, + PaginationDTO, + QueryDTO, +) + + +def _parse_scoping( + *, + project_id: str, +) -> ScopingDTO: + return ScopingDTO( + project_id=project_id, + ) + + +def _parse_windowing( + *, + windowing: Optional[str] = None, + # + earliest: Optional[str] = None, + latest: Optional[str] = None, +) -> Optional[WindowingDTO]: + # Parse JSON windowing + windowing_json_dto = None + if windowing: + try: + windowing_json_data = loads(windowing) + windowing_json_dto = WindowingDTO(**windowing_json_data) + except JSONDecodeError as e: + raise HTTPException( + status_code=400, + detail=f"Invalid JSON windowing provided: {str(e)}", + ) + + # Parse flat windowing + windowing_flat_dto = None + if earliest or latest: + windowing_flat_dto = WindowingDTO( + earliest=earliest, + latest=latest, + ) + + # Check for windowing conflict + if windowing_json_dto and windowing_flat_dto: + raise HTTPException( + status_code=400, + detail="Both flat and JSON windowing data provided", + ) + + _windowing = windowing_json_dto or windowing_flat_dto + + return _windowing + + +def _parse_filtering( + *, + filtering: Optional[str] = None, + # + field: Optional[str] = None, + key: Optional[str] = None, + value: Optional[Union[str, int, float, bool]] = None, + operator: Optional[str] = None, + exact_match: Optional[bool] = None, + case_sensitive: Optional[bool] = None, +) -> Optional[FilteringDTO]: + + # Parse JSON filtering + filtering_json_dto = None + if filtering: + try: + filtering_json_data = loads(filtering) + filtering_json_dto = FilteringDTO(**filtering_json_data) + except JSONDecodeError as e: + raise HTTPException( + status_code=400, + detail=f"Invalid JSON filtering provided: {str(e)}", + ) + + # Parse flat filtering + filtering_flat_dto = None + if field: + filtering_flat_dto = FilteringDTO( + operator="and", + conditions=[ + ConditionDTO( + field=field, + key=key, + value=value, + operator=operator, + options=TextOptionsDTO( + case_sensitive=case_sensitive, + exact_match=exact_match, + ), + ) + ], + ) + + # Check for filtering conflict + if filtering_json_dto and filtering_flat_dto: + raise HTTPException( + status_code=400, + detail="Both flat and JSON filtering data provided", + ) + + _filtering = filtering_json_dto or filtering_flat_dto + + return _filtering + + +def _parse_grouping( + *, + grouping: Optional[str] = None, + # + focus: Optional[str] = None, +) -> Optional[GroupingDTO]: + + # Parse JSON grouping + grouping_json_dto = None + if grouping: + try: + grouping_json_data = loads(grouping) + grouping_json_dto = GroupingDTO(**grouping_json_data) + except JSONDecodeError as e: + raise HTTPException( + status_code=400, + detail=f"Invalid JSON grouping provided: {str(e)}", + ) + + # Parse flat grouping + grouping_flat_dto = None + if focus: + grouping_flat_dto = GroupingDTO( + focus=focus, + ) + + # Check for grouping conflict + if grouping_json_dto and grouping_flat_dto: + raise HTTPException( + status_code=400, + detail="Both flat and JSON grouping data provided", + ) + + _grouping = grouping_json_dto or grouping_flat_dto + + return _grouping + + +def _parse_pagination( + *, + pagination: Optional[str] = None, + # + page: Optional[int] = None, + size: Optional[int] = None, +) -> Optional[PaginationDTO]: + + # Parse JSON pagination + pagination_json_dto = None + if pagination: + try: + pagination_json_data = loads(pagination) + pagination_json_dto = PaginationDTO(**pagination_json_data) + except JSONDecodeError as e: + raise HTTPException( + status_code=400, + detail=f"Invalid JSON pagination provided: {str(e)}", + ) + + # Parse flat pagination + pagination_flat_dto = None + if page and size: + pagination_flat_dto = PaginationDTO( + page=page, + size=size, + ) + + # Check for pagination conflict + if pagination_json_dto and pagination_flat_dto: + raise HTTPException( + status_code=400, + detail="Both flat and JSON pagination data provided", + ) + + _pagination = pagination_json_dto or pagination_flat_dto + + return _pagination + + +def parse_query_dto( + # SCOPING + project_id: str, + # WINDOWING + # - Option 1: Single query parameter as JSON + windowing: Optional[str] = Query(None), + # - Option 2: Flat query parameters + earliest: Optional[str] = Query(None), + latest: Optional[str] = Query(None), + # FILTERING + # - Option 1: Single query parameter as JSON + filtering: Optional[str] = Query(None), + # - Option 2: Flat query parameters (single condition with 'and' operator) + field: Optional[str] = Query(None), + key: Optional[str] = Query(None), + value: Optional[Union[str, int, float, bool]] = Query(None), + operator: Optional[str] = Query(None), + exact_match: Optional[bool] = Query(False), + case_sensitive: Optional[bool] = Query(False), + # GROUPING + # - Option 1: Single query parameter as JSON + grouping: Optional[str] = Query(None), + # - Option 2: Flat query parameters + focus: Optional[str] = Query(None), + # PAGINATION + # - Option 1: Single query parameter as JSON + pagination: Optional[str] = Query(None), + # - Option 2: Flat query parameters + page: Optional[int] = Query(None), + size: Optional[int] = Query(None), +) -> QueryDTO: + return QueryDTO( + scoping=_parse_scoping( + project_id=project_id, + ), + windowing=_parse_windowing( + windowing=windowing, + earliest=earliest, + latest=latest, + ), + filtering=_parse_filtering( + filtering=filtering, + field=field, + key=key, + value=value, + operator=operator, + exact_match=exact_match, + case_sensitive=case_sensitive, + ), + grouping=_parse_grouping( + grouping=grouping, + focus=focus, + ), + pagination=_parse_pagination( + pagination=pagination, + page=page, + size=size, + ), + ) + + +def _unmarshal_attributes( + marshalled: Dict[str, Any], +) -> Dict[str, Any]: + """ + Unmarshals a dictionary of marshalled attributes into a nested dictionary + + Example: + marshalled = { + "ag.type": "tree", + "ag.node.name": "root", + "ag.node.children.0.name": "child1", + "ag.node.children.1.name": "child2" + } + unmarshalled = { + "ag": { + "type": "tree", + "node": { + "name": "root", + "children": [ + { + "name": "child1", + }, + { + "name": "child2", + } + ] + } + } + } + """ + unmarshalled = {} + + for key, value in marshalled.items(): + keys = key.split(".") + + level = unmarshalled + + for i, part in enumerate(keys[:-1]): + + if part.isdigit(): + part = int(part) + + if not isinstance(level, list): + level = [] + + while len(level) <= part: + level.append({}) + + level = level[part] + + else: + if part not in level: + level[part] = {} if not keys[i + 1].isdigit() else [] + + level = level[part] + + last_key = keys[-1] + + if last_key.isdigit(): + last_key = int(last_key) + + if not isinstance(level, list): + level = [] + + while len(level) <= last_key: + level.append(None) + + level[last_key] = value + + else: + level[last_key] = value + + return unmarshalled + + +def _encode_key( + namespace, + key: str, +) -> str: + return f"ag.{namespace}.{key}" + + +def _decode_key( + namespace, + key: str, +) -> str: + return key.replace(f"ag.{namespace}.", "") + + +def _encode_value(value: Any) -> Optional[Any]: + if value is None: + return None + + if isinstance(value, (str, int, float, bool, bytes)): + return value + + if isinstance(value, dict) or isinstance(value, list): + encoded = dumps(value) + value = "@ag.type=json:" + encoded + return value + + return repr(value) + + +def _decode_value( + value: Any, +) -> Any: + if isinstance(value, (int, float, bool, bytes)): + return value + + if isinstance(value, str): + if value == "@ag.type=none:": + return None + + if value.startswith("@ag.type=json:"): + encoded = value[len("@ag.type=json:") :] + value = loads(encoded) + return value + + return value + + return value + + +def _get_attributes( + attributes: Attributes, + namespace: str, +): + return { + _decode_key(namespace, key): _decode_value(value) + for key, value in attributes.items() + if key != _decode_key(namespace, key) + } + + +def _parse_from_types( + otel_span_dto: OTelSpanDTO, +) -> dict: + types = _get_attributes(otel_span_dto.attributes, "type") + + if types.get("tree"): + del otel_span_dto.attributes[_encode_key("type", "tree")] + + if types.get("node"): + del otel_span_dto.attributes[_encode_key("type", "node")] + + return types + + +def _parse_from_semconv( + attributes: Attributes, +) -> None: + _attributes = copy(attributes) + + for old_key, value in _attributes.items(): + if old_key in CODEX["keys"]["attributes"]["exact"]["from"]: + new_key = CODEX["maps"]["attributes"]["exact"]["from"][old_key] + + attributes[new_key] = value + + del attributes[old_key] + + else: + for prefix_key in CODEX["keys"]["attributes"]["prefix"]["from"]: + if old_key.startswith(prefix_key): + prefix = CODEX["maps"]["attributes"]["prefix"]["from"][prefix_key] + + new_key = old_key.replace(prefix_key, prefix) + + attributes[new_key] = value + + del attributes[old_key] + + +def _parse_from_links( + otel_span_dto: OTelSpanDTO, +) -> dict: + # TESTING + otel_span_dto.links = [ + OTelLinkDTO( + context=otel_span_dto.context, + attributes={"ag.type.link": "testcase"}, + ) + ] + # ------- + + # LINKS + links = None + otel_links = None + + if otel_span_dto.links: + links = list() + otel_links = list() + + for link in otel_span_dto.links: + _links = _get_attributes(link.attributes, "type") + + if _links: + link_type = _links.get("link") + link_tree_id = str(UUID(link.context.trace_id[2:])) + link_node_id = str( + UUID(link.context.trace_id[2 + 16 :] + link.context.span_id[2:]) + ) + + links.append( + LinkDTO( + type=link_type, + tree_id=link_tree_id, + id=link_node_id, + ) + ) + else: + otel_links.append(link) + + links = links if links else None + otel_links = otel_links if otel_links else None + + otel_span_dto.links = otel_links + + return links + + +def _parse_from_attributes( + otel_span_dto: OTelSpanDTO, +) -> Tuple[dict, dict, dict, dict, dict]: + + # DATA + _data = _get_attributes(otel_span_dto.attributes, "data") + + for key in _data.keys(): + del otel_span_dto.attributes[_encode_key("data", key)] + + # _data = _unmarshal_attributes(_data) + _data = _data if _data else None + + # METRICS + _metrics = _get_attributes(otel_span_dto.attributes, "metrics") + + for key in _metrics.keys(): + del otel_span_dto.attributes[_encode_key("metrics", key)] + + # _metrics = _unmarshal_attributes(_metrics) + _metrics = _metrics if _metrics else None + + # META + _meta = _get_attributes(otel_span_dto.attributes, "meta") + + for key in _meta.keys(): + del otel_span_dto.attributes[_encode_key("meta", key)] + + # _meta = _unmarshal_attributes(_meta) + _meta = _meta if _meta else None + + # TAGS + _tags = _get_attributes(otel_span_dto.attributes, "tags") + + for key in _tags.keys(): + del otel_span_dto.attributes[_encode_key("tags", key)] + + _tags = _tags if _tags else None + + # REFS + _refs = _get_attributes(otel_span_dto.attributes, "refs") + + for key in _refs.keys(): + del otel_span_dto.attributes[_encode_key("refs", key)] + + _refs = _refs if _refs else None + + if len(otel_span_dto.attributes.keys()) < 1: + otel_span_dto.attributes = None + + return _data, _metrics, _meta, _tags, _refs + + +def parse_from_otel_span_dto( + project_id: str, + otel_span_dto: OTelSpanDTO, +) -> SpanCreateDTO: + scope = ProjectScopeDTO(project_id=UUID(project_id)) + + _parse_from_semconv(otel_span_dto.attributes) + + types = _parse_from_types(otel_span_dto) + + tree_id = UUID(otel_span_dto.context.trace_id[2:]) + + tree_type: str = types.get("tree") + + tree = TreeDTO( + id=tree_id, + type=tree_type.lower() if tree_type else None, + ) + + node_id = UUID(tree_id.hex[16:] + otel_span_dto.context.span_id[2:]) + + node_type: str = types.get("node") + + node = NodeDTO( + id=node_id, + type=node_type.lower() if node_type else None, + name=otel_span_dto.name, + ) + + parent = ( + ParentDTO( + id=( + UUID( + otel_span_dto.parent.trace_id[2 + 16 :] + + otel_span_dto.parent.span_id[2:] + ) + ) + ) + if otel_span_dto.parent + else None + ) + + duration = (otel_span_dto.end_time - otel_span_dto.start_time).total_seconds() + + time = TimeDTO( + start=otel_span_dto.start_time, + end=otel_span_dto.end_time, + span=round(duration * 1_000_000), # microseconds + ) + + status = StatusDTO( + code=otel_span_dto.status_code.value.replace("STATUS_CODE_", ""), + message=otel_span_dto.status_message, + ) + + links = _parse_from_links(otel_span_dto) + + data, metrics, meta, tags, refs = _parse_from_attributes(otel_span_dto) + + root_id = refs.get("scenario.id", "70befa7f3cf24485839673c8a361f900") + # root_id = str(tree_id) + # if refs is not None: + # root_id = refs.get("scenario.id", root_id) + + root = RootDTO(id=UUID(root_id)) + + otel = OTelExtraDTO( + kind=otel_span_dto.kind.value, + attributes=otel_span_dto.attributes, + events=otel_span_dto.events, + links=otel_span_dto.links, + ) + + span_dto = SpanCreateDTO( + scope=scope, + root=root, + tree=tree, + node=node, + parent=parent, + time=time, + status=status, + data=data, + metrics=metrics, + meta=meta, + tags=tags, + refs=refs, + links=links, + otel=otel, + ) + + return span_dto + + +def _parse_to_attributes( + span_dto: SpanDTO, +) -> Attributes: + attributes = dict() + + MAX_DEPTH = 4 + + # DATA + if span_dto.data: + _data = span_dto.data + + for key, value in _data.items(): + attributes[_encode_key("data", key)] = _encode_value(value) + + # METRICS + if span_dto.metrics: + _metrics = span_dto.metrics + + for key, value in _metrics.items(): + attributes[_encode_key("metrics", key)] = _encode_value(value) + + # META + if span_dto.meta: + _meta = span_dto.meta + + for key, value in _meta.items(): + attributes[_encode_key("meta", key)] = _encode_value(value) + + # TAGS + if span_dto.tags: + for key, value in span_dto.tags.items(): + attributes[_encode_key("tags", key)] = _encode_value(value) + + # REFS + if span_dto.refs: + for key, value in span_dto.refs.items(): + attributes[_encode_key("refs", key)] = _encode_value(value) + + return attributes + + +def _parse_to_types( + span_dto: SpanDTO, + attributes: Attributes, +) -> Attributes: + if span_dto.tree.type: + attributes[_encode_key("type", "tree")] = span_dto.tree.type.value + if span_dto.node.type: + attributes[_encode_key("type", "node")] = span_dto.node.type.value + + +def _parse_to_semconv( + attributes: Attributes, +) -> None: + _attributes = copy(attributes) + + for old_key, value in _attributes.items(): + if old_key in CODEX["keys"]["attributes"]["exact"]["to"]: + new_key = CODEX["maps"]["attributes"]["exact"]["to"][old_key] + + attributes[new_key] = value + + del attributes[old_key] + + else: + for prefix_key in CODEX["keys"]["attributes"]["prefix"]["to"]: + if old_key.startswith(prefix_key): + prefix = CODEX["maps"]["attributes"]["prefix"]["to"][prefix_key] + + new_key = old_key.replace(prefix_key, prefix) + + attributes[new_key] = value + + del attributes[old_key] + + +def _parse_to_links( + span_dto: SpanDTO, + links: List[LinkDTO], +) -> None: + for link in span_dto.links: + links.append( + OTelLinkDTO( + context=OTelContextDTO( + trace_id="0x" + link.tree_id.hex, + span_id="0x" + link.id.hex[16:], + ), + attributes={ + _encode_key("type", "link"): link.type, + }, + ) + ) + + +def parse_to_otel_span_dto( + span_dto: SpanDTO, +) -> OTelSpanDTO: + trace_id = "0x" + span_dto.tree.id.hex + span_id = "0x" + span_dto.node.id.hex[16:] + + context = OTelContextDTO(trace_id=trace_id, span_id=span_id) + + parent_id = "0x" + span_dto.parent.id.hex[16:] if span_dto.parent else None + + parent = OTelContextDTO(trace_id=trace_id, span_id=parent_id) if parent_id else None + + attributes = _parse_to_attributes(span_dto) + + _parse_to_types(span_dto, attributes) + + _parse_to_semconv(attributes) + + attributes = OrderedDict(sorted(attributes.items())) if attributes.keys() else None + + links = span_dto.otel.links or list() + + _parse_to_links(span_dto, links) + + links = links if links else None + + otel_span_dto = OTelSpanDTO( + context=context, + parent=parent, + name=span_dto.node.name, + kind=span_dto.otel.kind, + start_time=span_dto.time.start, + end_time=span_dto.time.end, + status_code="STATUS_CODE_" + span_dto.status.code.value, + status_message=span_dto.status.message, + attributes=attributes, + events=span_dto.otel.events, + links=links, + ) + + return otel_span_dto + + +def parse_to_agenta_span_dto( + span_dto: SpanDTO, +) -> SpanDTO: + # DATA + if span_dto.data: + span_dto.data = _unmarshal_attributes(span_dto.data) + + if "outputs" in span_dto.data: + if "__default__" in span_dto.data["outputs"]: + span_dto.data["outputs"] = span_dto.data["outputs"]["__default__"] + + # METRICS + if span_dto.metrics: + span_dto.metrics = _unmarshal_attributes(span_dto.metrics) + + # META + if span_dto.meta: + span_dto.meta = _unmarshal_attributes(span_dto.meta) + + # TAGS + if span_dto.tags: + span_dto.tags = _unmarshal_attributes(span_dto.tags) + + # REFS + if span_dto.refs: + span_dto.refs = _unmarshal_attributes(span_dto.refs) + + for link in span_dto.links: + link.tree_id = None + + if span_dto.nodes: + for v in span_dto.nodes.values(): + if isinstance(v, list): + for n in v: + parse_to_agenta_span_dto(n) + else: + parse_to_agenta_span_dto(v) + + return span_dto diff --git a/agenta-backend/agenta_backend/apis/fastapi/shared/__init__.py b/agenta-backend/agenta_backend/apis/fastapi/shared/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/agenta-backend/agenta_backend/apis/fastapi/shared/models.py b/agenta-backend/agenta_backend/apis/fastapi/shared/models.py new file mode 100644 index 0000000000..cb7a496db0 --- /dev/null +++ b/agenta-backend/agenta_backend/apis/fastapi/shared/models.py @@ -0,0 +1,83 @@ +from collections import OrderedDict + +from pydantic import BaseModel +from enum import Enum + + +## --- DISPLAY --- ## + + +NOF_CHARS = 8 + + +def _p_id(id): + return repr(str(id)[:NOF_CHARS]) + + +def _p_osa(o): + elements = [] + + for i in OrderedDict(sorted(o.items())).items(): + if not i[0].startswith("_"): + if i[1].__class__.__module__ != "builtins": + if repr(i[1]).startswith("<"): + elements.append(f"{i[0]}: {i[1].name}") + elif repr(i[1]).startswith("UUID("): + elements.append(f"{i[0]}: {_p_id(i[1])}") + else: + elements.append(f"{i[0]}: {i[1].__str__()}") + else: + if isinstance(i[1], list): + elements.append( + f"{i[0]}: [" + ", ".join([el.__str__() for el in i[1]]) + "]" + ) + elif isinstance(i[1], dict): + elements.append(f"{i[0]}: {{{_p_osa(i[1])}}}") + else: + if i[1] is not None: + if i[0] == "slug": + elements.append(f"{i[0]}: {repr(i[1][:8])}") + else: + elements.append(f"{i[0]}: {repr(i[1])}") + + return ", ".join(elements) + + +def _p_ora(o, open="{", close="}", sep=": ", foo=repr): + if o.__class__.__module__ != "builtins": + if o.__class__.__name__ == "UUID": + return repr(o) + if isinstance(o, Enum): + return o + return f"{o.__class__.__name__}({_p_ora(o.__dict__, open='', close='', sep='=', foo=lambda x : x)})" + elif isinstance(o, list): + return f"[{', '.join([repr(el) for el in o])}]" + elif isinstance(o, dict): + o = OrderedDict(sorted(o.items())) + return f"{open}{', '.join([f'{foo(elk)}{sep}{_p_ora(elv)}' for elk, elv in o.items()])}{close}" + else: + if o is not None: + return repr(o) + + +def _str(o): + return f"{{{_p_osa(o.__dict__)}}}" + + +def _repr(o): + return _p_ora(o) + + +class DisplayBase(BaseModel): + def __str__(self): + return _str(self) + + def __repr__(self): + return _repr(self) + + +## --- VERSIONED --- ## + + +class VersionedModel(BaseModel): + version: str diff --git a/agenta-backend/agenta_backend/apis/fastapi/shared/utils.py b/agenta-backend/agenta_backend/apis/fastapi/shared/utils.py new file mode 100644 index 0000000000..7a01c5b299 --- /dev/null +++ b/agenta-backend/agenta_backend/apis/fastapi/shared/utils.py @@ -0,0 +1,26 @@ +from functools import wraps +from traceback import print_exc +from fastapi import HTTPException +from uuid import uuid4 + + +def handle_exceptions(): + def decorator(func): + @wraps(func) + async def wrapper(*args, **kwargs): + try: + return await func(*args, **kwargs) + except HTTPException as e: + raise e + except Exception: + support_id = str(uuid4()) + print(f"ERROR : support_id={support_id} & operation_id={func.__name__}") + print_exc() + raise HTTPException( + status_code=500, + detail=f"An unexpected error occurred with operation_id={func.__name__}. Please contact support with support_id={support_id}.", + ) + + return wrapper + + return decorator diff --git a/agenta-backend/agenta_backend/core/__init__.py b/agenta-backend/agenta_backend/core/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/agenta-backend/agenta_backend/core/observability/__init__.py b/agenta-backend/agenta_backend/core/observability/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/agenta-backend/agenta_backend/core/observability/dtos.py b/agenta-backend/agenta_backend/core/observability/dtos.py new file mode 100644 index 0000000000..c84ba2fb9b --- /dev/null +++ b/agenta-backend/agenta_backend/core/observability/dtos.py @@ -0,0 +1,353 @@ +from typing import List, Dict, Any, Union, Optional, Sequence + +from enum import Enum +from datetime import datetime, time +from uuid import UUID + +from agenta_backend.core.shared.dtos import DisplayBase +from agenta_backend.core.shared.dtos import ProjectScopeDTO, LifecycleDTO + + +## --- TIME --- ## + + +class TimeDTO(DisplayBase): + start: datetime + end: datetime + span: int + + +## --- STATUS --- ## + + +class StatusCode(Enum): + UNSET = "UNSET" + OK = "OK" + ERROR = "ERROR" + + +class StatusDTO(DisplayBase): + code: StatusCode + message: Optional[str] = None + stacktrace: Optional[str] = None + + +## --- ATTRIBUTES --- ## + +AttributeValueType = Any # +""" +AttributeValueType = Union[ + str, + bool, + int, + float, + Sequence[Union[str, bool, int, float]], +] +""" +Attributes = Dict[str, AttributeValueType] + + +class AttributesDTO(DisplayBase): + data: Optional[Attributes] = None + metrics: Optional[Attributes] = None + meta: Optional[Attributes] = None + tags: Optional[Attributes] = None + semconv: Optional[Attributes] = None + + +## --- HIERARCHICAL STRUCTURE --- ## + + +class TreeType(Enum): + # --- VARIANTS --- # + INVOCATION = "invocation" + # --- VARIANTS --- # + + +class NodeType(Enum): + # --- VARIANTS --- # + ## SPAN_KIND_SERVER + AGENT = "agent" + WORKFLOW = "workflow" + CHAIN = "chain" + ## SPAN_KIND_INTERNAL + TASK = "task" + ## SPAN_KIND_CLIENT + TOOL = "tool" + EMBEDDING = "embedding" + QUERY = "query" + COMPLETION = "completion" + CHAT = "chat" + RERANK = "rerank" + # --- VARIANTS --- # + + +class RootDTO(DisplayBase): + id: UUID + + +class TreeDTO(DisplayBase): + id: UUID + type: Optional[TreeType] = None + + +class NodeDTO(DisplayBase): + id: UUID + type: Optional[NodeType] = None + name: str + + +Data = Dict[str, Any] +Metrics = Dict[str, Any] +Metadata = Dict[str, Any] +Tags = Dict[str, str] +Refs = Dict[str, str] + + +class LinkDTO(DisplayBase): + type: str + id: UUID + tree_id: Optional[UUID] = None + + +class ParentDTO(DisplayBase): + id: UUID + + +class OTelSpanKind(Enum): + SPAN_KIND_UNSPECIFIED = "SPAN_KIND_UNSPECIFIED" + # INTERNAL + SPAN_KIND_INTERNAL = "SPAN_KIND_INTERNAL" + # SYNCHRONOUS + SPAN_KIND_SERVER = "SPAN_KIND_SERVER" + SPAN_KIND_CLIENT = "SPAN_KIND_CLIENT" + # ASYNCHRONOUS + SPAN_KIND_PRODUCER = "SPAN_KIND_PRODUCER" + SPAN_KIND_CONSUMER = "SPAN_KIND_CONSUMER" + + +class OTelStatusCode(Enum): + STATUS_CODE_OK = "STATUS_CODE_OK" + STATUS_CODE_ERROR = "STATUS_CODE_ERROR" + STATUS_CODE_UNSET = "STATUS_CODE_UNSET" + + +class OTelContextDTO(DisplayBase): + trace_id: str + span_id: str + + +class OTelEventDTO(DisplayBase): + name: str + timestamp: datetime + + attributes: Optional[Attributes] = None + + +class OTelLinkDTO(DisplayBase): + context: OTelContextDTO + + attributes: Optional[Attributes] = None + + +class OTelExtraDTO(DisplayBase): + kind: Optional[str] = None + + attributes: Optional[Attributes] = None + events: Optional[List[OTelEventDTO]] = None + links: Optional[List[OTelLinkDTO]] = None + + +## --- ENTITIES --- ## + + +class SpanDTO(DisplayBase): # DBE + scope: ProjectScopeDTO # DBA + + lifecycle: LifecycleDTO + + root: RootDTO + tree: TreeDTO + node: NodeDTO + + parent: Optional[ParentDTO] = None + + time: TimeDTO + status: StatusDTO + + data: Optional[Data] = None + metrics: Optional[Metrics] = None + meta: Optional[Metadata] = None + tags: Optional[Tags] = None + refs: Optional[Refs] = None + + links: Optional[List[LinkDTO]] = None + + otel: Optional[OTelExtraDTO] = None + + nodes: Optional[Dict[str, Union["SpanDTO", List["SpanDTO"]]]] = None + + +class SpanCreateDTO(DisplayBase): # DBE + scope: ProjectScopeDTO # DBA + + root: RootDTO + tree: TreeDTO + node: NodeDTO + + parent: Optional[ParentDTO] = None + + time: TimeDTO + status: StatusDTO + + data: Optional[Data] = None + metrics: Optional[Metrics] = None + meta: Optional[Metadata] = None + tags: Optional[Tags] = None + refs: Optional[Refs] = None + + links: Optional[List[LinkDTO]] = None + + otel: Optional[OTelExtraDTO] = None + + +class OTelSpanDTO(DisplayBase): + context: OTelContextDTO + + name: str + kind: OTelSpanKind = OTelSpanKind.SPAN_KIND_UNSPECIFIED + + start_time: datetime + end_time: datetime + + status_code: OTelStatusCode = OTelStatusCode.STATUS_CODE_UNSET + status_message: Optional[str] = None + + attributes: Optional[Attributes] = None + events: Optional[List[OTelEventDTO]] = None + + parent: Optional[OTelContextDTO] = None + links: Optional[List[OTelLinkDTO]] = None + + +## --- QUERY --- ## + + +class ScopingDTO(ProjectScopeDTO): + pass + + +class WindowingDTO(DisplayBase): + earliest: Optional[datetime] = None + latest: Optional[datetime] = None + + +class LogicalOperator(Enum): + AND = "and" + OR = "or" + NOT = "not" + + +class NumericOperator(Enum): + EQ = "eq" + NEQ = "neq" + GT = "gt" + LT = "lt" + GTE = "gte" + LTE = "lte" + BETWEEN = "between" + + +class StringOperator(Enum): + STARTSWITH = "startswith" + ENDSWITH = "endswith" + CONTAINS = "contains" + MATCHES = "matches" + LIKE = "like" + + +class ListOperator(Enum): + IN = "in" + + +class ExistenceOperator(Enum): + EXISTS = "exists" + NOT_EXISTS = "not_exists" + + +class TextOptionsDTO(DisplayBase): + case_sensitive: Optional[bool] = False + exact_match: Optional[bool] = False + + +class ConditionDTO(DisplayBase): + field: str # column + + key: Optional[str] = None + value: Optional[Union[str, int, float, bool]] = None + + operator: Optional[ + Union[ + NumericOperator, + StringOperator, + ListOperator, + ExistenceOperator, + ] + ] = None + + options: Optional[TextOptionsDTO] = None + + +class FilteringDTO(DisplayBase): + operator: Optional[LogicalOperator] = LogicalOperator.AND + + conditions: List[Union[ConditionDTO, "FilteringDTO"]] + + class Config: + arbitrary_types_allowed = True + + +class DirectionType(Enum): + ASCENDING = "ascending" + DESCENDING = "descending" + + +class SortingDTO(DisplayBase): + field: str + + key: Optional[str] = None + + direction: DirectionType = "descending" + + +class Focus(Enum): + ROOT = "root" # SCENARIO + TREE = "tree" # TRACE + NODE = "node" # SPAN + + +class GroupingDTO(DisplayBase): + focus: Focus = "node" + # SET TO ROOT ? TO TREE ? TO NODE ? + + +class Standard(Enum): + OPENTELEMETRY = "opentelemetry" + AGENTA = "agenta" + + +class PaginationDTO(DisplayBase): + page: int + size: int + + +class FormattingDTO(DisplayBase): + standard: Standard = "agenta" + + +class QueryDTO(DisplayBase): + windowing: Optional[WindowingDTO] = None + filtering: Optional[FilteringDTO] = None + sorting: Optional[SortingDTO] = None + grouping: Optional[GroupingDTO] = None + pagination: Optional[PaginationDTO] = None diff --git a/agenta-backend/agenta_backend/core/observability/interfaces.py b/agenta-backend/agenta_backend/core/observability/interfaces.py new file mode 100644 index 0000000000..202b5f37a6 --- /dev/null +++ b/agenta-backend/agenta_backend/core/observability/interfaces.py @@ -0,0 +1,73 @@ +from typing import List + +from agenta_backend.core.observability.dtos import QueryDTO +from agenta_backend.core.observability.dtos import SpanDTO, SpanCreateDTO + + +class ObservabilityDAOInterface: + + def __init__(self): + raise NotImplementedError + + # ANALYTICS + + async def query( + self, + *, + project_id: str, + # + query_dto: QueryDTO, + ) -> List[SpanDTO]: + raise NotImplementedError + + # TRANSACTIONS + + async def create_one( + self, + *, + span_dto: SpanCreateDTO, + ) -> None: + raise NotImplementedError + + async def create_many( + self, + *, + span_dtos: List[SpanCreateDTO], + ) -> None: + raise NotImplementedError + + async def read_one( + self, + *, + project_id: str, + # + node_id: str, + ) -> SpanDTO: + raise NotImplementedError + + async def read_many( + self, + *, + project_id: str, + # + node_ids: List[str], + ) -> List[SpanDTO]: + raise NotImplementedError + + async def delete_one( + self, + *, + project_id: str, + # + node_id: str, + ) -> None: + raise NotImplementedError + + async def delete_many( + self, + *, + project_id: str, + # + node_ids: List[str], + ) -> None: + raise NotImplementedError diff --git a/agenta-backend/agenta_backend/core/observability/service.py b/agenta-backend/agenta_backend/core/observability/service.py new file mode 100644 index 0000000000..a0352641f4 --- /dev/null +++ b/agenta-backend/agenta_backend/core/observability/service.py @@ -0,0 +1,126 @@ +from typing import List, Optional + +from agenta_backend.core.observability.interfaces import ObservabilityDAOInterface +from agenta_backend.core.observability.dtos import QueryDTO +from agenta_backend.core.observability.dtos import SpanDTO, SpanCreateDTO +from agenta_backend.core.observability.utils import ( + parse_span_dtos_to_span_idx, + parse_span_idx_to_span_id_tree, + cumulate_costs, + cumulate_tokens, + connect_children, +) + + +class ObservabilityService: + + def __init__( + self, + observability_dao: ObservabilityDAOInterface, + ): + self.observability_dao = observability_dao + + async def query( + self, + *, + project_id: str, + # + query_dto: QueryDTO, + ) -> List[SpanDTO]: + + span_dtos = await self.observability_dao.query( + project_id=project_id, + query_dto=query_dto, + ) + + if query_dto.grouping: + span_idx = parse_span_dtos_to_span_idx(span_dtos) + + span_id_tree = parse_span_idx_to_span_id_tree(span_idx) + + connect_children(span_id_tree, span_idx) + + root_span_dtos = [ + span_dto for span_dto in span_idx.values() if span_dto.parent is None + ] + + return root_span_dtos + + return span_dtos + + async def ingest( + self, + *, + span_dtos: List[SpanCreateDTO], + ) -> SpanDTO: + + span_idx = parse_span_dtos_to_span_idx(span_dtos) + + span_id_tree = parse_span_idx_to_span_id_tree(span_idx) + + cumulate_costs(span_id_tree, span_idx) + + cumulate_tokens(span_id_tree, span_idx) + + span_ids = await self.observability_dao.create_many(span_dtos=span_idx.values()) + + return span_ids + + async def create( + self, + *, + span_dto: Optional[SpanCreateDTO] = None, + span_dtos: Optional[List[SpanCreateDTO]] = None, + ) -> SpanDTO: + + if span_dto: + return await self.observability_dao.create_one( + span_dto=span_dto, + ) + + if span_dtos: + return await self.observability_dao.create_many( + span_dtos=span_dtos, + ) + + async def read( + self, + *, + project_id: str, + # + node_id: Optional[str] = None, + node_ids: Optional[List[str]] = None, + ) -> SpanDTO: + + if node_id: + return await self.observability_dao.read_one( + project_id=project_id, + node_id=node_id, + ) + + if node_ids: + return await self.observability_dao.read_many( + project_id=project_id, + node_ids=node_ids, + ) + + async def delete( + self, + *, + project_id: str, + # + node_id: Optional[str] = None, + node_ids: Optional[List[str]] = None, + ): + + if node_id: + return await self.observability_dao.delete_one( + project_id=project_id, + node_id=node_id, + ) + + if node_ids: + return await self.observability_dao.delete_many( + project_id=project_id, + node_ids=node_ids, + ) diff --git a/agenta-backend/agenta_backend/core/observability/utils.py b/agenta-backend/agenta_backend/core/observability/utils.py new file mode 100644 index 0000000000..92a82a35d6 --- /dev/null +++ b/agenta-backend/agenta_backend/core/observability/utils.py @@ -0,0 +1,192 @@ +from typing import List, Dict, OrderedDict + +from agenta_backend.core.observability.dtos import SpanCreateDTO, SpanDTO + + +def parse_span_dtos_to_span_idx( + span_dtos: List[SpanCreateDTO], +) -> Dict[str, SpanCreateDTO]: + + span_idx = {span_dto.node.id: span_dto for span_dto in span_dtos} + + return span_idx + + +def parse_span_idx_to_span_id_tree( + span_idx: Dict[str, SpanCreateDTO], +) -> OrderedDict: + + span_id_tree = OrderedDict() + index = {} + + def push(span_dto: SpanCreateDTO) -> None: + if span_dto.parent is None: + span_id_tree[span_dto.node.id] = OrderedDict() + index[span_dto.node.id] = span_id_tree[span_dto.node.id] + elif span_dto.parent.id in index: + index[span_dto.parent.id][span_dto.node.id] = OrderedDict() + index[span_dto.node.id] = index[span_dto.parent.id][span_dto.node.id] + + for span_dto in sorted(span_idx.values(), key=lambda span_dto: span_dto.time.start): + push(span_dto) + + return span_id_tree + + +def cumulate_costs( + spans_id_tree: OrderedDict, + spans_idx: Dict[str, SpanCreateDTO], +) -> None: + + def _get_unit(span: SpanCreateDTO): + if span.metrics is not None: + return span.metrics.get("unit.costs.total", 0.0) + + return 0.0 + + def _get_acc(span: SpanCreateDTO): + if span.metrics is not None: + return span.metrics.get("acc.costs.total", 0.0) + + return 0.0 + + def _acc(a: float, b: float): + return a + b + + def _set(span: SpanCreateDTO, cost: float): + if span.metrics is None: + span.metrics = {} + + if cost != 0.0: + span.metrics["acc.costs.total"] = cost + + _cumulate_tree_dfs(spans_id_tree, spans_idx, _get_unit, _get_acc, _acc, _set) + + +def cumulate_tokens( + spans_id_tree: OrderedDict, + spans_idx: Dict[str, dict], +) -> None: + + def _get_unit(span: SpanCreateDTO): + _tokens = { + "prompt": 0.0, + "completion": 0.0, + "total": 0.0, + } + + if span.metrics is not None: + return { + "prompt": span.metrics.get("unit.tokens.prompt", 0.0), + "completion": span.metrics.get("unit.tokens.completion", 0.0), + "total": span.metrics.get("unit.tokens.total", 0.0), + } + + return _tokens + + def _get_acc(span: SpanCreateDTO): + _tokens = { + "prompt": 0.0, + "completion": 0.0, + "total": 0.0, + } + + if span.metrics is not None: + return { + "prompt": span.metrics.get("acc.tokens.prompt", 0.0), + "completion": span.metrics.get("acc.tokens.completion", 0.0), + "total": span.metrics.get("acc.tokens.total", 0.0), + } + + return _tokens + + def _acc(a: dict, b: dict): + return { + "prompt": a.get("prompt", 0.0) + b.get("prompt", 0.0), + "completion": a.get("completion", 0.0) + b.get("completion", 0.0), + "total": a.get("total", 0.0) + b.get("total", 0.0), + } + + def _set(span: SpanCreateDTO, tokens: dict): + if span.metrics is None: + span.metrics = {} + + if tokens.get("prompt", 0.0) != 0.0: + span.metrics["acc.tokens.prompt"] = tokens.get("prompt", 0.0) + if tokens.get("completion", 0.0) != 0.0: + span.metrics["acc.tokens.completion"] = ( + tokens.get("completion", 0.0) + if tokens.get("completion", 0.0) != 0.0 + else None + ) + if tokens.get("total", 0.0) != 0.0: + span.metrics["acc.tokens.total"] = ( + tokens.get("total", 0.0) if tokens.get("total", 0.0) != 0.0 else None + ) + + _cumulate_tree_dfs(spans_id_tree, spans_idx, _get_unit, _get_acc, _acc, _set) + + +def _cumulate_tree_dfs( + spans_id_tree: OrderedDict, + spans_idx: Dict[str, SpanCreateDTO], + get_unit_metric, + get_acc_metric, + accumulate_metric, + set_metric, +): + for span_id, children_spans_id_tree in spans_id_tree.items(): + children_spans_id_tree: OrderedDict + + cumulated_metric = get_unit_metric(spans_idx[span_id]) + + _cumulate_tree_dfs( + children_spans_id_tree, + spans_idx, + get_unit_metric, + get_acc_metric, + accumulate_metric, + set_metric, + ) + + for child_span_id in children_spans_id_tree.keys(): + marginal_metric = get_acc_metric(spans_idx[child_span_id]) + cumulated_metric = accumulate_metric(cumulated_metric, marginal_metric) + + set_metric(spans_idx[span_id], cumulated_metric) + + +def connect_children( + spans_id_tree: OrderedDict, + spans_idx: Dict[str, dict], +) -> None: + _connect_tree_dfs(spans_id_tree, spans_idx) + + +def _connect_tree_dfs( + spans_id_tree: OrderedDict, + spans_idx: Dict[str, SpanDTO], +): + for span_id, children_spans_id_tree in spans_id_tree.items(): + children_spans_id_tree: OrderedDict + + parent_span = spans_idx[span_id] + + parent_span.nodes = dict() + + _connect_tree_dfs(children_spans_id_tree, spans_idx) + + for child_span_id in children_spans_id_tree.keys(): + child_span_name = spans_idx[child_span_id].node.name + if child_span_name not in parent_span.nodes: + parent_span.nodes[child_span_name] = spans_idx[child_span_id] + else: + if not isinstance(parent_span.nodes[child_span_name], list): + parent_span.nodes[child_span_name] = [ + parent_span.nodes[child_span_name] + ] + + parent_span.nodes[child_span_name].append(spans_idx[child_span_id]) + + if len(parent_span.nodes) == 0: + parent_span.nodes = None diff --git a/agenta-backend/agenta_backend/core/shared/__init__.py b/agenta-backend/agenta_backend/core/shared/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/agenta-backend/agenta_backend/core/shared/dtos.py b/agenta-backend/agenta_backend/core/shared/dtos.py new file mode 100644 index 0000000000..1702afe939 --- /dev/null +++ b/agenta-backend/agenta_backend/core/shared/dtos.py @@ -0,0 +1,98 @@ +from typing import Optional + +from pydantic import BaseModel +from uuid import UUID +from datetime import datetime +from enum import Enum +from collections import OrderedDict + + +## --- DISPLAY --- ## + + +NOF_CHARS = 8 + + +def _p_id(id): + return repr(str(id)[:NOF_CHARS]) + + +def _p_osa(o): + elements = [] + + for i in OrderedDict(sorted(o.items())).items(): + if not i[0].startswith("_"): + if i[1].__class__.__module__ != "builtins": + if repr(i[1]).startswith("<"): + elements.append(f"{i[0]}: {i[1].name}") + elif repr(i[1]).startswith("UUID("): + elements.append(f"{i[0]}: {_p_id(i[1])}") + else: + elements.append(f"{i[0]}: {i[1].__str__()}") + else: + if isinstance(i[1], list): + elements.append( + f"{i[0]}: [" + ", ".join([el.__str__() for el in i[1]]) + "]" + ) + elif isinstance(i[1], dict): + elements.append(f"{i[0]}: {{{_p_osa(i[1])}}}") + else: + if i[1] is not None: + if i[0] == "slug": + elements.append(f"{i[0]}: {repr(i[1][:8])}") + else: + elements.append(f"{i[0]}: {repr(i[1])}") + + return ", ".join(elements) + + +def _p_ora(o, open="{", close="}", sep=": ", foo=repr): + if o.__class__.__module__ != "builtins": + if o.__class__.__name__ == "UUID": + return repr(o) + if isinstance(o, Enum): + return o + if isinstance(o, datetime): + return o.isoformat() + return f"{o.__class__.__name__}({_p_ora(o.__dict__, open='', close='', sep='=', foo=lambda x : x)})" + elif isinstance(o, list): + return f"[{', '.join([repr(el) for el in o])}]" + elif isinstance(o, dict): + o = OrderedDict(sorted(o.items())) + return f"{open}{', '.join([f'{foo(elk)}{sep}{_p_ora(elv)}' for elk, elv in o.items()])}{close}" + else: + if o is not None: + return repr(o) + + +def _str(o): + return f"{{{_p_osa(o.__dict__)}}}" + + +def _repr(o): + return _p_ora(o) + + +class DisplayBase(BaseModel): + def __str__(self): + return _str(self) + + def __repr__(self): + return _repr(self) + + +## --- SCOPE --- ## + + +class ProjectScopeDTO(DisplayBase): + project_id: UUID + + +## --- LIFECYCLE --- ## + + +class LifecycleDTO(DisplayBase): + created_at: datetime + updated_at: Optional[datetime] = None + + updated_by_id: Optional[UUID] = None diff --git a/agenta-backend/agenta_backend/dbs/__init__.py b/agenta-backend/agenta_backend/dbs/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/agenta-backend/agenta_backend/dbs/postgres/__init__.py b/agenta-backend/agenta_backend/dbs/postgres/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/__init__.py b/agenta-backend/agenta_backend/dbs/postgres/observability/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py new file mode 100644 index 0000000000..de0c826867 --- /dev/null +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py @@ -0,0 +1,396 @@ +from typing import Optional, List + +from sqlalchemy import and_, or_, not_, Column + +from sqlalchemy.future import select +from sqlalchemy import distinct + + +from agenta_backend.dbs.postgres.shared.engine import engine +from agenta_backend.dbs.postgres.observability.dbes import InvocationSpanDBE +from agenta_backend.dbs.postgres.observability.mappings import ( + map_span_create_dto_to_dbe, + map_span_dbe_to_dto, +) + +from agenta_backend.core.observability.interfaces import ObservabilityDAOInterface +from agenta_backend.core.observability.dtos import QueryDTO +from agenta_backend.core.observability.dtos import ( + FilteringDTO, + ConditionDTO, + LogicalOperator, + NumericOperator, + StringOperator, + ListOperator, + ExistenceOperator, +) +from agenta_backend.core.observability.dtos import ( + SpanDTO, + SpanCreateDTO, +) + + +## TODO +# - [ ] Implement Observability Query +# - [x] Implement grouping node/tree/link/ref +# - [x] Implement pagination page/size +# - [x] Implement filtering +# - [ ] Implement sorting +# - [ ] Optimize filtering (and schema/queries in general) +# - [ ] Implement cross-table querying (later ?) +## ---- + +## TODO +# - [ ] Implement Observability Mutation +# - [x] Implement create one/many +# - [x] Implement read one/many +# - [x] Implement delete one/many +# - [ ] Implement update one/many (immutable ?) +## ---- + + +class ObservabilityDAO(ObservabilityDAOInterface): + def __init__(self): + pass + + async def query( + self, + *, + project_id: str, + # + query_dto: QueryDTO, + ) -> List[SpanDTO]: + + async with engine.session() as session: + + # opts = { + # "dialect": dialect(), + # "compile_kwargs": {"literal_binds": True}, + # } + + # BASE (SUB-)QUERY + # print("------------------------------------------------------") + # print("BASE (SUB-)QUERY") + query = select(InvocationSpanDBE) + # ---------------- + # print("...") + # print(query.statement.compile(**opts)) + + # GROUPING + # print("------------------------------------------------------") + # print("GROUPING") + grouping = query_dto.grouping + grouping_column: Optional[Column] = None + # -------- + if grouping and grouping.focus.value != "node": + # print("GROUPING FOCUS:", grouping.focus.value) + grouping_column = getattr( + InvocationSpanDBE, grouping.focus.value + "_id" + ) + + query = select(distinct(grouping_column)) + # -------- + # print("...") + # print(query.statement.compile(**opts)) + + # SCOPING + # print("------------------------------------------------------") + # print("SCOPING") + # ------- + query = query.filter_by(project_id=project_id) + # ------- + # print("...") + # print(query.statement.compile(**opts)) + + # WINDOWING + # print("------------------------------------------------------") + # print("WINDOWING") + windowing = query_dto.windowing + windowing_column: Optional[Column] = InvocationSpanDBE.created_at + # --------- + if windowing: + # print("WINDOWING EARLIEST:", windowing.earliest) + # print("WINDOWING LATEST: ", windowing.latest) + if windowing.earliest: + query = query.filter(windowing_column >= windowing.earliest) + + if windowing.latest: + query = query.filter(windowing_column <= windowing.latest) + # --------- + # print("...") + # print(query.statement.compile(**opts)) + + # FILTERING + # print("------------------------------------------------------") + # print("FILTERING") + filtering = query_dto.filtering + # --------- + if filtering: + # print("FILTERING OPERATOR: ", filtering.operator) + # print("FILTERING CONDITIONS:", filtering.conditions) + operator = filtering.operator + conditions = filtering.conditions + + query = query.filter(_combine(operator, _filters(conditions))) + # --------- + # print("...") + # print(query.statement.compile(**opts)) + + # SORTING + # print("------------------------------------------------------") + # print("SORTING") + if grouping and grouping_column: + query = query.order_by(grouping_column) + query = query.order_by(windowing_column.desc()) + + # ------- + # print("...") + # print(query.statement.compile(**opts)) + + # PAGINATION + # print("------------------------------------------------------") + # print("PAGINATION") + pagination = query_dto.pagination + # ---------- + if pagination: + # print("PAGINATION PAGE:", pagination.page) + # print("PAGINATION SIZE:", pagination.size) + limit = pagination.size + offset = (pagination.page - 1) * pagination.size + + query = query.limit(limit).offset(offset) + # ---------- + # print("...") + # print(query.statement.compile(**opts)) + + # GROUPING + # print("------------------------------------------------------") + # print("GROUPING") + if grouping and grouping_column: + print("GROUPING FOCUS:", grouping.focus.value) + subquery = query # .subquery() + + query = select(InvocationSpanDBE) + query = query.filter(grouping_column.in_(subquery)) + # -------- + # print("...") + # print(query.statement.compile(**opts)) + + # QUERY EXECUTION + # print("------------------------------------------------------") + # rint("QUERY EXECUTION") + spans = (await session.execute(query)).all() + # --------------- + + # FORMATTING + # formatting = query_dto.formatting + # -------- + + # return [] + return [map_span_dbe_to_dto(span) for span in spans] + + async def create_one( + self, + *, + span_dto: SpanCreateDTO, + ) -> None: + span_dbe = map_span_create_dto_to_dbe(span_dto) + + async with engine.session() as session: + session.add(span_dbe) + session.commit() + + async def create_many( + self, + *, + span_dtos: List[SpanCreateDTO], + ) -> None: + span_dbes = [map_span_create_dto_to_dbe(span_dto) for span_dto in span_dtos] + + async with engine.session() as session: + for span_dbe in span_dbes: + session.add(span_dbe) + + session.commit() + + async def read_one( + self, + *, + project_id: str, + node_id: str, + to_dto: bool = True, + ) -> Optional[SpanDTO]: + span_dbe = None + async with engine.session() as session: + query = select(InvocationSpanDBE) + + query = query.filter_by( + project_id=project_id, + node_id=node_id, + ) + + span_dbe = (await session.execute(query)).one_or_none() + + span_dto = None + if span_dbe and to_dto: + span_dto = map_span_dbe_to_dto(span_dbe) + + return span_dto + + return span_dbe + + async def read_many( + self, + *, + project_id: str, + node_ids: List[str], + to_dto: bool = True, + ) -> List[SpanDTO]: + span_dbes = [] + async with engine.session() as session: + query = select(InvocationSpanDBE) + + query = query.filter_by(project_id=project_id) + + query = query.filter(InvocationSpanDBE.node_id.in_(node_ids)) + + span_dbes = (await session.execute(query)).all() + + span_dtos = [] + if span_dbes and to_dto: + span_dtos = [map_span_dbe_to_dto(span_dbe) for span_dbe in span_dbes] + + return span_dtos + + return span_dbes + + async def delete_one( + self, + *, + project_id: str, + node_id: str, + ) -> None: + span_dbe = self.read_one( + project_id=project_id, + node_id=node_id, + to_dto=False, + ) + + if span_dbe: + async with engine.session() as session: + session.delete(span_dbe) + session.commit() + + async def delete_many( + self, + *, + project_id: str, + node_ids: List[str], + ) -> None: + span_dbes = self.read_many( + project_id=project_id, + node_ids=node_ids, + to_dto=False, + ) + + if span_dbes: + async with engine.session() as session: + for span in span_dbes: + session.delete(span) + + session.commit() + + +def _combine( + operator: LogicalOperator, + conditions: list, +): + if operator == LogicalOperator.AND: + return and_(*conditions) + elif operator == LogicalOperator.OR: + return or_(*conditions) + elif operator == LogicalOperator.NOT: + return not_(and_(*conditions)) + else: + raise ValueError(f"Unknown operator: {operator}") + + +def _filters(filtering: FilteringDTO) -> list: + _conditions = [] + + for condition in filtering: + if isinstance(condition, FilteringDTO): + _conditions.append( + _combine( + condition.operator, + _filters( + condition.conditions, + ), + ) + ) + + elif isinstance(condition, ConditionDTO): + column: Column = getattr(InvocationSpanDBE, condition.field) + + # NUMERIC OPERATORS + if isinstance(condition.operator, NumericOperator): + if condition.operator == NumericOperator.EQ: + _conditions.append(column == condition.value) + elif condition.operator == NumericOperator.NEQ: + _conditions.append(column != condition.value) + elif condition.operator == NumericOperator.GT: + _conditions.append(column > condition.value) + elif condition.operator == NumericOperator.LT: + _conditions.append(column < condition.value) + elif condition.operator == NumericOperator.GTE: + _conditions.append(column >= condition.value) + elif condition.operator == NumericOperator.LTE: + _conditions.append(column <= condition.value) + elif condition.operator == NumericOperator.BETWEEN: + _conditions.append( + column.between(condition.value[0], condition.value[1]) + ) + + # STRING OPERATORS + elif isinstance(condition.operator, StringOperator): + if condition.operator == StringOperator.STARTSWITH: + _conditions.append(column.startswith(condition.value)) + elif condition.operator == StringOperator.ENDSWITH: + _conditions.append(column.endswith(condition.value)) + elif condition.operator == StringOperator.CONTAINS: + _conditions.append(column.contains(condition.value)) + elif condition.operator == StringOperator.LIKE: + _conditions.append(column.like(condition.value)) + elif condition.operator == StringOperator.MATCHES: + if condition.options: + case_sensitive = condition.options.case_sensitive + exact_match = condition.options.exact_match + else: + case_sensitive = False + exact_match = False + + if exact_match: + if case_sensitive: + _conditions.append(column.like(condition.value)) + else: + _conditions.append(column.ilike(condition.value)) + else: + pattern = f"%{condition.value}%" + if case_sensitive: + _conditions.append(column.like(pattern)) + else: + _conditions.append(column.ilike(pattern)) + + # LIST OPERATORS + elif isinstance(condition.operator, ListOperator): + if condition.operator == ListOperator.IN: + _conditions.append(column.in_(condition.value)) + + # EXISTENCE OPERATORS + elif isinstance(condition.operator, ExistenceOperator): + if condition.operator == ExistenceOperator.EXISTS: + _conditions.append(column.isnot(None)) + elif condition.operator == ExistenceOperator.NOT_EXISTS: + _conditions.append(column.is_(None)) + + return _conditions diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py new file mode 100644 index 0000000000..26c9c66e12 --- /dev/null +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py @@ -0,0 +1,109 @@ +from sqlalchemy import Column, UUID, TIMESTAMP, Enum as SQLEnum, String, BigInteger +from sqlalchemy.dialects.postgresql import HSTORE, JSON, JSONB + +from agenta_backend.core.observability.dtos import StatusCode, TreeType, NodeType + +from agenta_backend.dbs.postgres.shared.dbas import DisplayBase +from agenta_backend.dbs.postgres.shared.dbas import ProjectScopeDBA, LifecycleDBA + + +## --- TIME (DBA) --- ## + + +class TimeDBA(DisplayBase): # TBD + __abstract__ = True + + time_start = Column(TIMESTAMP, nullable=False) + time_end = Column(TIMESTAMP, nullable=False) + time_span = Column(BigInteger, nullable=True) + + +## --- STATUS (DBA) --- ## + + +class StatusDBA(DisplayBase): + __abstract__ = True + + status_code = Column(SQLEnum(StatusCode), nullable=False) + status_message = Column(String, nullable=True) + + +## --- ATTRIBUTES (DBA) --- ## + + +class AttributesDBA(DisplayBase): + __abstract__ = True + + # inputs, internals, outputs, etc. + data = Column(JSON, nullable=True) + # scores, costs, tokens, durations, etc. + metrics = Column(JSON, nullable=True) + # configs, resources, etc. + meta = Column(JSON, nullable=True) + # tags, etc. + tags = Column(HSTORE, nullable=True) + # references, etc. + refs = Column(HSTORE, nullable=True) + + +## --- HIERARCHICAL STRUCTURE --- ## + + +class RootDBA(DisplayBase): + __abstract__ = True + + root_id = Column(UUID(as_uuid=True), nullable=False) + + +class TreeDBA(DisplayBase): + __abstract__ = True + + tree_id = Column(UUID(as_uuid=True), nullable=False) + + tree_type = Column(SQLEnum(TreeType), nullable=True) + + +class NodeDBA(DisplayBase): + __abstract__ = True + + node_id = Column(UUID(as_uuid=True), nullable=False) + node_name = Column(String, nullable=False) + + node_type = Column(SQLEnum(NodeType), nullable=True) + + +class LinksDBA(DisplayBase): + __abstract__ = True + + links = Column(HSTORE, nullable=True) + + +class ParentDBA(DisplayBase): + __abstract__ = True + + parent_id = Column(UUID(as_uuid=True), nullable=True) + + +## --- TABLES --- ## + + +class OTelDBA(DisplayBase): + __abstract__ = True + + otel = Column(JSONB, nullable=False) + + +class SpanDBA( + ProjectScopeDBA, + LifecycleDBA, + RootDBA, + TreeDBA, + NodeDBA, + ParentDBA, + TimeDBA, + StatusDBA, + AttributesDBA, + LinksDBA, + OTelDBA, +): + __abstract__ = True diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py new file mode 100644 index 0000000000..064026c09b --- /dev/null +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py @@ -0,0 +1,32 @@ +from sqlalchemy import PrimaryKeyConstraint, Index + +from agenta_backend.dbs.postgres.shared.base import Base + + +from agenta_backend.dbs.postgres.observability.dbas import SpanDBA + + +class InvocationSpanDBE(Base, SpanDBA): + __tablename__ = "invocation_span" + + __table_args__ = ( + PrimaryKeyConstraint( + "project_id", + "node_id", + ), + Index( + "index_project_id_tree_id", + "project_id", + "tree_id", + ), + Index( + "index_project_id_root_id", + "project_id", + "root_id", + ), + Index( + "index_project_id_node_id", + "project_id", + "created_at", + ), + ) diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py new file mode 100644 index 0000000000..b260a2fc93 --- /dev/null +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py @@ -0,0 +1,167 @@ +from uuid import UUID + +from agenta_backend.dbs.postgres.observability.dbes import InvocationSpanDBE + +from agenta_backend.core.shared.dtos import ProjectScopeDTO, LifecycleDTO +from agenta_backend.core.observability.dtos import SpanDTO, SpanCreateDTO +from agenta_backend.core.observability.dtos import ( + RootDTO, + TreeDTO, + NodeDTO, + ParentDTO, + TimeDTO, + StatusDTO, + LinkDTO, + OTelExtraDTO, +) + + +def map_span_dbe_to_dto(span: InvocationSpanDBE) -> SpanDTO: + return SpanDTO( + scope=ProjectScopeDTO( + project_id=span.project_id.hex, + ), + lifecycle=LifecycleDTO( + created_at=span.created_at, + updated_at=span.updated_at, + updated_by_id=span.updated_by_id.hex if span.updated_by_id else None, + ), + root=RootDTO( + id=span.root_id, + ), + tree=TreeDTO( + id=span.tree_id, + type=span.tree_type, + ), + node=NodeDTO( + id=span.node_id, + type=span.node_type, + name=span.node_name, + ), + parent=( + ParentDTO( + id=span.parent_id, + ) + if span.parent_id + else None + ), + time=TimeDTO( + start=span.time_start, + end=span.time_end, + span=span.time_span, + ), + status=StatusDTO( + code=span.status_code, + message=span.status_message, + ), + # ATTRIBUTES + data=span.data, + metrics=span.metrics, + meta=span.meta, + tags=span.tags, + refs=span.refs, + # ---------- + links=[ + LinkDTO( + type=link.split(":")[0], + tree_id=link.split(":")[1] + UUID(id).hex[:16], + id=id, + ) + for id, link in span.links.items() + ], + otel=OTelExtraDTO(**span.otel), + ) + + +def map_span_create_dto_to_dbe( + span_create_dto: SpanCreateDTO, +) -> InvocationSpanDBE: + span_dbe = InvocationSpanDBE( + # SCOPE + project_id=span_create_dto.scope.project_id, + # LIFECYCLE + # --------- + # ROOT + root_id=span_create_dto.root.id, + # TREE + tree_id=span_create_dto.tree.id, + tree_type=(span_create_dto.tree.type), + # NODE + node_id=span_create_dto.node.id, + node_type=(span_create_dto.node.type), + node_name=span_create_dto.node.name, + # PARENT + parent_id=span_create_dto.parent.id if span_create_dto.parent else None, + # TIME + time_start=span_create_dto.time.start, + time_end=span_create_dto.time.end, + time_span=span_create_dto.time.span, + # STATUS + status_code=span_create_dto.status.code, + status_message=span_create_dto.status.message, + # ATTRIBUTES + data=span_create_dto.data, + metrics=span_create_dto.metrics, + meta=span_create_dto.meta, + tags=span_create_dto.tags, + refs=span_create_dto.refs, + # LINKS + links={ + str(link.id): f"{link.type}:{link.tree_id.hex[:16]}" + for link in span_create_dto.links + }, + # OTEL + otel=span_create_dto.otel.model_dump(exclude_none=True), + ) + + return span_dbe + + +def map_span_dto_to_dbe( + span_dto: SpanDTO, +) -> InvocationSpanDBE: + span_dbe = InvocationSpanDBE( + # SCOPE + project_id=span_dto.scope.project_id, + # LIFECYCLE + created_at=span_dto.lifecycle.created_at, + updated_at=span_dto.lifecycle.updated_at, + updated_by_id=span_dto.lifecycle.updated_by_id, + # ROOT + root_id=span_dto.root.id, + # TREE + tree_id=span_dto.tree.id, + tree_type=span_dto.tree.type, + # NODE + node_id=span_dto.node.id, + node_type=span_dto.node.type, + node_name=span_dto.node.name, + # PARENT + parent_id=span_dto.parent.id if span_dto.parent.span_id else None, + # TIME + time_start=span_dto.time.start, + time_end=span_dto.time.end, + time_span=span_dto.time.span, + # STATUS + status_code=span_dto.status.code, + status_message=span_dto.status.message, + # ATTRIBUTES + data=span_dto.data, + metrics=span_dto.metrics, + meta=span_dto.meta, + tags=span_dto.tags, + refs=span_dto.refs, + # LINKS + links={ + str(link.id): f"{link.type}:{link.tree_id.hex[:16]}" + for link in span_dto.links + }, + # OTEL + otel=span_dto.otel.model_dump(exclude_none=True), + ) + + return span_dbe + + +def map_span_dbe_to_dict(dbe: InvocationSpanDBE) -> dict: + return {c.name: getattr(dbe, c.name) for c in dbe.__table__.columns} diff --git a/agenta-backend/agenta_backend/dbs/postgres/shared/__init__.py b/agenta-backend/agenta_backend/dbs/postgres/shared/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/agenta-backend/agenta_backend/dbs/postgres/shared/base.py b/agenta-backend/agenta_backend/dbs/postgres/shared/base.py new file mode 100644 index 0000000000..fa2b68a5d2 --- /dev/null +++ b/agenta-backend/agenta_backend/dbs/postgres/shared/base.py @@ -0,0 +1,5 @@ +from sqlalchemy.orm import DeclarativeBase + + +class Base(DeclarativeBase): + pass diff --git a/agenta-backend/agenta_backend/dbs/postgres/shared/config.py b/agenta-backend/agenta_backend/dbs/postgres/shared/config.py new file mode 100644 index 0000000000..24102ad4c1 --- /dev/null +++ b/agenta-backend/agenta_backend/dbs/postgres/shared/config.py @@ -0,0 +1,5 @@ +import os + +DATABASE_MODE = os.environ.get("DATABASE_MODE", "v2") +POSTGRES_URI = os.environ.get("POSTGRES_URI") +MONGODB_URI = os.environ.get("MONGODB_URI") diff --git a/agenta-backend/agenta_backend/dbs/postgres/shared/dbas.py b/agenta-backend/agenta_backend/dbs/postgres/shared/dbas.py new file mode 100644 index 0000000000..3385d1d164 --- /dev/null +++ b/agenta-backend/agenta_backend/dbs/postgres/shared/dbas.py @@ -0,0 +1,101 @@ +from collections import OrderedDict + +from sqlalchemy import Column, UUID, TIMESTAMP, func + +## --- DISPLAY --- ## + + +NOF_CHARS = 8 + + +def _p_id(id): + return repr(str(id)[:NOF_CHARS]) + + +def _str(o): + return f"{{ {_p_osa(o)} }}" + + +def _repr(o): + return f"{o.__class__.__name__}({_p_ora(o)})" + + +def _p_osa(o): + return ", ".join( + [ + ( + f"{i[0]}: {_p_id(i[1])}" + if repr(i[1]).startswith("UUID(") + or (i[0] == "slug" and i[1] is not None) + else ( + f"{i[0]}: {i[1].name}" + if repr(i[1]).startswith("<") + else f"{i[0]}: {repr(i[1])}" + ) + ) + for i in OrderedDict( + sorted(o.__dict__.items(), key=lambda item: item[0]) + ).items() + if not i[0].startswith("_") + ] + ) + + +def _p_ora(o): + return ", ".join( + [ + ( + f"{i[0]}={str(i[1])}" + if repr(i[1]).startswith("<") + else f"{i[0]}={repr(i[1])}" + ) + for i in OrderedDict( + sorted(o.__dict__.items(), key=lambda item: item[0]) + ).items() + if not i[0].startswith("_") + ] + ) + + +class DisplayBase: + __abstract__ = True + + def __str__(self): + return _str(self) + + def __repr__(self): + return _repr(self) + + +## --- SCOPE (DBA) --- ## + + +class ProjectScopeDBA(DisplayBase): + __abstract__ = True + + project_id = Column( + UUID(as_uuid=True), + nullable=False, + ) + + +## --- LIFECYCLE (DBA) --- ## + + +class LifecycleDBA(DisplayBase): + __abstract__ = True + + created_at = Column( + TIMESTAMP, + server_default=func.current_timestamp(), + nullable=False, + ) + updated_at = Column( + TIMESTAMP, + server_onupdate=func.current_timestamp(), + nullable=True, + ) + updated_by_id = Column( + UUID(as_uuid=True), + nullable=True, + ) diff --git a/agenta-backend/agenta_backend/dbs/postgres/shared/engine.py b/agenta-backend/agenta_backend/dbs/postgres/shared/engine.py new file mode 100644 index 0000000000..168bf36e37 --- /dev/null +++ b/agenta-backend/agenta_backend/dbs/postgres/shared/engine.py @@ -0,0 +1,79 @@ +from asyncio import current_task +from typing import AsyncGenerator +from contextlib import asynccontextmanager + +from sqlalchemy.ext.asyncio import ( + AsyncSession, + AsyncEngine, + create_async_engine, + async_sessionmaker, + async_scoped_session, +) + +from agenta_backend.dbs.postgres.shared.config import ( + POSTGRES_URI, + DATABASE_MODE, + MONGODB_URI, +) + + +class Engine: + def __init__(self) -> None: + self.postgres_uri = POSTGRES_URI + + self.async_engine: AsyncEngine = create_async_engine( + url=self.postgres_uri, + ) + self.async_session_maker = async_sessionmaker( + autocommit=False, + autoflush=False, + class_=AsyncSession, + expire_on_commit=False, + bind=self.async_engine, + ) + self.async_session = async_scoped_session( + session_factory=self.async_session_maker, + scopefunc=current_task, + ) + + ### LEGACY CODE ### + self.mode = DATABASE_MODE + self.mongo_uri = MONGODB_URI + ### ----------- ### + + async def open(self): + raise NotImplementedError() + + async def close(self): + if self.async_engine is None: + raise Exception("Engine is not open, cannot close it.") + + await self.async_engine.dispose() + + @asynccontextmanager + async def session(self) -> AsyncGenerator[AsyncSession, None]: + session: AsyncSession = self.async_session() + + try: + yield session + + await session.commit() + + except Exception as e: + await session.rollback() + + raise e + + finally: + await session.close() + + ### LEGACY CODE ### + + async def init_db(self): + self.open() + + async def close_db(self): + self.close() + + +engine = Engine() diff --git a/agenta-backend/agenta_backend/main.py b/agenta-backend/agenta_backend/main.py index 8fdfef5bfb..e8962a30b5 100644 --- a/agenta-backend/agenta_backend/main.py +++ b/agenta-backend/agenta_backend/main.py @@ -33,6 +33,11 @@ from celery import Celery +from agenta_backend.dbs.postgres.observability.dao import ObservabilityDAO +from agenta_backend.core.observability.service import ObservabilityService +from agenta_backend.apis.fastapi.observability.router import ObservabilityRouter + + origins = [ "http://localhost:3000", "http://localhost:3001", @@ -111,6 +116,10 @@ async def lifespan(application: FastAPI, cache=True): app.include_router(bases_router.router, prefix="/bases", tags=["Bases"]) app.include_router(configs_router.router, prefix="/configs", tags=["Configs"]) +observability = ObservabilityRouter(ObservabilityService(ObservabilityDAO())) + +app.include_router(router=observability.router, prefix="/observability/v1") + if isCloudEE(): import agenta_backend.cloud.main as cloud diff --git a/agenta-backend/agenta_backend/migrations/mongo_to_postgres/utils.py b/agenta-backend/agenta_backend/migrations/mongo_to_postgres/utils.py index c93167be61..3e70eba07e 100644 --- a/agenta-backend/agenta_backend/migrations/mongo_to_postgres/utils.py +++ b/agenta-backend/agenta_backend/migrations/mongo_to_postgres/utils.py @@ -19,6 +19,8 @@ from agenta_backend.models.base import Base from agenta_backend.migrations.mongo_to_postgres.mongo_db_engine import mongo_db +from agenta_backend.dbs.postgres.shared.engine import engine + BATCH_SIZE = 1000 @@ -37,7 +39,7 @@ async def create_all_tables(tables): async def store_mapping(table_name, mongo_id, uuid): """Store the mapping of MongoDB ObjectId to UUID in the mapping table.""" id_ = generate_uuid() - async with db_engine.get_session() as session: + async with engine.session() as session: mapping = IDsMappingDB( id=id_, table_name=table_name, objectid=str(mongo_id), uuid=uuid ) @@ -47,7 +49,7 @@ async def store_mapping(table_name, mongo_id, uuid): async def get_mapped_uuid(table_name, mongo_id): """Retrieve the mapped UUID for a given MongoDB ObjectId and table name.""" - async with db_engine.get_session() as session: + async with engine.session() as session: stmt = select(IDsMappingDB.uuid).filter( IDsMappingDB.table_name == table_name, IDsMappingDB.objectid == str(mongo_id), @@ -138,7 +140,7 @@ async def migrate_collection( migrated_docs = 0 skipped_docs = 0 - async with db_engine.get_session() as session: + async with engine.session() as session: for skip in tqdm( range(0, total_docs, BATCH_SIZE), total=(total_docs - 1) // BATCH_SIZE + 1, diff --git a/agenta-backend/agenta_backend/migrations/postgres/env.py b/agenta-backend/agenta_backend/migrations/postgres/env.py index ace04d9522..67351157d2 100644 --- a/agenta-backend/agenta_backend/migrations/postgres/env.py +++ b/agenta-backend/agenta_backend/migrations/postgres/env.py @@ -7,13 +7,13 @@ from alembic import context -from agenta_backend.models.db.postgres_engine import db_engine +from agenta_backend.dbs.postgres.shared.config import POSTGRES_URI # this is the Alembic Config object, which provides # access to the values within the .ini file in use. config = context.config -config.set_main_option("sqlalchemy.url", db_engine.postgres_uri) # type: ignore +config.set_main_option("sqlalchemy.url", POSTGRES_URI) # type: ignore # Interpret the config file for Python logging. @@ -23,7 +23,10 @@ # add your model's MetaData object here # for 'autogenerate' support -from agenta_backend.models.db_models import Base # noqa: F403 +from agenta_backend.dbs.postgres.shared.base import Base + +import agenta_backend.dbs.postgres.observability.dbes + # from myapp import mymodel # target_metadata = mymodel.Base.metadata diff --git a/agenta-backend/agenta_backend/migrations/postgres/versions/b7af1b4dc016_add_observability_in_oss.py b/agenta-backend/agenta_backend/migrations/postgres/versions/b7af1b4dc016_add_observability_in_oss.py new file mode 100644 index 0000000000..2ad7cefbf1 --- /dev/null +++ b/agenta-backend/agenta_backend/migrations/postgres/versions/b7af1b4dc016_add_observability_in_oss.py @@ -0,0 +1,103 @@ +"""add_observability_in_oss + +Revision ID: b7af1b4dc016 +Revises: d0dca996e123 +Create Date: 2024-10-11 10:19:12.331361 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision: str = "b7af1b4dc016" +down_revision: Union[str, None] = "d0dca996e123" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "invocation_span", + sa.Column("project_id", sa.UUID(), nullable=False), + sa.Column( + "created_at", + sa.TIMESTAMP(), + server_default=sa.text("CURRENT_TIMESTAMP"), + nullable=False, + ), + sa.Column("updated_at", sa.TIMESTAMP(), nullable=True), + sa.Column("updated_by_id", sa.UUID(), nullable=True), + sa.Column("root_id", sa.UUID(), nullable=False), + sa.Column("tree_id", sa.UUID(), nullable=False), + sa.Column("tree_type", sa.Enum("INVOCATION", name="treetype"), nullable=True), + sa.Column("node_id", sa.UUID(), nullable=False), + sa.Column("node_name", sa.String(), nullable=False), + sa.Column( + "node_type", + sa.Enum( + "AGENT", + "WORKFLOW", + "CHAIN", + "TASK", + "TOOL", + "EMBEDDING", + "QUERY", + "COMPLETION", + "CHAT", + "RERANK", + name="nodetype", + ), + nullable=True, + ), + sa.Column("parent_id", sa.UUID(), nullable=True), + sa.Column("time_start", sa.TIMESTAMP(), nullable=False), + sa.Column("time_end", sa.TIMESTAMP(), nullable=False), + sa.Column("time_span", sa.BigInteger(), nullable=True), + sa.Column( + "status_code", + sa.Enum("UNSET", "OK", "ERROR", name="statuscode"), + nullable=False, + ), + sa.Column("status_message", sa.String(), nullable=True), + sa.Column("data", postgresql.JSON(astext_type=sa.Text()), nullable=True), + sa.Column("metrics", postgresql.JSON(astext_type=sa.Text()), nullable=True), + sa.Column("meta", postgresql.JSON(astext_type=sa.Text()), nullable=True), + sa.Column("tags", postgresql.HSTORE(text_type=sa.Text()), nullable=True), + sa.Column("refs", postgresql.HSTORE(text_type=sa.Text()), nullable=True), + sa.Column("links", postgresql.HSTORE(text_type=sa.Text()), nullable=True), + sa.Column("otel", postgresql.JSONB(astext_type=sa.Text()), nullable=False), + sa.PrimaryKeyConstraint("project_id", "node_id"), + ) + op.create_index( + "index_project_id_node_id", + "invocation_span", + ["project_id", "created_at"], + unique=False, + ) + op.create_index( + "index_project_id_root_id", + "invocation_span", + ["project_id", "root_id"], + unique=False, + ) + op.create_index( + "index_project_id_tree_id", + "invocation_span", + ["project_id", "tree_id"], + unique=False, + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index("index_project_id_tree_id", table_name="invocation_span") + op.drop_index("index_project_id_root_id", table_name="invocation_span") + op.drop_index("index_project_id_node_id", table_name="invocation_span") + op.drop_table("invocation_span") + # ### end Alembic commands ### diff --git a/agenta-backend/agenta_backend/migrations/postgres/versions/d0dca996e123_add_hstore_extension.py b/agenta-backend/agenta_backend/migrations/postgres/versions/d0dca996e123_add_hstore_extension.py new file mode 100644 index 0000000000..623339404d --- /dev/null +++ b/agenta-backend/agenta_backend/migrations/postgres/versions/d0dca996e123_add_hstore_extension.py @@ -0,0 +1,19 @@ +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision: str = "d0dca996e123" +down_revision: Union[str, None] = "55bdd2e9a465" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.execute("CREATE EXTENSION IF NOT EXISTS hstore;") + + +def downgrade() -> None: + op.execute("DROP EXTENSION IF EXISTS hstore;") diff --git a/agenta-backend/agenta_backend/models/db_models.py b/agenta-backend/agenta_backend/models/db_models.py index 339f554eb5..10ff94dc36 100644 --- a/agenta-backend/agenta_backend/models/db_models.py +++ b/agenta-backend/agenta_backend/models/db_models.py @@ -14,7 +14,7 @@ from sqlalchemy_json import mutable_json_type from sqlalchemy.dialects.postgresql import UUID, JSONB -from agenta_backend.models.base import Base +from agenta_backend.dbs.postgres.shared.base import Base from agenta_backend.models.shared_models import TemplateType diff --git a/agenta-backend/agenta_backend/services/db_manager.py b/agenta-backend/agenta_backend/services/db_manager.py index 70293e23bc..6d6bc8f273 100644 --- a/agenta-backend/agenta_backend/services/db_manager.py +++ b/agenta-backend/agenta_backend/services/db_manager.py @@ -7,17 +7,19 @@ from fastapi import HTTPException -from agenta_backend.models import converters -from agenta_backend.utils.common import isCloudEE -from agenta_backend.models.db.postgres_engine import db_engine -from agenta_backend.services.json_importer_helper import get_json - from sqlalchemy import func, or_ from sqlalchemy.future import select from sqlalchemy.exc import NoResultFound from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import joinedload, aliased, load_only +from agenta_backend.models import converters +from agenta_backend.utils.common import isCloudEE +from agenta_backend.services.json_importer_helper import get_json + +from agenta_backend.dbs.postgres.shared.engine import engine + + if isCloudEE(): from agenta_backend.commons.services import db_manager_ee from agenta_backend.commons.utils.permissions import check_rbac_permission @@ -105,7 +107,7 @@ async def add_testset_to_app_variant( project_id (str): The ID of the project """ - async with db_engine.get_session() as session: + async with engine.session() as session: try: json_path = os.path.join( PARENT_DIRECTORY, @@ -143,7 +145,7 @@ async def get_image_by_id(image_id: str) -> ImageDB: ImageDB: instance of image object """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(ImageDB).filter_by(id=uuid.UUID(image_id)) ) @@ -160,7 +162,7 @@ async def fetch_app_by_id(app_id: str) -> AppDB: assert app_id is not None, "app_id cannot be None" app_uuid = await get_object_uuid(object_id=app_id, table_name="app_db") - async with db_engine.get_session() as session: + async with engine.session() as session: base_query = select(AppDB).filter_by(id=uuid.UUID(app_uuid)) result = await session.execute(base_query) app = result.unique().scalars().first() @@ -179,7 +181,7 @@ async def fetch_app_variant_by_id(app_variant_id: str) -> Optional[AppVariantDB] """ assert app_variant_id is not None, "app_variant_id cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: base_query = select(AppVariantDB).options( joinedload(AppVariantDB.app.of_type(AppDB)).load_only(AppDB.id, AppDB.app_name), # type: ignore joinedload(AppVariantDB.base.of_type(VariantBaseDB)).joinedload(VariantBaseDB.deployment.of_type(DeploymentDB)).load_only(DeploymentDB.id, DeploymentDB.uri), # type: ignore @@ -210,7 +212,7 @@ async def fetch_app_variant_by_base_id(base_id: str) -> Optional[AppVariantDB]: """ assert base_id is not None, "base_id cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantDB).filter_by(base_id=uuid.UUID(base_id)) ) @@ -234,7 +236,7 @@ async def fetch_app_variant_by_base_id_and_config_name( assert base_id is not None, "base_id cannot be None" assert config_name is not None, "config_name cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantDB).filter_by( base_id=uuid.UUID(base_id), config_name=config_name @@ -260,7 +262,7 @@ async def fetch_app_variant_revision_by_variant( assert app_variant_id is not None, "app_variant_id cannot be None" assert revision is not None, "revision cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantRevisionsDB).filter_by( variant_id=uuid.UUID(app_variant_id), @@ -289,7 +291,7 @@ async def fetch_base_by_id(base_id: str) -> Optional[VariantBaseDB]: assert base_id is not None, "no base_id provided" base_uuid = await get_object_uuid(object_id=base_id, table_name="bases") - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(VariantBaseDB) .options( @@ -314,7 +316,7 @@ async def fetch_app_variant_by_name_and_appid( AppVariantDB: the instance of the app variant """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantDB).filter_by( variant_name=variant_name, app_id=uuid.UUID(app_id) @@ -341,7 +343,7 @@ async def create_new_variant_base( """ logger.debug(f"Creating new base: {base_name} with image: {image} for app: {app}") - async with db_engine.get_session() as session: + async with engine.session() as session: base = VariantBaseDB( app_id=app.id, project_id=uuid.UUID(project_id), @@ -404,7 +406,7 @@ async def create_new_app_variant( config.parameters == {} ), "Parameters should be empty when calling create_new_app_variant (otherwise revision should not be set to 0)" - async with db_engine.get_session() as session: + async with engine.session() as session: variant = AppVariantDB( app_id=app.id, project_id=uuid.UUID(project_id), @@ -482,7 +484,7 @@ async def create_image( elif image_type == "zip" and template_uri is None: raise Exception("template_uri must be provided for type zip") - async with db_engine.get_session() as session: + async with engine.session() as session: image = ImageDB( deletable=deletable, project_id=uuid.UUID(project_id), @@ -530,7 +532,7 @@ async def create_deployment( DeploymentDB: The created deployment. """ - async with db_engine.get_session() as session: + async with engine.session() as session: try: deployment = DeploymentDB( app_id=uuid.UUID(app_id), @@ -577,7 +579,7 @@ async def create_app_and_envs( if app is not None: raise ValueError("App with the same name already exists") - async with db_engine.get_session() as session: + async with engine.session() as session: app = AppDB(app_name=app_name, project_id=uuid.UUID(project_id)) session.add(app) @@ -596,7 +598,7 @@ async def update_app(app_id: str, values_to_update: dict) -> None: values_to_update (dict): The values to update in the app """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(AppDB).filter_by(id=uuid.UUID(app_id))) app = result.scalars().first() if not app: @@ -619,7 +621,7 @@ async def get_deployment_by_id(deployment_id: str) -> DeploymentDB: DeploymentDB: instance of deployment object """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(DeploymentDB).filter_by(id=uuid.UUID(deployment_id)) ) @@ -637,7 +639,7 @@ async def get_deployment_by_appid(app_id: str) -> DeploymentDB: DeploymentDB: instance of deployment object """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(DeploymentDB).filter_by(app_id=uuid.UUID(app_id)) ) @@ -659,7 +661,7 @@ async def list_app_variants_for_app_id(app_id: str, project_id: str): """ assert app_id is not None, "app_id cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantDB).filter_by( app_id=uuid.UUID(app_id), project_id=uuid.UUID(project_id) @@ -681,7 +683,7 @@ async def list_bases_for_app_id(app_id: str, base_name: Optional[str] = None): """ assert app_id is not None, "app_id cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: query = select(VariantBaseDB).filter_by(app_id=uuid.UUID(app_id)) if base_name: query = query.filter_by(base_name=base_name) @@ -703,7 +705,7 @@ async def list_variants_for_base(base: VariantBaseDB): """ assert base is not None, "base cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantDB) .filter_by(base_id=base.id) @@ -723,7 +725,7 @@ async def get_user(user_uid: str) -> UserDB: UserDB: instance of user """ - async with db_engine.get_session() as session: + async with engine.session() as session: # NOTE: Backward Compatibility # --------------------------- # Previously, the user_id field in the api_keys collection in MongoDB used the @@ -767,7 +769,7 @@ async def get_user_with_id(user_id: str): Exception: If an error occurs while getting the user from the database. """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(UserDB).filter_by(id=uuid.UUID(user_id))) user = result.scalars().first() if user is None: @@ -797,7 +799,7 @@ async def get_user_with_email(email: str): if "@" not in email: raise Exception("Please provide a valid email address") - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(UserDB).filter_by(email=email)) user = result.scalars().first() return user @@ -811,7 +813,7 @@ async def get_users_by_ids(user_ids: List): user_ids (List): A list of user IDs to retrieve. """ - async with db_engine.get_session() as session: + async with engine.session() as session: user_uids = [uuid.UUID(user_id) for user_id in user_ids] result = await session.execute(select(UserDB).where(UserDB.id.in_(user_uids))) users = result.scalars().all() @@ -831,7 +833,7 @@ async def get_orga_image_instance_by_docker_id( ImageDB: instance of image object """ - async with db_engine.get_session() as session: + async with engine.session() as session: query = select(ImageDB).filter_by( docker_id=docker_id, project_id=uuid.UUID(project_id) ) @@ -854,7 +856,7 @@ async def get_orga_image_instance_by_uri(template_uri: str) -> ImageDB: if not parsed_url.scheme and not parsed_url.netloc: raise ValueError(f"Invalid URL: {template_uri}") - async with db_engine.get_session() as session: + async with engine.session() as session: query = select(ImageDB).filter_by(template_uri=template_uri) result = await session.execute(query) image = result.scalars().first() @@ -871,7 +873,7 @@ async def get_app_instance_by_id(app_id: str) -> AppDB: AppDB: instance of app object """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(AppDB).filter_by(id=uuid.UUID(app_id))) app = result.scalars().first() return app @@ -916,7 +918,7 @@ async def add_variant_from_base_and_config( raise ValueError("App variant with the same name already exists") user_db = await get_user(user_uid) - async with db_engine.get_session() as session: + async with engine.session() as session: db_app_variant = AppVariantDB( app_id=previous_app_variant_db.app_id, variant_name=new_variant_name, @@ -991,7 +993,7 @@ async def list_apps( detail="You do not have access to perform this action. Please contact your organization admin.", ) - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppDB).filter_by(project_id=project.id) ) @@ -999,7 +1001,7 @@ async def list_apps( return [converters.app_db_to_pydantic(app) for app in apps] else: - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppDB).filter_by(project_id=uuid.UUID(project_id)) ) @@ -1020,7 +1022,7 @@ async def list_app_variants(app_id: str): """ app_uuid = await get_object_uuid(object_id=app_id, table_name="app_db") - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantDB) .options( @@ -1047,7 +1049,7 @@ async def check_is_last_variant_for_image( true if it's the last variant, false otherwise """ - async with db_engine.get_session() as session: + async with engine.session() as session: query = select(AppVariantDB).filter_by( base_id=uuid.UUID(variant_base_id), project_id=uuid.UUID(project_id) ) @@ -1068,7 +1070,7 @@ async def remove_deployment(deployment_id: str): logger.debug("Removing deployment") assert deployment_id is not None, "deployment_id is missing" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(DeploymentDB).filter_by(id=uuid.UUID(deployment_id)) ) @@ -1090,7 +1092,7 @@ async def list_deployments(app_id: str): a list/sequence of all the deployments that were retrieved """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(DeploymentDB).filter_by(app_id=uuid.UUID(app_id)) ) @@ -1115,7 +1117,7 @@ async def remove_app_variant_from_db(app_variant_db: AppVariantDB, project_id: s app_variant_db, project_id ) - async with db_engine.get_session() as session: + async with engine.session() as session: # Delete all the revisions associated with the variant for app_variant_revision in app_variant_revisions: await session.delete(app_variant_revision) @@ -1156,7 +1158,7 @@ async def deploy_to_environment( assert "user_uid" in user_org_data, "User uid is required" user = await get_user(user_uid=user_org_data["user_uid"]) - async with db_engine.get_session() as session: + async with engine.session() as session: # Find the environment for the given app name and user result = await session.execute( select(AppEnvironmentDB).filter_by( @@ -1201,7 +1203,7 @@ async def fetch_app_environment_by_name_and_appid( AppEnvironmentDB: app environment object """ - async with db_engine.get_session() as session: + async with engine.session() as session: query = select(AppEnvironmentDB).filter_by( app_id=uuid.UUID(app_id), name=environment_name ) @@ -1226,7 +1228,7 @@ async def fetch_app_variant_revision_by_id( AppVariantRevisionsDB: app variant revision object """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantRevisionsDB).filter_by(id=uuid.UUID(variant_revision_id)) ) @@ -1247,7 +1249,7 @@ async def fetch_environment_revisions_for_environment( List[AppEnvironmentRevisionDB]: A list of AppEnvironmentRevisionDB objects. """ - async with db_engine.get_session() as session: + async with engine.session() as session: query = select(AppEnvironmentRevisionDB).filter_by( environment_id=environment.id ) @@ -1267,7 +1269,7 @@ async def fetch_app_environment_revision(revision_id: str) -> AppEnvironmentRevi revision_id (str): The ID of the revision """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppEnvironmentRevisionDB).filter_by(id=uuid.UUID(revision_id)) ) @@ -1285,7 +1287,7 @@ async def update_app_environment( values_to_update (dict): the values to update with """ - async with db_engine.get_session() as session: + async with engine.session() as session: for key, value in values_to_update.items(): if hasattr(app_environment, key): setattr(app_environment, key, value) @@ -1304,7 +1306,7 @@ async def update_app_environment_deployed_variant_revision( deployed_variant_revision (str): the ID of the deployed variant revision """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantRevisionsDB).filter_by( id=uuid.UUID(deployed_variant_revision) @@ -1343,7 +1345,7 @@ async def list_environments(app_id: str, **kwargs: dict): logging.error(f"App with id {app_id} not found") raise ValueError("App not found") - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppEnvironmentDB) .options( @@ -1470,7 +1472,7 @@ async def list_app_variant_revisions_by_variant( List[AppVariantRevisionsDB]: A list of AppVariantRevisionsDB objects. """ - async with db_engine.get_session() as session: + async with engine.session() as session: base_query = select(AppVariantRevisionsDB).filter_by( variant_id=app_variant.id, project_id=uuid.UUID(project_id) ) @@ -1496,7 +1498,7 @@ async def fetch_app_variant_revision(app_variant: str, revision_number: int): List[AppVariantRevisionsDB]: A list of AppVariantRevisionsDB objects. """ - async with db_engine.get_session() as session: + async with engine.session() as session: base_query = select(AppVariantRevisionsDB).filter_by( variant_id=uuid.UUID(app_variant), revision=revision_number ) @@ -1535,7 +1537,7 @@ async def remove_image(image: ImageDB, project_id: str): if image is None: raise ValueError("Image is None") - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(ImageDB).filter_by(id=image.id, project_id=uuid.UUID(project_id)) ) @@ -1560,7 +1562,7 @@ async def remove_environment(environment_db: AppEnvironmentDB): """ assert environment_db is not None, "environment_db is missing" - async with db_engine.get_session() as session: + async with engine.session() as session: await session.delete(environment_db) await session.commit() @@ -1573,7 +1575,7 @@ async def remove_testsets(testset_ids: List[str]): testset_ids (List[str]): The testset identifiers """ - async with db_engine.get_session() as session: + async with engine.session() as session: query = select(TestSetDB).where(TestSetDB.id.in_(testset_ids)) result = await session.execute(query) testsets = result.scalars().all() @@ -1595,7 +1597,7 @@ async def remove_app_testsets(app_id: str): # Find testsets owned by the app deleted_count: int = 0 - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(TestSetDB).filter_by(app_id=uuid.UUID(app_id)) ) @@ -1629,7 +1631,7 @@ async def remove_base_from_db(base_id: str): """ assert base_id is None, "base_id is required" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(VariantBaseDB).filter_by(id=uuid.UUID(base_id)) ) @@ -1657,7 +1659,7 @@ async def remove_app_by_id(app_id: str, project_id: str): """ assert app_id is not None, "app_id cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppDB).filter_by( id=uuid.UUID(app_id), project_id=uuid.UUID(project_id) @@ -1688,7 +1690,7 @@ async def update_variant_parameters( """ user = await get_user(user_uid) - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantDB).filter_by( id=uuid.UUID(app_variant_id), project_id=uuid.UUID(project_id) @@ -1739,7 +1741,7 @@ async def get_app_variant_instance_by_id( AppVariantDB: instance of app variant object """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantDB) .options( @@ -1768,7 +1770,7 @@ async def fetch_testset_by_id(testset_id: str) -> Optional[TestSetDB]: except ValueError as e: raise ValueError(f"testset_id {testset_id} is not a valid UUID") from e - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(TestSetDB).filter_by(id=testset_uuid)) testset = result.scalars().first() return testset @@ -1787,7 +1789,7 @@ async def create_testset(app: AppDB, project_id: str, testset_data: Dict[str, An returns the newly created TestsetDB """ - async with db_engine.get_session() as session: + async with engine.session() as session: testset_db = TestSetDB(**testset_data, project_id=uuid.UUID(project_id)) session.add(testset_db) @@ -1805,7 +1807,7 @@ async def update_testset(testset_id: str, values_to_update: dict) -> None: values_to_update (dict): The values to update """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(TestSetDB).filter_by(id=uuid.UUID(testset_id)) ) @@ -1830,7 +1832,7 @@ async def fetch_testsets_by_project_id(project_id: str): List[TestSetDB]: The fetched testsets. """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(TestSetDB).filter_by(project_id=uuid.UUID(project_id)) ) @@ -1849,7 +1851,7 @@ async def fetch_evaluation_by_id(evaluation_id: str) -> Optional[EvaluationDB]: """ assert evaluation_id is not None, "evaluation_id cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: base_query = select(EvaluationDB).filter_by(id=uuid.UUID(evaluation_id)) if isCloudEE(): query = base_query.options( @@ -1883,7 +1885,7 @@ async def list_human_evaluations(app_id: str, project_id: str): app_id (str): The application identifier """ - async with db_engine.get_session() as session: + async with engine.session() as session: base_query = ( select(HumanEvaluationDB) .filter_by(app_id=uuid.UUID(app_id), project_id=uuid.UUID(project_id)) @@ -1920,7 +1922,7 @@ async def create_human_evaluation( variants_ids (List[str]): The IDs of the variants for the evaluation """ - async with db_engine.get_session() as session: + async with engine.session() as session: human_evaluation = HumanEvaluationDB( app_id=app.id, project_id=app.project_id, @@ -1952,7 +1954,7 @@ async def fetch_human_evaluation_variants(human_evaluation_id: str): The human evaluation variants. """ - async with db_engine.get_session() as session: + async with engine.session() as session: base_query = select(HumanEvaluationVariantDB).filter_by( human_evaluation_id=uuid.UUID(human_evaluation_id) ) @@ -2004,7 +2006,7 @@ async def create_human_evaluation_variants( if set(variants_dict.keys()) != set(variants_revisions_dict.keys()): raise ValueError("Mismatch between variants and their revisions") - async with db_engine.get_session() as session: + async with engine.session() as session: for variant_id in variants_ids: variant = variants_dict[variant_id] variant_revision = variants_revisions_dict[variant_id] @@ -2032,7 +2034,7 @@ async def fetch_human_evaluation_by_id( """ assert evaluation_id is not None, "evaluation_id cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: base_query = select(HumanEvaluationDB).filter_by(id=uuid.UUID(evaluation_id)) if isCloudEE(): query = base_query.options( @@ -2058,7 +2060,7 @@ async def update_human_evaluation(evaluation_id: str, values_to_update: dict): NoResultFound: if human evaluation is not found """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(HumanEvaluationDB).filter_by(id=uuid.UUID(evaluation_id)) ) @@ -2082,7 +2084,7 @@ async def delete_human_evaluation(evaluation_id: str): """ assert evaluation_id is not None, "evaluation_id cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(HumanEvaluationDB).filter_by(id=uuid.UUID(evaluation_id)) ) @@ -2109,7 +2111,7 @@ async def create_human_evaluation_scenario( evaluation_extend (Dict[str, any]): An extended required payload for the evaluation scenario. Contains score, vote, and correct_answer. """ - async with db_engine.get_session() as session: + async with engine.session() as session: evaluation_scenario = HumanEvaluationScenarioDB( **evaluation_extend, project_id=uuid.UUID(project_id), @@ -2135,7 +2137,7 @@ async def update_human_evaluation_scenario( NoResultFound: if human evaluation scenario is not found """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(HumanEvaluationScenarioDB).filter_by( id=uuid.UUID(evaluation_scenario_id) @@ -2166,7 +2168,7 @@ async def fetch_human_evaluation_scenarios(evaluation_id: str): The evaluation scenarios. """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(HumanEvaluationScenarioDB).filter_by( evaluation_id=uuid.UUID(evaluation_id) @@ -2188,7 +2190,7 @@ async def fetch_evaluation_scenarios(evaluation_id: str, project_id: str): The evaluation scenarios. """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(EvaluationScenarioDB) .filter_by( @@ -2213,7 +2215,7 @@ async def fetch_evaluation_scenario_by_id( """ assert evaluation_scenario_id is not None, "evaluation_scenario_id cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(EvaluationScenarioDB).filter_by(id=uuid.UUID(evaluation_scenario_id)) ) @@ -2234,7 +2236,7 @@ async def fetch_human_evaluation_scenario_by_id( """ assert evaluation_scenario_id is not None, "evaluation_scenario_id cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(HumanEvaluationScenarioDB).filter_by( id=uuid.UUID(evaluation_scenario_id) @@ -2255,7 +2257,7 @@ async def fetch_human_evaluation_scenario_by_evaluation_id( """ evaluation = await fetch_human_evaluation_by_id(evaluation_id) - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(HumanEvaluationScenarioDB).filter_by( evaluation_id=evaluation.id # type: ignore @@ -2279,7 +2281,7 @@ async def find_previous_variant_from_base_id( """ assert base_id is not None, "base_id cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantDB) .filter_by(base_id=uuid.UUID(base_id), project_id=uuid.UUID(project_id)) @@ -2302,7 +2304,7 @@ async def add_template(**kwargs: dict) -> str: template_id (str): The Id of the created template. """ - async with db_engine.get_session() as session: + async with engine.session() as session: conditions = [ TemplateDB.tag_id == kwargs["tag_id"], TemplateDB.name == kwargs["name"], @@ -2335,7 +2337,7 @@ async def add_zip_template(key, value): template_id (Str): The Id of the created template. """ - async with db_engine.get_session() as session: + async with engine.session() as session: query = select(TemplateDB).where(TemplateDB.name == key) result = await session.execute(query) existing_template = result.scalars().first() @@ -2384,7 +2386,7 @@ async def get_template(template_id: str) -> TemplateDB: """ assert template_id is not None, "template_id cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(TemplateDB).filter_by(id=uuid.UUID(template_id)) ) @@ -2399,7 +2401,7 @@ async def remove_old_template_from_db(tag_ids: list) -> None: tag_ids -- list of template IDs you want to keep """ - async with db_engine.get_session() as session: + async with engine.session() as session: # Fetch all templates with tag_id in tag_ids templates = await session.execute(select(TemplateDB)) templates = templates.scalars().all() @@ -2425,7 +2427,7 @@ async def get_templates(): The docker templates to create an LLM app from the UI. """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(TemplateDB)) templates = result.scalars().all() return converters.templates_db_to_pydantic(templates) # type: ignore @@ -2441,7 +2443,7 @@ async def update_base( base (VariantBaseDB): The base object to update. """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(VariantBaseDB).filter_by(id=uuid.UUID(base_id)) ) @@ -2463,7 +2465,7 @@ async def remove_base(base_db: VariantBaseDB): base (VariantBaseDB): The base object to update. """ - async with db_engine.get_session() as session: + async with engine.session() as session: await session.delete(base_db) await session.commit() @@ -2478,7 +2480,7 @@ async def update_app_variant( app_variant_id (str): The app variant oIDbject to update. """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantDB).filter_by(id=uuid.UUID(app_variant_id)) ) @@ -2529,7 +2531,7 @@ async def fetch_app_by_name_and_parameters( app_name=app_name, project_id=uuid.UUID(project_id) ) - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(query) app_db = result.unique().scalars().first() return app_db @@ -2548,7 +2550,7 @@ async def create_new_evaluation( EvaluationScenarioDB: The created evaluation scenario. """ - async with db_engine.get_session() as session: + async with engine.session() as session: evaluation = EvaluationDB( app_id=app.id, project_id=uuid.UUID(project_id), @@ -2581,7 +2583,7 @@ async def list_evaluations(app_id: str, project_id: str): project_id (str): The ID of the project """ - async with db_engine.get_session() as session: + async with engine.session() as session: base_query = select(EvaluationDB).filter_by( app_id=uuid.UUID(app_id), project_id=uuid.UUID(project_id) ) @@ -2629,7 +2631,7 @@ async def fetch_evaluations_by_resource( ids = list(map(uuid.UUID, resource_ids)) - async with db_engine.get_session() as session: + async with engine.session() as session: if resource_type == "variant": result_evaluations = await session.execute( select(EvaluationDB) @@ -2700,7 +2702,7 @@ async def delete_evaluations(evaluation_ids: List[str]) -> None: evaluations_ids (list[str]): The IDs of the evaluation """ - async with db_engine.get_session() as session: + async with engine.session() as session: query = select(EvaluationDB).where(EvaluationDB.id.in_(evaluation_ids)) result = await session.execute(query) evaluations = result.scalars().all() @@ -2726,7 +2728,7 @@ async def create_new_evaluation_scenario( EvaluationScenarioDB: The created evaluation scenario. """ - async with db_engine.get_session() as session: + async with engine.session() as session: evaluation_scenario = EvaluationScenarioDB( project_id=uuid.UUID(project_id), evaluation_id=uuid.UUID(evaluation_id), @@ -2765,7 +2767,7 @@ async def create_new_evaluation_scenario( async def update_evaluation_with_aggregated_results( evaluation_id: str, aggregated_results: List[AggregatedResult] ): - async with db_engine.get_session() as session: + async with engine.session() as session: for result in aggregated_results: aggregated_result = EvaluationAggregatedResultDB( evaluation_id=uuid.UUID(evaluation_id), @@ -2788,7 +2790,7 @@ async def fetch_eval_aggregated_results(evaluation_id: str): The evaluation aggregated results by evaluation identifier. """ - async with db_engine.get_session() as session: + async with engine.session() as session: base_query = select(EvaluationAggregatedResultDB).filter_by( evaluation_id=uuid.UUID(evaluation_id) ) @@ -2831,7 +2833,7 @@ async def fetch_evaluators_configs(project_id: str): List[EvaluatorConfigDB]: A list of evaluator configuration objects. """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(EvaluatorConfigDB).filter_by(project_id=uuid.UUID(project_id)) ) @@ -2849,7 +2851,7 @@ async def fetch_evaluator_config(evaluator_config_id: str): EvaluatorConfigDB: the evaluator configuration object. """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(EvaluatorConfigDB).filter_by(id=uuid.UUID(evaluator_config_id)) ) @@ -2870,7 +2872,7 @@ async def check_if_evaluators_exist_in_list_of_evaluators_configs( bool: True if all evaluators exist, False otherwise. """ - async with db_engine.get_session() as session: + async with engine.session() as session: evaluator_config_uuids = [ uuid.UUID(evaluator_config_id) for evaluator_config_id in evaluators_configs_ids @@ -2903,7 +2905,7 @@ async def fetch_evaluator_config_by_appId( EvaluatorConfigDB: the evaluator configuration object. """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(EvaluatorConfigDB).filter_by( app_id=uuid.UUID(app_id), evaluator_key=evaluator_name @@ -2922,7 +2924,7 @@ async def create_evaluator_config( ) -> EvaluatorConfigDB: """Create a new evaluator configuration in the database.""" - async with db_engine.get_session() as session: + async with engine.session() as session: new_evaluator_config = EvaluatorConfigDB( project_id=uuid.UUID(project_id), name=f"{name} ({app_name})", @@ -2951,7 +2953,7 @@ async def update_evaluator_config( EvaluatorConfigDB: The updated evaluator configuration object. """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(EvaluatorConfigDB).filter_by(id=uuid.UUID(evaluator_config_id)) ) @@ -2976,7 +2978,7 @@ async def delete_evaluator_config(evaluator_config_id: str) -> bool: """Delete an evaluator configuration from the database.""" assert evaluator_config_id is not None, "Evaluator Config ID cannot be None" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(EvaluatorConfigDB).filter_by(id=uuid.UUID(evaluator_config_id)) ) @@ -3007,7 +3009,7 @@ async def update_evaluation( EvaluatorConfigDB: The updated evaluator configuration object. """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(EvaluationDB).filter_by( id=uuid.UUID(evaluation_id), project_id=uuid.UUID(project_id) @@ -3027,7 +3029,7 @@ async def update_evaluation( async def check_if_evaluation_contains_failed_evaluation_scenarios( evaluation_id: str, ) -> bool: - async with db_engine.get_session() as session: + async with engine.session() as session: EvaluationResultAlias = aliased(EvaluationScenarioResultDB) query = ( select(func.count(EvaluationScenarioDB.id)) @@ -3094,7 +3096,7 @@ async def fetch_corresponding_object_uuid(table_name: str, object_id: str) -> st The corresponding object uuid as string. """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(IDsMappingDB).filter_by(table_name=table_name, objectid=object_id) ) @@ -3110,7 +3112,7 @@ async def fetch_default_project() -> ProjectDB: ProjectDB: The default project instance. """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(ProjectDB).filter_by(is_default=True)) default_project = result.scalars().first() return default_project diff --git a/agenta-backend/agenta_backend/services/evaluation_service.py b/agenta-backend/agenta_backend/services/evaluation_service.py index 7f8f2290f7..599cfd6399 100644 --- a/agenta-backend/agenta_backend/services/evaluation_service.py +++ b/agenta-backend/agenta_backend/services/evaluation_service.py @@ -5,14 +5,9 @@ from fastapi import HTTPException -from sqlalchemy.orm import Session -from sqlalchemy.future import select -from sqlalchemy.ext.asyncio import AsyncSession - from agenta_backend.models import converters from agenta_backend.services import db_manager from agenta_backend.utils.common import isCloudEE -from agenta_backend.models.db.postgres_engine import db_engine from agenta_backend.models.api.evaluation_model import ( Evaluation, diff --git a/agenta-backend/agenta_backend/services/user_service.py b/agenta-backend/agenta_backend/services/user_service.py index 0de5b95ac0..0565998542 100644 --- a/agenta-backend/agenta_backend/services/user_service.py +++ b/agenta-backend/agenta_backend/services/user_service.py @@ -4,7 +4,6 @@ from sqlalchemy.exc import NoResultFound from agenta_backend.utils.common import isCloud -from agenta_backend.models.db.postgres_engine import db_engine if isCloud(): from agenta_backend.commons.models.db_models import UserDB_ as UserDB @@ -13,6 +12,8 @@ from agenta_backend.models.api.user_models import UserUpdate +from agenta_backend.dbs.postgres.shared.engine import engine + async def create_new_user(payload: dict) -> UserDB: """ @@ -25,7 +26,7 @@ async def create_new_user(payload: dict) -> UserDB: UserDB: The created user object. """ - async with db_engine.get_session() as session: + async with engine.session() as session: user = UserDB(**payload) session.add(user) @@ -50,7 +51,7 @@ async def update_user(user_uid: str, payload: UserUpdate) -> UserDB: NoResultFound: User with session id xxxx not found. """ - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(UserDB).filter_by(uid=user_uid)) user = result.scalars().first() diff --git a/agenta-backend/agenta_backend/tests/engine.py b/agenta-backend/agenta_backend/tests/engine.py index c056032bcd..7388e96084 100644 --- a/agenta-backend/agenta_backend/tests/engine.py +++ b/agenta-backend/agenta_backend/tests/engine.py @@ -1,18 +1,11 @@ -from agenta_backend.models.db.models import models -from agenta_backend.models.db.postgres_engine import DBEngine +from agenta_backend.dbs.postgres.shared.engine import Engine +from agenta_backend.tests.models import models -class TestDBEngine(DBEngine): - """ - Database engine to remove database tables in test db. - """ +class TestDBEngine(Engine): async def remove_db(self): - """ - Remove the database tables. - """ - - async with self.engine.begin() as conn: + async with self.async_engine.begin() as conn: for model in models: await conn.run_sync(model.metadata.drop_all) diff --git a/agenta-backend/agenta_backend/tests/models.py b/agenta-backend/agenta_backend/tests/models.py new file mode 100644 index 0000000000..aba8703273 --- /dev/null +++ b/agenta-backend/agenta_backend/tests/models.py @@ -0,0 +1,76 @@ +from typing import List + +from agenta_backend.utils.common import isCloudEE + +from agenta_backend.dbs.postgres.shared.base import Base + +if isCloudEE(): + from agenta_backend.commons.models.db_models import ( + APIKeyDB, + WorkspaceDB, + OrganizationDB, + InvitationDB, + OrganizationMemberDB, + WorkspaceMemberDB, + ProjectMemberDB, + AppDB_ as AppDB, + UserDB_ as UserDB, + ImageDB_ as ImageDB, + TestSetDB_ as TestSetDB, + AppVariantDB_ as AppVariantDB, + EvaluationDB_ as EvaluationDB, + DeploymentDB_ as DeploymentDB, + VariantBaseDB_ as VariantBaseDB, + AppEnvironmentDB_ as AppEnvironmentDB, + AppEnvironmentRevisionDB_ as AppEnvironmentRevisionDB, + EvaluatorConfigDB_ as EvaluatorConfigDB, + HumanEvaluationDB_ as HumanEvaluationDB, + EvaluationScenarioDB_ as EvaluationScenarioDB, + HumanEvaluationScenarioDB_ as HumanEvaluationScenarioDB, + ) +else: + from agenta_backend.models.db_models import ( + AppDB, + UserDB, + ProjectDB, + ImageDB, + TestSetDB, + EvaluationDB, + DeploymentDB, + AppVariantDB, + VariantBaseDB, + AppEnvironmentDB, + AppEnvironmentRevisionDB, + EvaluatorConfigDB, + HumanEvaluationDB, + EvaluationScenarioDB, + HumanEvaluationScenarioDB, + ) + +from agenta_backend.models.db_models import ( + TemplateDB, + AppVariantRevisionsDB, +) + +models: List[Base] = [ + AppDB, + UserDB, + ProjectDB, + ImageDB, + TestSetDB, + TemplateDB, + AppVariantDB, + DeploymentDB, + EvaluationDB, + VariantBaseDB, + AppEnvironmentDB, + AppEnvironmentRevisionDB, + EvaluatorConfigDB, + HumanEvaluationDB, + EvaluationScenarioDB, + AppVariantRevisionsDB, + HumanEvaluationScenarioDB, +] + +if isCloudEE(): + models.extend([OrganizationDB, WorkspaceDB, APIKeyDB, InvitationDB, OrganizationMemberDB, ProjectMemberDB, WorkspaceMemberDB]) # type: ignore diff --git a/agenta-backend/agenta_backend/tests/variants_main_router/conftest.py b/agenta-backend/agenta_backend/tests/variants_main_router/conftest.py index 515be62617..991b839a60 100644 --- a/agenta-backend/agenta_backend/tests/variants_main_router/conftest.py +++ b/agenta-backend/agenta_backend/tests/variants_main_router/conftest.py @@ -3,7 +3,9 @@ import logging from datetime import datetime, timezone -from agenta_backend.models.db.postgres_engine import db_engine +import httpx +from sqlalchemy.future import select + from agenta_backend.models.shared_models import ConfigDB from agenta_backend.models.db_models import ( ProjectDB, @@ -20,8 +22,8 @@ ) from agenta_backend.resources.evaluators.evaluators import get_all_evaluators -import httpx -from sqlalchemy.future import select + +from agenta_backend.dbs.postgres.shared.engine import engine # Initialize logger @@ -41,7 +43,7 @@ async def get_first_user_object(): """Get the user object from the database or create a new one if not found.""" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(UserDB).filter_by(uid="0")) user = result.scalars().first() if user is None: @@ -57,7 +59,7 @@ async def get_first_user_object(): async def get_second_user_object(): """Create a second user object.""" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(UserDB).filter_by(uid="1")) user = result.scalars().first() if user is None: @@ -75,7 +77,7 @@ async def get_second_user_object(): async def get_first_user_app(get_first_user_object): user = await get_first_user_object - async with db_engine.get_session() as session: + async with engine.session() as session: project = ProjectDB(project_name="default", is_default=True) session.add(project) await session.commit() @@ -142,7 +144,7 @@ async def get_first_user_app(get_first_user_object): @pytest.fixture(scope="session") async def fetch_user(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(UserDB).filter_by(uid="0")) user = result.scalars().first() return user diff --git a/agenta-backend/agenta_backend/tests/variants_main_router/test_app_variant_router.py b/agenta-backend/agenta_backend/tests/variants_main_router/test_app_variant_router.py index 29a144f037..51c65980ce 100644 --- a/agenta-backend/agenta_backend/tests/variants_main_router/test_app_variant_router.py +++ b/agenta-backend/agenta_backend/tests/variants_main_router/test_app_variant_router.py @@ -5,9 +5,10 @@ import logging from bson import ObjectId +from sqlalchemy.future import select + from agenta_backend.routers import app_router from agenta_backend.services import db_manager -from agenta_backend.models.db.postgres_engine import db_engine from agenta_backend.models.shared_models import ConfigDB from agenta_backend.models.db_models import ( ProjectDB, @@ -18,7 +19,7 @@ AppVariantDB, ) -from sqlalchemy.future import select +from agenta_backend.dbs.postgres.shared.engine import engine # Initialize http client @@ -67,7 +68,7 @@ async def test_create_app_for_renaming(): @pytest.mark.asyncio async def test_update_app(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(AppDB).filter_by(app_name="app_test")) app = result.scalars().first() @@ -109,7 +110,7 @@ async def test_list_apps(): async def test_create_app_variant(get_first_user_object): user = await get_first_user_object - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppDB).filter_by(app_name="app_variant_test") ) @@ -171,7 +172,7 @@ async def test_create_app_variant(get_first_user_object): @pytest.mark.asyncio async def test_list_app_variants(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppDB).filter_by(app_name="app_variant_test") ) @@ -185,7 +186,7 @@ async def test_list_app_variants(): @pytest.mark.asyncio async def test_list_environments(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppDB).filter_by(app_name="app_variant_test") ) diff --git a/agenta-backend/agenta_backend/tests/variants_main_router/test_variant_evaluators_router.py b/agenta-backend/agenta_backend/tests/variants_main_router/test_variant_evaluators_router.py index e8fd22c5e2..6b58455c7d 100644 --- a/agenta-backend/agenta_backend/tests/variants_main_router/test_variant_evaluators_router.py +++ b/agenta-backend/agenta_backend/tests/variants_main_router/test_variant_evaluators_router.py @@ -3,7 +3,9 @@ import pytest import asyncio -from agenta_backend.models.db.postgres_engine import db_engine +from sqlalchemy.future import select +from sqlalchemy.orm import joinedload + from agenta_backend.models.api.evaluation_model import EvaluationStatusEnum from agenta_backend.models.db_models import ( AppDB, @@ -14,8 +16,7 @@ EvaluationScenarioDB, ) -from sqlalchemy.future import select -from sqlalchemy.orm import joinedload +from agenta_backend.dbs.postgres.shared.engine import engine # Initialize http client @@ -60,7 +61,7 @@ async def test_get_evaluators_endpoint(): async def test_create_auto_exact_match_evaluator_config( auto_exact_match_evaluator_config, ): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(AppDB).filter_by(app_name=APP_NAME)) app = result.scalars().first() @@ -80,7 +81,7 @@ async def test_create_auto_exact_match_evaluator_config( async def test_create_auto_similarity_match_evaluator_config( auto_similarity_match_evaluator_config, ): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(AppDB).filter_by(app_name=APP_NAME)) app = result.scalars().first() @@ -100,7 +101,7 @@ async def test_create_auto_similarity_match_evaluator_config( async def test_create_auto_regex_test_evaluator_config( auto_regex_test_evaluator_config, ): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(AppDB).filter_by(app_name=APP_NAME)) app = result.scalars().first() @@ -121,7 +122,7 @@ async def test_create_auto_regex_test_evaluator_config( async def test_create_auto_webhook_test_evaluator_config( auto_webhook_test_evaluator_config, ): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(AppDB).filter_by(app_name=APP_NAME)) app = result.scalars().first() @@ -141,7 +142,7 @@ async def test_create_auto_webhook_test_evaluator_config( async def test_create_auto_ai_critique_evaluator_config( auto_ai_critique_evaluator_config, ): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(AppDB).filter_by(app_name=APP_NAME)) app = result.scalars().first() @@ -159,7 +160,7 @@ async def test_create_auto_ai_critique_evaluator_config( @pytest.mark.asyncio async def test_get_evaluator_configs(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(AppDB).filter_by(app_name=APP_NAME)) app = result.scalars().first() @@ -203,7 +204,7 @@ async def wait_for_evaluation_to_finish(evaluation_id): async def create_evaluation_with_evaluator(evaluator_config_name): # Fetch app, app_variant and testset - async with db_engine.get_session() as session: + async with engine.session() as session: app_result = await session.execute(select(AppDB).filter_by(app_name=APP_NAME)) app = app_result.scalars().first() @@ -270,7 +271,7 @@ async def create_evaluation_with_evaluator(evaluator_config_name): @pytest.mark.asyncio async def test_create_evaluation_with_no_llm_keys(evaluators_requiring_llm_keys): - async with db_engine.get_session() as session: + async with engine.session() as session: app_result = await session.execute(select(AppDB).filter_by(app_name=APP_NAME)) app = app_result.scalars().first() @@ -352,7 +353,7 @@ async def test_create_evaluation_auto_ai_critique(): @pytest.mark.asyncio async def test_delete_evaluator_config(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(AppDB).filter_by(app_name=APP_NAME)) app = result.scalars().first() @@ -375,7 +376,7 @@ async def test_delete_evaluator_config(): @pytest.mark.asyncio async def test_evaluation_scenario_match_evaluation_testset_length(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(EvaluationDB).options(joinedload(EvaluationDB.testset)) ) @@ -396,7 +397,7 @@ async def test_remove_running_template_app_container(): # Connect to the Docker daemon client = docker.from_env() - async with db_engine.get_session() as session: + async with engine.session() as session: app_result = await session.execute(select(AppDB).filter_by(app_name=APP_NAME)) app = app_result.scalars().first() diff --git a/agenta-backend/agenta_backend/tests/variants_main_router/test_variant_testset_router.py b/agenta-backend/agenta_backend/tests/variants_main_router/test_variant_testset_router.py index 84f29650c7..f8b9e73dc7 100644 --- a/agenta-backend/agenta_backend/tests/variants_main_router/test_variant_testset_router.py +++ b/agenta-backend/agenta_backend/tests/variants_main_router/test_variant_testset_router.py @@ -4,12 +4,12 @@ import pytest from sqlalchemy.future import select -from agenta_backend.models.db.postgres_engine import db_engine from agenta_backend.models.db_models import ( AppDB, TestSetDB, ) +from agenta_backend.dbs.postgres.shared.engine import engine # Initialize http client test_client = httpx.AsyncClient() @@ -29,7 +29,7 @@ @pytest.mark.asyncio async def test_create_testset(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppDB).filter_by(app_name="app_variant_test") ) @@ -61,7 +61,7 @@ async def test_create_testset(): @pytest.mark.asyncio async def test_update_testset(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppDB).filter_by(app_name="app_variant_test") ) @@ -101,7 +101,7 @@ async def test_update_testset(): @pytest.mark.asyncio async def test_get_testsets(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppDB).filter_by(app_name="app_variant_test") ) @@ -117,7 +117,7 @@ async def test_get_testsets(): @pytest.mark.asyncio() async def test_get_testset(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppDB).filter_by(app_name="app_variant_test") ) @@ -139,7 +139,7 @@ async def test_get_testset(): @pytest.mark.asyncio async def test_delete_testsets(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppDB).filter_by(app_name="app_variant_test") ) diff --git a/agenta-backend/agenta_backend/tests/variants_main_router/test_variant_versioning_deployment.py b/agenta-backend/agenta_backend/tests/variants_main_router/test_variant_versioning_deployment.py index 43d9c5ac0e..6aa02dc06f 100644 --- a/agenta-backend/agenta_backend/tests/variants_main_router/test_variant_versioning_deployment.py +++ b/agenta-backend/agenta_backend/tests/variants_main_router/test_variant_versioning_deployment.py @@ -5,12 +5,13 @@ from sqlalchemy.future import select -from agenta_backend.models.db.postgres_engine import db_engine from agenta_backend.models.db_models import ( AppDB, AppVariantDB, ) +from agenta_backend.dbs.postgres.shared.engine import engine + # Initialize http client test_client = httpx.AsyncClient() @@ -28,7 +29,7 @@ @pytest.mark.asyncio async def test_update_app_variant_parameters(app_variant_parameters_updated): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppDB).filter_by(app_name="evaluation_in_backend") ) @@ -55,7 +56,7 @@ async def test_update_app_variant_parameters(app_variant_parameters_updated): @pytest.mark.asyncio async def test_deploy_to_environment(deploy_to_environment_payload): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppDB).filter_by(app_name="evaluation_in_backend") ) diff --git a/agenta-backend/agenta_backend/tests/variants_user_profile_router/test_user_profile.py b/agenta-backend/agenta_backend/tests/variants_user_profile_router/test_user_profile.py index d7fd237994..fac63fb1cf 100644 --- a/agenta-backend/agenta_backend/tests/variants_user_profile_router/test_user_profile.py +++ b/agenta-backend/agenta_backend/tests/variants_user_profile_router/test_user_profile.py @@ -6,9 +6,10 @@ from sqlalchemy.future import select from agenta_backend.models.db_models import UserDB -from agenta_backend.models.db.postgres_engine import db_engine from agenta_backend.models.api.user_models import User +from agenta_backend.dbs.postgres.shared.engine import engine + # Initialize http client test_client = httpx.AsyncClient() @@ -24,7 +25,7 @@ @pytest.mark.asyncio async def test_fetch_user_profile_without_user_id(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(UserDB).filter_by(uid="0")) user_db = result.scalars().first() if not user_db: @@ -50,7 +51,7 @@ async def test_fetch_user_profile_without_user_id(): @pytest.mark.asyncio async def test_fetch_user_profile_with_valid_user_id(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(UserDB).filter_by(uid="0")) user_db = result.scalars().first() if not user_db: From 8b77913884e8b3594abf77f41f6cc97a4d0ef63b Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 11 Oct 2024 13:45:14 +0200 Subject: [PATCH 058/305] fix query() --- .../apis/fastapi/observability/router.py | 3 +- .../dbs/postgres/observability/dao.py | 109 ++++-------------- .../dbs/postgres/observability/dbes.py | 3 +- .../dbs/postgres/shared/dbas.py | 2 + 4 files changed, 25 insertions(+), 92 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py index 50df5f308f..f5737fe7e8 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py @@ -260,11 +260,12 @@ async def otlp_collect_traces( """ project_id = request.headers.get("AG-PROJECT-ID") + app_id = request.headers.get("AG-APP-ID") otel_span_dtos = parse_otlp_stream(await request.body()) span_dtos = [ - parse_from_otel_span_dto(project_id, otel_span_dto) + parse_from_otel_span_dto(project_id or app_id, otel_span_dto) for otel_span_dto in otel_span_dtos ] diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py index de0c826867..00cd12a7ab 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py @@ -1,10 +1,7 @@ from typing import Optional, List -from sqlalchemy import and_, or_, not_, Column - +from sqlalchemy import and_, or_, not_, distinct, Column from sqlalchemy.future import select -from sqlalchemy import distinct - from agenta_backend.dbs.postgres.shared.engine import engine from agenta_backend.dbs.postgres.observability.dbes import InvocationSpanDBE @@ -30,25 +27,6 @@ ) -## TODO -# - [ ] Implement Observability Query -# - [x] Implement grouping node/tree/link/ref -# - [x] Implement pagination page/size -# - [x] Implement filtering -# - [ ] Implement sorting -# - [ ] Optimize filtering (and schema/queries in general) -# - [ ] Implement cross-table querying (later ?) -## ---- - -## TODO -# - [ ] Implement Observability Mutation -# - [x] Implement create one/many -# - [x] Implement read one/many -# - [x] Implement delete one/many -# - [ ] Implement update one/many (immutable ?) -## ---- - - class ObservabilityDAO(ObservabilityDAOInterface): def __init__(self): pass @@ -62,131 +40,84 @@ async def query( ) -> List[SpanDTO]: async with engine.session() as session: - - # opts = { - # "dialect": dialect(), - # "compile_kwargs": {"literal_binds": True}, - # } - # BASE (SUB-)QUERY - # print("------------------------------------------------------") - # print("BASE (SUB-)QUERY") query = select(InvocationSpanDBE) # ---------------- - # print("...") - # print(query.statement.compile(**opts)) + + # WINDOWING + windowing_column: Optional[Column] = InvocationSpanDBE.created_at + # --------- # GROUPING - # print("------------------------------------------------------") - # print("GROUPING") grouping = query_dto.grouping grouping_column: Optional[Column] = None # -------- if grouping and grouping.focus.value != "node": - # print("GROUPING FOCUS:", grouping.focus.value) grouping_column = getattr( InvocationSpanDBE, grouping.focus.value + "_id" ) - query = select(distinct(grouping_column)) + query = select( + distinct(grouping_column).label("grouping_key"), + windowing_column, + ) # -------- - # print("...") - # print(query.statement.compile(**opts)) # SCOPING - # print("------------------------------------------------------") - # print("SCOPING") - # ------- query = query.filter_by(project_id=project_id) # ------- - # print("...") - # print(query.statement.compile(**opts)) # WINDOWING - # print("------------------------------------------------------") - # print("WINDOWING") windowing = query_dto.windowing - windowing_column: Optional[Column] = InvocationSpanDBE.created_at # --------- if windowing: - # print("WINDOWING EARLIEST:", windowing.earliest) - # print("WINDOWING LATEST: ", windowing.latest) if windowing.earliest: query = query.filter(windowing_column >= windowing.earliest) if windowing.latest: query = query.filter(windowing_column <= windowing.latest) # --------- - # print("...") - # print(query.statement.compile(**opts)) # FILTERING - # print("------------------------------------------------------") - # print("FILTERING") filtering = query_dto.filtering # --------- if filtering: - # print("FILTERING OPERATOR: ", filtering.operator) - # print("FILTERING CONDITIONS:", filtering.conditions) operator = filtering.operator conditions = filtering.conditions query = query.filter(_combine(operator, _filters(conditions))) # --------- - # print("...") - # print(query.statement.compile(**opts)) # SORTING - # print("------------------------------------------------------") - # print("SORTING") if grouping and grouping_column: query = query.order_by(grouping_column) query = query.order_by(windowing_column.desc()) - # ------- - # print("...") - # print(query.statement.compile(**opts)) # PAGINATION - # print("------------------------------------------------------") - # print("PAGINATION") pagination = query_dto.pagination # ---------- if pagination: - # print("PAGINATION PAGE:", pagination.page) - # print("PAGINATION SIZE:", pagination.size) limit = pagination.size offset = (pagination.page - 1) * pagination.size query = query.limit(limit).offset(offset) # ---------- - # print("...") - # print(query.statement.compile(**opts)) # GROUPING - # print("------------------------------------------------------") - # print("GROUPING") if grouping and grouping_column: - print("GROUPING FOCUS:", grouping.focus.value) - subquery = query # .subquery() + subquery = query.subquery() query = select(InvocationSpanDBE) - query = query.filter(grouping_column.in_(subquery)) + query = query.filter( + grouping_column.in_(select(subquery.c["grouping_key"])) + ) # -------- - # print("...") - # print(query.statement.compile(**opts)) # QUERY EXECUTION - # print("------------------------------------------------------") - # rint("QUERY EXECUTION") - spans = (await session.execute(query)).all() + spans = (await session.execute(query)).scalars().all() # --------------- - # FORMATTING - # formatting = query_dto.formatting - # -------- - - # return [] return [map_span_dbe_to_dto(span) for span in spans] async def create_one( @@ -198,7 +129,7 @@ async def create_one( async with engine.session() as session: session.add(span_dbe) - session.commit() + await session.commit() async def create_many( self, @@ -211,7 +142,7 @@ async def create_many( for span_dbe in span_dbes: session.add(span_dbe) - session.commit() + await session.commit() async def read_one( self, @@ -229,7 +160,7 @@ async def read_one( node_id=node_id, ) - span_dbe = (await session.execute(query)).one_or_none() + span_dbe = (await session.execute(query)).scalars().one_or_none() span_dto = None if span_dbe and to_dto: @@ -254,7 +185,7 @@ async def read_many( query = query.filter(InvocationSpanDBE.node_id.in_(node_ids)) - span_dbes = (await session.execute(query)).all() + span_dbes = (await session.execute(query)).scalars().all() span_dtos = [] if span_dbes and to_dto: @@ -279,7 +210,7 @@ async def delete_one( if span_dbe: async with engine.session() as session: session.delete(span_dbe) - session.commit() + await session.commit() async def delete_many( self, @@ -298,7 +229,7 @@ async def delete_many( for span in span_dbes: session.delete(span) - session.commit() + await session.commit() def _combine( diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py index 064026c09b..1c6100f714 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py @@ -1,8 +1,7 @@ from sqlalchemy import PrimaryKeyConstraint, Index -from agenta_backend.dbs.postgres.shared.base import Base - +from agenta_backend.dbs.postgres.shared.base import Base from agenta_backend.dbs.postgres.observability.dbas import SpanDBA diff --git a/agenta-backend/agenta_backend/dbs/postgres/shared/dbas.py b/agenta-backend/agenta_backend/dbs/postgres/shared/dbas.py index 3385d1d164..9810bf18f0 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/shared/dbas.py +++ b/agenta-backend/agenta_backend/dbs/postgres/shared/dbas.py @@ -2,6 +2,8 @@ from sqlalchemy import Column, UUID, TIMESTAMP, func +from agenta_backend.dbs.postgres.shared.base import Base + ## --- DISPLAY --- ## From cd45b70b1464b687427fa8718797923f7417ff47 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 11 Oct 2024 13:58:24 +0200 Subject: [PATCH 059/305] Add check timeout and switch to head, add app_id to headers, Fix hardcoded values --- agenta-cli/agenta/sdk/tracing/tracing.py | 25 +++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/tracing.py b/agenta-cli/agenta/sdk/tracing/tracing.py index 61681b2bc3..1a361db144 100644 --- a/agenta-cli/agenta/sdk/tracing/tracing.py +++ b/agenta-cli/agenta/sdk/tracing/tracing.py @@ -1,6 +1,6 @@ from typing import Optional, Any, Dict -from httpx import get as check +from httpx import head as check from opentelemetry.trace import ( get_current_span, @@ -34,8 +34,7 @@ def __init__( url: str, ) -> None: # ENDPOINT (OTLP) - # self.otlp_url = url - self.otlp_url = "http://127.0.0.1:8000/api/observability/v1/otlp/traces" + self.otlp_url = url # AUTHENTICATION (OTLP) self.project_id: Optional[str] = None # AUTHORIZATION (OTLP) @@ -62,17 +61,20 @@ def configure( app_id: Optional[str] = None, ): # AUTHENTICATION (OTLP) - # self.project_id = project_id - self.project_id = "f7943e42-ec69-498e-bf58-8db034b9286e" + self.project_id = project_id # "f7943e42-ec69-498e-bf58-8db034b9286e" + self.app_id = app_id # AUTHORIZATION (OTLP) self.api_key = api_key # HEADERS (OTLP) - self.headers = {"AG-PROJECT-ID": self.project_id} + self.headers = {} + if project_id: + self.headers.update(**{"AG-PROJECT-ID": project_id}) + if app_id: + self.headers.update(**{"AG-APP-ID": app_id}) if api_key: - # self.headers.update(**{"Authorization": f"Api-Key {self.api_key}"}) self.headers.update(**{"Authorization": self.api_key}) # REFERENCES - self.references = {"application.id": app_id} + self.references = {"application_id": app_id} # TRACER PROVIDER self.tracer_provider = TracerProvider( @@ -94,7 +96,7 @@ def configure( try: log.info(f"Connecting to the remote trace receiver at {self.otlp_url}...") - check(self.otlp_url, headers=self.headers) + check(self.otlp_url, headers=self.headers, timeout=0.001) log.info(f"Connection established.") @@ -104,7 +106,8 @@ def configure( ) self.tracer_provider.add_span_processor(_otlp) - except: + except Exception as e: + print(e) log.warning(f"Connection failed.") log.warning( f"Warning: Your traces will not be exported since {self.otlp_url} is unreachable." @@ -159,7 +162,7 @@ def get_inline_trace( if not otel_spans: return {} - inline_trace = parse_inline_trace(self.project_id, otel_spans) + inline_trace = parse_inline_trace(self.project_id or self.app_id, otel_spans) return inline_trace From cd1596fbdce54427c7f612331adc4ba590c977e5 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 11 Oct 2024 14:05:18 +0200 Subject: [PATCH 060/305] apply black --- .../apis/fastapi/observability/opentelemetry/otlp.py | 1 - .../agenta_backend/apis/fastapi/observability/utils.py | 5 ----- .../agenta_backend/core/observability/interfaces.py | 1 - agenta-backend/agenta_backend/core/observability/service.py | 6 ------ agenta-backend/agenta_backend/core/observability/utils.py | 4 ---- .../agenta_backend/dbs/postgres/observability/dao.py | 1 - agenta-backend/agenta_backend/tests/engine.py | 1 - 7 files changed, 19 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py index 3060e20639..7621606fbe 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py @@ -64,7 +64,6 @@ def _parse_timestamp( def parse_otlp_stream( otlp_stream: bytes, ) -> List[OTelSpanDTO]: - # otlp_stream = b"\n\x94$\n6\n\x18\n\x0cservice.name\x12\x08\n\x06agenta\n\x1a\n\x0fservice.version\x12\x07\n\x050.1.0\x12\x95\x1d\n1\n'opentelemetry.instrumentation.openai.v1\x12\x060.30.0\x12\xa3\x04\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\xd9N#YhoL\n\"\x08\x1dh\x04X\xa0\xf4\xf8P*\x11openai.embeddings0\x039\x00\x90\xa2\tWP\xfa\x17A\x08\xb2 WP\xfa\x17J\x1f\n\x10llm.request.type\x12\x0b\n\tembeddingJ9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307J\x19\n\rgen_ai.system\x12\x08\n\x06OpenAIJ0\n\x14gen_ai.request.model\x12\x18\n\x16text-embedding-ada-002J\x15\n\x0bllm.headers\x12\x06\n\x04NoneJ\x16\n\x10llm.is_streaming\x12\x02\x10\x00JI\n\x17gen_ai.prompt.0.content\x12.\n,Movies about witches in the genre of comedy.J6\n\x16gen_ai.openai.api_base\x12\x1c\n\x1ahttps://api.openai.com/v1/J1\n\x15gen_ai.response.model\x12\x18\n\x16text-embedding-ada-002J\x1c\n\x16llm.usage.total_tokens\x12\x02\x18\tJ \n\x1agen_ai.usage.prompt_tokens\x12\x02\x18\tz\x00\x85\x01\x00\x01\x00\x00\x12\xb9\x18\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\x1b\x05'\x8e\xb4\xaar\xda\"\x08\xac\t\x88\x86\x96\x87\xce\xb4*\x0bopenai.chat0\x039\x10h\x16&WP\xfa\x17A\x10{0qWP\xfa\x17J\x1a\n\x10llm.request.type\x12\x06\n\x04chatJ9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307J\x19\n\rgen_ai.system\x12\x08\n\x06OpenAIJ'\n\x14gen_ai.request.model\x12\x0f\n\rgpt-3.5-turboJ'\n\x1agen_ai.request.temperature\x12\t!\x9a\x99\x99\x99\x99\x99\xe9?J\x15\n\x0bllm.headers\x12\x06\n\x04NoneJ\x16\n\x10llm.is_streaming\x12\x02\x10\x00J6\n\x16gen_ai.openai.api_base\x12\x1c\n\x1ahttps://api.openai.com/v1/J \n\x14gen_ai.prompt.0.role\x12\x08\n\x06systemJ\x84\x11\n\x17gen_ai.prompt.0.content\x12\xe8\x10\n\xe5\x10Given the following list of suggested movies:\n\nThe Craft (1996) in ['Drama', 'Fantasy', 'Horror']: A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.\nWicked Stepmother (1989) in ['Comedy', 'Fantasy']: A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...\nOz the Great and Powerful (2013) in ['Adventure', 'Family', 'Fantasy']: A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.\nInto the Woods (2014) in ['Adventure', 'Fantasy', 'Musical']: A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.\nSnow White: A Tale of Terror (1997) in ['Fantasy', 'Horror']: In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.\nBedknobs and Broomsticks (1971) in ['Adventure', 'Family', 'Fantasy']: An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.\nMy Neighbor Totoro (1988) in ['Animation', 'Family', 'Fantasy']: When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.\nHocus Pocus (1993) in ['Comedy', 'Family', 'Fantasy']: After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.\nPractical Magic (1998) in ['Comedy', 'Fantasy', 'Romance']: The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love.J\x1e\n\x14gen_ai.prompt.1.role\x12\x06\n\x04userJ]\n\x17gen_ai.prompt.1.content\x12B\n@Provide a list of 3 movies about witches in the genre of comedy.J-\n\x15gen_ai.response.model\x12\x14\n\x12gpt-3.5-turbo-0125J\x1d\n\x16llm.usage.total_tokens\x12\x03\x18\xc2\x04J$\n\x1egen_ai.usage.completion_tokens\x12\x02\x18*J!\n\x1agen_ai.usage.prompt_tokens\x12\x03\x18\x98\x04J+\n!gen_ai.completion.0.finish_reason\x12\x06\n\x04stopJ'\n\x18gen_ai.completion.0.role\x12\x0b\n\tassistantJ\xa7\x01\n\x1bgen_ai.completion.0.content\x12\x87\x01\n\x84\x01Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989)\n2. Hocus Pocus (1993)\n3. Practical Magic (1998)z\x00\x85\x01\x00\x01\x00\x00\x12\xc1\x06\n\x0f\n\ragenta.tracer\x12\x86\x01\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\x1dh\x04X\xa0\xf4\xf8P\"\x08\xac\xb2\x8e\xff\xb2\xe8\xea\x14*\x05embed0\x019@\xc1\x9f\tWP\xfa\x17A\xb0H- WP\xfa\x17J9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307z\x02\x18\x01\x85\x01\x00\x01\x00\x00\x12\x87\x01\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\xcaD;\xdf \xbb\x13|\"\x08\xac\xb2\x8e\xff\xb2\xe8\xea\x14*\x06search0\x019`\xf0/ WP\xfa\x17A\xc0\x16\x03&WP\xfa\x17J9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307z\x02\x18\x01\x85\x01\x00\x01\x00\x00\x12\x8a\x01\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\xac\xb2\x8e\xff\xb2\xe8\xea\x14\"\x08\x9f\xad5\xa6\\\xc9\xf9\xeb*\tretriever0\x019\xc8o\x9b\tWP\xfa\x17A\xd8w\x07&WP\xfa\x17J9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307z\x02\x18\x01\x85\x01\x00\x01\x00\x00\x12\x85\x01\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\xac\t\x88\x86\x96\x87\xce\xb4\"\x08\xf8T\xa3\xe9\x07;\x90\x86*\x04chat0\x019\x08\xbd\x0f&WP\xfa\x17Ax\xfe:qWP\xfa\x17J9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307z\x02\x18\x01\x85\x01\x00\x01\x00\x00\x12\x89\x01\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\xf8T\xa3\xe9\x07;\x90\x86\"\x08\x9f\xad5\xa6\\\xc9\xf9\xeb*\x08reporter0\x019\xb8\x17\n&WP\xfa\x17A0\x81 Optional[FilteringDTO]: - # Parse JSON filtering filtering_json_dto = None if filtering: @@ -145,7 +144,6 @@ def _parse_grouping( # focus: Optional[str] = None, ) -> Optional[GroupingDTO]: - # Parse JSON grouping grouping_json_dto = None if grouping: @@ -184,7 +182,6 @@ def _parse_pagination( page: Optional[int] = None, size: Optional[int] = None, ) -> Optional[PaginationDTO]: - # Parse JSON pagination pagination_json_dto = None if pagination: @@ -316,7 +313,6 @@ def _unmarshal_attributes( level = unmarshalled for i, part in enumerate(keys[:-1]): - if part.isdigit(): part = int(part) @@ -503,7 +499,6 @@ def _parse_from_links( def _parse_from_attributes( otel_span_dto: OTelSpanDTO, ) -> Tuple[dict, dict, dict, dict, dict]: - # DATA _data = _get_attributes(otel_span_dto.attributes, "data") diff --git a/agenta-backend/agenta_backend/core/observability/interfaces.py b/agenta-backend/agenta_backend/core/observability/interfaces.py index 202b5f37a6..d6ff244f8d 100644 --- a/agenta-backend/agenta_backend/core/observability/interfaces.py +++ b/agenta-backend/agenta_backend/core/observability/interfaces.py @@ -5,7 +5,6 @@ class ObservabilityDAOInterface: - def __init__(self): raise NotImplementedError diff --git a/agenta-backend/agenta_backend/core/observability/service.py b/agenta-backend/agenta_backend/core/observability/service.py index a0352641f4..6a2d024d8d 100644 --- a/agenta-backend/agenta_backend/core/observability/service.py +++ b/agenta-backend/agenta_backend/core/observability/service.py @@ -13,7 +13,6 @@ class ObservabilityService: - def __init__( self, observability_dao: ObservabilityDAOInterface, @@ -27,7 +26,6 @@ async def query( # query_dto: QueryDTO, ) -> List[SpanDTO]: - span_dtos = await self.observability_dao.query( project_id=project_id, query_dto=query_dto, @@ -53,7 +51,6 @@ async def ingest( *, span_dtos: List[SpanCreateDTO], ) -> SpanDTO: - span_idx = parse_span_dtos_to_span_idx(span_dtos) span_id_tree = parse_span_idx_to_span_id_tree(span_idx) @@ -72,7 +69,6 @@ async def create( span_dto: Optional[SpanCreateDTO] = None, span_dtos: Optional[List[SpanCreateDTO]] = None, ) -> SpanDTO: - if span_dto: return await self.observability_dao.create_one( span_dto=span_dto, @@ -91,7 +87,6 @@ async def read( node_id: Optional[str] = None, node_ids: Optional[List[str]] = None, ) -> SpanDTO: - if node_id: return await self.observability_dao.read_one( project_id=project_id, @@ -112,7 +107,6 @@ async def delete( node_id: Optional[str] = None, node_ids: Optional[List[str]] = None, ): - if node_id: return await self.observability_dao.delete_one( project_id=project_id, diff --git a/agenta-backend/agenta_backend/core/observability/utils.py b/agenta-backend/agenta_backend/core/observability/utils.py index 92a82a35d6..05af93c3de 100644 --- a/agenta-backend/agenta_backend/core/observability/utils.py +++ b/agenta-backend/agenta_backend/core/observability/utils.py @@ -6,7 +6,6 @@ def parse_span_dtos_to_span_idx( span_dtos: List[SpanCreateDTO], ) -> Dict[str, SpanCreateDTO]: - span_idx = {span_dto.node.id: span_dto for span_dto in span_dtos} return span_idx @@ -15,7 +14,6 @@ def parse_span_dtos_to_span_idx( def parse_span_idx_to_span_id_tree( span_idx: Dict[str, SpanCreateDTO], ) -> OrderedDict: - span_id_tree = OrderedDict() index = {} @@ -37,7 +35,6 @@ def cumulate_costs( spans_id_tree: OrderedDict, spans_idx: Dict[str, SpanCreateDTO], ) -> None: - def _get_unit(span: SpanCreateDTO): if span.metrics is not None: return span.metrics.get("unit.costs.total", 0.0) @@ -67,7 +64,6 @@ def cumulate_tokens( spans_id_tree: OrderedDict, spans_idx: Dict[str, dict], ) -> None: - def _get_unit(span: SpanCreateDTO): _tokens = { "prompt": 0.0, diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py index 00cd12a7ab..857930d0c6 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py @@ -38,7 +38,6 @@ async def query( # query_dto: QueryDTO, ) -> List[SpanDTO]: - async with engine.session() as session: # BASE (SUB-)QUERY query = select(InvocationSpanDBE) diff --git a/agenta-backend/agenta_backend/tests/engine.py b/agenta-backend/agenta_backend/tests/engine.py index 7388e96084..76fd1407f1 100644 --- a/agenta-backend/agenta_backend/tests/engine.py +++ b/agenta-backend/agenta_backend/tests/engine.py @@ -3,7 +3,6 @@ class TestDBEngine(Engine): - async def remove_db(self): async with self.async_engine.begin() as conn: for model in models: From 3aac5aad50b4ada9ff56b69329fd973e072f03af Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 11 Oct 2024 14:56:38 +0200 Subject: [PATCH 061/305] add backward compatibility --- .../apis/fastapi/observability/router.py | 20 +++++++++++++++++-- agenta-backend/agenta_backend/main.py | 10 +++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py index f5737fe7e8..71e6a5fd5c 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Union +from typing import Dict, List, Union, Optional, Callable from fastapi import ( APIRouter, @@ -46,9 +46,12 @@ class ObservabilityRouter: def __init__( self, observability_service: ObservabilityService, + observability_legacy_receiver: Optional[Callable] = None, ): self.service = observability_service + self.legacy_receiver = observability_legacy_receiver + self.router = APIRouter() ### STATUS @@ -259,10 +262,18 @@ async def otlp_collect_traces( Collect traces via OTLP. """ + otlp_stream = await request.body() + + ### LEGACY ### + res = None + if self.legacy_receiver: + res = self.legacy_receiver(otlp_stream) + ### LEGACY ### + project_id = request.headers.get("AG-PROJECT-ID") app_id = request.headers.get("AG-APP-ID") - otel_span_dtos = parse_otlp_stream(await request.body()) + otel_span_dtos = parse_otlp_stream(otlp_stream) span_dtos = [ parse_from_otel_span_dto(project_id or app_id, otel_span_dto) @@ -271,4 +282,9 @@ async def otlp_collect_traces( background_tasks.add_task(self.service.ingest, span_dtos=span_dtos) + ### LEGACY ### + if res: + return res + ### LEGACY ### + return CollectStatusResponse(version=self.VERSION, status="processing") diff --git a/agenta-backend/agenta_backend/main.py b/agenta-backend/agenta_backend/main.py index e8962a30b5..ed9c86145c 100644 --- a/agenta-backend/agenta_backend/main.py +++ b/agenta-backend/agenta_backend/main.py @@ -116,7 +116,15 @@ async def lifespan(application: FastAPI, cache=True): app.include_router(bases_router.router, prefix="/bases", tags=["Bases"]) app.include_router(configs_router.router, prefix="/configs", tags=["Configs"]) -observability = ObservabilityRouter(ObservabilityService(ObservabilityDAO())) + +observability_legacy_receiver = None +if isCloudEE(): + observability_legacy_receiver = cloud.observability_legacy_receiver + +observability = ObservabilityRouter( + ObservabilityService(ObservabilityDAO()), + observability_legacy_receiver=observability_legacy_receiver, +) app.include_router(router=observability.router, prefix="/observability/v1") From ea1a1083f5686b7c3270c1474f76a03c1546dcee Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 11 Oct 2024 15:04:44 +0200 Subject: [PATCH 062/305] Fix timeout --- agenta-cli/agenta/sdk/tracing/tracing.py | 4 ++-- agenta-cli/pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/tracing.py b/agenta-cli/agenta/sdk/tracing/tracing.py index 1a361db144..ecbb0195ef 100644 --- a/agenta-cli/agenta/sdk/tracing/tracing.py +++ b/agenta-cli/agenta/sdk/tracing/tracing.py @@ -1,6 +1,6 @@ from typing import Optional, Any, Dict -from httpx import head as check +from httpx import get as check from opentelemetry.trace import ( get_current_span, @@ -96,7 +96,7 @@ def configure( try: log.info(f"Connecting to the remote trace receiver at {self.otlp_url}...") - check(self.otlp_url, headers=self.headers, timeout=0.001) + check(self.otlp_url, headers=self.headers, timeout=1) log.info(f"Connection established.") diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index 9ce622bba5..d3ec15a803 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.25.3a1" +version = "0.25.3a2" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] From a5b175737a085416902caa764554d5863dc148c4 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Fri, 11 Oct 2024 15:32:17 +0200 Subject: [PATCH 063/305] fix(frontend): exported trace types --- agenta-web/src/services/observability/types/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agenta-web/src/services/observability/types/index.ts b/agenta-web/src/services/observability/types/index.ts index dd6f16032e..ff9f42f069 100644 --- a/agenta-web/src/services/observability/types/index.ts +++ b/agenta-web/src/services/observability/types/index.ts @@ -12,7 +12,7 @@ interface RootContextDTO { id: string } -interface AgentaTreeDTO { +export interface AgentaTreeDTO { tree: TreeContextDTO nodes: Record } @@ -21,9 +21,9 @@ interface TreeContextDTO { id: string } -interface AgentaNodeDTO extends NodeDTO {} +export interface AgentaNodeDTO extends NodeDTO {} -interface NodeDTO { +export interface NodeDTO { scope: ProjectScopeDTO lifecycle: LifecycleDTO root: RootContextDTO From 7484010b8b86065f6672c7b3ffc75cc2df8a942d Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Fri, 11 Oct 2024 15:32:55 +0200 Subject: [PATCH 064/305] feat(frontend): created trace hook to mock api request --- agenta-web/src/lib/hooks/useTraces.ts | 29 +++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 agenta-web/src/lib/hooks/useTraces.ts diff --git a/agenta-web/src/lib/hooks/useTraces.ts b/agenta-web/src/lib/hooks/useTraces.ts new file mode 100644 index 0000000000..68471dbef2 --- /dev/null +++ b/agenta-web/src/lib/hooks/useTraces.ts @@ -0,0 +1,29 @@ +import {fetchAllTraces} from "@/services/observability/api" +import {AgentaRootsResponse} from "@/services/observability/types" +import {useEffect, useState} from "react" + +export const useTraces = () => { + const [traces, setTraces] = useState(null) + const [isLoadingTraces, setIsLoadingTraces] = useState(true) + + const fetchTraces = async () => { + try { + setIsLoadingTraces(true) + const data = await fetchAllTraces() + setTraces(data) + } catch (error) { + console.error(error) + } finally { + setIsLoadingTraces(false) + } + } + + useEffect(() => { + fetchTraces() + }, []) + + return { + traces: traces?.roots, + isLoadingTraces, + } +} From 472af129dbf825bd5fdb15814a9d1856ffc9a585 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sun, 13 Oct 2024 14:27:40 +0200 Subject: [PATCH 065/305] feat(frontend): implement TraceTree view --- .../pages/observability/drawer/TraceTree.tsx | 304 ++++-------------- 1 file changed, 69 insertions(+), 235 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx index d934673436..e1e7ae6115 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx @@ -1,19 +1,17 @@ +import {formatLatency} from "@/lib/helpers/formatters" import {JSSTheme} from "@/lib/Types" -import { - Coins, - Database, - Download, - FileArrowDown, - ListChecks, - PlusCircle, - Sparkle, - Timer, - TreeStructure, -} from "@phosphor-icons/react" +import {AgentaNodeDTO} from "@/services/observability/types" +import {Coins, PlusCircle, Timer, TreeStructure} from "@phosphor-icons/react" import {Avatar, Space, Tree, TreeDataNode, Typography} from "antd" import React from "react" import {createUseStyles} from "react-jss" +interface TraceTreeProps { + activeTrace: Record + selectedKeys: string[] + onSelect: (keys: React.Key[]) => void +} + const useStyles = createUseStyles((theme: JSSTheme) => ({ tree: { overflowY: "auto", @@ -54,243 +52,79 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ }, })) -const TraceTree = () => { +const TreeContent = ({nodeValue}: {nodeValue: AgentaNodeDTO}) => { + const {node, time} = nodeValue const classes = useStyles() - const treeData: TreeDataNode[] = [ - { - title: ( -
- } - /> -
- Root trace - -
- - 983ms -
-
- - $0.002 -
-
- - 72 -
-
+ return ( +
+ } + /> +
+ {node.name} + +
+ + {formatLatency(time?.span)}
-
- ), - key: "0-0", - children: [ - { - title: ( -
- } - /> -
- - Retrieval - - -
- - 983ms -
-
- - $0.002 -
-
- - 72 -
-
-
-
- ), - key: "0-0-1-0", - }, - { - title: ( -
- } - /> -
- - vector-store - - -
- - 983ms -
-
- - $0.002 -
-
- - 72 -
-
-
-
- ), - key: "0-0-1-1", - }, - { - title: ( -
- } - /> -
- - prompt-embedding - - -
- - 983ms -
-
- - $0.002 -
-
- - 72 -
-
-
-
- ), - key: "0-0-1-2", - }, - { - title: ( -
- } - /> -
- - context-encoding - - -
- - 983ms -
-
- - $0.002 -
-
- - 72 -
-
-
-
- ), - key: "0-0-1-3", - }, - ], - }, - { - title: ( -
- } - /> -
- - fetch-prompt-from-langfuse dendeinde deindeindeiw dwidnwndienw - - -
- - 983ms -
-
- - $0.002 -
-
- - 72 -
-
+
+ + $0.002
-
- ), - key: "0-1", - }, - { - title: ( -
- } - /> -
- generation - -
- - 983ms -
-
- - $0.002 -
-
- - 72 -
-
+
+ + 72
-
- ), - key: "0-2", - }, - ] + +
+
+ ) +} + +const buildTreeData = (nodes: Record): TreeDataNode[] => { + const createTreeNode = (node: AgentaNodeDTO): TreeDataNode => { + const hasChildren = node.nodes && Object.keys(node.nodes).length > 0 + + return { + key: node.node.id, + title: , + children: hasChildren + ? buildTreeData(node.nodes as Record) + : undefined, + } + } + + return Object.entries(nodes).flatMap(([_, value]) => { + if (Array.isArray(value)) { + return value.map((item, index) => + createTreeNode({ + ...item, + node: {...item.node, name: `${item.node.name}[${index}]`}, + }), + ) + } else { + return createTreeNode(value) + } + }) +} + +const TraceTree = ({activeTrace, selectedKeys, onSelect}: TraceTreeProps) => { + const classes = useStyles() + const treeData = buildTreeData(activeTrace) return ( ) } From 06523c67507f79d2e68e130276178e24dd00f5e8 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Mon, 14 Oct 2024 07:45:06 +0200 Subject: [PATCH 066/305] feat(frontend): added stopPropagation flag to CopyButton component --- agenta-web/src/components/CopyButton/CopyButton.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/agenta-web/src/components/CopyButton/CopyButton.tsx b/agenta-web/src/components/CopyButton/CopyButton.tsx index e3fcf6e509..5fd6ad34af 100644 --- a/agenta-web/src/components/CopyButton/CopyButton.tsx +++ b/agenta-web/src/components/CopyButton/CopyButton.tsx @@ -7,12 +7,14 @@ type Props = { text: string buttonText?: string | null icon?: boolean + stopPropagation?: boolean } const CopyButton: React.FC> = ({ text, buttonText = "Copy", icon = false, + stopPropagation = false, ...props }) => { const [buttonIcon, setButtonIcon] = useState() @@ -21,7 +23,10 @@ const CopyButton: React.FC> = ({
- + {/*
Evaluation - - - -
+
*/} ) } From 5e4bbb1e1bb20f53203ca637bf6b15ab632d20e7 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Mon, 14 Oct 2024 08:41:30 +0200 Subject: [PATCH 068/305] feat(frontend): improved trace tree and header ui logic --- .../observability/drawer/TraceHeader.tsx | 32 +++++++------- .../pages/observability/drawer/TraceTree.tsx | 27 +++++++++--- .../apps/[app_id]/observability/index.tsx | 44 +++++++++++++++---- 3 files changed, 73 insertions(+), 30 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx b/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx index af9067def9..4d2f7f9392 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx @@ -3,7 +3,7 @@ import {AgentaRootsDTO} from "@/services/observability/types" import {DeleteOutlined} from "@ant-design/icons" import {CaretDown, CaretUp} from "@phosphor-icons/react" import {Button, Space, Tag, Typography} from "antd" -import React, {useMemo} from "react" +import React, {useMemo, useCallback} from "react" import {createUseStyles} from "react-jss" interface TraceHeaderProps { @@ -29,44 +29,42 @@ const TraceHeader = ({ }: TraceHeaderProps) => { const classes = useStyles() - const activeTraceIndex = useMemo(() => { - return traces?.findIndex((item) => item.root.id === selectedTraceId) - }, [selectedTraceId, traces]) + const activeTraceIndex = useMemo( + () => traces?.findIndex((item) => item.root.id === selectedTraceId), + [selectedTraceId, traces], + ) - const handleNextTrace = () => { - if (traces && activeTraceIndex !== undefined && activeTraceIndex < traces.length - 1) { + const handleNextTrace = useCallback(() => { + if (activeTraceIndex !== undefined && activeTraceIndex < traces.length - 1) { setSelectedTraceId(traces[activeTraceIndex + 1].root.id) } - } + }, [activeTraceIndex, traces, setSelectedTraceId]) - const handlePrevTrace = () => { - if (traces && activeTraceIndex !== undefined && activeTraceIndex > 0) { + const handlePrevTrace = useCallback(() => { + if (activeTraceIndex !== undefined && activeTraceIndex > 0) { setSelectedTraceId(traces[activeTraceIndex - 1].root.id) } - } + }, [activeTraceIndex, traces, setSelectedTraceId]) return (
Trace - # {activeTrace.root.id}
diff --git a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx index e1e7ae6115..c0fa9d0e12 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx @@ -10,6 +10,7 @@ interface TraceTreeProps { activeTrace: Record selectedKeys: string[] onSelect: (keys: React.Key[]) => void + defaultSelectedTraceKey: string | undefined } const useStyles = createUseStyles((theme: JSSTheme) => ({ @@ -85,15 +86,23 @@ const TreeContent = ({nodeValue}: {nodeValue: AgentaNodeDTO}) => { ) } -const buildTreeData = (nodes: Record): TreeDataNode[] => { +const buildTreeData = ( + nodes: Record, + expandedKeys: string[], +): TreeDataNode[] => { const createTreeNode = (node: AgentaNodeDTO): TreeDataNode => { const hasChildren = node.nodes && Object.keys(node.nodes).length > 0 + const key = node.node.id + expandedKeys.push(key) return { - key: node.node.id, + key: key, title: , children: hasChildren - ? buildTreeData(node.nodes as Record) + ? buildTreeData( + node.nodes as Record, + expandedKeys, + ) : undefined, } } @@ -112,9 +121,15 @@ const buildTreeData = (nodes: Record): }) } -const TraceTree = ({activeTrace, selectedKeys, onSelect}: TraceTreeProps) => { +const TraceTree = ({ + activeTrace, + selectedKeys, + onSelect, + defaultSelectedTraceKey, +}: TraceTreeProps) => { const classes = useStyles() - const treeData = buildTreeData(activeTrace) + const expandedKeys: string[] = [] + const treeData = buildTreeData(activeTrace, expandedKeys) return ( { className={classes.tree} defaultExpandAll onSelect={onSelect} + defaultExpandParent + expandedKeys={expandedKeys} selectedKeys={selectedKeys} /> ) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index b206ad911e..d515a23eb6 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -3,12 +3,13 @@ import TraceContent from "@/components/pages/observability/drawer/TraceContent" import TraceHeader from "@/components/pages/observability/drawer/TraceHeader" import TraceTree from "@/components/pages/observability/drawer/TraceTree" import {useQueryParam} from "@/hooks/useQuery" +import {findTraceNodeById} from "@/lib/helpers/observability" import {useTraces} from "@/lib/hooks/useTraces" import {JSSTheme} from "@/lib/Types" -import {AgentaRootsDTO} from "@/services/observability/types" +import {AgentaNodeDTO, AgentaRootsDTO} from "@/services/observability/types" import {Table, Typography} from "antd" import {ColumnsType} from "antd/es/table" -import React, {useMemo} from "react" +import React, {useCallback, useMemo, useState} from "react" import {createUseStyles} from "react-jss" const useStyles = createUseStyles((theme: JSSTheme) => ({ @@ -21,14 +22,34 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ interface Props {} -const ObservabilityDashboard: React.FC = () => { +const ObservabilityDashboard = ({}: Props) => { const classes = useStyles() const [selectedTraceId, setSelectedTraceId] = useQueryParam("trace", "") const {traces} = useTraces() - const activeTrace = useMemo(() => { - return traces?.find((item) => item.root.id === selectedTraceId) - }, [selectedTraceId, traces]) + const activeTrace = useMemo( + () => traces?.find((item) => item.root.id === selectedTraceId) ?? null, + [selectedTraceId, traces], + ) + + const defaultSelectedTraceKey = useMemo(() => { + if (!activeTrace || !activeTrace.trees.length) return undefined + const firstNodeKey = Object.keys(activeTrace.trees[0].nodes)[0] + return activeTrace.trees[0].nodes[firstNodeKey].node.id + }, [activeTrace]) + + const [selectedKeys, setSelectedKeys] = useState([]) + const [selectedItem, setSelectedItem] = useState(null) + + const onSelect = useCallback( + (keys: React.Key[]) => { + const selectedId = keys[0] as string + setSelectedKeys([selectedId]) + const foundItem = findTraceNodeById(activeTrace?.trees[0].nodes, selectedId) + setSelectedItem(foundItem) + }, + [activeTrace], + ) const columns: ColumnsType = [ { @@ -74,8 +95,15 @@ const ObservabilityDashboard: React.FC = () => { setSelectedTraceId={setSelectedTraceId} /> } - mainContent={} - sideContent={} + mainContent={selectedItem ? : null} + sideContent={ + + } /> )}
From 73cfd0cf651bb12391bfc5f719b28be7c8d7acdb Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Mon, 14 Oct 2024 08:41:45 +0200 Subject: [PATCH 069/305] feat(frontend): added observability helper file --- agenta-web/src/lib/helpers/observability.ts | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 agenta-web/src/lib/helpers/observability.ts diff --git a/agenta-web/src/lib/helpers/observability.ts b/agenta-web/src/lib/helpers/observability.ts new file mode 100644 index 0000000000..6bb1025f9d --- /dev/null +++ b/agenta-web/src/lib/helpers/observability.ts @@ -0,0 +1,37 @@ +import {AgentaNodeDTO} from "@/services/observability/types" + +export const findTraceNodeById = ( + nodes: Record | undefined, + id: string, +): AgentaNodeDTO | null => { + for (const key in nodes) { + const node = nodes[key] + + if (Array.isArray(node)) { + for (const childNode of node) { + if (childNode.node.id === id) { + return childNode + } + + const found = findTraceNodeById( + childNode.nodes as Record, + id, + ) + if (found) return found + } + } else { + if (node.node.id === id) { + return node + } + + if (node.nodes) { + const found = findTraceNodeById( + node.nodes as Record, + id, + ) + if (found) return found + } + } + } + return null +} From 310b33d95c46a82e88038412d00d3078f3f03324 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Mon, 14 Oct 2024 09:16:41 +0200 Subject: [PATCH 070/305] bug fix(frontend) --- agenta-web/src/lib/hooks/useTraces.ts | 2 +- .../src/services/observability/core/index.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 agenta-web/src/services/observability/core/index.ts diff --git a/agenta-web/src/lib/hooks/useTraces.ts b/agenta-web/src/lib/hooks/useTraces.ts index 68471dbef2..57cbc70d75 100644 --- a/agenta-web/src/lib/hooks/useTraces.ts +++ b/agenta-web/src/lib/hooks/useTraces.ts @@ -1,4 +1,4 @@ -import {fetchAllTraces} from "@/services/observability/api" +import {fetchAllTraces} from "@/services/observability/core" import {AgentaRootsResponse} from "@/services/observability/types" import {useEffect, useState} from "react" diff --git a/agenta-web/src/services/observability/core/index.ts b/agenta-web/src/services/observability/core/index.ts new file mode 100644 index 0000000000..11aa5d90a7 --- /dev/null +++ b/agenta-web/src/services/observability/core/index.ts @@ -0,0 +1,15 @@ +import {delay} from "@/lib/helpers/utils" +import data from "@/lib/test_trace.json" +import {AgentaRootsResponse} from "../types" + +//Prefix convention: +// - fetch: GET single entity from server +// - fetchAll: GET all entities from server +// - create: POST data to server +// - update: PUT data to server +// - delete: DELETE data from server + +export const fetchAllTraces = async () => { + await delay(1000) + return data as AgentaRootsResponse +} From e9fb4cdd36fd1f16708ecb329c0758cd9b520e69 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Mon, 14 Oct 2024 11:10:49 +0200 Subject: [PATCH 071/305] fix(frontend): rename observability helper file to avoid conflict --- .../lib/helpers/{observability.ts => observability_helpers.ts} | 0 agenta-web/src/pages/apps/[app_id]/observability/index.tsx | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename agenta-web/src/lib/helpers/{observability.ts => observability_helpers.ts} (100%) diff --git a/agenta-web/src/lib/helpers/observability.ts b/agenta-web/src/lib/helpers/observability_helpers.ts similarity index 100% rename from agenta-web/src/lib/helpers/observability.ts rename to agenta-web/src/lib/helpers/observability_helpers.ts diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index d515a23eb6..b7df3bf9ba 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -3,7 +3,7 @@ import TraceContent from "@/components/pages/observability/drawer/TraceContent" import TraceHeader from "@/components/pages/observability/drawer/TraceHeader" import TraceTree from "@/components/pages/observability/drawer/TraceTree" import {useQueryParam} from "@/hooks/useQuery" -import {findTraceNodeById} from "@/lib/helpers/observability" +import {findTraceNodeById} from "@/lib/helpers/observability_helpers" import {useTraces} from "@/lib/hooks/useTraces" import {JSSTheme} from "@/lib/Types" import {AgentaNodeDTO, AgentaRootsDTO} from "@/services/observability/types" From 8930ed4b1938d791535636f4ed756011272a3491 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Mon, 14 Oct 2024 16:44:17 +0200 Subject: [PATCH 072/305] fix SDK --- agenta-backend/agenta_backend/__init__.py | 4 ++-- agenta-cli/agenta/sdk/decorators/routing.py | 4 ++++ agenta-cli/agenta/sdk/decorators/tracing.py | 4 ++++ agenta-cli/agenta/sdk/tracing/inline.py | 6 +++--- agenta-cli/agenta/sdk/tracing/tracing.py | 6 +++--- agenta-cli/agenta/sdk/utils/exceptions.py | 1 + 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/agenta-backend/agenta_backend/__init__.py b/agenta-backend/agenta_backend/__init__.py index ff650b4015..4077714df4 100644 --- a/agenta-backend/agenta_backend/__init__.py +++ b/agenta-backend/agenta_backend/__init__.py @@ -1,6 +1,6 @@ import os -if os.environ["FEATURE_FLAG"] in ["cloud", "cloud-dev"]: +if os.environ.get("FEATURE_FLAG") in ["cloud", "cloud-dev"]: import agenta_backend.cloud.__init__ -if os.environ["FEATURE_FLAG"] in ["ee"]: +if os.environ.get("FEATURE_FLAG") in ["ee"]: import agenta_backend.ee.__init__ diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index bfb1f403b1..114ac504dc 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -394,6 +394,10 @@ async def execute_function( ) data = self.patch_result(result) except Exception as e: + log.error(f"Agenta SDK - Routing Exception") + + traceback.print_exc() + self.handle_exception(e) with suppress(): diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index ac88af0d84..5a6f507f1a 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -1,4 +1,5 @@ import inspect +import traceback from functools import wraps from itertools import chain from typing import Callable, Optional, Any, Dict, List @@ -139,6 +140,8 @@ async def wrapped_func(*args, **kwargs): try: result = await func(*args, **kwargs) except Exception as e: + traceback.print_exc() + span.record_exception(e) span.set_status("ERROR") @@ -148,6 +151,7 @@ async def wrapped_func(*args, **kwargs): with suppress(): cost = None usage = {} + if isinstance(result, dict): cost = result.get("cost", None) usage = result.get("usage", {}) diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py index 9d9a36a486..63e20e3bb0 100644 --- a/agenta-cli/agenta/sdk/tracing/inline.py +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -1042,7 +1042,7 @@ def parse_inline_trace( ### services.observability.service.query() ### ### -------------------------------------- ### connect_children(span_id_tree, span_idx) - root_span_dtos = [span_dto for span_dto in span_idx.values()] + root_span_dtos = [span_idx[span_id] for span_id in span_id_tree.keys()] agenta_span_dtos = [ parse_to_agenta_span_dto(span_dto) for span_dto in root_span_dtos ] @@ -1058,10 +1058,10 @@ def parse_inline_trace( _parse_to_legacy_span(span_dto) for span_dto in span_idx.values() ] - root_span = root_span_dtos[0] + root_span = agenta_span_dtos[0] trace_id = root_span.root.id.hex - latency = root_span.time.span + latency = root_span.time.span / 1_000_000 cost = root_span.metrics.get("acc", {}).get("costs", {}).get("total", 0.0) tokens = { "prompt_tokens": root_span.metrics.get("acc", {}) diff --git a/agenta-cli/agenta/sdk/tracing/tracing.py b/agenta-cli/agenta/sdk/tracing/tracing.py index 1a361db144..5c91969a80 100644 --- a/agenta-cli/agenta/sdk/tracing/tracing.py +++ b/agenta-cli/agenta/sdk/tracing/tracing.py @@ -1,6 +1,6 @@ from typing import Optional, Any, Dict -from httpx import head as check +from httpx import get as check from opentelemetry.trace import ( get_current_span, @@ -96,7 +96,7 @@ def configure( try: log.info(f"Connecting to the remote trace receiver at {self.otlp_url}...") - check(self.otlp_url, headers=self.headers, timeout=0.001) + check(self.otlp_url, headers=self.headers, timeout=1) log.info(f"Connection established.") @@ -107,7 +107,7 @@ def configure( self.tracer_provider.add_span_processor(_otlp) except Exception as e: - print(e) + log.error(e) log.warning(f"Connection failed.") log.warning( f"Warning: Your traces will not be exported since {self.otlp_url} is unreachable." diff --git a/agenta-cli/agenta/sdk/utils/exceptions.py b/agenta-cli/agenta/sdk/utils/exceptions.py index 111f7c552e..9eea8f1f21 100644 --- a/agenta-cli/agenta/sdk/utils/exceptions.py +++ b/agenta-cli/agenta/sdk/utils/exceptions.py @@ -14,5 +14,6 @@ def __exit__(self, exc_type, exc_value, exc_tb): if exc_type is None: return else: + print("Agenta SDK - Tracing Exception") log.error(f"{exc_type.__name__}: {exc_value}\n{format_exc()}") return From a77f04d1a8236e850c1b24673ee2926ba9a13508 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Mon, 14 Oct 2024 17:36:02 +0200 Subject: [PATCH 073/305] fix litellm --- agenta-cli/agenta/sdk/litellm/litellm.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/agenta-cli/agenta/sdk/litellm/litellm.py b/agenta-cli/agenta/sdk/litellm/litellm.py index 43f608e397..f51be93f44 100644 --- a/agenta-cli/agenta/sdk/litellm/litellm.py +++ b/agenta-cli/agenta/sdk/litellm/litellm.py @@ -1,5 +1,7 @@ import agenta as ag +from opentelemetry.trace import SpanKind + from agenta.sdk.tracing.spans import CustomSpan from agenta.sdk.utils.exceptions import suppress from agenta.sdk.utils.logging import log @@ -42,10 +44,10 @@ def log_pre_api_call( else "embedding" ) - kind = "CLIENT" + kind = SpanKind.CLIENT self.span = CustomSpan( - ag.tracer.start_span(name=f"litellm_{kind.lower()}", kind=kind) + ag.tracer.start_span(name=f"litellm_{kind.name.lower()}", kind=kind) ) self.span.set_attributes( From 57fe63d71a5a5af039325aa6489b8c47a7436a81 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 15 Oct 2024 10:53:34 +0200 Subject: [PATCH 074/305] fix cost and usage --- agenta-cli/agenta/sdk/decorators/routing.py | 7 +- agenta-cli/agenta/sdk/tracing/inline.py | 42 +- agenta-cli/poetry.lock | 2542 +++++++++++++------ agenta-cli/pyproject.toml | 3 +- 4 files changed, 1770 insertions(+), 824 deletions(-) diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index 114ac504dc..33a2ee8ee5 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -656,8 +656,7 @@ def handle_terminal_run( SHOW_DETAILS = True SHOW_DATA = False - SHOW_SPANS = True - SHOW_SPAN_ATTRIBUTES = False + SHOW_TRACE = False log.info("\n========= Result =========\n") @@ -665,14 +664,14 @@ def handle_terminal_run( if SHOW_DETAILS: log.info(f"latency: {result.trace.get('latency')}") log.info(f"cost: {result.trace.get('cost')}") - log.info(f"tokens: {list(result.trace.get('tokens', {}).values())}") + log.info(f"usage: {list(result.trace.get('usage', {}).values())}") if SHOW_DATA: log.info(" ") log.info(f"data:") log.info(json.dumps(result.data, indent=2)) - if SHOW_SPANS: + if SHOW_TRACE: log.info(" ") log.info(f"trace:") log.info(f"----------------") diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py index 63e20e3bb0..f99b3b9543 100644 --- a/agenta-cli/agenta/sdk/tracing/inline.py +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -999,7 +999,7 @@ def parse_to_agenta_span_dto( ######################################## -from copy import deepcopy +from litellm import cost_calculator from opentelemetry.sdk.trace import ReadableSpan @@ -1029,6 +1029,8 @@ def parse_inline_trace( ### services.observability.service.ingest/query() ### ##################################################### + _calculate_cost(span_idx) + ############################################### ### services.observability.service.ingest() ### ### --------------------------------------- ### @@ -1083,7 +1085,7 @@ def parse_inline_trace( "trace_id": trace_id, "latency": latency, "cost": cost, - "tokens": tokens, + "usage": tokens, "spans": spans, } @@ -1267,3 +1269,39 @@ def _parse_to_legacy_span(span: SpanDTO) -> CreateSpan: ) return legacy_span + + +PAYING_TYPES = [ + "embedding", + "query", + "completion", + "chat", + "rerank", +] + + +def _calculate_cost(span_idx: Dict[str, SpanCreateDTO]): + for span in span_idx.values(): + if span.node.type.name.lower() in PAYING_TYPES and span.meta and span.metrics: + + try: + costs = cost_calculator.cost_per_token( + model=span.meta.get("response.model"), + prompt_tokens=span.metrics.get("unit.tokens.prompt", 0.0), + completion_tokens=span.metrics.get("unit.tokens.completion", 0.0), + call_type=span.node.type.name.lower(), + response_time_ms=span.time.span // 1_000, + ) + + if not costs: + continue + + prompt_cost, completion_cost = costs + total_cost = prompt_cost + completion_cost + + span.metrics["unit.costs.prompt"] = prompt_cost + span.metrics["unit.costs.completion"] = completion_cost + span.metrics["unit.costs.total"] = total_cost + + except: + pass diff --git a/agenta-cli/poetry.lock b/agenta-cli/poetry.lock index e11ef6cb4d..c8c0659e48 100644 --- a/agenta-cli/poetry.lock +++ b/agenta-cli/poetry.lock @@ -1,5 +1,142 @@ # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +[[package]] +name = "aiohappyeyeballs" +version = "2.4.3" +description = "Happy Eyeballs for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohappyeyeballs-2.4.3-py3-none-any.whl", hash = "sha256:8a7a83727b2756f394ab2895ea0765a0a8c475e3c71e98d43d76f22b4b435572"}, + {file = "aiohappyeyeballs-2.4.3.tar.gz", hash = "sha256:75cf88a15106a5002a8eb1dab212525c00d1f4c0fa96e551c9fbe6f09a621586"}, +] + +[[package]] +name = "aiohttp" +version = "3.10.10" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be7443669ae9c016b71f402e43208e13ddf00912f47f623ee5994e12fc7d4b3f"}, + {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b06b7843929e41a94ea09eb1ce3927865387e3e23ebe108e0d0d09b08d25be9"}, + {file = "aiohttp-3.10.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:333cf6cf8e65f6a1e06e9eb3e643a0c515bb850d470902274239fea02033e9a8"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:274cfa632350225ce3fdeb318c23b4a10ec25c0e2c880eff951a3842cf358ac1"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9e5e4a85bdb56d224f412d9c98ae4cbd032cc4f3161818f692cd81766eee65a"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b606353da03edcc71130b52388d25f9a30a126e04caef1fd637e31683033abd"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab5a5a0c7a7991d90446a198689c0535be89bbd6b410a1f9a66688f0880ec026"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:578a4b875af3e0daaf1ac6fa983d93e0bbfec3ead753b6d6f33d467100cdc67b"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8105fd8a890df77b76dd3054cddf01a879fc13e8af576805d667e0fa0224c35d"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3bcd391d083f636c06a68715e69467963d1f9600f85ef556ea82e9ef25f043f7"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fbc6264158392bad9df19537e872d476f7c57adf718944cc1e4495cbabf38e2a"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e48d5021a84d341bcaf95c8460b152cfbad770d28e5fe14a768988c461b821bc"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2609e9ab08474702cc67b7702dbb8a80e392c54613ebe80db7e8dbdb79837c68"}, + {file = "aiohttp-3.10.10-cp310-cp310-win32.whl", hash = "sha256:84afcdea18eda514c25bc68b9af2a2b1adea7c08899175a51fe7c4fb6d551257"}, + {file = "aiohttp-3.10.10-cp310-cp310-win_amd64.whl", hash = "sha256:9c72109213eb9d3874f7ac8c0c5fa90e072d678e117d9061c06e30c85b4cf0e6"}, + {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c30a0eafc89d28e7f959281b58198a9fa5e99405f716c0289b7892ca345fe45f"}, + {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:258c5dd01afc10015866114e210fb7365f0d02d9d059c3c3415382ab633fcbcb"}, + {file = "aiohttp-3.10.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:15ecd889a709b0080f02721255b3f80bb261c2293d3c748151274dfea93ac871"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3935f82f6f4a3820270842e90456ebad3af15810cf65932bd24da4463bc0a4c"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:413251f6fcf552a33c981c4709a6bba37b12710982fec8e558ae944bfb2abd38"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1720b4f14c78a3089562b8875b53e36b51c97c51adc53325a69b79b4b48ebcb"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:679abe5d3858b33c2cf74faec299fda60ea9de62916e8b67e625d65bf069a3b7"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79019094f87c9fb44f8d769e41dbb664d6e8fcfd62f665ccce36762deaa0e911"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe2fb38c2ed905a2582948e2de560675e9dfbee94c6d5ccdb1301c6d0a5bf092"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a3f00003de6eba42d6e94fabb4125600d6e484846dbf90ea8e48a800430cc142"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1bbb122c557a16fafc10354b9d99ebf2f2808a660d78202f10ba9d50786384b9"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:30ca7c3b94708a9d7ae76ff281b2f47d8eaf2579cd05971b5dc681db8caac6e1"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df9270660711670e68803107d55c2b5949c2e0f2e4896da176e1ecfc068b974a"}, + {file = "aiohttp-3.10.10-cp311-cp311-win32.whl", hash = "sha256:aafc8ee9b742ce75044ae9a4d3e60e3d918d15a4c2e08a6c3c3e38fa59b92d94"}, + {file = "aiohttp-3.10.10-cp311-cp311-win_amd64.whl", hash = "sha256:362f641f9071e5f3ee6f8e7d37d5ed0d95aae656adf4ef578313ee585b585959"}, + {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9294bbb581f92770e6ed5c19559e1e99255e4ca604a22c5c6397b2f9dd3ee42c"}, + {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a8fa23fe62c436ccf23ff930149c047f060c7126eae3ccea005f0483f27b2e28"}, + {file = "aiohttp-3.10.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c6a5b8c7926ba5d8545c7dd22961a107526562da31a7a32fa2456baf040939f"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:007ec22fbc573e5eb2fb7dec4198ef8f6bf2fe4ce20020798b2eb5d0abda6138"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9627cc1a10c8c409b5822a92d57a77f383b554463d1884008e051c32ab1b3742"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:50edbcad60d8f0e3eccc68da67f37268b5144ecc34d59f27a02f9611c1d4eec7"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a45d85cf20b5e0d0aa5a8dca27cce8eddef3292bc29d72dcad1641f4ed50aa16"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b00807e2605f16e1e198f33a53ce3c4523114059b0c09c337209ae55e3823a8"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f2d4324a98062be0525d16f768a03e0bbb3b9fe301ceee99611dc9a7953124e6"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:438cd072f75bb6612f2aca29f8bd7cdf6e35e8f160bc312e49fbecab77c99e3a"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:baa42524a82f75303f714108fea528ccacf0386af429b69fff141ffef1c534f9"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a7d8d14fe962153fc681f6366bdec33d4356f98a3e3567782aac1b6e0e40109a"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c1277cd707c465cd09572a774559a3cc7c7a28802eb3a2a9472588f062097205"}, + {file = "aiohttp-3.10.10-cp312-cp312-win32.whl", hash = "sha256:59bb3c54aa420521dc4ce3cc2c3fe2ad82adf7b09403fa1f48ae45c0cbde6628"}, + {file = "aiohttp-3.10.10-cp312-cp312-win_amd64.whl", hash = "sha256:0e1b370d8007c4ae31ee6db7f9a2fe801a42b146cec80a86766e7ad5c4a259cf"}, + {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ad7593bb24b2ab09e65e8a1d385606f0f47c65b5a2ae6c551db67d6653e78c28"}, + {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1eb89d3d29adaf533588f209768a9c02e44e4baf832b08118749c5fad191781d"}, + {file = "aiohttp-3.10.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3fe407bf93533a6fa82dece0e74dbcaaf5d684e5a51862887f9eaebe6372cd79"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50aed5155f819873d23520919e16703fc8925e509abbb1a1491b0087d1cd969e"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f05e9727ce409358baa615dbeb9b969db94324a79b5a5cea45d39bdb01d82e6"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dffb610a30d643983aeb185ce134f97f290f8935f0abccdd32c77bed9388b42"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa6658732517ddabe22c9036479eabce6036655ba87a0224c612e1ae6af2087e"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:741a46d58677d8c733175d7e5aa618d277cd9d880301a380fd296975a9cdd7bc"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e00e3505cd80440f6c98c6d69269dcc2a119f86ad0a9fd70bccc59504bebd68a"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ffe595f10566f8276b76dc3a11ae4bb7eba1aac8ddd75811736a15b0d5311414"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdfcf6443637c148c4e1a20c48c566aa694fa5e288d34b20fcdc58507882fed3"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d183cf9c797a5291e8301790ed6d053480ed94070637bfaad914dd38b0981f67"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:77abf6665ae54000b98b3c742bc6ea1d1fb31c394bcabf8b5d2c1ac3ebfe7f3b"}, + {file = "aiohttp-3.10.10-cp313-cp313-win32.whl", hash = "sha256:4470c73c12cd9109db8277287d11f9dd98f77fc54155fc71a7738a83ffcc8ea8"}, + {file = "aiohttp-3.10.10-cp313-cp313-win_amd64.whl", hash = "sha256:486f7aabfa292719a2753c016cc3a8f8172965cabb3ea2e7f7436c7f5a22a151"}, + {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1b66ccafef7336a1e1f0e389901f60c1d920102315a56df85e49552308fc0486"}, + {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:acd48d5b80ee80f9432a165c0ac8cbf9253eaddb6113269a5e18699b33958dbb"}, + {file = "aiohttp-3.10.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3455522392fb15ff549d92fbf4b73b559d5e43dc522588f7eb3e54c3f38beee7"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c3b868724137f713a38376fef8120c166d1eadd50da1855c112fe97954aed8"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:da1dee8948d2137bb51fbb8a53cce6b1bcc86003c6b42565f008438b806cccd8"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5ce2ce7c997e1971b7184ee37deb6ea9922ef5163c6ee5aa3c274b05f9e12fa"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28529e08fde6f12eba8677f5a8608500ed33c086f974de68cc65ab218713a59d"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7db54c7914cc99d901d93a34704833568d86c20925b2762f9fa779f9cd2e70f"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03a42ac7895406220124c88911ebee31ba8b2d24c98507f4a8bf826b2937c7f2"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:7e338c0523d024fad378b376a79faff37fafb3c001872a618cde1d322400a572"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:038f514fe39e235e9fef6717fbf944057bfa24f9b3db9ee551a7ecf584b5b480"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:64f6c17757251e2b8d885d728b6433d9d970573586a78b78ba8929b0f41d045a"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:93429602396f3383a797a2a70e5f1de5df8e35535d7806c9f91df06f297e109b"}, + {file = "aiohttp-3.10.10-cp38-cp38-win32.whl", hash = "sha256:c823bc3971c44ab93e611ab1a46b1eafeae474c0c844aff4b7474287b75fe49c"}, + {file = "aiohttp-3.10.10-cp38-cp38-win_amd64.whl", hash = "sha256:54ca74df1be3c7ca1cf7f4c971c79c2daf48d9aa65dea1a662ae18926f5bc8ce"}, + {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01948b1d570f83ee7bbf5a60ea2375a89dfb09fd419170e7f5af029510033d24"}, + {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9fc1500fd2a952c5c8e3b29aaf7e3cc6e27e9cfc0a8819b3bce48cc1b849e4cc"}, + {file = "aiohttp-3.10.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f614ab0c76397661b90b6851a030004dac502e48260ea10f2441abd2207fbcc7"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00819de9e45d42584bed046314c40ea7e9aea95411b38971082cad449392b08c"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05646ebe6b94cc93407b3bf34b9eb26c20722384d068eb7339de802154d61bc5"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:998f3bd3cfc95e9424a6acd7840cbdd39e45bc09ef87533c006f94ac47296090"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9010c31cd6fa59438da4e58a7f19e4753f7f264300cd152e7f90d4602449762"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ea7ffc6d6d6f8a11e6f40091a1040995cdff02cfc9ba4c2f30a516cb2633554"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ef9c33cc5cbca35808f6c74be11eb7f5f6b14d2311be84a15b594bd3e58b5527"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ce0cdc074d540265bfeb31336e678b4e37316849d13b308607efa527e981f5c2"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:597a079284b7ee65ee102bc3a6ea226a37d2b96d0418cc9047490f231dc09fe8"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:7789050d9e5d0c309c706953e5e8876e38662d57d45f936902e176d19f1c58ab"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e7f8b04d83483577fd9200461b057c9f14ced334dcb053090cea1da9c8321a91"}, + {file = "aiohttp-3.10.10-cp39-cp39-win32.whl", hash = "sha256:c02a30b904282777d872266b87b20ed8cc0d1501855e27f831320f471d54d983"}, + {file = "aiohttp-3.10.10-cp39-cp39-win_amd64.whl", hash = "sha256:edfe3341033a6b53a5c522c802deb2079eee5cbfbb0af032a55064bd65c73a23"}, + {file = "aiohttp-3.10.10.tar.gz", hash = "sha256:0631dd7c9f0822cc61c88586ca76d5b5ada26538097d0f1df510b082bad3411a"}, +] + +[package.dependencies] +aiohappyeyeballs = ">=2.3.0" +aiosignal = ">=1.1.2" +async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.12.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + [[package]] name = "annotated-types" version = "0.7.0" @@ -13,13 +150,13 @@ files = [ [[package]] name = "anyio" -version = "4.4.0" +version = "4.6.2.post1" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, - {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, + {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, + {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, ] [package.dependencies] @@ -29,9 +166,9 @@ sniffio = ">=1.1" typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} [package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.23)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] [[package]] name = "asttokens" @@ -51,6 +188,36 @@ six = ">=1.12.0" astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "attrs" +version = "24.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, +] + +[package.extras] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + [[package]] name = "backoff" version = "2.2.1" @@ -64,123 +231,138 @@ files = [ [[package]] name = "cachetools" -version = "5.4.0" +version = "5.5.0" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" files = [ - {file = "cachetools-5.4.0-py3-none-any.whl", hash = "sha256:3ae3b49a3d5e28a77a0be2b37dbcb89005058959cb2323858c2657c4a8cab474"}, - {file = "cachetools-5.4.0.tar.gz", hash = "sha256:b8adc2e7c07f105ced7bc56dbb6dfbe7c4a00acce20e2227b3f355be89bc6827"}, + {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, + {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, ] [[package]] name = "certifi" -version = "2024.7.4" +version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, - {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] [[package]] name = "charset-normalizer" -version = "3.3.2" +version = "3.4.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] [[package]] @@ -236,23 +418,34 @@ wrapt = ">=1.10,<2" [package.extras] dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] +[[package]] +name = "distro" +version = "1.9.0" +description = "Distro - an OS platform information API" +optional = false +python-versions = ">=3.6" +files = [ + {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, + {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, +] + [[package]] name = "dnspython" -version = "2.6.1" +version = "2.7.0" description = "DNS toolkit" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, - {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, + {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, + {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, ] [package.extras] -dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] -dnssec = ["cryptography (>=41)"] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.16.0)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "quart-trio (>=0.11.0)", "sphinx (>=7.2.0)", "sphinx-rtd-theme (>=2.0.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=43)"] doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] -doq = ["aioquic (>=0.9.25)"] -idna = ["idna (>=3.6)"] +doq = ["aioquic (>=1.0.0)"] +idna = ["idna (>=3.7)"] trio = ["trio (>=0.23)"] wmi = ["wmi (>=1.5.1)"] @@ -278,21 +471,6 @@ docs = ["myst-parser (==0.18.0)", "sphinx (==5.1.1)"] ssh = ["paramiko (>=2.4.3)"] websockets = ["websocket-client (>=1.3.0)"] -[[package]] -name = "email-validator" -version = "2.2.0" -description = "A robust email address syntax and deliverability validation library." -optional = false -python-versions = ">=3.8" -files = [ - {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"}, - {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"}, -] - -[package.dependencies] -dnspython = ">=2.0.0" -idna = ">=2.0.0" - [[package]] name = "exceptiongroup" version = "1.2.2" @@ -309,13 +487,13 @@ test = ["pytest (>=6)"] [[package]] name = "executing" -version = "2.0.1" +version = "2.1.0" description = "Get the currently executing AST node of a frame, and other information" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" files = [ - {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, - {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, + {file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"}, + {file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"}, ] [package.extras] @@ -323,39 +501,164 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "fastapi" -version = "0.114.0" +version = "0.115.2" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.114.0-py3-none-any.whl", hash = "sha256:fee75aa1b1d3d73f79851c432497e4394e413e1dece6234f68d3ce250d12760a"}, - {file = "fastapi-0.114.0.tar.gz", hash = "sha256:9908f2a5cc733004de6ca5e1412698f35085cefcbfd41d539245b9edf87b73c1"}, + {file = "fastapi-0.115.2-py3-none-any.whl", hash = "sha256:61704c71286579cc5a598763905928f24ee98bfcc07aabe84cfefb98812bbc86"}, + {file = "fastapi-0.115.2.tar.gz", hash = "sha256:3995739e0b09fa12f984bce8fa9ae197b35d433750d3d312422d846e283697ee"}, ] [package.dependencies] pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.37.2,<0.39.0" +starlette = ">=0.37.2,<0.41.0" typing-extensions = ">=4.8.0" [package.extras] -all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] [[package]] -name = "fastapi-cli" -version = "0.0.4" -description = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀" +name = "filelock" +version = "3.16.1" +description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "fastapi_cli-0.0.4-py3-none-any.whl", hash = "sha256:a2552f3a7ae64058cdbb530be6fa6dbfc975dc165e4fa66d224c3d396e25e809"}, - {file = "fastapi_cli-0.0.4.tar.gz", hash = "sha256:e2e9ffaffc1f7767f488d6da34b6f5a377751c996f397902eb6abb99a67bde32"}, + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, ] -[package.dependencies] -typer = ">=0.12.3" +[package.extras] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +typing = ["typing-extensions (>=4.12.2)"] + +[[package]] +name = "frozenlist" +version = "1.4.1" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, + {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, + {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, + {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, + {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, + {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, + {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, + {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, + {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, + {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, + {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, + {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, + {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, +] + +[[package]] +name = "fsspec" +version = "2024.9.0" +description = "File-system specification" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fsspec-2024.9.0-py3-none-any.whl", hash = "sha256:a0947d552d8a6efa72cc2c730b12c41d043509156966cca4fb157b0f2a0c574b"}, + {file = "fsspec-2024.9.0.tar.gz", hash = "sha256:4b0afb90c2f21832df142f292649035d80b421f60a9e1c027802e5a0da2b04e8"}, +] [package.extras] -standard = ["fastapi", "uvicorn[standard] (>=0.15.0)"] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +dev = ["pre-commit", "ruff"] +doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] +test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] +test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] +tqdm = ["tqdm"] [[package]] name = "googleapis-common-protos" @@ -376,61 +679,70 @@ grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] [[package]] name = "grpcio" -version = "1.66.1" +version = "1.66.2" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.66.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:4877ba180591acdf127afe21ec1c7ff8a5ecf0fe2600f0d3c50e8c4a1cbc6492"}, - {file = "grpcio-1.66.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3750c5a00bd644c75f4507f77a804d0189d97a107eb1481945a0cf3af3e7a5ac"}, - {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a013c5fbb12bfb5f927444b477a26f1080755a931d5d362e6a9a720ca7dbae60"}, - {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1b24c23d51a1e8790b25514157d43f0a4dce1ac12b3f0b8e9f66a5e2c4c132f"}, - {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7ffb8ea674d68de4cac6f57d2498fef477cef582f1fa849e9f844863af50083"}, - {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:307b1d538140f19ccbd3aed7a93d8f71103c5d525f3c96f8616111614b14bf2a"}, - {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1c17ebcec157cfb8dd445890a03e20caf6209a5bd4ac5b040ae9dbc59eef091d"}, - {file = "grpcio-1.66.1-cp310-cp310-win32.whl", hash = "sha256:ef82d361ed5849d34cf09105d00b94b6728d289d6b9235513cb2fcc79f7c432c"}, - {file = "grpcio-1.66.1-cp310-cp310-win_amd64.whl", hash = "sha256:292a846b92cdcd40ecca46e694997dd6b9be6c4c01a94a0dfb3fcb75d20da858"}, - {file = "grpcio-1.66.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:c30aeceeaff11cd5ddbc348f37c58bcb96da8d5aa93fed78ab329de5f37a0d7a"}, - {file = "grpcio-1.66.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8a1e224ce6f740dbb6b24c58f885422deebd7eb724aff0671a847f8951857c26"}, - {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a66fe4dc35d2330c185cfbb42959f57ad36f257e0cc4557d11d9f0a3f14311df"}, - {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3ba04659e4fce609de2658fe4dbf7d6ed21987a94460f5f92df7579fd5d0e22"}, - {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4573608e23f7e091acfbe3e84ac2045680b69751d8d67685ffa193a4429fedb1"}, - {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7e06aa1f764ec8265b19d8f00140b8c4b6ca179a6dc67aa9413867c47e1fb04e"}, - {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3885f037eb11f1cacc41f207b705f38a44b69478086f40608959bf5ad85826dd"}, - {file = "grpcio-1.66.1-cp311-cp311-win32.whl", hash = "sha256:97ae7edd3f3f91480e48ede5d3e7d431ad6005bfdbd65c1b56913799ec79e791"}, - {file = "grpcio-1.66.1-cp311-cp311-win_amd64.whl", hash = "sha256:cfd349de4158d797db2bd82d2020554a121674e98fbe6b15328456b3bf2495bb"}, - {file = "grpcio-1.66.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:a92c4f58c01c77205df6ff999faa008540475c39b835277fb8883b11cada127a"}, - {file = "grpcio-1.66.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fdb14bad0835914f325349ed34a51940bc2ad965142eb3090081593c6e347be9"}, - {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f03a5884c56256e08fd9e262e11b5cfacf1af96e2ce78dc095d2c41ccae2c80d"}, - {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ca2559692d8e7e245d456877a85ee41525f3ed425aa97eb7a70fc9a79df91a0"}, - {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ca1be089fb4446490dd1135828bd42a7c7f8421e74fa581611f7afdf7ab761"}, - {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d639c939ad7c440c7b2819a28d559179a4508783f7e5b991166f8d7a34b52815"}, - {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b9feb4e5ec8dc2d15709f4d5fc367794d69277f5d680baf1910fc9915c633524"}, - {file = "grpcio-1.66.1-cp312-cp312-win32.whl", hash = "sha256:7101db1bd4cd9b880294dec41a93fcdce465bdbb602cd8dc5bd2d6362b618759"}, - {file = "grpcio-1.66.1-cp312-cp312-win_amd64.whl", hash = "sha256:b0aa03d240b5539648d996cc60438f128c7f46050989e35b25f5c18286c86734"}, - {file = "grpcio-1.66.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:ecfe735e7a59e5a98208447293ff8580e9db1e890e232b8b292dc8bd15afc0d2"}, - {file = "grpcio-1.66.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4825a3aa5648010842e1c9d35a082187746aa0cdbf1b7a2a930595a94fb10fce"}, - {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f517fd7259fe823ef3bd21e508b653d5492e706e9f0ef82c16ce3347a8a5620c"}, - {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1fe60d0772831d96d263b53d83fb9a3d050a94b0e94b6d004a5ad111faa5b5b"}, - {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31a049daa428f928f21090403e5d18ea02670e3d5d172581670be006100db9ef"}, - {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f914386e52cbdeb5d2a7ce3bf1fdfacbe9d818dd81b6099a05b741aaf3848bb"}, - {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bff2096bdba686019fb32d2dde45b95981f0d1490e054400f70fc9a8af34b49d"}, - {file = "grpcio-1.66.1-cp38-cp38-win32.whl", hash = "sha256:aa8ba945c96e73de29d25331b26f3e416e0c0f621e984a3ebdb2d0d0b596a3b3"}, - {file = "grpcio-1.66.1-cp38-cp38-win_amd64.whl", hash = "sha256:161d5c535c2bdf61b95080e7f0f017a1dfcb812bf54093e71e5562b16225b4ce"}, - {file = "grpcio-1.66.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:d0cd7050397b3609ea51727b1811e663ffda8bda39c6a5bb69525ef12414b503"}, - {file = "grpcio-1.66.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0e6c9b42ded5d02b6b1fea3a25f036a2236eeb75d0579bfd43c0018c88bf0a3e"}, - {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:c9f80f9fad93a8cf71c7f161778ba47fd730d13a343a46258065c4deb4b550c0"}, - {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dd67ed9da78e5121efc5c510f0122a972216808d6de70953a740560c572eb44"}, - {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48b0d92d45ce3be2084b92fb5bae2f64c208fea8ceed7fccf6a7b524d3c4942e"}, - {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d813316d1a752be6f5c4360c49f55b06d4fe212d7df03253dfdae90c8a402bb"}, - {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c9bebc6627873ec27a70fc800f6083a13c70b23a5564788754b9ee52c5aef6c"}, - {file = "grpcio-1.66.1-cp39-cp39-win32.whl", hash = "sha256:30a1c2cf9390c894c90bbc70147f2372130ad189cffef161f0432d0157973f45"}, - {file = "grpcio-1.66.1-cp39-cp39-win_amd64.whl", hash = "sha256:17663598aadbedc3cacd7bbde432f541c8e07d2496564e22b214b22c7523dac8"}, - {file = "grpcio-1.66.1.tar.gz", hash = "sha256:35334f9c9745add3e357e3372756fd32d925bd52c41da97f4dfdafbde0bf0ee2"}, + {file = "grpcio-1.66.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:fe96281713168a3270878255983d2cb1a97e034325c8c2c25169a69289d3ecfa"}, + {file = "grpcio-1.66.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:73fc8f8b9b5c4a03e802b3cd0c18b2b06b410d3c1dcbef989fdeb943bd44aff7"}, + {file = "grpcio-1.66.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:03b0b307ba26fae695e067b94cbb014e27390f8bc5ac7a3a39b7723fed085604"}, + {file = "grpcio-1.66.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d69ce1f324dc2d71e40c9261d3fdbe7d4c9d60f332069ff9b2a4d8a257c7b2b"}, + {file = "grpcio-1.66.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05bc2ceadc2529ab0b227b1310d249d95d9001cd106aa4d31e8871ad3c428d73"}, + {file = "grpcio-1.66.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ac475e8da31484efa25abb774674d837b343afb78bb3bcdef10f81a93e3d6bf"}, + {file = "grpcio-1.66.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0be4e0490c28da5377283861bed2941d1d20ec017ca397a5df4394d1c31a9b50"}, + {file = "grpcio-1.66.2-cp310-cp310-win32.whl", hash = "sha256:4e504572433f4e72b12394977679161d495c4c9581ba34a88d843eaf0f2fbd39"}, + {file = "grpcio-1.66.2-cp310-cp310-win_amd64.whl", hash = "sha256:2018b053aa15782db2541ca01a7edb56a0bf18c77efed975392583725974b249"}, + {file = "grpcio-1.66.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:2335c58560a9e92ac58ff2bc5649952f9b37d0735608242973c7a8b94a6437d8"}, + {file = "grpcio-1.66.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:45a3d462826f4868b442a6b8fdbe8b87b45eb4f5b5308168c156b21eca43f61c"}, + {file = "grpcio-1.66.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a9539f01cb04950fd4b5ab458e64a15f84c2acc273670072abe49a3f29bbad54"}, + {file = "grpcio-1.66.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce89f5876662f146d4c1f695dda29d4433a5d01c8681fbd2539afff535da14d4"}, + {file = "grpcio-1.66.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25a14af966438cddf498b2e338f88d1c9706f3493b1d73b93f695c99c5f0e2a"}, + {file = "grpcio-1.66.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6001e575b8bbd89eee11960bb640b6da6ae110cf08113a075f1e2051cc596cae"}, + {file = "grpcio-1.66.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4ea1d062c9230278793820146c95d038dc0f468cbdd172eec3363e42ff1c7d01"}, + {file = "grpcio-1.66.2-cp311-cp311-win32.whl", hash = "sha256:38b68498ff579a3b1ee8f93a05eb48dc2595795f2f62716e797dc24774c1aaa8"}, + {file = "grpcio-1.66.2-cp311-cp311-win_amd64.whl", hash = "sha256:6851de821249340bdb100df5eacfecfc4e6075fa85c6df7ee0eb213170ec8e5d"}, + {file = "grpcio-1.66.2-cp312-cp312-linux_armv7l.whl", hash = "sha256:802d84fd3d50614170649853d121baaaa305de7b65b3e01759247e768d691ddf"}, + {file = "grpcio-1.66.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:80fd702ba7e432994df208f27514280b4b5c6843e12a48759c9255679ad38db8"}, + {file = "grpcio-1.66.2-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:12fda97ffae55e6526825daf25ad0fa37483685952b5d0f910d6405c87e3adb6"}, + {file = "grpcio-1.66.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:950da58d7d80abd0ea68757769c9db0a95b31163e53e5bb60438d263f4bed7b7"}, + {file = "grpcio-1.66.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e636ce23273683b00410f1971d209bf3689238cf5538d960adc3cdfe80dd0dbd"}, + {file = "grpcio-1.66.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a917d26e0fe980b0ac7bfcc1a3c4ad6a9a4612c911d33efb55ed7833c749b0ee"}, + {file = "grpcio-1.66.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49f0ca7ae850f59f828a723a9064cadbed90f1ece179d375966546499b8a2c9c"}, + {file = "grpcio-1.66.2-cp312-cp312-win32.whl", hash = "sha256:31fd163105464797a72d901a06472860845ac157389e10f12631025b3e4d0453"}, + {file = "grpcio-1.66.2-cp312-cp312-win_amd64.whl", hash = "sha256:ff1f7882e56c40b0d33c4922c15dfa30612f05fb785074a012f7cda74d1c3679"}, + {file = "grpcio-1.66.2-cp313-cp313-linux_armv7l.whl", hash = "sha256:3b00efc473b20d8bf83e0e1ae661b98951ca56111feb9b9611df8efc4fe5d55d"}, + {file = "grpcio-1.66.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1caa38fb22a8578ab8393da99d4b8641e3a80abc8fd52646f1ecc92bcb8dee34"}, + {file = "grpcio-1.66.2-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:c408f5ef75cfffa113cacd8b0c0e3611cbfd47701ca3cdc090594109b9fcbaed"}, + {file = "grpcio-1.66.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c806852deaedee9ce8280fe98955c9103f62912a5b2d5ee7e3eaa284a6d8d8e7"}, + {file = "grpcio-1.66.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f145cc21836c332c67baa6fc81099d1d27e266401565bf481948010d6ea32d46"}, + {file = "grpcio-1.66.2-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:73e3b425c1e155730273f73e419de3074aa5c5e936771ee0e4af0814631fb30a"}, + {file = "grpcio-1.66.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9c509a4f78114cbc5f0740eb3d7a74985fd2eff022971bc9bc31f8bc93e66a3b"}, + {file = "grpcio-1.66.2-cp313-cp313-win32.whl", hash = "sha256:20657d6b8cfed7db5e11b62ff7dfe2e12064ea78e93f1434d61888834bc86d75"}, + {file = "grpcio-1.66.2-cp313-cp313-win_amd64.whl", hash = "sha256:fb70487c95786e345af5e854ffec8cb8cc781bcc5df7930c4fbb7feaa72e1cdf"}, + {file = "grpcio-1.66.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:a18e20d8321c6400185b4263e27982488cb5cdd62da69147087a76a24ef4e7e3"}, + {file = "grpcio-1.66.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:02697eb4a5cbe5a9639f57323b4c37bcb3ab2d48cec5da3dc2f13334d72790dd"}, + {file = "grpcio-1.66.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:99a641995a6bc4287a6315989ee591ff58507aa1cbe4c2e70d88411c4dcc0839"}, + {file = "grpcio-1.66.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ed71e81782966ffead60268bbda31ea3f725ebf8aa73634d5dda44f2cf3fb9c"}, + {file = "grpcio-1.66.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbd27c24a4cc5e195a7f56cfd9312e366d5d61b86e36d46bbe538457ea6eb8dd"}, + {file = "grpcio-1.66.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d9a9724a156c8ec6a379869b23ba3323b7ea3600851c91489b871e375f710bc8"}, + {file = "grpcio-1.66.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d8d4732cc5052e92cea2f78b233c2e2a52998ac40cd651f40e398893ad0d06ec"}, + {file = "grpcio-1.66.2-cp38-cp38-win32.whl", hash = "sha256:7b2c86457145ce14c38e5bf6bdc19ef88e66c5fee2c3d83285c5aef026ba93b3"}, + {file = "grpcio-1.66.2-cp38-cp38-win_amd64.whl", hash = "sha256:e88264caad6d8d00e7913996030bac8ad5f26b7411495848cc218bd3a9040b6c"}, + {file = "grpcio-1.66.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:c400ba5675b67025c8a9f48aa846f12a39cf0c44df5cd060e23fda5b30e9359d"}, + {file = "grpcio-1.66.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:66a0cd8ba6512b401d7ed46bb03f4ee455839957f28b8d61e7708056a806ba6a"}, + {file = "grpcio-1.66.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:06de8ec0bd71be123eec15b0e0d457474931c2c407869b6c349bd9bed4adbac3"}, + {file = "grpcio-1.66.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb57870449dfcfac428afbb5a877829fcb0d6db9d9baa1148705739e9083880e"}, + {file = "grpcio-1.66.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b672abf90a964bfde2d0ecbce30f2329a47498ba75ce6f4da35a2f4532b7acbc"}, + {file = "grpcio-1.66.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ad2efdbe90c73b0434cbe64ed372e12414ad03c06262279b104a029d1889d13e"}, + {file = "grpcio-1.66.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c3a99c519f4638e700e9e3f83952e27e2ea10873eecd7935823dab0c1c9250e"}, + {file = "grpcio-1.66.2-cp39-cp39-win32.whl", hash = "sha256:78fa51ebc2d9242c0fc5db0feecc57a9943303b46664ad89921f5079e2e4ada7"}, + {file = "grpcio-1.66.2-cp39-cp39-win_amd64.whl", hash = "sha256:728bdf36a186e7f51da73be7f8d09457a03061be848718d0edf000e709418987"}, + {file = "grpcio-1.66.2.tar.gz", hash = "sha256:563588c587b75c34b928bc428548e5b00ea38c46972181a4d8b75ba7e3f24231"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.66.1)"] +protobuf = ["grpcio-tools (>=1.66.2)"] [[package]] name = "h11" @@ -445,13 +757,13 @@ files = [ [[package]] name = "httpcore" -version = "1.0.5" +version = "1.0.6" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, - {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, + {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, + {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, ] [package.dependencies] @@ -462,65 +774,17 @@ h11 = ">=0.13,<0.15" asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<0.26.0)"] - -[[package]] -name = "httptools" -version = "0.6.1" -description = "A collection of framework independent HTTP protocol utils." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, - {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, - {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, - {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, - {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, - {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, - {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, - {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, - {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, - {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, - {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, - {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, - {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, - {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, - {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, - {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, - {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, - {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, - {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, - {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, - {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, - {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, - {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, - {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, - {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, - {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, - {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, - {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, - {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, - {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, - {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, - {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, - {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, - {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, - {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, - {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, -] - -[package.extras] -test = ["Cython (>=0.29.24,<0.30.0)"] +trio = ["trio (>=0.22.0,<1.0)"] [[package]] name = "httpx" -version = "0.27.0" +version = "0.27.2" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, - {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, + {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, + {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, ] [package.dependencies] @@ -535,27 +799,65 @@ brotli = ["brotli", "brotlicffi"] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "huggingface-hub" +version = "0.25.2" +description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "huggingface_hub-0.25.2-py3-none-any.whl", hash = "sha256:1897caf88ce7f97fe0110603d8f66ac264e3ba6accdf30cd66cc0fed5282ad25"}, + {file = "huggingface_hub-0.25.2.tar.gz", hash = "sha256:a1014ea111a5f40ccd23f7f7ba8ac46e20fa3b658ced1f86a00c75c06ec6423c"}, +] + +[package.dependencies] +filelock = "*" +fsspec = ">=2023.5.0" +packaging = ">=20.9" +pyyaml = ">=5.1" +requests = "*" +tqdm = ">=4.42.1" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +cli = ["InquirerPy (==0.3.4)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] +hf-transfer = ["hf-transfer (>=0.1.4)"] +inference = ["aiohttp", "minijinja (>=1.0)"] +quality = ["mypy (==1.5.1)", "ruff (>=0.5.0)"] +tensorflow = ["graphviz", "pydot", "tensorflow"] +tensorflow-testing = ["keras (<3.0)", "tensorflow"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +torch = ["safetensors[torch]", "torch"] +typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] [[package]] name = "idna" -version = "3.7" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + [[package]] name = "importlib-metadata" -version = "8.2.0" +version = "8.4.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-8.2.0-py3-none-any.whl", hash = "sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369"}, - {file = "importlib_metadata-8.2.0.tar.gz", hash = "sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d"}, + {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"}, + {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"}, ] [package.dependencies] @@ -667,96 +969,218 @@ MarkupSafe = ">=2.0" i18n = ["Babel (>=2.7)"] [[package]] -name = "markdown-it-py" -version = "3.0.0" -description = "Python port of markdown-it. Markdown parsing, done right!" +name = "jiter" +version = "0.6.1" +description = "Fast iterable JSON parser." optional = false python-versions = ">=3.8" files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, + {file = "jiter-0.6.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:d08510593cb57296851080018006dfc394070178d238b767b1879dc1013b106c"}, + {file = "jiter-0.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:adef59d5e2394ebbad13b7ed5e0306cceb1df92e2de688824232a91588e77aa7"}, + {file = "jiter-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3e02f7a27f2bcc15b7d455c9df05df8ffffcc596a2a541eeda9a3110326e7a3"}, + {file = "jiter-0.6.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed69a7971d67b08f152c17c638f0e8c2aa207e9dd3a5fcd3cba294d39b5a8d2d"}, + {file = "jiter-0.6.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2019d966e98f7c6df24b3b8363998575f47d26471bfb14aade37630fae836a1"}, + {file = "jiter-0.6.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36c0b51a285b68311e207a76c385650322734c8717d16c2eb8af75c9d69506e7"}, + {file = "jiter-0.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:220e0963b4fb507c525c8f58cde3da6b1be0bfddb7ffd6798fb8f2531226cdb1"}, + {file = "jiter-0.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aa25c7a9bf7875a141182b9c95aed487add635da01942ef7ca726e42a0c09058"}, + {file = "jiter-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e90552109ca8ccd07f47ca99c8a1509ced93920d271bb81780a973279974c5ab"}, + {file = "jiter-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:67723a011964971864e0b484b0ecfee6a14de1533cff7ffd71189e92103b38a8"}, + {file = "jiter-0.6.1-cp310-none-win32.whl", hash = "sha256:33af2b7d2bf310fdfec2da0177eab2fedab8679d1538d5b86a633ebfbbac4edd"}, + {file = "jiter-0.6.1-cp310-none-win_amd64.whl", hash = "sha256:7cea41c4c673353799906d940eee8f2d8fd1d9561d734aa921ae0f75cb9732f4"}, + {file = "jiter-0.6.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b03c24e7da7e75b170c7b2b172d9c5e463aa4b5c95696a368d52c295b3f6847f"}, + {file = "jiter-0.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:47fee1be677b25d0ef79d687e238dc6ac91a8e553e1a68d0839f38c69e0ee491"}, + {file = "jiter-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25f0d2f6e01a8a0fb0eab6d0e469058dab2be46ff3139ed2d1543475b5a1d8e7"}, + {file = "jiter-0.6.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b809e39e342c346df454b29bfcc7bca3d957f5d7b60e33dae42b0e5ec13e027"}, + {file = "jiter-0.6.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e9ac7c2f092f231f5620bef23ce2e530bd218fc046098747cc390b21b8738a7a"}, + {file = "jiter-0.6.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e51a2d80d5fe0ffb10ed2c82b6004458be4a3f2b9c7d09ed85baa2fbf033f54b"}, + {file = "jiter-0.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3343d4706a2b7140e8bd49b6c8b0a82abf9194b3f0f5925a78fc69359f8fc33c"}, + {file = "jiter-0.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82521000d18c71e41c96960cb36e915a357bc83d63a8bed63154b89d95d05ad1"}, + {file = "jiter-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3c843e7c1633470708a3987e8ce617ee2979ee18542d6eb25ae92861af3f1d62"}, + {file = "jiter-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a2e861658c3fe849efc39b06ebb98d042e4a4c51a8d7d1c3ddc3b1ea091d0784"}, + {file = "jiter-0.6.1-cp311-none-win32.whl", hash = "sha256:7d72fc86474862c9c6d1f87b921b70c362f2b7e8b2e3c798bb7d58e419a6bc0f"}, + {file = "jiter-0.6.1-cp311-none-win_amd64.whl", hash = "sha256:3e36a320634f33a07794bb15b8da995dccb94f944d298c8cfe2bd99b1b8a574a"}, + {file = "jiter-0.6.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1fad93654d5a7dcce0809aff66e883c98e2618b86656aeb2129db2cd6f26f867"}, + {file = "jiter-0.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4e6e340e8cd92edab7f6a3a904dbbc8137e7f4b347c49a27da9814015cc0420c"}, + {file = "jiter-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:691352e5653af84ed71763c3c427cff05e4d658c508172e01e9c956dfe004aba"}, + {file = "jiter-0.6.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:defee3949313c1f5b55e18be45089970cdb936eb2a0063f5020c4185db1b63c9"}, + {file = "jiter-0.6.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26d2bdd5da097e624081c6b5d416d3ee73e5b13f1703bcdadbb1881f0caa1933"}, + {file = "jiter-0.6.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18aa9d1626b61c0734b973ed7088f8a3d690d0b7f5384a5270cd04f4d9f26c86"}, + {file = "jiter-0.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a3567c8228afa5ddcce950631c6b17397ed178003dc9ee7e567c4c4dcae9fa0"}, + {file = "jiter-0.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e5c0507131c922defe3f04c527d6838932fcdfd69facebafd7d3574fa3395314"}, + {file = "jiter-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:540fcb224d7dc1bcf82f90f2ffb652df96f2851c031adca3c8741cb91877143b"}, + {file = "jiter-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e7b75436d4fa2032b2530ad989e4cb0ca74c655975e3ff49f91a1a3d7f4e1df2"}, + {file = "jiter-0.6.1-cp312-none-win32.whl", hash = "sha256:883d2ced7c21bf06874fdeecab15014c1c6d82216765ca6deef08e335fa719e0"}, + {file = "jiter-0.6.1-cp312-none-win_amd64.whl", hash = "sha256:91e63273563401aadc6c52cca64a7921c50b29372441adc104127b910e98a5b6"}, + {file = "jiter-0.6.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:852508a54fe3228432e56019da8b69208ea622a3069458252f725d634e955b31"}, + {file = "jiter-0.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f491cc69ff44e5a1e8bc6bf2b94c1f98d179e1aaf4a554493c171a5b2316b701"}, + {file = "jiter-0.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc56c8f0b2a28ad4d8047f3ae62d25d0e9ae01b99940ec0283263a04724de1f3"}, + {file = "jiter-0.6.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:51b58f7a0d9e084a43b28b23da2b09fc5e8df6aa2b6a27de43f991293cab85fd"}, + {file = "jiter-0.6.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f79ce15099154c90ef900d69c6b4c686b64dfe23b0114e0971f2fecd306ec6c"}, + {file = "jiter-0.6.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:03a025b52009f47e53ea619175d17e4ded7c035c6fbd44935cb3ada11e1fd592"}, + {file = "jiter-0.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c74a8d93718137c021d9295248a87c2f9fdc0dcafead12d2930bc459ad40f885"}, + {file = "jiter-0.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40b03b75f903975f68199fc4ec73d546150919cb7e534f3b51e727c4d6ccca5a"}, + {file = "jiter-0.6.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:825651a3f04cf92a661d22cad61fc913400e33aa89b3e3ad9a6aa9dc8a1f5a71"}, + {file = "jiter-0.6.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:928bf25eb69ddb292ab8177fe69d3fbf76c7feab5fce1c09265a7dccf25d3991"}, + {file = "jiter-0.6.1-cp313-none-win32.whl", hash = "sha256:352cd24121e80d3d053fab1cc9806258cad27c53cad99b7a3cac57cf934b12e4"}, + {file = "jiter-0.6.1-cp313-none-win_amd64.whl", hash = "sha256:be7503dd6f4bf02c2a9bacb5cc9335bc59132e7eee9d3e931b13d76fd80d7fda"}, + {file = "jiter-0.6.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:31d8e00e1fb4c277df8ab6f31a671f509ebc791a80e5c61fdc6bc8696aaa297c"}, + {file = "jiter-0.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77c296d65003cd7ee5d7b0965f6acbe6cffaf9d1fa420ea751f60ef24e85fed5"}, + {file = "jiter-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeeb0c0325ef96c12a48ea7e23e2e86fe4838e6e0a995f464cf4c79fa791ceeb"}, + {file = "jiter-0.6.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a31c6fcbe7d6c25d6f1cc6bb1cba576251d32795d09c09961174fe461a1fb5bd"}, + {file = "jiter-0.6.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59e2b37f3b9401fc9e619f4d4badcab2e8643a721838bcf695c2318a0475ae42"}, + {file = "jiter-0.6.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bae5ae4853cb9644144e9d0755854ce5108d470d31541d83f70ca7ecdc2d1637"}, + {file = "jiter-0.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9df588e9c830b72d8db1dd7d0175af6706b0904f682ea9b1ca8b46028e54d6e9"}, + {file = "jiter-0.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15f8395e835cf561c85c1adee72d899abf2733d9df72e9798e6d667c9b5c1f30"}, + {file = "jiter-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a99d4e0b5fc3b05ea732d67eb2092fe894e95a90e6e413f2ea91387e228a307"}, + {file = "jiter-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a311df1fa6be0ccd64c12abcd85458383d96e542531bafbfc0a16ff6feda588f"}, + {file = "jiter-0.6.1-cp38-none-win32.whl", hash = "sha256:81116a6c272a11347b199f0e16b6bd63f4c9d9b52bc108991397dd80d3c78aba"}, + {file = "jiter-0.6.1-cp38-none-win_amd64.whl", hash = "sha256:13f9084e3e871a7c0b6e710db54444088b1dd9fbefa54d449b630d5e73bb95d0"}, + {file = "jiter-0.6.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:f1c53615fcfec3b11527c08d19cff6bc870da567ce4e57676c059a3102d3a082"}, + {file = "jiter-0.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f791b6a4da23238c17a81f44f5b55d08a420c5692c1fda84e301a4b036744eb1"}, + {file = "jiter-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c97e90fec2da1d5f68ef121444c2c4fa72eabf3240829ad95cf6bbeca42a301"}, + {file = "jiter-0.6.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3cbc1a66b4e41511209e97a2866898733c0110b7245791ac604117b7fb3fedb7"}, + {file = "jiter-0.6.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4e85f9e12cd8418ab10e1fcf0e335ae5bb3da26c4d13a0fd9e6a17a674783b6"}, + {file = "jiter-0.6.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08be33db6dcc374c9cc19d3633af5e47961a7b10d4c61710bd39e48d52a35824"}, + {file = "jiter-0.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:677be9550004f5e010d673d3b2a2b815a8ea07a71484a57d3f85dde7f14cf132"}, + {file = "jiter-0.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e8bd065be46c2eecc328e419d6557bbc37844c88bb07b7a8d2d6c91c7c4dedc9"}, + {file = "jiter-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bd95375ce3609ec079a97c5d165afdd25693302c071ca60c7ae1cf826eb32022"}, + {file = "jiter-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db459ed22d0208940d87f614e1f0ea5a946d29a3cfef71f7e1aab59b6c6b2afb"}, + {file = "jiter-0.6.1-cp39-none-win32.whl", hash = "sha256:d71c962f0971347bd552940ab96aa42ceefcd51b88c4ced8a27398182efa8d80"}, + {file = "jiter-0.6.1-cp39-none-win_amd64.whl", hash = "sha256:d465db62d2d10b489b7e7a33027c4ae3a64374425d757e963f86df5b5f2e7fc5"}, + {file = "jiter-0.6.1.tar.gz", hash = "sha256:e19cd21221fc139fb032e4112986656cb2739e9fe6d84c13956ab30ccc7d4449"}, +] + +[[package]] +name = "jsonschema" +version = "4.23.0" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, + {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +jsonschema-specifications = ">=2023.03.6" +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=24.6.0)"] + +[[package]] +name = "jsonschema-specifications" +version = "2024.10.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.9" +files = [ + {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, + {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, +] + +[package.dependencies] +referencing = ">=0.31.0" + +[[package]] +name = "litellm" +version = "1.49.3" +description = "Library to easily interface with LLM API providers" +optional = false +python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" +files = [ + {file = "litellm-1.49.3-py3-none-any.whl", hash = "sha256:300c3c9e1600441f8b6d3afe0fd79c6193f901b2091f3730883ffe3709eebfa2"}, + {file = "litellm-1.49.3.tar.gz", hash = "sha256:e51ce30286894803dcf2949ddb4aab5c2e00809694a48ce6e997953566113c0b"}, ] [package.dependencies] -mdurl = ">=0.1,<1.0" +aiohttp = "*" +click = "*" +importlib-metadata = ">=6.8.0" +jinja2 = ">=3.1.2,<4.0.0" +jsonschema = ">=4.22.0,<5.0.0" +openai = ">=1.51.0" +pydantic = ">=2.0.0,<3.0.0" +python-dotenv = ">=0.2.0" +requests = ">=2.31.0,<3.0.0" +tiktoken = ">=0.7.0" +tokenizers = "*" [package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] -linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] -profiling = ["gprof2dot"] -rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "resend (>=0.8.0,<0.9.0)"] +proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "cryptography (>=42.0.5,<43.0.0)", "fastapi (>=0.111.0,<0.112.0)", "fastapi-sso (>=0.10.0,<0.11.0)", "gunicorn (>=22.0.0,<23.0.0)", "orjson (>=3.9.7,<4.0.0)", "pynacl (>=1.5.0,<2.0.0)", "python-multipart (>=0.0.9,<0.0.10)", "pyyaml (>=6.0.1,<7.0.0)", "rq", "uvicorn (>=0.22.0,<0.23.0)"] [[package]] name = "markupsafe" -version = "2.1.5" +version = "3.0.1" description = "Safely add untrusted strings to HTML/XML markup." optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:db842712984e91707437461930e6011e60b39136c7331e971952bb30465bc1a1"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ffb4a8e7d46ed96ae48805746755fadd0909fea2306f93d5d8233ba23dda12a"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67c519635a4f64e495c50e3107d9b4075aec33634272b5db1cde839e07367589"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48488d999ed50ba8d38c581d67e496f955821dc183883550a6fbc7f1aefdc170"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f31ae06f1328595d762c9a2bf29dafd8621c7d3adc130cbb46278079758779ca"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80fcbf3add8790caddfab6764bde258b5d09aefbe9169c183f88a7410f0f6dea"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3341c043c37d78cc5ae6e3e305e988532b072329639007fd408a476642a89fd6"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cb53e2a99df28eee3b5f4fea166020d3ef9116fdc5764bc5117486e6d1211b25"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-win32.whl", hash = "sha256:db15ce28e1e127a0013dfb8ac243a8e392db8c61eae113337536edb28bdc1f97"}, + {file = "MarkupSafe-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:4ffaaac913c3f7345579db4f33b0020db693f302ca5137f106060316761beea9"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:26627785a54a947f6d7336ce5963569b5d75614619e75193bdb4e06e21d447ad"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b954093679d5750495725ea6f88409946d69cfb25ea7b4c846eef5044194f583"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:973a371a55ce9ed333a3a0f8e0bcfae9e0d637711534bcb11e130af2ab9334e7"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d98e66a24497637dd31ccab090b34392dddb1f2f811c4b4cd80c230205c074a3"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad91738f14eb8da0ff82f2acd0098b6257621410dcbd4df20aaa5b4233d75a50"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7044312a928a66a4c2a22644147bc61a199c1709712069a344a3fb5cfcf16915"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a4792d3b3a6dfafefdf8e937f14906a51bd27025a36f4b188728a73382231d91"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-win32.whl", hash = "sha256:fa7d686ed9883f3d664d39d5a8e74d3c5f63e603c2e3ff0abcba23eac6542635"}, + {file = "MarkupSafe-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ba25a71ebf05b9bb0e2ae99f8bc08a07ee8e98c612175087112656ca0f5c8bf"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8ae369e84466aa70f3154ee23c1451fda10a8ee1b63923ce76667e3077f2b0c4"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40f1e10d51c92859765522cbd79c5c8989f40f0419614bcdc5015e7b6bf97fc5"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a4cb365cb49b750bdb60b846b0c0bc49ed62e59a76635095a179d440540c346"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee3941769bd2522fe39222206f6dd97ae83c442a94c90f2b7a25d847d40f4729"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62fada2c942702ef8952754abfc1a9f7658a4d5460fabe95ac7ec2cbe0d02abc"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c2d64fdba74ad16138300815cfdc6ab2f4647e23ced81f59e940d7d4a1469d9"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fb532dd9900381d2e8f48172ddc5a59db4c445a11b9fab40b3b786da40d3b56b"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0f84af7e813784feb4d5e4ff7db633aba6c8ca64a833f61d8e4eade234ef0c38"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-win32.whl", hash = "sha256:cbf445eb5628981a80f54087f9acdbf84f9b7d862756110d172993b9a5ae81aa"}, + {file = "MarkupSafe-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:a10860e00ded1dd0a65b83e717af28845bb7bd16d8ace40fe5531491de76b79f"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e81c52638315ff4ac1b533d427f50bc0afc746deb949210bc85f05d4f15fd772"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:312387403cd40699ab91d50735ea7a507b788091c416dd007eac54434aee51da"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ae99f31f47d849758a687102afdd05bd3d3ff7dbab0a8f1587981b58a76152a"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c97ff7fedf56d86bae92fa0a646ce1a0ec7509a7578e1ed238731ba13aabcd1c"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7420ceda262dbb4b8d839a4ec63d61c261e4e77677ed7c66c99f4e7cb5030dd"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45d42d132cff577c92bfba536aefcfea7e26efb975bd455db4e6602f5c9f45e7"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c8817557d0de9349109acb38b9dd570b03cc5014e8aabf1cbddc6e81005becd"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a54c43d3ec4cf2a39f4387ad044221c66a376e58c0d0e971d47c475ba79c6b5"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-win32.whl", hash = "sha256:c91b394f7601438ff79a4b93d16be92f216adb57d813a78be4446fe0f6bc2d8c"}, + {file = "MarkupSafe-3.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:fe32482b37b4b00c7a52a07211b479653b7fe4f22b2e481b9a9b099d8a430f2f"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:17b2aea42a7280db02ac644db1d634ad47dcc96faf38ab304fe26ba2680d359a"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:852dc840f6d7c985603e60b5deaae1d89c56cb038b577f6b5b8c808c97580f1d"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0778de17cff1acaeccc3ff30cd99a3fd5c50fc58ad3d6c0e0c4c58092b859396"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:800100d45176652ded796134277ecb13640c1a537cad3b8b53da45aa96330453"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d06b24c686a34c86c8c1fba923181eae6b10565e4d80bdd7bc1c8e2f11247aa4"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:33d1c36b90e570ba7785dacd1faaf091203d9942bc036118fab8110a401eb1a8"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:beeebf760a9c1f4c07ef6a53465e8cfa776ea6a2021eda0d0417ec41043fe984"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bbde71a705f8e9e4c3e9e33db69341d040c827c7afa6789b14c6e16776074f5a"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-win32.whl", hash = "sha256:82b5dba6eb1bcc29cc305a18a3c5365d2af06ee71b123216416f7e20d2a84e5b"}, + {file = "MarkupSafe-3.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:730d86af59e0e43ce277bb83970530dd223bf7f2a838e086b50affa6ec5f9295"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4935dd7883f1d50e2ffecca0aa33dc1946a94c8f3fdafb8df5c330e48f71b132"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e9393357f19954248b00bed7c56f29a25c930593a77630c719653d51e7669c2a"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40621d60d0e58aa573b68ac5e2d6b20d44392878e0bfc159012a5787c4e35bc8"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f94190df587738280d544971500b9cafc9b950d32efcb1fba9ac10d84e6aa4e6"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6a387d61fe41cdf7ea95b38e9af11cfb1a63499af2759444b99185c4ab33f5b"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8ad4ad1429cd4f315f32ef263c1342166695fad76c100c5d979c45d5570ed58b"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e24bfe89c6ac4c31792793ad9f861b8f6dc4546ac6dc8f1c9083c7c4f2b335cd"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2a4b34a8d14649315c4bc26bbfa352663eb51d146e35eef231dd739d54a5430a"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-win32.whl", hash = "sha256:242d6860f1fd9191aef5fae22b51c5c19767f93fb9ead4d21924e0bcb17619d8"}, + {file = "MarkupSafe-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:93e8248d650e7e9d49e8251f883eed60ecbc0e8ffd6349e18550925e31bd029b"}, + {file = "markupsafe-3.0.1.tar.gz", hash = "sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344"}, ] [[package]] @@ -773,17 +1197,6 @@ files = [ [package.dependencies] traitlets = "*" -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - [[package]] name = "monotonic" version = "1.6" @@ -795,6 +1208,134 @@ files = [ {file = "monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7"}, ] +[[package]] +name = "multidict" +version = "6.1.0" +description = "multidict implementation" +optional = false +python-versions = ">=3.8" +files = [ + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, + {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, + {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, + {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, + {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, + {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, + {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, + {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, + {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, + {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, + {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, + {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, + {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, + {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, + {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "openai" +version = "1.51.2" +description = "The official Python library for the openai API" +optional = false +python-versions = ">=3.7.1" +files = [ + {file = "openai-1.51.2-py3-none-any.whl", hash = "sha256:5c5954711cba931423e471c37ff22ae0fd3892be9b083eee36459865fbbb83fa"}, + {file = "openai-1.51.2.tar.gz", hash = "sha256:c6a51fac62a1ca9df85a522e462918f6bb6bc51a8897032217e453a0730123a6"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +jiter = ">=0.4.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +tqdm = ">4" +typing-extensions = ">=4.11,<5" + +[package.extras] +datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] + [[package]] name = "opentelemetry-api" version = "1.27.0" @@ -981,13 +1522,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "posthog" -version = "3.5.0" +version = "3.7.0" description = "Integrate PostHog into any python application." optional = false python-versions = "*" files = [ - {file = "posthog-3.5.0-py2.py3-none-any.whl", hash = "sha256:3c672be7ba6f95d555ea207d4486c171d06657eb34b3ce25eb043bfe7b6b5b76"}, - {file = "posthog-3.5.0.tar.gz", hash = "sha256:8f7e3b2c6e8714d0c0c542a2109b83a7549f63b7113a133ab2763a89245ef2ef"}, + {file = "posthog-3.7.0-py2.py3-none-any.whl", hash = "sha256:3555161c3a9557b5666f96d8e1f17f410ea0f07db56e399e336a1656d4e5c722"}, + {file = "posthog-3.7.0.tar.gz", hash = "sha256:b095d4354ba23f8b346ab5daed8ecfc5108772f922006982dfe8b2d29ebc6e0e"}, ] [package.dependencies] @@ -1000,7 +1541,7 @@ six = ">=1.5" [package.extras] dev = ["black", "flake8", "flake8-print", "isort", "pre-commit"] sentry = ["django", "sentry-sdk"] -test = ["coverage", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint", "pytest", "pytest-timeout"] +test = ["coverage", "django", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint", "pytest", "pytest-timeout"] [[package]] name = "prompt-toolkit" @@ -1016,24 +1557,131 @@ files = [ [package.dependencies] wcwidth = "*" +[[package]] +name = "propcache" +version = "0.2.0" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.8" +files = [ + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336"}, + {file = "propcache-0.2.0-cp310-cp310-win32.whl", hash = "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad"}, + {file = "propcache-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b"}, + {file = "propcache-0.2.0-cp311-cp311-win32.whl", hash = "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1"}, + {file = "propcache-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348"}, + {file = "propcache-0.2.0-cp312-cp312-win32.whl", hash = "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5"}, + {file = "propcache-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544"}, + {file = "propcache-0.2.0-cp313-cp313-win32.whl", hash = "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032"}, + {file = "propcache-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed"}, + {file = "propcache-0.2.0-cp38-cp38-win32.whl", hash = "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d"}, + {file = "propcache-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798"}, + {file = "propcache-0.2.0-cp39-cp39-win32.whl", hash = "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9"}, + {file = "propcache-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df"}, + {file = "propcache-0.2.0-py3-none-any.whl", hash = "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036"}, + {file = "propcache-0.2.0.tar.gz", hash = "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70"}, +] + [[package]] name = "protobuf" -version = "4.25.4" +version = "4.25.5" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.4-cp310-abi3-win32.whl", hash = "sha256:db9fd45183e1a67722cafa5c1da3e85c6492a5383f127c86c4c4aa4845867dc4"}, - {file = "protobuf-4.25.4-cp310-abi3-win_amd64.whl", hash = "sha256:ba3d8504116a921af46499471c63a85260c1a5fc23333154a427a310e015d26d"}, - {file = "protobuf-4.25.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:eecd41bfc0e4b1bd3fa7909ed93dd14dd5567b98c941d6c1ad08fdcab3d6884b"}, - {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4c8a70fdcb995dcf6c8966cfa3a29101916f7225e9afe3ced4395359955d3835"}, - {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:3319e073562e2515c6ddc643eb92ce20809f5d8f10fead3332f71c63be6a7040"}, - {file = "protobuf-4.25.4-cp38-cp38-win32.whl", hash = "sha256:7e372cbbda66a63ebca18f8ffaa6948455dfecc4e9c1029312f6c2edcd86c4e1"}, - {file = "protobuf-4.25.4-cp38-cp38-win_amd64.whl", hash = "sha256:051e97ce9fa6067a4546e75cb14f90cf0232dcb3e3d508c448b8d0e4265b61c1"}, - {file = "protobuf-4.25.4-cp39-cp39-win32.whl", hash = "sha256:90bf6fd378494eb698805bbbe7afe6c5d12c8e17fca817a646cd6a1818c696ca"}, - {file = "protobuf-4.25.4-cp39-cp39-win_amd64.whl", hash = "sha256:ac79a48d6b99dfed2729ccccee547b34a1d3d63289c71cef056653a846a2240f"}, - {file = "protobuf-4.25.4-py3-none-any.whl", hash = "sha256:bfbebc1c8e4793cfd58589acfb8a1026be0003e852b9da7db5a4285bde996978"}, - {file = "protobuf-4.25.4.tar.gz", hash = "sha256:0dc4a62cc4052a036ee2204d26fe4d835c62827c855c8a03f29fe6da146b380d"}, + {file = "protobuf-4.25.5-cp310-abi3-win32.whl", hash = "sha256:5e61fd921603f58d2f5acb2806a929b4675f8874ff5f330b7d6f7e2e784bbcd8"}, + {file = "protobuf-4.25.5-cp310-abi3-win_amd64.whl", hash = "sha256:4be0571adcbe712b282a330c6e89eae24281344429ae95c6d85e79e84780f5ea"}, + {file = "protobuf-4.25.5-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:b2fde3d805354df675ea4c7c6338c1aecd254dfc9925e88c6d31a2bcb97eb173"}, + {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:919ad92d9b0310070f8356c24b855c98df2b8bd207ebc1c0c6fcc9ab1e007f3d"}, + {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fe14e16c22be926d3abfcb500e60cab068baf10b542b8c858fa27e098123e331"}, + {file = "protobuf-4.25.5-cp38-cp38-win32.whl", hash = "sha256:98d8d8aa50de6a2747efd9cceba361c9034050ecce3e09136f90de37ddba66e1"}, + {file = "protobuf-4.25.5-cp38-cp38-win_amd64.whl", hash = "sha256:b0234dd5a03049e4ddd94b93400b67803c823cfc405689688f59b34e0742381a"}, + {file = "protobuf-4.25.5-cp39-cp39-win32.whl", hash = "sha256:abe32aad8561aa7cc94fc7ba4fdef646e576983edb94a73381b03c53728a626f"}, + {file = "protobuf-4.25.5-cp39-cp39-win_amd64.whl", hash = "sha256:7a183f592dc80aa7c8da7ad9e55091c4ffc9497b3054452d629bb85fa27c2a45"}, + {file = "protobuf-4.25.5-py3-none-any.whl", hash = "sha256:0aebecb809cae990f8129ada5ca273d9d670b76d9bfc9b1809f0a9c02b7dbf41"}, + {file = "protobuf-4.25.5.tar.gz", hash = "sha256:7f8249476b4a9473645db7f8ab42b02fe1488cbe5fb72fddd445e0665afd8584"}, ] [[package]] @@ -1063,18 +1711,18 @@ tests = ["pytest"] [[package]] name = "pydantic" -version = "2.8.2" +version = "2.9.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"}, - {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"}, + {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, + {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, ] [package.dependencies] -annotated-types = ">=0.4.0" -pydantic-core = "2.20.1" +annotated-types = ">=0.6.0" +pydantic-core = "2.23.4" typing-extensions = [ {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, {version = ">=4.6.1", markers = "python_version < \"3.13\""}, @@ -1082,103 +1730,104 @@ typing-extensions = [ [package.extras] email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.20.1" +version = "2.23.4" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"}, - {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"}, - {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"}, - {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"}, - {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"}, - {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"}, - {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"}, - {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"}, - {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"}, - {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"}, - {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"}, - {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"}, - {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"}, - {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"}, - {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"}, - {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"}, - {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"}, - {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"}, - {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"}, - {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"}, - {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"}, - {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"}, - {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"}, - {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"}, - {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"}, - {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"}, - {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"}, - {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"}, - {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"}, - {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"}, - {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"}, - {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"}, - {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"}, - {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"}, - {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"}, - {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"}, - {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"}, - {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"}, - {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"}, - {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"}, - {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"}, - {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"}, - {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"}, - {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"}, - {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"}, - {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"}, - {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"}, - {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"}, - {file = "pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91"}, - {file = "pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b"}, - {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a"}, - {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f"}, - {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad"}, - {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c"}, - {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598"}, - {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd"}, - {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa"}, - {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987"}, - {file = "pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a"}, - {file = "pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434"}, - {file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"}, - {file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"}, - {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"}, - {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"}, - {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"}, - {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"}, - {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"}, - {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"}, - {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"}, - {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"}, - {file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"}, - {file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"}, - {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"}, - {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"}, - {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, + {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, + {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, + {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, + {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, + {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, + {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, + {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, + {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, + {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, + {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, + {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, + {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, + {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, ] [package.dependencies] @@ -1200,61 +1849,70 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymongo" -version = "4.8.0" +version = "4.10.1" description = "Python driver for MongoDB " optional = false python-versions = ">=3.8" files = [ - {file = "pymongo-4.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2b7bec27e047e84947fbd41c782f07c54c30c76d14f3b8bf0c89f7413fac67a"}, - {file = "pymongo-4.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c68fe128a171493018ca5c8020fc08675be130d012b7ab3efe9e22698c612a1"}, - {file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:920d4f8f157a71b3cb3f39bc09ce070693d6e9648fb0e30d00e2657d1dca4e49"}, - {file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52b4108ac9469febba18cea50db972605cc43978bedaa9fea413378877560ef8"}, - {file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:180d5eb1dc28b62853e2f88017775c4500b07548ed28c0bd9c005c3d7bc52526"}, - {file = "pymongo-4.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aec2b9088cdbceb87e6ca9c639d0ff9b9d083594dda5ca5d3c4f6774f4c81b33"}, - {file = "pymongo-4.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0cf61450feadca81deb1a1489cb1a3ae1e4266efd51adafecec0e503a8dcd84"}, - {file = "pymongo-4.8.0-cp310-cp310-win32.whl", hash = "sha256:8b18c8324809539c79bd6544d00e0607e98ff833ca21953df001510ca25915d1"}, - {file = "pymongo-4.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e5df28f74002e37bcbdfdc5109799f670e4dfef0fb527c391ff84f078050e7b5"}, - {file = "pymongo-4.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b50040d9767197b77ed420ada29b3bf18a638f9552d80f2da817b7c4a4c9c68"}, - {file = "pymongo-4.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:417369ce39af2b7c2a9c7152c1ed2393edfd1cbaf2a356ba31eb8bcbd5c98dd7"}, - {file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf821bd3befb993a6db17229a2c60c1550e957de02a6ff4dd0af9476637b2e4d"}, - {file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9365166aa801c63dff1a3cb96e650be270da06e3464ab106727223123405510f"}, - {file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc8b8582f4209c2459b04b049ac03c72c618e011d3caa5391ff86d1bda0cc486"}, - {file = "pymongo-4.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e5019f75f6827bb5354b6fef8dfc9d6c7446894a27346e03134d290eb9e758"}, - {file = "pymongo-4.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b5802151fc2b51cd45492c80ed22b441d20090fb76d1fd53cd7760b340ff554"}, - {file = "pymongo-4.8.0-cp311-cp311-win32.whl", hash = "sha256:4bf58e6825b93da63e499d1a58de7de563c31e575908d4e24876234ccb910eba"}, - {file = "pymongo-4.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:b747c0e257b9d3e6495a018309b9e0c93b7f0d65271d1d62e572747f4ffafc88"}, - {file = "pymongo-4.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e6a720a3d22b54183352dc65f08cd1547204d263e0651b213a0a2e577e838526"}, - {file = "pymongo-4.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:31e4d21201bdf15064cf47ce7b74722d3e1aea2597c6785882244a3bb58c7eab"}, - {file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6b804bb4f2d9dc389cc9e827d579fa327272cdb0629a99bfe5b83cb3e269ebf"}, - {file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2fbdb87fe5075c8beb17a5c16348a1ea3c8b282a5cb72d173330be2fecf22f5"}, - {file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd39455b7ee70aabee46f7399b32ab38b86b236c069ae559e22be6b46b2bbfc4"}, - {file = "pymongo-4.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:940d456774b17814bac5ea7fc28188c7a1338d4a233efbb6ba01de957bded2e8"}, - {file = "pymongo-4.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:236bbd7d0aef62e64caf4b24ca200f8c8670d1a6f5ea828c39eccdae423bc2b2"}, - {file = "pymongo-4.8.0-cp312-cp312-win32.whl", hash = "sha256:47ec8c3f0a7b2212dbc9be08d3bf17bc89abd211901093e3ef3f2adea7de7a69"}, - {file = "pymongo-4.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e84bc7707492f06fbc37a9f215374d2977d21b72e10a67f1b31893ec5a140ad8"}, - {file = "pymongo-4.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:519d1bab2b5e5218c64340b57d555d89c3f6c9d717cecbf826fb9d42415e7750"}, - {file = "pymongo-4.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:87075a1feb1e602e539bdb1ef8f4324a3427eb0d64208c3182e677d2c0718b6f"}, - {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f53429515d2b3e86dcc83dadecf7ff881e538c168d575f3688698a8707b80a"}, - {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fdc20cd1e1141b04696ffcdb7c71e8a4a665db31fe72e51ec706b3bdd2d09f36"}, - {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:284d0717d1a7707744018b0b6ee7801b1b1ff044c42f7be7a01bb013de639470"}, - {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5bf0eb8b6ef40fa22479f09375468c33bebb7fe49d14d9c96c8fd50355188b0"}, - {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ecd71b9226bd1d49416dc9f999772038e56f415a713be51bf18d8676a0841c8"}, - {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0061af6e8c5e68b13f1ec9ad5251247726653c5af3c0bbdfbca6cf931e99216"}, - {file = "pymongo-4.8.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:658d0170f27984e0d89c09fe5c42296613b711a3ffd847eb373b0dbb5b648d5f"}, - {file = "pymongo-4.8.0-cp38-cp38-win32.whl", hash = "sha256:3ed1c316718a2836f7efc3d75b4b0ffdd47894090bc697de8385acd13c513a70"}, - {file = "pymongo-4.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:7148419eedfea9ecb940961cfe465efaba90595568a1fb97585fb535ea63fe2b"}, - {file = "pymongo-4.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8400587d594761e5136a3423111f499574be5fd53cf0aefa0d0f05b180710b0"}, - {file = "pymongo-4.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af3e98dd9702b73e4e6fd780f6925352237f5dce8d99405ff1543f3771201704"}, - {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de3a860f037bb51f968de320baef85090ff0bbb42ec4f28ec6a5ddf88be61871"}, - {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0fc18b3a093f3db008c5fea0e980dbd3b743449eee29b5718bc2dc15ab5088bb"}, - {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18c9d8f975dd7194c37193583fd7d1eb9aea0c21ee58955ecf35362239ff31ac"}, - {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:408b2f8fdbeca3c19e4156f28fff1ab11c3efb0407b60687162d49f68075e63c"}, - {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6564780cafd6abeea49759fe661792bd5a67e4f51bca62b88faab497ab5fe89"}, - {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d18d86bc9e103f4d3d4f18b85a0471c0e13ce5b79194e4a0389a224bb70edd53"}, - {file = "pymongo-4.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9097c331577cecf8034422956daaba7ec74c26f7b255d718c584faddd7fa2e3c"}, - {file = "pymongo-4.8.0-cp39-cp39-win32.whl", hash = "sha256:d5428dbcd43d02f6306e1c3c95f692f68b284e6ee5390292242f509004c9e3a8"}, - {file = "pymongo-4.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:ef7225755ed27bfdb18730c68f6cb023d06c28f2b734597480fb4c0e500feb6f"}, - {file = "pymongo-4.8.0.tar.gz", hash = "sha256:454f2295875744dc70f1881e4b2eb99cdad008a33574bc8aaf120530f66c0cde"}, + {file = "pymongo-4.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e699aa68c4a7dea2ab5a27067f7d3e08555f8d2c0dc6a0c8c60cfd9ff2e6a4b1"}, + {file = "pymongo-4.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:70645abc714f06b4ad6b72d5bf73792eaad14e3a2cfe29c62a9c81ada69d9e4b"}, + {file = "pymongo-4.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae2fd94c9fe048c94838badcc6e992d033cb9473eb31e5710b3707cba5e8aee2"}, + {file = "pymongo-4.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ded27a4a5374dae03a92e084a60cdbcecd595306555bda553b833baf3fc4868"}, + {file = "pymongo-4.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ecc2455e3974a6c429687b395a0bc59636f2d6aedf5785098cf4e1f180f1c71"}, + {file = "pymongo-4.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920fee41f7d0259f5f72c1f1eb331bc26ffbdc952846f9bd8c3b119013bb52c"}, + {file = "pymongo-4.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0a15665b2d6cf364f4cd114d62452ce01d71abfbd9c564ba8c74dcd7bbd6822"}, + {file = "pymongo-4.10.1-cp310-cp310-win32.whl", hash = "sha256:29e1c323c28a4584b7095378ff046815e39ff82cdb8dc4cc6dfe3acf6f9ad1f8"}, + {file = "pymongo-4.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:88dc4aa45f8744ccfb45164aedb9a4179c93567bbd98a33109d7dc400b00eb08"}, + {file = "pymongo-4.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:57ee6becae534e6d47848c97f6a6dff69e3cce7c70648d6049bd586764febe59"}, + {file = "pymongo-4.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6f437a612f4d4f7aca1812311b1e84477145e950fdafe3285b687ab8c52541f3"}, + {file = "pymongo-4.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a970fd3117ab40a4001c3dad333bbf3c43687d90f35287a6237149b5ccae61d"}, + {file = "pymongo-4.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c4d0e7cd08ef9f8fbf2d15ba281ed55604368a32752e476250724c3ce36c72e"}, + {file = "pymongo-4.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca6f700cff6833de4872a4e738f43123db34400173558b558ae079b5535857a4"}, + {file = "pymongo-4.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cec237c305fcbeef75c0bcbe9d223d1e22a6e3ba1b53b2f0b79d3d29c742b45b"}, + {file = "pymongo-4.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3337804ea0394a06e916add4e5fac1c89902f1b6f33936074a12505cab4ff05"}, + {file = "pymongo-4.10.1-cp311-cp311-win32.whl", hash = "sha256:778ac646ce6ac1e469664062dfe9ae1f5c9961f7790682809f5ec3b8fda29d65"}, + {file = "pymongo-4.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:9df4ab5594fdd208dcba81be815fa8a8a5d8dedaf3b346cbf8b61c7296246a7a"}, + {file = "pymongo-4.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fbedc4617faa0edf423621bb0b3b8707836687161210d470e69a4184be9ca011"}, + {file = "pymongo-4.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7bd26b2aec8ceeb95a5d948d5cc0f62b0eb6d66f3f4230705c1e3d3d2c04ec76"}, + {file = "pymongo-4.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb104c3c2a78d9d85571c8ac90ec4f95bca9b297c6eee5ada71fabf1129e1674"}, + {file = "pymongo-4.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4924355245a9c79f77b5cda2db36e0f75ece5faf9f84d16014c0a297f6d66786"}, + {file = "pymongo-4.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:11280809e5dacaef4971113f0b4ff4696ee94cfdb720019ff4fa4f9635138252"}, + {file = "pymongo-4.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5d55f2a82e5eb23795f724991cac2bffbb1c0f219c0ba3bf73a835f97f1bb2e"}, + {file = "pymongo-4.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e974ab16a60be71a8dfad4e5afccf8dd05d41c758060f5d5bda9a758605d9a5d"}, + {file = "pymongo-4.10.1-cp312-cp312-win32.whl", hash = "sha256:544890085d9641f271d4f7a47684450ed4a7344d6b72d5968bfae32203b1bb7c"}, + {file = "pymongo-4.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:dcc07b1277e8b4bf4d7382ca133850e323b7ab048b8353af496d050671c7ac52"}, + {file = "pymongo-4.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:90bc6912948dfc8c363f4ead54d54a02a15a7fee6cfafb36dc450fc8962d2cb7"}, + {file = "pymongo-4.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:594dd721b81f301f33e843453638e02d92f63c198358e5a0fa8b8d0b1218dabc"}, + {file = "pymongo-4.10.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0783e0c8e95397c84e9cf8ab092ab1e5dd7c769aec0ef3a5838ae7173b98dea0"}, + {file = "pymongo-4.10.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6fb6a72e88df46d1c1040fd32cd2d2c5e58722e5d3e31060a0393f04ad3283de"}, + {file = "pymongo-4.10.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e3a593333e20c87415420a4fb76c00b7aae49b6361d2e2205b6fece0563bf40"}, + {file = "pymongo-4.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72e2ace7456167c71cfeca7dcb47bd5dceda7db2231265b80fc625c5e8073186"}, + {file = "pymongo-4.10.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ad05eb9c97e4f589ed9e74a00fcaac0d443ccd14f38d1258eb4c39a35dd722b"}, + {file = "pymongo-4.10.1-cp313-cp313-win32.whl", hash = "sha256:ee4c86d8e6872a61f7888fc96577b0ea165eb3bdb0d841962b444fa36001e2bb"}, + {file = "pymongo-4.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:45ee87a4e12337353242bc758accc7fb47a2f2d9ecc0382a61e64c8f01e86708"}, + {file = "pymongo-4.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:442ca247f53ad24870a01e80a71cd81b3f2318655fd9d66748ee2bd1b1569d9e"}, + {file = "pymongo-4.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:23e1d62df5592518204943b507be7b457fb8a4ad95a349440406fd42db5d0923"}, + {file = "pymongo-4.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6131bc6568b26e7495a9f3ef2b1700566b76bbecd919f4472bfe90038a61f425"}, + {file = "pymongo-4.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fdeba88c540c9ed0338c0b2062d9f81af42b18d6646b3e6dda05cf6edd46ada9"}, + {file = "pymongo-4.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15a624d752dd3c89d10deb0ef6431559b6d074703cab90a70bb849ece02adc6b"}, + {file = "pymongo-4.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba164e73fdade9b4614a2497321c5b7512ddf749ed508950bdecc28d8d76a2d9"}, + {file = "pymongo-4.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9235fa319993405ae5505bf1333366388add2e06848db7b3deee8f990b69808e"}, + {file = "pymongo-4.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e4a65567bd17d19f03157c7ec992c6530eafd8191a4e5ede25566792c4fe3fa2"}, + {file = "pymongo-4.10.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f1945d48fb9b8a87d515da07f37e5b2c35b364a435f534c122e92747881f4a7c"}, + {file = "pymongo-4.10.1-cp38-cp38-win32.whl", hash = "sha256:345f8d340802ebce509f49d5833cc913da40c82f2e0daf9f60149cacc9ca680f"}, + {file = "pymongo-4.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:3a70d5efdc0387ac8cd50f9a5f379648ecfc322d14ec9e1ba8ec957e5d08c372"}, + {file = "pymongo-4.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15b1492cc5c7cd260229590be7218261e81684b8da6d6de2660cf743445500ce"}, + {file = "pymongo-4.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95207503c41b97e7ecc7e596d84a61f441b4935f11aa8332828a754e7ada8c82"}, + {file = "pymongo-4.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb99f003c720c6d83be02c8f1a7787c22384a8ca9a4181e406174db47a048619"}, + {file = "pymongo-4.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2bc1ee4b1ca2c4e7e6b7a5e892126335ec8d9215bcd3ac2fe075870fefc3358"}, + {file = "pymongo-4.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:93a0833c10a967effcd823b4e7445ec491f0bf6da5de0ca33629c0528f42b748"}, + {file = "pymongo-4.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f56707497323150bd2ed5d63067f4ffce940d0549d4ea2dfae180deec7f9363"}, + {file = "pymongo-4.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:409ab7d6c4223e5c85881697f365239dd3ed1b58f28e4124b846d9d488c86880"}, + {file = "pymongo-4.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dac78a650dc0637d610905fd06b5fa6419ae9028cf4d04d6a2657bc18a66bbce"}, + {file = "pymongo-4.10.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1ec3fa88b541e0481aff3c35194c9fac96e4d57ec5d1c122376000eb28c01431"}, + {file = "pymongo-4.10.1-cp39-cp39-win32.whl", hash = "sha256:e0e961923a7b8a1c801c43552dcb8153e45afa41749d9efbd3a6d33f45489f7a"}, + {file = "pymongo-4.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:dabe8bf1ad644e6b93f3acf90ff18536d94538ca4d27e583c6db49889e98e48f"}, + {file = "pymongo-4.10.1.tar.gz", hash = "sha256:a9de02be53b6bb98efe0b9eda84ffa1ec027fcb23a2de62c4f941d9a2f2f3330"}, ] [package.dependencies] @@ -1262,23 +1920,23 @@ dnspython = ">=1.16.0,<3.0.0" [package.extras] aws = ["pymongo-auth-aws (>=1.1.0,<2.0.0)"] -docs = ["furo (==2023.9.10)", "readthedocs-sphinx-search (>=0.3,<1.0)", "sphinx (>=5.3,<8)", "sphinx-rtd-theme (>=2,<3)", "sphinxcontrib-shellcheck (>=1,<2)"] -encryption = ["certifi", "pymongo-auth-aws (>=1.1.0,<2.0.0)", "pymongocrypt (>=1.6.0,<2.0.0)"] +docs = ["furo (==2023.9.10)", "readthedocs-sphinx-search (>=0.3,<1.0)", "sphinx (>=5.3,<8)", "sphinx-autobuild (>=2020.9.1)", "sphinx-rtd-theme (>=2,<3)", "sphinxcontrib-shellcheck (>=1,<2)"] +encryption = ["certifi", "pymongo-auth-aws (>=1.1.0,<2.0.0)", "pymongocrypt (>=1.10.0,<2.0.0)"] gssapi = ["pykerberos", "winkerberos (>=0.5.0)"] ocsp = ["certifi", "cryptography (>=2.5)", "pyopenssl (>=17.2.0)", "requests (<3.0.0)", "service-identity (>=18.1.0)"] snappy = ["python-snappy"] -test = ["pytest (>=7)"] +test = ["pytest (>=8.2)", "pytest-asyncio (>=0.24.0)"] zstd = ["zstandard"] [[package]] name = "pytest" -version = "8.3.1" +version = "8.3.3" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.1-py3-none-any.whl", hash = "sha256:e9600ccf4f563976e2c99fa02c7624ab938296551f280835ee6516df8bc4ae8c"}, - {file = "pytest-8.3.1.tar.gz", hash = "sha256:7e8e5c5abd6e93cb1cc151f23e57adc31fcf8cfd2a3ff2da63e23f732de35db6"}, + {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, + {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, ] [package.dependencies] @@ -1336,25 +1994,29 @@ dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatc [[package]] name = "pywin32" -version = "306" +version = "308" description = "Python for Window Extensions" optional = false python-versions = "*" files = [ - {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, - {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, - {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, - {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, - {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, - {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, - {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, - {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, - {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, - {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, - {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, - {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, - {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, - {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, + {file = "pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e"}, + {file = "pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e"}, + {file = "pywin32-308-cp310-cp310-win_arm64.whl", hash = "sha256:a5ab5381813b40f264fa3495b98af850098f814a25a63589a8e9eb12560f450c"}, + {file = "pywin32-308-cp311-cp311-win32.whl", hash = "sha256:5d8c8015b24a7d6855b1550d8e660d8daa09983c80e5daf89a273e5c6fb5095a"}, + {file = "pywin32-308-cp311-cp311-win_amd64.whl", hash = "sha256:575621b90f0dc2695fec346b2d6302faebd4f0f45c05ea29404cefe35d89442b"}, + {file = "pywin32-308-cp311-cp311-win_arm64.whl", hash = "sha256:100a5442b7332070983c4cd03f2e906a5648a5104b8a7f50175f7906efd16bb6"}, + {file = "pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897"}, + {file = "pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47"}, + {file = "pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091"}, + {file = "pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed"}, + {file = "pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4"}, + {file = "pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd"}, + {file = "pywin32-308-cp37-cp37m-win32.whl", hash = "sha256:1f696ab352a2ddd63bd07430080dd598e6369152ea13a25ebcdd2f503a38f1ff"}, + {file = "pywin32-308-cp37-cp37m-win_amd64.whl", hash = "sha256:13dcb914ed4347019fbec6697a01a0aec61019c1046c2b905410d197856326a6"}, + {file = "pywin32-308-cp38-cp38-win32.whl", hash = "sha256:5794e764ebcabf4ff08c555b31bd348c9025929371763b2183172ff4708152f0"}, + {file = "pywin32-308-cp38-cp38-win_amd64.whl", hash = "sha256:3b92622e29d651c6b783e368ba7d6722b1634b8e70bd376fd7610fe1992e19de"}, + {file = "pywin32-308-cp39-cp39-win32.whl", hash = "sha256:7873ca4dc60ab3287919881a7d4f88baee4a6e639aa6962de25a98ba6b193341"}, + {file = "pywin32-308-cp39-cp39-win_amd64.whl", hash = "sha256:71b3322d949b4cc20776436a9c9ba0eeedcbc9c650daa536df63f0ff111bb920"}, ] [[package]] @@ -1433,6 +2095,124 @@ files = [ [package.dependencies] prompt_toolkit = ">=2.0,<=3.0.36" +[[package]] +name = "referencing" +version = "0.35.1" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, + {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + +[[package]] +name = "regex" +version = "2024.9.11" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.8" +files = [ + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, + {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, + {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, + {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, + {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, + {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, + {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, + {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, + {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"}, + {file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"}, + {file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, + {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, + {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, + {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, +] + [[package]] name = "requests" version = "2.32.3" @@ -1455,23 +2235,117 @@ socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] -name = "rich" -version = "13.7.1" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +name = "rpds-py" +version = "0.20.0" +description = "Python bindings to Rust's persistent data structures (rpds)" optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.8" files = [ - {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, - {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, + {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, + {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, + {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, + {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, + {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, + {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, + {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, + {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, + {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, + {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, + {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, + {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, + {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, ] -[package.dependencies] -markdown-it-py = ">=2.2.0" -pygments = ">=2.13.0,<3.0.0" - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<9)"] - [[package]] name = "setuptools" version = "71.1.0" @@ -1488,17 +2362,6 @@ core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.te doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -[[package]] -name = "shellingham" -version = "1.5.4" -description = "Tool to Detect Surrounding Shell" -optional = false -python-versions = ">=3.7" -files = [ - {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, - {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, -] - [[package]] name = "six" version = "1.16.0" @@ -1542,13 +2405,13 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "starlette" -version = "0.37.2" +version = "0.40.0" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, - {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, + {file = "starlette-0.40.0-py3-none-any.whl", hash = "sha256:c494a22fae73805376ea6bf88439783ecfba9aac88a43911b48c653437e784c4"}, + {file = "starlette-0.40.0.tar.gz", hash = "sha256:1a3139688fb298ce5e2d661d37046a66ad996ce94be4d4983be019a23a04ea35"}, ] [package.dependencies] @@ -1558,6 +2421,170 @@ typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\"" [package.extras] full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] +[[package]] +name = "tiktoken" +version = "0.8.0" +description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" +optional = false +python-versions = ">=3.9" +files = [ + {file = "tiktoken-0.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b07e33283463089c81ef1467180e3e00ab00d46c2c4bbcef0acab5f771d6695e"}, + {file = "tiktoken-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9269348cb650726f44dd3bbb3f9110ac19a8dcc8f54949ad3ef652ca22a38e21"}, + {file = "tiktoken-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e13f37bc4ef2d012731e93e0fef21dc3b7aea5bb9009618de9a4026844e560"}, + {file = "tiktoken-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f13d13c981511331eac0d01a59b5df7c0d4060a8be1e378672822213da51e0a2"}, + {file = "tiktoken-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6b2ddbc79a22621ce8b1166afa9f9a888a664a579350dc7c09346a3b5de837d9"}, + {file = "tiktoken-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d8c2d0e5ba6453a290b86cd65fc51fedf247e1ba170191715b049dac1f628005"}, + {file = "tiktoken-0.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d622d8011e6d6f239297efa42a2657043aaed06c4f68833550cac9e9bc723ef1"}, + {file = "tiktoken-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2efaf6199717b4485031b4d6edb94075e4d79177a172f38dd934d911b588d54a"}, + {file = "tiktoken-0.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5637e425ce1fc49cf716d88df3092048359a4b3bbb7da762840426e937ada06d"}, + {file = "tiktoken-0.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fb0e352d1dbe15aba082883058b3cce9e48d33101bdaac1eccf66424feb5b47"}, + {file = "tiktoken-0.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:56edfefe896c8f10aba372ab5706b9e3558e78db39dd497c940b47bf228bc419"}, + {file = "tiktoken-0.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:326624128590def898775b722ccc327e90b073714227175ea8febbc920ac0a99"}, + {file = "tiktoken-0.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:881839cfeae051b3628d9823b2e56b5cc93a9e2efb435f4cf15f17dc45f21586"}, + {file = "tiktoken-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fe9399bdc3f29d428f16a2f86c3c8ec20be3eac5f53693ce4980371c3245729b"}, + {file = "tiktoken-0.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a58deb7075d5b69237a3ff4bb51a726670419db6ea62bdcd8bd80c78497d7ab"}, + {file = "tiktoken-0.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2908c0d043a7d03ebd80347266b0e58440bdef5564f84f4d29fb235b5df3b04"}, + {file = "tiktoken-0.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:294440d21a2a51e12d4238e68a5972095534fe9878be57d905c476017bff99fc"}, + {file = "tiktoken-0.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:d8f3192733ac4d77977432947d563d7e1b310b96497acd3c196c9bddb36ed9db"}, + {file = "tiktoken-0.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:02be1666096aff7da6cbd7cdaa8e7917bfed3467cd64b38b1f112e96d3b06a24"}, + {file = "tiktoken-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94ff53c5c74b535b2cbf431d907fc13c678bbd009ee633a2aca269a04389f9a"}, + {file = "tiktoken-0.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b231f5e8982c245ee3065cd84a4712d64692348bc609d84467c57b4b72dcbc5"}, + {file = "tiktoken-0.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4177faa809bd55f699e88c96d9bb4635d22e3f59d635ba6fd9ffedf7150b9953"}, + {file = "tiktoken-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5376b6f8dc4753cd81ead935c5f518fa0fbe7e133d9e25f648d8c4dabdd4bad7"}, + {file = "tiktoken-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:18228d624807d66c87acd8f25fc135665617cab220671eb65b50f5d70fa51f69"}, + {file = "tiktoken-0.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e17807445f0cf1f25771c9d86496bd8b5c376f7419912519699f3cc4dc5c12e"}, + {file = "tiktoken-0.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:886f80bd339578bbdba6ed6d0567a0d5c6cfe198d9e587ba6c447654c65b8edc"}, + {file = "tiktoken-0.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6adc8323016d7758d6de7313527f755b0fc6c72985b7d9291be5d96d73ecd1e1"}, + {file = "tiktoken-0.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b591fb2b30d6a72121a80be24ec7a0e9eb51c5500ddc7e4c2496516dd5e3816b"}, + {file = "tiktoken-0.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:845287b9798e476b4d762c3ebda5102be87ca26e5d2c9854002825d60cdb815d"}, + {file = "tiktoken-0.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:1473cfe584252dc3fa62adceb5b1c763c1874e04511b197da4e6de51d6ce5a02"}, + {file = "tiktoken-0.8.0.tar.gz", hash = "sha256:9ccbb2740f24542534369c5635cfd9b2b3c2490754a78ac8831d99f89f94eeb2"}, +] + +[package.dependencies] +regex = ">=2022.1.18" +requests = ">=2.26.0" + +[package.extras] +blobfile = ["blobfile (>=2)"] + +[[package]] +name = "tokenizers" +version = "0.20.1" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tokenizers-0.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:439261da7c0a5c88bda97acb284d49fbdaf67e9d3b623c0bfd107512d22787a9"}, + {file = "tokenizers-0.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03dae629d99068b1ea5416d50de0fea13008f04129cc79af77a2a6392792d93c"}, + {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b61f561f329ffe4b28367798b89d60c4abf3f815d37413b6352bc6412a359867"}, + {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec870fce1ee5248a10be69f7a8408a234d6f2109f8ea827b4f7ecdbf08c9fd15"}, + {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d388d1ea8b7447da784e32e3b86a75cce55887e3b22b31c19d0b186b1c677800"}, + {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:299c85c1d21135bc01542237979bf25c32efa0d66595dd0069ae259b97fb2dbe"}, + {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e96f6c14c9752bb82145636b614d5a78e9cde95edfbe0a85dad0dd5ddd6ec95c"}, + {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc9e95ad49c932b80abfbfeaf63b155761e695ad9f8a58c52a47d962d76e310f"}, + {file = "tokenizers-0.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f22dee205329a636148c325921c73cf3e412e87d31f4d9c3153b302a0200057b"}, + {file = "tokenizers-0.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2ffd9a8895575ac636d44500c66dffaef133823b6b25067604fa73bbc5ec09d"}, + {file = "tokenizers-0.20.1-cp310-none-win32.whl", hash = "sha256:2847843c53f445e0f19ea842a4e48b89dd0db4e62ba6e1e47a2749d6ec11f50d"}, + {file = "tokenizers-0.20.1-cp310-none-win_amd64.whl", hash = "sha256:f9aa93eacd865f2798b9e62f7ce4533cfff4f5fbd50c02926a78e81c74e432cd"}, + {file = "tokenizers-0.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4a717dcb08f2dabbf27ae4b6b20cbbb2ad7ed78ce05a829fae100ff4b3c7ff15"}, + {file = "tokenizers-0.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f84dad1ff1863c648d80628b1b55353d16303431283e4efbb6ab1af56a75832"}, + {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:929c8f3afa16a5130a81ab5079c589226273ec618949cce79b46d96e59a84f61"}, + {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d10766473954397e2d370f215ebed1cc46dcf6fd3906a2a116aa1d6219bfedc3"}, + {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9300fac73ddc7e4b0330acbdda4efaabf74929a4a61e119a32a181f534a11b47"}, + {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ecaf7b0e39caeb1aa6dd6e0975c405716c82c1312b55ac4f716ef563a906969"}, + {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5170be9ec942f3d1d317817ced8d749b3e1202670865e4fd465e35d8c259de83"}, + {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f1ae08fa9aea5891cbd69df29913e11d3841798e0bfb1ff78b78e4e7ea0a4"}, + {file = "tokenizers-0.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ee86d4095d3542d73579e953c2e5e07d9321af2ffea6ecc097d16d538a2dea16"}, + {file = "tokenizers-0.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:86dcd08da163912e17b27bbaba5efdc71b4fbffb841530fdb74c5707f3c49216"}, + {file = "tokenizers-0.20.1-cp311-none-win32.whl", hash = "sha256:9af2dc4ee97d037bc6b05fa4429ddc87532c706316c5e11ce2f0596dfcfa77af"}, + {file = "tokenizers-0.20.1-cp311-none-win_amd64.whl", hash = "sha256:899152a78b095559c287b4c6d0099469573bb2055347bb8154db106651296f39"}, + {file = "tokenizers-0.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:407ab666b38e02228fa785e81f7cf79ef929f104bcccf68a64525a54a93ceac9"}, + {file = "tokenizers-0.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f13a2d16032ebc8bd812eb8099b035ac65887d8f0c207261472803b9633cf3e"}, + {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e98eee4dca22849fbb56a80acaa899eec5b72055d79637dd6aa15d5e4b8628c9"}, + {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47c1bcdd61e61136087459cb9e0b069ff23b5568b008265e5cbc927eae3387ce"}, + {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:128c1110e950534426e2274837fc06b118ab5f2fa61c3436e60e0aada0ccfd67"}, + {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2e2d47a819d2954f2c1cd0ad51bb58ffac6f53a872d5d82d65d79bf76b9896d"}, + {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bdd67a0e3503a9a7cf8bc5a4a49cdde5fa5bada09a51e4c7e1c73900297539bd"}, + {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:689b93d2e26d04da337ac407acec8b5d081d8d135e3e5066a88edd5bdb5aff89"}, + {file = "tokenizers-0.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0c6a796ddcd9a19ad13cf146997cd5895a421fe6aec8fd970d69f9117bddb45c"}, + {file = "tokenizers-0.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3ea919687aa7001a8ff1ba36ac64f165c4e89035f57998fa6cedcfd877be619d"}, + {file = "tokenizers-0.20.1-cp312-none-win32.whl", hash = "sha256:6d3ac5c1f48358ffe20086bf065e843c0d0a9fce0d7f0f45d5f2f9fba3609ca5"}, + {file = "tokenizers-0.20.1-cp312-none-win_amd64.whl", hash = "sha256:b0874481aea54a178f2bccc45aa2d0c99cd3f79143a0948af6a9a21dcc49173b"}, + {file = "tokenizers-0.20.1-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:96af92e833bd44760fb17f23f402e07a66339c1dcbe17d79a9b55bb0cc4f038e"}, + {file = "tokenizers-0.20.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:65f34e5b731a262dfa562820818533c38ce32a45864437f3d9c82f26c139ca7f"}, + {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17f98fccb5c12ab1ce1f471731a9cd86df5d4bd2cf2880c5a66b229802d96145"}, + {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8c0fc3542cf9370bf92c932eb71bdeb33d2d4aeeb4126d9fd567b60bd04cb30"}, + {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b39356df4575d37f9b187bb623aab5abb7b62c8cb702867a1768002f814800c"}, + {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfdad27b0e50544f6b838895a373db6114b85112ba5c0cefadffa78d6daae563"}, + {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:094663dd0e85ee2e573126918747bdb40044a848fde388efb5b09d57bc74c680"}, + {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e4cf033a2aa207d7ac790e91adca598b679999710a632c4a494aab0fc3a1b2"}, + {file = "tokenizers-0.20.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9310951c92c9fb91660de0c19a923c432f110dbfad1a2d429fbc44fa956bf64f"}, + {file = "tokenizers-0.20.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:05e41e302c315bd2ed86c02e917bf03a6cf7d2f652c9cee1a0eb0d0f1ca0d32c"}, + {file = "tokenizers-0.20.1-cp37-none-win32.whl", hash = "sha256:212231ab7dfcdc879baf4892ca87c726259fa7c887e1688e3f3cead384d8c305"}, + {file = "tokenizers-0.20.1-cp37-none-win_amd64.whl", hash = "sha256:896195eb9dfdc85c8c052e29947169c1fcbe75a254c4b5792cdbd451587bce85"}, + {file = "tokenizers-0.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:741fb22788482d09d68e73ece1495cfc6d9b29a06c37b3df90564a9cfa688e6d"}, + {file = "tokenizers-0.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:10be14ebd8082086a342d969e17fc2d6edc856c59dbdbddd25f158fa40eaf043"}, + {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:514cf279b22fa1ae0bc08e143458c74ad3b56cd078b319464959685a35c53d5e"}, + {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a647c5b7cb896d6430cf3e01b4e9a2d77f719c84cefcef825d404830c2071da2"}, + {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cdf379219e1e1dd432091058dab325a2e6235ebb23e0aec8d0508567c90cd01"}, + {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ba72260449e16c4c2f6f3252823b059fbf2d31b32617e582003f2b18b415c39"}, + {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:910b96ed87316e4277b23c7bcaf667ce849c7cc379a453fa179e7e09290eeb25"}, + {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e53975a6694428a0586534cc1354b2408d4e010a3103117f617cbb550299797c"}, + {file = "tokenizers-0.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:07c4b7be58da142b0730cc4e5fd66bb7bf6f57f4986ddda73833cd39efef8a01"}, + {file = "tokenizers-0.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b605c540753e62199bf15cf69c333e934077ef2350262af2ccada46026f83d1c"}, + {file = "tokenizers-0.20.1-cp38-none-win32.whl", hash = "sha256:88b3bc76ab4db1ab95ead623d49c95205411e26302cf9f74203e762ac7e85685"}, + {file = "tokenizers-0.20.1-cp38-none-win_amd64.whl", hash = "sha256:d412a74cf5b3f68a90c615611a5aa4478bb303d1c65961d22db45001df68afcb"}, + {file = "tokenizers-0.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a25dcb2f41a0a6aac31999e6c96a75e9152fa0127af8ece46c2f784f23b8197a"}, + {file = "tokenizers-0.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a12c3cebb8c92e9c35a23ab10d3852aee522f385c28d0b4fe48c0b7527d59762"}, + {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02e18da58cf115b7c40de973609c35bde95856012ba42a41ee919c77935af251"}, + {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f326a1ac51ae909b9760e34671c26cd0dfe15662f447302a9d5bb2d872bab8ab"}, + {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b4872647ea6f25224e2833b044b0b19084e39400e8ead3cfe751238b0802140"}, + {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce6238a3311bb8e4c15b12600927d35c267b92a52c881ef5717a900ca14793f7"}, + {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57b7a8880b208866508b06ce365dc631e7a2472a3faa24daa430d046fb56c885"}, + {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a908c69c2897a68f412aa05ba38bfa87a02980df70f5a72fa8490479308b1f2d"}, + {file = "tokenizers-0.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:da1001aa46f4490099c82e2facc4fbc06a6a32bf7de3918ba798010954b775e0"}, + {file = "tokenizers-0.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:42c097390e2f0ed0a5c5d569e6669dd4e9fff7b31c6a5ce6e9c66a61687197de"}, + {file = "tokenizers-0.20.1-cp39-none-win32.whl", hash = "sha256:3d4d218573a3d8b121a1f8c801029d70444ffb6d8f129d4cca1c7b672ee4a24c"}, + {file = "tokenizers-0.20.1-cp39-none-win_amd64.whl", hash = "sha256:37d1e6f616c84fceefa7c6484a01df05caf1e207669121c66213cb5b2911d653"}, + {file = "tokenizers-0.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48689da7a395df41114f516208d6550e3e905e1239cc5ad386686d9358e9cef0"}, + {file = "tokenizers-0.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:712f90ea33f9bd2586b4a90d697c26d56d0a22fd3c91104c5858c4b5b6489a79"}, + {file = "tokenizers-0.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:359eceb6a620c965988fc559cebc0a98db26713758ec4df43fb76d41486a8ed5"}, + {file = "tokenizers-0.20.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d3caf244ce89d24c87545aafc3448be15870096e796c703a0d68547187192e1"}, + {file = "tokenizers-0.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03b03cf8b9a32254b1bf8a305fb95c6daf1baae0c1f93b27f2b08c9759f41dee"}, + {file = "tokenizers-0.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:218e5a3561561ea0f0ef1559c6d95b825308dbec23fb55b70b92589e7ff2e1e8"}, + {file = "tokenizers-0.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f40df5e0294a95131cc5f0e0eb91fe86d88837abfbee46b9b3610b09860195a7"}, + {file = "tokenizers-0.20.1-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:08aaa0d72bb65058e8c4b0455f61b840b156c557e2aca57627056624c3a93976"}, + {file = "tokenizers-0.20.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:998700177b45f70afeb206ad22c08d9e5f3a80639dae1032bf41e8cbc4dada4b"}, + {file = "tokenizers-0.20.1-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62f7fbd3c2c38b179556d879edae442b45f68312019c3a6013e56c3947a4e648"}, + {file = "tokenizers-0.20.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31e87fca4f6bbf5cc67481b562147fe932f73d5602734de7dd18a8f2eee9c6dd"}, + {file = "tokenizers-0.20.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:956f21d359ae29dd51ca5726d2c9a44ffafa041c623f5aa33749da87cfa809b9"}, + {file = "tokenizers-0.20.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1fbbaf17a393c78d8aedb6a334097c91cb4119a9ced4764ab8cfdc8d254dc9f9"}, + {file = "tokenizers-0.20.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ebe63e31f9c1a970c53866d814e35ec2ec26fda03097c486f82f3891cee60830"}, + {file = "tokenizers-0.20.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:81970b80b8ac126910295f8aab2d7ef962009ea39e0d86d304769493f69aaa1e"}, + {file = "tokenizers-0.20.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130e35e76f9337ed6c31be386e75d4925ea807055acf18ca1a9b0eec03d8fe23"}, + {file = "tokenizers-0.20.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd28a8614f5c82a54ab2463554e84ad79526c5184cf4573bbac2efbbbcead457"}, + {file = "tokenizers-0.20.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9041ee665d0fa7f5c4ccf0f81f5e6b7087f797f85b143c094126fc2611fec9d0"}, + {file = "tokenizers-0.20.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:62eb9daea2a2c06bcd8113a5824af8ef8ee7405d3a71123ba4d52c79bb3d9f1a"}, + {file = "tokenizers-0.20.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f861889707b54a9ab1204030b65fd6c22bdd4a95205deec7994dc22a8baa2ea4"}, + {file = "tokenizers-0.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:89d5c337d74ea6e5e7dc8af124cf177be843bbb9ca6e58c01f75ea103c12c8a9"}, + {file = "tokenizers-0.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0b7f515c83397e73292accdbbbedc62264e070bae9682f06061e2ddce67cacaf"}, + {file = "tokenizers-0.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e0305fc1ec6b1e5052d30d9c1d5c807081a7bd0cae46a33d03117082e91908c"}, + {file = "tokenizers-0.20.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc611e6ac0fa00a41de19c3bf6391a05ea201d2d22b757d63f5491ec0e67faa"}, + {file = "tokenizers-0.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5ffe0d7f7bfcfa3b2585776ecf11da2e01c317027c8573c78ebcb8985279e23"}, + {file = "tokenizers-0.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e7edb8ec12c100d5458d15b1e47c0eb30ad606a05641f19af7563bc3d1608c14"}, + {file = "tokenizers-0.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:de291633fb9303555793cc544d4a86e858da529b7d0b752bcaf721ae1d74b2c9"}, + {file = "tokenizers-0.20.1.tar.gz", hash = "sha256:84edcc7cdeeee45ceedb65d518fffb77aec69311c9c8e30f77ad84da3025f002"}, +] + +[package.dependencies] +huggingface-hub = ">=0.16.4,<1.0" + +[package.extras] +dev = ["tokenizers[testing]"] +docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] +testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests", "ruff"] + [[package]] name = "toml" version = "0.10.2" @@ -1571,15 +2598,35 @@ files = [ [[package]] name = "tomli" -version = "2.0.1" +version = "2.0.2" description = "A lil' TOML parser" optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, + {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, +] + +[[package]] +name = "tqdm" +version = "4.66.5" +description = "Fast, Extensible Progress Meter" +optional = false python-versions = ">=3.7" files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, + {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, + {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, ] +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + [[package]] name = "traitlets" version = "5.14.3" @@ -1595,23 +2642,6 @@ files = [ docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] -[[package]] -name = "typer" -version = "0.12.3" -description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -optional = false -python-versions = ">=3.7" -files = [ - {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, - {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, -] - -[package.dependencies] -click = ">=8.0.0" -rich = ">=10.11.0" -shellingham = ">=1.3.0" -typing-extensions = ">=3.7.4.3" - [[package]] name = "typing-extensions" version = "4.12.2" @@ -1625,13 +2655,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.2" +version = "2.2.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, - {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, ] [package.extras] @@ -1640,163 +2670,6 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] -[[package]] -name = "uvicorn" -version = "0.30.3" -description = "The lightning-fast ASGI server." -optional = false -python-versions = ">=3.8" -files = [ - {file = "uvicorn-0.30.3-py3-none-any.whl", hash = "sha256:94a3608da0e530cea8f69683aa4126364ac18e3826b6630d1a65f4638aade503"}, - {file = "uvicorn-0.30.3.tar.gz", hash = "sha256:0d114d0831ff1adbf231d358cbf42f17333413042552a624ea6a9b4c33dcfd81"}, -] - -[package.dependencies] -click = ">=7.0" -colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} -h11 = ">=0.8" -httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} -python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} -typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} -uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} -watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} - -[package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] - -[[package]] -name = "uvloop" -version = "0.19.0" -description = "Fast implementation of asyncio event loop on top of libuv" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, - {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, - {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, - {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, - {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, - {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, - {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, - {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, - {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, - {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, - {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, - {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, - {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, - {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, - {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, - {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, - {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, - {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, - {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, - {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, - {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, - {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, - {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, - {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, - {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, - {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, - {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, - {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, - {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, - {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, - {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, -] - -[package.extras] -docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] -test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] - -[[package]] -name = "watchfiles" -version = "0.22.0" -description = "Simple, modern and high performance file watching and code reload in python." -optional = false -python-versions = ">=3.8" -files = [ - {file = "watchfiles-0.22.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:da1e0a8caebf17976e2ffd00fa15f258e14749db5e014660f53114b676e68538"}, - {file = "watchfiles-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61af9efa0733dc4ca462347becb82e8ef4945aba5135b1638bfc20fad64d4f0e"}, - {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d9188979a58a096b6f8090e816ccc3f255f137a009dd4bbec628e27696d67c1"}, - {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2bdadf6b90c099ca079d468f976fd50062905d61fae183f769637cb0f68ba59a"}, - {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:067dea90c43bf837d41e72e546196e674f68c23702d3ef80e4e816937b0a3ffd"}, - {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbf8a20266136507abf88b0df2328e6a9a7c7309e8daff124dda3803306a9fdb"}, - {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1235c11510ea557fe21be5d0e354bae2c655a8ee6519c94617fe63e05bca4171"}, - {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2444dc7cb9d8cc5ab88ebe792a8d75709d96eeef47f4c8fccb6df7c7bc5be71"}, - {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c5af2347d17ab0bd59366db8752d9e037982e259cacb2ba06f2c41c08af02c39"}, - {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9624a68b96c878c10437199d9a8b7d7e542feddda8d5ecff58fdc8e67b460848"}, - {file = "watchfiles-0.22.0-cp310-none-win32.whl", hash = "sha256:4b9f2a128a32a2c273d63eb1fdbf49ad64852fc38d15b34eaa3f7ca2f0d2b797"}, - {file = "watchfiles-0.22.0-cp310-none-win_amd64.whl", hash = "sha256:2627a91e8110b8de2406d8b2474427c86f5a62bf7d9ab3654f541f319ef22bcb"}, - {file = "watchfiles-0.22.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8c39987a1397a877217be1ac0fb1d8b9f662c6077b90ff3de2c05f235e6a8f96"}, - {file = "watchfiles-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a927b3034d0672f62fb2ef7ea3c9fc76d063c4b15ea852d1db2dc75fe2c09696"}, - {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052d668a167e9fc345c24203b104c313c86654dd6c0feb4b8a6dfc2462239249"}, - {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e45fb0d70dda1623a7045bd00c9e036e6f1f6a85e4ef2c8ae602b1dfadf7550"}, - {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c49b76a78c156979759d759339fb62eb0549515acfe4fd18bb151cc07366629c"}, - {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a65474fd2b4c63e2c18ac67a0c6c66b82f4e73e2e4d940f837ed3d2fd9d4da"}, - {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc0cba54f47c660d9fa3218158b8963c517ed23bd9f45fe463f08262a4adae1"}, - {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ebe84a035993bb7668f58a0ebf998174fb723a39e4ef9fce95baabb42b787f"}, - {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e0f0a874231e2839abbf473256efffe577d6ee2e3bfa5b540479e892e47c172d"}, - {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:213792c2cd3150b903e6e7884d40660e0bcec4465e00563a5fc03f30ea9c166c"}, - {file = "watchfiles-0.22.0-cp311-none-win32.whl", hash = "sha256:b44b70850f0073b5fcc0b31ede8b4e736860d70e2dbf55701e05d3227a154a67"}, - {file = "watchfiles-0.22.0-cp311-none-win_amd64.whl", hash = "sha256:00f39592cdd124b4ec5ed0b1edfae091567c72c7da1487ae645426d1b0ffcad1"}, - {file = "watchfiles-0.22.0-cp311-none-win_arm64.whl", hash = "sha256:3218a6f908f6a276941422b035b511b6d0d8328edd89a53ae8c65be139073f84"}, - {file = "watchfiles-0.22.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c7b978c384e29d6c7372209cbf421d82286a807bbcdeb315427687f8371c340a"}, - {file = "watchfiles-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd4c06100bce70a20c4b81e599e5886cf504c9532951df65ad1133e508bf20be"}, - {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:425440e55cd735386ec7925f64d5dde392e69979d4c8459f6bb4e920210407f2"}, - {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68fe0c4d22332d7ce53ad094622b27e67440dacefbaedd29e0794d26e247280c"}, - {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8a31bfd98f846c3c284ba694c6365620b637debdd36e46e1859c897123aa232"}, - {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc2e8fe41f3cac0660197d95216c42910c2b7e9c70d48e6d84e22f577d106fc1"}, - {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b7cc10261c2786c41d9207193a85c1db1b725cf87936df40972aab466179b6"}, - {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28585744c931576e535860eaf3f2c0ec7deb68e3b9c5a85ca566d69d36d8dd27"}, - {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00095dd368f73f8f1c3a7982a9801190cc88a2f3582dd395b289294f8975172b"}, - {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:52fc9b0dbf54d43301a19b236b4a4614e610605f95e8c3f0f65c3a456ffd7d35"}, - {file = "watchfiles-0.22.0-cp312-none-win32.whl", hash = "sha256:581f0a051ba7bafd03e17127735d92f4d286af941dacf94bcf823b101366249e"}, - {file = "watchfiles-0.22.0-cp312-none-win_amd64.whl", hash = "sha256:aec83c3ba24c723eac14225194b862af176d52292d271c98820199110e31141e"}, - {file = "watchfiles-0.22.0-cp312-none-win_arm64.whl", hash = "sha256:c668228833c5619f6618699a2c12be057711b0ea6396aeaece4ded94184304ea"}, - {file = "watchfiles-0.22.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d47e9ef1a94cc7a536039e46738e17cce058ac1593b2eccdede8bf72e45f372a"}, - {file = "watchfiles-0.22.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28f393c1194b6eaadcdd8f941307fc9bbd7eb567995232c830f6aef38e8a6e88"}, - {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd64f3a4db121bc161644c9e10a9acdb836853155a108c2446db2f5ae1778c3d"}, - {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2abeb79209630da981f8ebca30a2c84b4c3516a214451bfc5f106723c5f45843"}, - {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cc382083afba7918e32d5ef12321421ef43d685b9a67cc452a6e6e18920890e"}, - {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d048ad5d25b363ba1d19f92dcf29023988524bee6f9d952130b316c5802069cb"}, - {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:103622865599f8082f03af4214eaff90e2426edff5e8522c8f9e93dc17caee13"}, - {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e1f3cf81f1f823e7874ae563457828e940d75573c8fbf0ee66818c8b6a9099"}, - {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8597b6f9dc410bdafc8bb362dac1cbc9b4684a8310e16b1ff5eee8725d13dcd6"}, - {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b04a2cbc30e110303baa6d3ddce8ca3664bc3403be0f0ad513d1843a41c97d1"}, - {file = "watchfiles-0.22.0-cp38-none-win32.whl", hash = "sha256:b610fb5e27825b570554d01cec427b6620ce9bd21ff8ab775fc3a32f28bba63e"}, - {file = "watchfiles-0.22.0-cp38-none-win_amd64.whl", hash = "sha256:fe82d13461418ca5e5a808a9e40f79c1879351fcaeddbede094028e74d836e86"}, - {file = "watchfiles-0.22.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3973145235a38f73c61474d56ad6199124e7488822f3a4fc97c72009751ae3b0"}, - {file = "watchfiles-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:280a4afbc607cdfc9571b9904b03a478fc9f08bbeec382d648181c695648202f"}, - {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0d883351a34c01bd53cfa75cd0292e3f7e268bacf2f9e33af4ecede7e21d1d"}, - {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9165bcab15f2b6d90eedc5c20a7f8a03156b3773e5fb06a790b54ccecdb73385"}, - {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc1b9b56f051209be458b87edb6856a449ad3f803315d87b2da4c93b43a6fe72"}, - {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc1fc25a1dedf2dd952909c8e5cb210791e5f2d9bc5e0e8ebc28dd42fed7562"}, - {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc92d2d2706d2b862ce0568b24987eba51e17e14b79a1abcd2edc39e48e743c8"}, - {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97b94e14b88409c58cdf4a8eaf0e67dfd3ece7e9ce7140ea6ff48b0407a593ec"}, - {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96eec15e5ea7c0b6eb5bfffe990fc7c6bd833acf7e26704eb18387fb2f5fd087"}, - {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:28324d6b28bcb8d7c1041648d7b63be07a16db5510bea923fc80b91a2a6cbed6"}, - {file = "watchfiles-0.22.0-cp39-none-win32.whl", hash = "sha256:8c3e3675e6e39dc59b8fe5c914a19d30029e36e9f99468dddffd432d8a7b1c93"}, - {file = "watchfiles-0.22.0-cp39-none-win_amd64.whl", hash = "sha256:25c817ff2a86bc3de3ed2df1703e3d24ce03479b27bb4527c57e722f8554d971"}, - {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b810a2c7878cbdecca12feae2c2ae8af59bea016a78bc353c184fa1e09f76b68"}, - {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7e1f9c5d1160d03b93fc4b68a0aeb82fe25563e12fbcdc8507f8434ab6f823c"}, - {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:030bc4e68d14bcad2294ff68c1ed87215fbd9a10d9dea74e7cfe8a17869785ab"}, - {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace7d060432acde5532e26863e897ee684780337afb775107c0a90ae8dbccfd2"}, - {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5834e1f8b71476a26df97d121c0c0ed3549d869124ed2433e02491553cb468c2"}, - {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0bc3b2f93a140df6806c8467c7f51ed5e55a931b031b5c2d7ff6132292e803d6"}, - {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fdebb655bb1ba0122402352b0a4254812717a017d2dc49372a1d47e24073795"}, - {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8e0aa0e8cc2a43561e0184c0513e291ca891db13a269d8d47cb9841ced7c71"}, - {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2f350cbaa4bb812314af5dab0eb8d538481e2e2279472890864547f3fe2281ed"}, - {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7a74436c415843af2a769b36bf043b6ccbc0f8d784814ba3d42fc961cdb0a9dc"}, - {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00ad0bcd399503a84cc688590cdffbe7a991691314dde5b57b3ed50a41319a31"}, - {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a44e9481afc7a5ee3291b09c419abab93b7e9c306c9ef9108cb76728ca58d2"}, - {file = "watchfiles-0.22.0.tar.gz", hash = "sha256:988e981aaab4f3955209e7e28c7794acdb690be1efa7f16f8ea5aba7ffdadacb"}, -] - -[package.dependencies] -anyio = ">=3.0.0" - [[package]] name = "wcwidth" version = "0.2.13" @@ -1808,87 +2681,6 @@ files = [ {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] -[[package]] -name = "websockets" -version = "12.0" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, - {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, - {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, - {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, - {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, - {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, - {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, - {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, - {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, - {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, - {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, - {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, - {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, - {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, - {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, - {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, - {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, - {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, - {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, - {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, - {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, - {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, - {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, - {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, - {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, -] - [[package]] name = "wrapt" version = "1.16.0" @@ -1968,22 +2760,138 @@ files = [ {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, ] +[[package]] +name = "yarl" +version = "1.15.2" +description = "Yet another URL library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "yarl-1.15.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e4ee8b8639070ff246ad3649294336b06db37a94bdea0d09ea491603e0be73b8"}, + {file = "yarl-1.15.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a7cf963a357c5f00cb55b1955df8bbe68d2f2f65de065160a1c26b85a1e44172"}, + {file = "yarl-1.15.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:43ebdcc120e2ca679dba01a779333a8ea76b50547b55e812b8b92818d604662c"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3433da95b51a75692dcf6cc8117a31410447c75a9a8187888f02ad45c0a86c50"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38d0124fa992dbacd0c48b1b755d3ee0a9f924f427f95b0ef376556a24debf01"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ded1b1803151dd0f20a8945508786d57c2f97a50289b16f2629f85433e546d47"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace4cad790f3bf872c082366c9edd7f8f8f77afe3992b134cfc810332206884f"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c77494a2f2282d9bbbbcab7c227a4d1b4bb829875c96251f66fb5f3bae4fb053"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b7f227ca6db5a9fda0a2b935a2ea34a7267589ffc63c8045f0e4edb8d8dcf956"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:31561a5b4d8dbef1559b3600b045607cf804bae040f64b5f5bca77da38084a8a"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3e52474256a7db9dcf3c5f4ca0b300fdea6c21cca0148c8891d03a025649d935"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0e1af74a9529a1137c67c887ed9cde62cff53aa4d84a3adbec329f9ec47a3936"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:15c87339490100c63472a76d87fe7097a0835c705eb5ae79fd96e343473629ed"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:74abb8709ea54cc483c4fb57fb17bb66f8e0f04438cff6ded322074dbd17c7ec"}, + {file = "yarl-1.15.2-cp310-cp310-win32.whl", hash = "sha256:ffd591e22b22f9cb48e472529db6a47203c41c2c5911ff0a52e85723196c0d75"}, + {file = "yarl-1.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:1695497bb2a02a6de60064c9f077a4ae9c25c73624e0d43e3aa9d16d983073c2"}, + {file = "yarl-1.15.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9fcda20b2de7042cc35cf911702fa3d8311bd40055a14446c1e62403684afdc5"}, + {file = "yarl-1.15.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0545de8c688fbbf3088f9e8b801157923be4bf8e7b03e97c2ecd4dfa39e48e0e"}, + {file = "yarl-1.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fbda058a9a68bec347962595f50546a8a4a34fd7b0654a7b9697917dc2bf810d"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1ac2bc069f4a458634c26b101c2341b18da85cb96afe0015990507efec2e417"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd126498171f752dd85737ab1544329a4520c53eed3997f9b08aefbafb1cc53b"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3db817b4e95eb05c362e3b45dafe7144b18603e1211f4a5b36eb9522ecc62bcf"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:076b1ed2ac819933895b1a000904f62d615fe4533a5cf3e052ff9a1da560575c"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f8cfd847e6b9ecf9f2f2531c8427035f291ec286c0a4944b0a9fce58c6446046"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:32b66be100ac5739065496c74c4b7f3015cef792c3174982809274d7e51b3e04"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:34a2d76a1984cac04ff8b1bfc939ec9dc0914821264d4a9c8fd0ed6aa8d4cfd2"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0afad2cd484908f472c8fe2e8ef499facee54a0a6978be0e0cff67b1254fd747"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c68e820879ff39992c7f148113b46efcd6ec765a4865581f2902b3c43a5f4bbb"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:98f68df80ec6ca3015186b2677c208c096d646ef37bbf8b49764ab4a38183931"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56ec1eacd0a5d35b8a29f468659c47f4fe61b2cab948ca756c39b7617f0aa5"}, + {file = "yarl-1.15.2-cp311-cp311-win32.whl", hash = "sha256:eedc3f247ee7b3808ea07205f3e7d7879bc19ad3e6222195cd5fbf9988853e4d"}, + {file = "yarl-1.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:0ccaa1bc98751fbfcf53dc8dfdb90d96e98838010fc254180dd6707a6e8bb179"}, + {file = "yarl-1.15.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:82d5161e8cb8f36ec778fd7ac4d740415d84030f5b9ef8fe4da54784a1f46c94"}, + {file = "yarl-1.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fa2bea05ff0a8fb4d8124498e00e02398f06d23cdadd0fe027d84a3f7afde31e"}, + {file = "yarl-1.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:99e12d2bf587b44deb74e0d6170fec37adb489964dbca656ec41a7cd8f2ff178"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:243fbbbf003754fe41b5bdf10ce1e7f80bcc70732b5b54222c124d6b4c2ab31c"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:856b7f1a7b98a8c31823285786bd566cf06226ac4f38b3ef462f593c608a9bd6"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:553dad9af802a9ad1a6525e7528152a015b85fb8dbf764ebfc755c695f488367"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30c3ff305f6e06650a761c4393666f77384f1cc6c5c0251965d6bfa5fbc88f7f"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:353665775be69bbfc6d54c8d134bfc533e332149faeddd631b0bc79df0897f46"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f4fe99ce44128c71233d0d72152db31ca119711dfc5f2c82385ad611d8d7f897"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9c1e3ff4b89cdd2e1a24c214f141e848b9e0451f08d7d4963cb4108d4d798f1f"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:711bdfae4e699a6d4f371137cbe9e740dc958530cb920eb6f43ff9551e17cfbc"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4388c72174868884f76affcdd3656544c426407e0043c89b684d22fb265e04a5"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f0e1844ad47c7bd5d6fa784f1d4accc5f4168b48999303a868fe0f8597bde715"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a5cafb02cf097a82d74403f7e0b6b9df3ffbfe8edf9415ea816314711764a27b"}, + {file = "yarl-1.15.2-cp312-cp312-win32.whl", hash = "sha256:156ececdf636143f508770bf8a3a0498de64da5abd890c7dbb42ca9e3b6c05b8"}, + {file = "yarl-1.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:435aca062444a7f0c884861d2e3ea79883bd1cd19d0a381928b69ae1b85bc51d"}, + {file = "yarl-1.15.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:416f2e3beaeae81e2f7a45dc711258be5bdc79c940a9a270b266c0bec038fb84"}, + {file = "yarl-1.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:173563f3696124372831007e3d4b9821746964a95968628f7075d9231ac6bb33"}, + {file = "yarl-1.15.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9ce2e0f6123a60bd1a7f5ae3b2c49b240c12c132847f17aa990b841a417598a2"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eaea112aed589131f73d50d570a6864728bd7c0c66ef6c9154ed7b59f24da611"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4ca3b9f370f218cc2a0309542cab8d0acdfd66667e7c37d04d617012485f904"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23ec1d3c31882b2a8a69c801ef58ebf7bae2553211ebbddf04235be275a38548"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75119badf45f7183e10e348edff5a76a94dc19ba9287d94001ff05e81475967b"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78e6fdc976ec966b99e4daa3812fac0274cc28cd2b24b0d92462e2e5ef90d368"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8657d3f37f781d987037f9cc20bbc8b40425fa14380c87da0cb8dfce7c92d0fb"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:93bed8a8084544c6efe8856c362af08a23e959340c87a95687fdbe9c9f280c8b"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:69d5856d526802cbda768d3e6246cd0d77450fa2a4bc2ea0ea14f0d972c2894b"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ccad2800dfdff34392448c4bf834be124f10a5bc102f254521d931c1c53c455a"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a880372e2e5dbb9258a4e8ff43f13888039abb9dd6d515f28611c54361bc5644"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c998d0558805860503bc3a595994895ca0f7835e00668dadc673bbf7f5fbfcbe"}, + {file = "yarl-1.15.2-cp313-cp313-win32.whl", hash = "sha256:533a28754e7f7439f217550a497bb026c54072dbe16402b183fdbca2431935a9"}, + {file = "yarl-1.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:5838f2b79dc8f96fdc44077c9e4e2e33d7089b10788464609df788eb97d03aad"}, + {file = "yarl-1.15.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fbbb63bed5fcd70cd3dd23a087cd78e4675fb5a2963b8af53f945cbbca79ae16"}, + {file = "yarl-1.15.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2e93b88ecc8f74074012e18d679fb2e9c746f2a56f79cd5e2b1afcf2a8a786b"}, + {file = "yarl-1.15.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af8ff8d7dc07ce873f643de6dfbcd45dc3db2c87462e5c387267197f59e6d776"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66f629632220a4e7858b58e4857927dd01a850a4cef2fb4044c8662787165cf7"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:833547179c31f9bec39b49601d282d6f0ea1633620701288934c5f66d88c3e50"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2aa738e0282be54eede1e3f36b81f1e46aee7ec7602aa563e81e0e8d7b67963f"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a13a07532e8e1c4a5a3afff0ca4553da23409fad65def1b71186fb867eeae8d"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c45817e3e6972109d1a2c65091504a537e257bc3c885b4e78a95baa96df6a3f8"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:670eb11325ed3a6209339974b276811867defe52f4188fe18dc49855774fa9cf"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:d417a4f6943112fae3924bae2af7112562285848d9bcee737fc4ff7cbd450e6c"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:bc8936d06cd53fddd4892677d65e98af514c8d78c79864f418bbf78a4a2edde4"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:954dde77c404084c2544e572f342aef384240b3e434e06cecc71597e95fd1ce7"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:5bc0df728e4def5e15a754521e8882ba5a5121bd6b5a3a0ff7efda5d6558ab3d"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b71862a652f50babab4a43a487f157d26b464b1dedbcc0afda02fd64f3809d04"}, + {file = "yarl-1.15.2-cp38-cp38-win32.whl", hash = "sha256:63eab904f8630aed5a68f2d0aeab565dcfc595dc1bf0b91b71d9ddd43dea3aea"}, + {file = "yarl-1.15.2-cp38-cp38-win_amd64.whl", hash = "sha256:2cf441c4b6e538ba0d2591574f95d3fdd33f1efafa864faa077d9636ecc0c4e9"}, + {file = "yarl-1.15.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a32d58f4b521bb98b2c0aa9da407f8bd57ca81f34362bcb090e4a79e9924fefc"}, + {file = "yarl-1.15.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:766dcc00b943c089349d4060b935c76281f6be225e39994c2ccec3a2a36ad627"}, + {file = "yarl-1.15.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bed1b5dbf90bad3bfc19439258c97873eab453c71d8b6869c136346acfe497e7"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed20a4bdc635f36cb19e630bfc644181dd075839b6fc84cac51c0f381ac472e2"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d538df442c0d9665664ab6dd5fccd0110fa3b364914f9c85b3ef9b7b2e157980"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c6cf1d92edf936ceedc7afa61b07e9d78a27b15244aa46bbcd534c7458ee1b"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce44217ad99ffad8027d2fde0269ae368c86db66ea0571c62a000798d69401fb"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47a6000a7e833ebfe5886b56a31cb2ff12120b1efd4578a6fcc38df16cc77bd"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e52f77a0cd246086afde8815039f3e16f8d2be51786c0a39b57104c563c5cbb0"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:f9ca0e6ce7774dc7830dc0cc4bb6b3eec769db667f230e7c770a628c1aa5681b"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:136f9db0f53c0206db38b8cd0c985c78ded5fd596c9a86ce5c0b92afb91c3a19"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:173866d9f7409c0fb514cf6e78952e65816600cb888c68b37b41147349fe0057"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:6e840553c9c494a35e449a987ca2c4f8372668ee954a03a9a9685075228e5036"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:458c0c65802d816a6b955cf3603186de79e8fdb46d4f19abaec4ef0a906f50a7"}, + {file = "yarl-1.15.2-cp39-cp39-win32.whl", hash = "sha256:5b48388ded01f6f2429a8c55012bdbd1c2a0c3735b3e73e221649e524c34a58d"}, + {file = "yarl-1.15.2-cp39-cp39-win_amd64.whl", hash = "sha256:81dadafb3aa124f86dc267a2168f71bbd2bfb163663661ab0038f6e4b8edb810"}, + {file = "yarl-1.15.2-py3-none-any.whl", hash = "sha256:0d3105efab7c5c091609abacad33afff33bdff0035bece164c98bcf5a85ef90a"}, + {file = "yarl-1.15.2.tar.gz", hash = "sha256:a39c36f4218a5bb668b4f06874d676d35a035ee668e6e7e3538835c703634b84"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" +propcache = ">=0.2.0" + [[package]] name = "zipp" -version = "3.19.2" +version = "3.20.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, - {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, ] [package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "8d9266ec491f0f3c5661315ed214fe711c61c92c3c6133694fadd6644addec3e" +content-hash = "0e8e55bf71f6d6beec422962a8753a06be09ab98bf5677669c15162e94e11e04" diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index 2568982797..6084efb65c 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.25.4a1" +version = "0.25.4a2" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] @@ -36,6 +36,7 @@ pyyaml = "^6.0.2" opentelemetry-api = "^1.27.0" opentelemetry-sdk = "^1.27.0" opentelemetry-exporter-otlp = "^1.27.0" +litellm ="^1.48.0" [tool.poetry.dev-dependencies] pytest = "^8.3" From 1af7de71a486fd7c20b1e4dd91e45f78bd62dc08 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Tue, 15 Oct 2024 13:55:33 +0200 Subject: [PATCH 075/305] fix(frontend): time span format --- .../src/components/pages/observability/drawer/TraceTree.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx index c0fa9d0e12..6f054efd69 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx @@ -70,7 +70,7 @@ const TreeContent = ({nodeValue}: {nodeValue: AgentaNodeDTO}) => {
- {formatLatency(time?.span)} + {formatLatency(time?.span / 1000000)}
From 01a7b677d1245e29147ed01cf75026397cfbedf5 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 15 Oct 2024 18:14:05 +0200 Subject: [PATCH 076/305] fix calculate cost --- agenta-cli/agenta/sdk/tracing/inline.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py index f99b3b9543..7945ae4d64 100644 --- a/agenta-cli/agenta/sdk/tracing/inline.py +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -1029,11 +1029,10 @@ def parse_inline_trace( ### services.observability.service.ingest/query() ### ##################################################### - _calculate_cost(span_idx) - ############################################### ### services.observability.service.ingest() ### ### --------------------------------------- ### + calculate_cost(span_idx) cumulate_costs(span_id_tree, span_idx) cumulate_tokens(span_id_tree, span_idx) ### --------------------------------------- ### @@ -1280,7 +1279,7 @@ def _parse_to_legacy_span(span: SpanDTO) -> CreateSpan: ] -def _calculate_cost(span_idx: Dict[str, SpanCreateDTO]): +def calculate_cost(span_idx: Dict[str, SpanCreateDTO]): for span in span_idx.values(): if span.node.type.name.lower() in PAYING_TYPES and span.meta and span.metrics: From 9ea789267730ac84e012f7519fbdc5d2c6d142fd Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 15 Oct 2024 18:25:10 +0200 Subject: [PATCH 077/305] update poetry --- agenta-backend/poetry.lock | 857 ++++++++++++++++++++++++++-------- agenta-backend/pyproject.toml | 1 + 2 files changed, 653 insertions(+), 205 deletions(-) diff --git a/agenta-backend/poetry.lock b/agenta-backend/poetry.lock index 72c0a5c0b3..a287df7a74 100644 --- a/agenta-backend/poetry.lock +++ b/agenta-backend/poetry.lock @@ -68,102 +68,102 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.9" +version = "3.10.10" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8b3fb28a9ac8f2558760d8e637dbf27aef1e8b7f1d221e8669a1074d1a266bb2"}, - {file = "aiohttp-3.10.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91aa966858593f64c8a65cdefa3d6dc8fe3c2768b159da84c1ddbbb2c01ab4ef"}, - {file = "aiohttp-3.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63649309da83277f06a15bbdc2a54fbe75efb92caa2c25bb57ca37762789c746"}, - {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3e7fabedb3fe06933f47f1538df7b3a8d78e13d7167195f51ca47ee12690373"}, - {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c070430fda1a550a1c3a4c2d7281d3b8cfc0c6715f616e40e3332201a253067"}, - {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:51d0a4901b27272ae54e42067bc4b9a90e619a690b4dc43ea5950eb3070afc32"}, - {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fec5fac7aea6c060f317f07494961236434928e6f4374e170ef50b3001e14581"}, - {file = "aiohttp-3.10.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:172ad884bb61ad31ed7beed8be776eb17e7fb423f1c1be836d5cb357a096bf12"}, - {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d646fdd74c25bbdd4a055414f0fe32896c400f38ffbdfc78c68e62812a9e0257"}, - {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e86260b76786c28acf0b5fe31c8dca4c2add95098c709b11e8c35b424ebd4f5b"}, - {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d7cafc11d70fdd8801abfc2ff276744ae4cb39d8060b6b542c7e44e5f2cfc2"}, - {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fc262c3df78c8ff6020c782d9ce02e4bcffe4900ad71c0ecdad59943cba54442"}, - {file = "aiohttp-3.10.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:482c85cf3d429844396d939b22bc2a03849cb9ad33344689ad1c85697bcba33a"}, - {file = "aiohttp-3.10.9-cp310-cp310-win32.whl", hash = "sha256:aeebd3061f6f1747c011e1d0b0b5f04f9f54ad1a2ca183e687e7277bef2e0da2"}, - {file = "aiohttp-3.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:fa430b871220dc62572cef9c69b41e0d70fcb9d486a4a207a5de4c1f25d82593"}, - {file = "aiohttp-3.10.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:16e6a51d8bc96b77f04a6764b4ad03eeef43baa32014fce71e882bd71302c7e4"}, - {file = "aiohttp-3.10.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8bd9125dd0cc8ebd84bff2be64b10fdba7dc6fd7be431b5eaf67723557de3a31"}, - {file = "aiohttp-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dcf354661f54e6a49193d0b5653a1b011ba856e0b7a76bda2c33e4c6892f34ea"}, - {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42775de0ca04f90c10c5c46291535ec08e9bcc4756f1b48f02a0657febe89b10"}, - {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87d1e4185c5d7187684d41ebb50c9aeaaaa06ca1875f4c57593071b0409d2444"}, - {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2695c61cf53a5d4345a43d689f37fc0f6d3a2dc520660aec27ec0f06288d1f9"}, - {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a3f063b41cc06e8d0b3fcbbfc9c05b7420f41287e0cd4f75ce0a1f3d80729e6"}, - {file = "aiohttp-3.10.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d37f4718002863b82c6f391c8efd4d3a817da37030a29e2682a94d2716209de"}, - {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2746d8994ebca1bdc55a1e998feff4e94222da709623bb18f6e5cfec8ec01baf"}, - {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6f3c6648aa123bcd73d6f26607d59967b607b0da8ffcc27d418a4b59f4c98c7c"}, - {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:558b3d223fd631ad134d89adea876e7fdb4c93c849ef195049c063ada82b7d08"}, - {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4e6cb75f8ddd9c2132d00bc03c9716add57f4beff1263463724f6398b813e7eb"}, - {file = "aiohttp-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:608cecd8d58d285bfd52dbca5b6251ca8d6ea567022c8a0eaae03c2589cd9af9"}, - {file = "aiohttp-3.10.9-cp311-cp311-win32.whl", hash = "sha256:36d4fba838be5f083f5490ddd281813b44d69685db910907636bc5dca6322316"}, - {file = "aiohttp-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:8be1a65487bdfc285bd5e9baf3208c2132ca92a9b4020e9f27df1b16fab998a9"}, - {file = "aiohttp-3.10.9-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4fd16b30567c5b8e167923be6e027eeae0f20cf2b8a26b98a25115f28ad48ee0"}, - {file = "aiohttp-3.10.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:40ff5b7660f903dc587ed36ef08a88d46840182d9d4b5694e7607877ced698a1"}, - {file = "aiohttp-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4edc3fd701e2b9a0d605a7b23d3de4ad23137d23fc0dbab726aa71d92f11aaaf"}, - {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e525b69ee8a92c146ae5b4da9ecd15e518df4d40003b01b454ad694a27f498b5"}, - {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5002a02c17fcfd796d20bac719981d2fca9c006aac0797eb8f430a58e9d12431"}, - {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd4ceeae2fb8cabdd1b71c82bfdd39662473d3433ec95b962200e9e752fb70d0"}, - {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6e395c3d1f773cf0651cd3559e25182eb0c03a2777b53b4575d8adc1149c6e9"}, - {file = "aiohttp-3.10.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbdb8def5268f3f9cd753a265756f49228a20ed14a480d151df727808b4531dd"}, - {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f82ace0ec57c94aaf5b0e118d4366cff5889097412c75aa14b4fd5fc0c44ee3e"}, - {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6ebdc3b3714afe1b134b3bbeb5f745eed3ecbcff92ab25d80e4ef299e83a5465"}, - {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f9ca09414003c0e96a735daa1f071f7d7ed06962ef4fa29ceb6c80d06696d900"}, - {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1298b854fd31d0567cbb916091be9d3278168064fca88e70b8468875ef9ff7e7"}, - {file = "aiohttp-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:60ad5b8a7452c0f5645c73d4dad7490afd6119d453d302cd5b72b678a85d6044"}, - {file = "aiohttp-3.10.9-cp312-cp312-win32.whl", hash = "sha256:1a0ee6c0d590c917f1b9629371fce5f3d3f22c317aa96fbdcce3260754d7ea21"}, - {file = "aiohttp-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:c46131c6112b534b178d4e002abe450a0a29840b61413ac25243f1291613806a"}, - {file = "aiohttp-3.10.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2bd9f3eac515c16c4360a6a00c38119333901b8590fe93c3257a9b536026594d"}, - {file = "aiohttp-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8cc0d13b4e3b1362d424ce3f4e8c79e1f7247a00d792823ffd640878abf28e56"}, - {file = "aiohttp-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ba1a599255ad6a41022e261e31bc2f6f9355a419575b391f9655c4d9e5df5ff5"}, - {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:776e9f3c9b377fcf097c4a04b241b15691e6662d850168642ff976780609303c"}, - {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8debb45545ad95b58cc16c3c1cc19ad82cffcb106db12b437885dbee265f0ab5"}, - {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2555e4949c8d8782f18ef20e9d39730d2656e218a6f1a21a4c4c0b56546a02e"}, - {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c54dc329cd44f7f7883a9f4baaefe686e8b9662e2c6c184ea15cceee587d8d69"}, - {file = "aiohttp-3.10.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e709d6ac598c5416f879bb1bae3fd751366120ac3fa235a01de763537385d036"}, - {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:17c272cfe7b07a5bb0c6ad3f234e0c336fb53f3bf17840f66bd77b5815ab3d16"}, - {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c21c82df33b264216abffff9f8370f303dab65d8eee3767efbbd2734363f677"}, - {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9331dd34145ff105177855017920dde140b447049cd62bb589de320fd6ddd582"}, - {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ac3196952c673822ebed8871cf8802e17254fff2a2ed4835d9c045d9b88c5ec7"}, - {file = "aiohttp-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2c33fa6e10bb7ed262e3ff03cc69d52869514f16558db0626a7c5c61dde3c29f"}, - {file = "aiohttp-3.10.9-cp313-cp313-win32.whl", hash = "sha256:a14e4b672c257a6b94fe934ee62666bacbc8e45b7876f9dd9502d0f0fe69db16"}, - {file = "aiohttp-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:a35ed3d03910785f7d9d6f5381f0c24002b2b888b298e6f941b2fc94c5055fcd"}, - {file = "aiohttp-3.10.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f392ef50e22c31fa49b5a46af7f983fa3f118f3eccb8522063bee8bfa6755f8"}, - {file = "aiohttp-3.10.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d1f5c9169e26db6a61276008582d945405b8316aae2bb198220466e68114a0f5"}, - {file = "aiohttp-3.10.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8d9d10d10ec27c0d46ddaecc3c5598c4db9ce4e6398ca872cdde0525765caa2f"}, - {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d97273a52d7f89a75b11ec386f786d3da7723d7efae3034b4dda79f6f093edc1"}, - {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d271f770b52e32236d945911b2082f9318e90ff835d45224fa9e28374303f729"}, - {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7003f33f5f7da1eb02f0446b0f8d2ccf57d253ca6c2e7a5732d25889da82b517"}, - {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6e00c8a92e7663ed2be6fcc08a2997ff06ce73c8080cd0df10cc0321a3168d7"}, - {file = "aiohttp-3.10.9-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a61df62966ce6507aafab24e124e0c3a1cfbe23c59732987fc0fd0d71daa0b88"}, - {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:60555211a006d26e1a389222e3fab8cd379f28e0fbf7472ee55b16c6c529e3a6"}, - {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d15a29424e96fad56dc2f3abed10a89c50c099f97d2416520c7a543e8fddf066"}, - {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:a19caae0d670771ea7854ca30df76f676eb47e0fd9b2ee4392d44708f272122d"}, - {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:99f9678bf0e2b1b695e8028fedac24ab6770937932eda695815d5a6618c37e04"}, - {file = "aiohttp-3.10.9-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2914caa46054f3b5ff910468d686742ff8cff54b8a67319d75f5d5945fd0a13d"}, - {file = "aiohttp-3.10.9-cp38-cp38-win32.whl", hash = "sha256:0bc059ecbce835630e635879f5f480a742e130d9821fbe3d2f76610a6698ee25"}, - {file = "aiohttp-3.10.9-cp38-cp38-win_amd64.whl", hash = "sha256:e883b61b75ca6efc2541fcd52a5c8ccfe288b24d97e20ac08fdf343b8ac672ea"}, - {file = "aiohttp-3.10.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fcd546782d03181b0b1d20b43d612429a90a68779659ba8045114b867971ab71"}, - {file = "aiohttp-3.10.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:85711eec2d875cd88c7eb40e734c4ca6d9ae477d6f26bd2b5bb4f7f60e41b156"}, - {file = "aiohttp-3.10.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:02d1d6610588bcd743fae827bd6f2e47e0d09b346f230824b4c6fb85c6065f9c"}, - {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3668d0c2a4d23fb136a753eba42caa2c0abbd3d9c5c87ee150a716a16c6deec1"}, - {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7c071235a47d407b0e93aa6262b49422dbe48d7d8566e1158fecc91043dd948"}, - {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac74e794e3aee92ae8f571bfeaa103a141e409863a100ab63a253b1c53b707eb"}, - {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bbf94d4a0447705b7775417ca8bb8086cc5482023a6e17cdc8f96d0b1b5aba6"}, - {file = "aiohttp-3.10.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb0b2d5d51f96b6cc19e6ab46a7b684be23240426ae951dcdac9639ab111b45e"}, - {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e83dfefb4f7d285c2d6a07a22268344a97d61579b3e0dce482a5be0251d672ab"}, - {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f0a44bb40b6aaa4fb9a5c1ee07880570ecda2065433a96ccff409c9c20c1624a"}, - {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c2b627d3c8982691b06d89d31093cee158c30629fdfebe705a91814d49b554f8"}, - {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:03690541e4cc866eef79626cfa1ef4dd729c5c1408600c8cb9e12e1137eed6ab"}, - {file = "aiohttp-3.10.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad3675c126f2a95bde637d162f8231cff6bc0bc9fbe31bd78075f9ff7921e322"}, - {file = "aiohttp-3.10.9-cp39-cp39-win32.whl", hash = "sha256:1321658f12b6caffafdc35cfba6c882cb014af86bef4e78c125e7e794dfb927b"}, - {file = "aiohttp-3.10.9-cp39-cp39-win_amd64.whl", hash = "sha256:9fdf5c839bf95fc67be5794c780419edb0dbef776edcfc6c2e5e2ffd5ee755fa"}, - {file = "aiohttp-3.10.9.tar.gz", hash = "sha256:143b0026a9dab07a05ad2dd9e46aa859bffdd6348ddc5967b42161168c24f857"}, + {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be7443669ae9c016b71f402e43208e13ddf00912f47f623ee5994e12fc7d4b3f"}, + {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b06b7843929e41a94ea09eb1ce3927865387e3e23ebe108e0d0d09b08d25be9"}, + {file = "aiohttp-3.10.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:333cf6cf8e65f6a1e06e9eb3e643a0c515bb850d470902274239fea02033e9a8"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:274cfa632350225ce3fdeb318c23b4a10ec25c0e2c880eff951a3842cf358ac1"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9e5e4a85bdb56d224f412d9c98ae4cbd032cc4f3161818f692cd81766eee65a"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b606353da03edcc71130b52388d25f9a30a126e04caef1fd637e31683033abd"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab5a5a0c7a7991d90446a198689c0535be89bbd6b410a1f9a66688f0880ec026"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:578a4b875af3e0daaf1ac6fa983d93e0bbfec3ead753b6d6f33d467100cdc67b"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8105fd8a890df77b76dd3054cddf01a879fc13e8af576805d667e0fa0224c35d"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3bcd391d083f636c06a68715e69467963d1f9600f85ef556ea82e9ef25f043f7"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fbc6264158392bad9df19537e872d476f7c57adf718944cc1e4495cbabf38e2a"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e48d5021a84d341bcaf95c8460b152cfbad770d28e5fe14a768988c461b821bc"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2609e9ab08474702cc67b7702dbb8a80e392c54613ebe80db7e8dbdb79837c68"}, + {file = "aiohttp-3.10.10-cp310-cp310-win32.whl", hash = "sha256:84afcdea18eda514c25bc68b9af2a2b1adea7c08899175a51fe7c4fb6d551257"}, + {file = "aiohttp-3.10.10-cp310-cp310-win_amd64.whl", hash = "sha256:9c72109213eb9d3874f7ac8c0c5fa90e072d678e117d9061c06e30c85b4cf0e6"}, + {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c30a0eafc89d28e7f959281b58198a9fa5e99405f716c0289b7892ca345fe45f"}, + {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:258c5dd01afc10015866114e210fb7365f0d02d9d059c3c3415382ab633fcbcb"}, + {file = "aiohttp-3.10.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:15ecd889a709b0080f02721255b3f80bb261c2293d3c748151274dfea93ac871"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3935f82f6f4a3820270842e90456ebad3af15810cf65932bd24da4463bc0a4c"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:413251f6fcf552a33c981c4709a6bba37b12710982fec8e558ae944bfb2abd38"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1720b4f14c78a3089562b8875b53e36b51c97c51adc53325a69b79b4b48ebcb"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:679abe5d3858b33c2cf74faec299fda60ea9de62916e8b67e625d65bf069a3b7"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79019094f87c9fb44f8d769e41dbb664d6e8fcfd62f665ccce36762deaa0e911"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe2fb38c2ed905a2582948e2de560675e9dfbee94c6d5ccdb1301c6d0a5bf092"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a3f00003de6eba42d6e94fabb4125600d6e484846dbf90ea8e48a800430cc142"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1bbb122c557a16fafc10354b9d99ebf2f2808a660d78202f10ba9d50786384b9"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:30ca7c3b94708a9d7ae76ff281b2f47d8eaf2579cd05971b5dc681db8caac6e1"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df9270660711670e68803107d55c2b5949c2e0f2e4896da176e1ecfc068b974a"}, + {file = "aiohttp-3.10.10-cp311-cp311-win32.whl", hash = "sha256:aafc8ee9b742ce75044ae9a4d3e60e3d918d15a4c2e08a6c3c3e38fa59b92d94"}, + {file = "aiohttp-3.10.10-cp311-cp311-win_amd64.whl", hash = "sha256:362f641f9071e5f3ee6f8e7d37d5ed0d95aae656adf4ef578313ee585b585959"}, + {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9294bbb581f92770e6ed5c19559e1e99255e4ca604a22c5c6397b2f9dd3ee42c"}, + {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a8fa23fe62c436ccf23ff930149c047f060c7126eae3ccea005f0483f27b2e28"}, + {file = "aiohttp-3.10.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c6a5b8c7926ba5d8545c7dd22961a107526562da31a7a32fa2456baf040939f"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:007ec22fbc573e5eb2fb7dec4198ef8f6bf2fe4ce20020798b2eb5d0abda6138"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9627cc1a10c8c409b5822a92d57a77f383b554463d1884008e051c32ab1b3742"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:50edbcad60d8f0e3eccc68da67f37268b5144ecc34d59f27a02f9611c1d4eec7"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a45d85cf20b5e0d0aa5a8dca27cce8eddef3292bc29d72dcad1641f4ed50aa16"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b00807e2605f16e1e198f33a53ce3c4523114059b0c09c337209ae55e3823a8"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f2d4324a98062be0525d16f768a03e0bbb3b9fe301ceee99611dc9a7953124e6"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:438cd072f75bb6612f2aca29f8bd7cdf6e35e8f160bc312e49fbecab77c99e3a"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:baa42524a82f75303f714108fea528ccacf0386af429b69fff141ffef1c534f9"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a7d8d14fe962153fc681f6366bdec33d4356f98a3e3567782aac1b6e0e40109a"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c1277cd707c465cd09572a774559a3cc7c7a28802eb3a2a9472588f062097205"}, + {file = "aiohttp-3.10.10-cp312-cp312-win32.whl", hash = "sha256:59bb3c54aa420521dc4ce3cc2c3fe2ad82adf7b09403fa1f48ae45c0cbde6628"}, + {file = "aiohttp-3.10.10-cp312-cp312-win_amd64.whl", hash = "sha256:0e1b370d8007c4ae31ee6db7f9a2fe801a42b146cec80a86766e7ad5c4a259cf"}, + {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ad7593bb24b2ab09e65e8a1d385606f0f47c65b5a2ae6c551db67d6653e78c28"}, + {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1eb89d3d29adaf533588f209768a9c02e44e4baf832b08118749c5fad191781d"}, + {file = "aiohttp-3.10.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3fe407bf93533a6fa82dece0e74dbcaaf5d684e5a51862887f9eaebe6372cd79"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50aed5155f819873d23520919e16703fc8925e509abbb1a1491b0087d1cd969e"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f05e9727ce409358baa615dbeb9b969db94324a79b5a5cea45d39bdb01d82e6"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dffb610a30d643983aeb185ce134f97f290f8935f0abccdd32c77bed9388b42"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa6658732517ddabe22c9036479eabce6036655ba87a0224c612e1ae6af2087e"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:741a46d58677d8c733175d7e5aa618d277cd9d880301a380fd296975a9cdd7bc"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e00e3505cd80440f6c98c6d69269dcc2a119f86ad0a9fd70bccc59504bebd68a"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ffe595f10566f8276b76dc3a11ae4bb7eba1aac8ddd75811736a15b0d5311414"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdfcf6443637c148c4e1a20c48c566aa694fa5e288d34b20fcdc58507882fed3"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d183cf9c797a5291e8301790ed6d053480ed94070637bfaad914dd38b0981f67"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:77abf6665ae54000b98b3c742bc6ea1d1fb31c394bcabf8b5d2c1ac3ebfe7f3b"}, + {file = "aiohttp-3.10.10-cp313-cp313-win32.whl", hash = "sha256:4470c73c12cd9109db8277287d11f9dd98f77fc54155fc71a7738a83ffcc8ea8"}, + {file = "aiohttp-3.10.10-cp313-cp313-win_amd64.whl", hash = "sha256:486f7aabfa292719a2753c016cc3a8f8172965cabb3ea2e7f7436c7f5a22a151"}, + {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1b66ccafef7336a1e1f0e389901f60c1d920102315a56df85e49552308fc0486"}, + {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:acd48d5b80ee80f9432a165c0ac8cbf9253eaddb6113269a5e18699b33958dbb"}, + {file = "aiohttp-3.10.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3455522392fb15ff549d92fbf4b73b559d5e43dc522588f7eb3e54c3f38beee7"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c3b868724137f713a38376fef8120c166d1eadd50da1855c112fe97954aed8"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:da1dee8948d2137bb51fbb8a53cce6b1bcc86003c6b42565f008438b806cccd8"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5ce2ce7c997e1971b7184ee37deb6ea9922ef5163c6ee5aa3c274b05f9e12fa"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28529e08fde6f12eba8677f5a8608500ed33c086f974de68cc65ab218713a59d"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7db54c7914cc99d901d93a34704833568d86c20925b2762f9fa779f9cd2e70f"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03a42ac7895406220124c88911ebee31ba8b2d24c98507f4a8bf826b2937c7f2"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:7e338c0523d024fad378b376a79faff37fafb3c001872a618cde1d322400a572"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:038f514fe39e235e9fef6717fbf944057bfa24f9b3db9ee551a7ecf584b5b480"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:64f6c17757251e2b8d885d728b6433d9d970573586a78b78ba8929b0f41d045a"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:93429602396f3383a797a2a70e5f1de5df8e35535d7806c9f91df06f297e109b"}, + {file = "aiohttp-3.10.10-cp38-cp38-win32.whl", hash = "sha256:c823bc3971c44ab93e611ab1a46b1eafeae474c0c844aff4b7474287b75fe49c"}, + {file = "aiohttp-3.10.10-cp38-cp38-win_amd64.whl", hash = "sha256:54ca74df1be3c7ca1cf7f4c971c79c2daf48d9aa65dea1a662ae18926f5bc8ce"}, + {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01948b1d570f83ee7bbf5a60ea2375a89dfb09fd419170e7f5af029510033d24"}, + {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9fc1500fd2a952c5c8e3b29aaf7e3cc6e27e9cfc0a8819b3bce48cc1b849e4cc"}, + {file = "aiohttp-3.10.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f614ab0c76397661b90b6851a030004dac502e48260ea10f2441abd2207fbcc7"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00819de9e45d42584bed046314c40ea7e9aea95411b38971082cad449392b08c"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05646ebe6b94cc93407b3bf34b9eb26c20722384d068eb7339de802154d61bc5"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:998f3bd3cfc95e9424a6acd7840cbdd39e45bc09ef87533c006f94ac47296090"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9010c31cd6fa59438da4e58a7f19e4753f7f264300cd152e7f90d4602449762"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ea7ffc6d6d6f8a11e6f40091a1040995cdff02cfc9ba4c2f30a516cb2633554"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ef9c33cc5cbca35808f6c74be11eb7f5f6b14d2311be84a15b594bd3e58b5527"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ce0cdc074d540265bfeb31336e678b4e37316849d13b308607efa527e981f5c2"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:597a079284b7ee65ee102bc3a6ea226a37d2b96d0418cc9047490f231dc09fe8"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:7789050d9e5d0c309c706953e5e8876e38662d57d45f936902e176d19f1c58ab"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e7f8b04d83483577fd9200461b057c9f14ced334dcb053090cea1da9c8321a91"}, + {file = "aiohttp-3.10.10-cp39-cp39-win32.whl", hash = "sha256:c02a30b904282777d872266b87b20ed8cc0d1501855e27f831320f471d54d983"}, + {file = "aiohttp-3.10.10-cp39-cp39-win_amd64.whl", hash = "sha256:edfe3341033a6b53a5c522c802deb2079eee5cbfbb0af032a55064bd65c73a23"}, + {file = "aiohttp-3.10.10.tar.gz", hash = "sha256:0631dd7c9f0822cc61c88586ca76d5b5ada26538097d0f1df510b082bad3411a"}, ] [package.dependencies] @@ -1161,6 +1161,45 @@ files = [ {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, ] +[[package]] +name = "fsspec" +version = "2024.9.0" +description = "File-system specification" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fsspec-2024.9.0-py3-none-any.whl", hash = "sha256:a0947d552d8a6efa72cc2c730b12c41d043509156966cca4fb157b0f2a0c574b"}, + {file = "fsspec-2024.9.0.tar.gz", hash = "sha256:4b0afb90c2f21832df142f292649035d80b421f60a9e1c027802e5a0da2b04e8"}, +] + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +dev = ["pre-commit", "ruff"] +doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] +test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] +test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] +tqdm = ["tqdm"] + [[package]] name = "google-auth" version = "2.35.0" @@ -1325,6 +1364,40 @@ cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] +[[package]] +name = "huggingface-hub" +version = "0.25.2" +description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "huggingface_hub-0.25.2-py3-none-any.whl", hash = "sha256:1897caf88ce7f97fe0110603d8f66ac264e3ba6accdf30cd66cc0fed5282ad25"}, + {file = "huggingface_hub-0.25.2.tar.gz", hash = "sha256:a1014ea111a5f40ccd23f7f7ba8ac46e20fa3b658ced1f86a00c75c06ec6423c"}, +] + +[package.dependencies] +filelock = "*" +fsspec = ">=2023.5.0" +packaging = ">=20.9" +pyyaml = ">=5.1" +requests = "*" +tqdm = ">=4.42.1" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +cli = ["InquirerPy (==0.3.4)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] +hf-transfer = ["hf-transfer (>=0.1.4)"] +inference = ["aiohttp", "minijinja (>=1.0)"] +quality = ["mypy (==1.5.1)", "ruff (>=0.5.0)"] +tensorflow = ["graphviz", "pydot", "tensorflow"] +tensorflow-testing = ["keras (<3.0)", "tensorflow"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +torch = ["safetensors[torch]", "torch"] +typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] + [[package]] name = "idna" version = "3.10" @@ -1339,6 +1412,29 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[[package]] +name = "importlib-metadata" +version = "8.5.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -1350,6 +1446,23 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + [[package]] name = "jiter" version = "0.6.1" @@ -1645,6 +1758,34 @@ files = [ [package.dependencies] rapidfuzz = ">=3.9.0,<4.0.0" +[[package]] +name = "litellm" +version = "1.49.3" +description = "Library to easily interface with LLM API providers" +optional = false +python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" +files = [ + {file = "litellm-1.49.3-py3-none-any.whl", hash = "sha256:300c3c9e1600441f8b6d3afe0fd79c6193f901b2091f3730883ffe3709eebfa2"}, + {file = "litellm-1.49.3.tar.gz", hash = "sha256:e51ce30286894803dcf2949ddb4aab5c2e00809694a48ce6e997953566113c0b"}, +] + +[package.dependencies] +aiohttp = "*" +click = "*" +importlib-metadata = ">=6.8.0" +jinja2 = ">=3.1.2,<4.0.0" +jsonschema = ">=4.22.0,<5.0.0" +openai = ">=1.51.0" +pydantic = ">=2.0.0,<3.0.0" +python-dotenv = ">=0.2.0" +requests = ">=2.31.0,<3.0.0" +tiktoken = ">=0.7.0" +tokenizers = "*" + +[package.extras] +extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "resend (>=0.8.0,<0.9.0)"] +proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "cryptography (>=42.0.5,<43.0.0)", "fastapi (>=0.111.0,<0.112.0)", "fastapi-sso (>=0.10.0,<0.11.0)", "gunicorn (>=22.0.0,<23.0.0)", "orjson (>=3.9.7,<4.0.0)", "pynacl (>=1.5.0,<2.0.0)", "python-multipart (>=0.0.9,<0.0.10)", "pyyaml (>=6.0.1,<7.0.0)", "rq", "uvicorn (>=0.22.0,<0.23.0)"] + [[package]] name = "mako" version = "1.3.5" @@ -2629,6 +2770,20 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + [[package]] name = "python-http-client" version = "3.3.7" @@ -2667,29 +2822,29 @@ files = [ [[package]] name = "pywin32" -version = "307" +version = "308" description = "Python for Window Extensions" optional = false python-versions = "*" files = [ - {file = "pywin32-307-cp310-cp310-win32.whl", hash = "sha256:f8f25d893c1e1ce2d685ef6d0a481e87c6f510d0f3f117932781f412e0eba31b"}, - {file = "pywin32-307-cp310-cp310-win_amd64.whl", hash = "sha256:36e650c5e5e6b29b5d317385b02d20803ddbac5d1031e1f88d20d76676dd103d"}, - {file = "pywin32-307-cp310-cp310-win_arm64.whl", hash = "sha256:0c12d61e0274e0c62acee79e3e503c312426ddd0e8d4899c626cddc1cafe0ff4"}, - {file = "pywin32-307-cp311-cp311-win32.whl", hash = "sha256:fec5d27cc893178fab299de911b8e4d12c5954e1baf83e8a664311e56a272b75"}, - {file = "pywin32-307-cp311-cp311-win_amd64.whl", hash = "sha256:987a86971753ed7fdd52a7fb5747aba955b2c7fbbc3d8b76ec850358c1cc28c3"}, - {file = "pywin32-307-cp311-cp311-win_arm64.whl", hash = "sha256:fd436897c186a2e693cd0437386ed79f989f4d13d6f353f8787ecbb0ae719398"}, - {file = "pywin32-307-cp312-cp312-win32.whl", hash = "sha256:07649ec6b01712f36debf39fc94f3d696a46579e852f60157a729ac039df0815"}, - {file = "pywin32-307-cp312-cp312-win_amd64.whl", hash = "sha256:00d047992bb5dcf79f8b9b7c81f72e0130f9fe4b22df613f755ab1cc021d8347"}, - {file = "pywin32-307-cp312-cp312-win_arm64.whl", hash = "sha256:b53658acbfc6a8241d72cc09e9d1d666be4e6c99376bc59e26cdb6223c4554d2"}, - {file = "pywin32-307-cp313-cp313-win32.whl", hash = "sha256:ea4d56e48dc1ab2aa0a5e3c0741ad6e926529510516db7a3b6981a1ae74405e5"}, - {file = "pywin32-307-cp313-cp313-win_amd64.whl", hash = "sha256:576d09813eaf4c8168d0bfd66fb7cb3b15a61041cf41598c2db4a4583bf832d2"}, - {file = "pywin32-307-cp313-cp313-win_arm64.whl", hash = "sha256:b30c9bdbffda6a260beb2919f918daced23d32c79109412c2085cbc513338a0a"}, - {file = "pywin32-307-cp37-cp37m-win32.whl", hash = "sha256:5101472f5180c647d4525a0ed289ec723a26231550dbfd369ec19d5faf60e511"}, - {file = "pywin32-307-cp37-cp37m-win_amd64.whl", hash = "sha256:05de55a7c110478dc4b202230e98af5e0720855360d2b31a44bb4e296d795fba"}, - {file = "pywin32-307-cp38-cp38-win32.whl", hash = "sha256:13d059fb7f10792542082f5731d5d3d9645320fc38814759313e5ee97c3fac01"}, - {file = "pywin32-307-cp38-cp38-win_amd64.whl", hash = "sha256:7e0b2f93769d450a98ac7a31a087e07b126b6d571e8b4386a5762eb85325270b"}, - {file = "pywin32-307-cp39-cp39-win32.whl", hash = "sha256:55ee87f2f8c294e72ad9d4261ca423022310a6e79fb314a8ca76ab3f493854c6"}, - {file = "pywin32-307-cp39-cp39-win_amd64.whl", hash = "sha256:e9d5202922e74985b037c9ef46778335c102b74b95cec70f629453dbe7235d87"}, + {file = "pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e"}, + {file = "pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e"}, + {file = "pywin32-308-cp310-cp310-win_arm64.whl", hash = "sha256:a5ab5381813b40f264fa3495b98af850098f814a25a63589a8e9eb12560f450c"}, + {file = "pywin32-308-cp311-cp311-win32.whl", hash = "sha256:5d8c8015b24a7d6855b1550d8e660d8daa09983c80e5daf89a273e5c6fb5095a"}, + {file = "pywin32-308-cp311-cp311-win_amd64.whl", hash = "sha256:575621b90f0dc2695fec346b2d6302faebd4f0f45c05ea29404cefe35d89442b"}, + {file = "pywin32-308-cp311-cp311-win_arm64.whl", hash = "sha256:100a5442b7332070983c4cd03f2e906a5648a5104b8a7f50175f7906efd16bb6"}, + {file = "pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897"}, + {file = "pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47"}, + {file = "pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091"}, + {file = "pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed"}, + {file = "pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4"}, + {file = "pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd"}, + {file = "pywin32-308-cp37-cp37m-win32.whl", hash = "sha256:1f696ab352a2ddd63bd07430080dd598e6369152ea13a25ebcdd2f503a38f1ff"}, + {file = "pywin32-308-cp37-cp37m-win_amd64.whl", hash = "sha256:13dcb914ed4347019fbec6697a01a0aec61019c1046c2b905410d197856326a6"}, + {file = "pywin32-308-cp38-cp38-win32.whl", hash = "sha256:5794e764ebcabf4ff08c555b31bd348c9025929371763b2183172ff4708152f0"}, + {file = "pywin32-308-cp38-cp38-win_amd64.whl", hash = "sha256:3b92622e29d651c6b783e368ba7d6722b1634b8e70bd376fd7610fe1992e19de"}, + {file = "pywin32-308-cp39-cp39-win32.whl", hash = "sha256:7873ca4dc60ab3287919881a7d4f88baee4a6e639aa6962de25a98ba6b193341"}, + {file = "pywin32-308-cp39-cp39-win_amd64.whl", hash = "sha256:71b3322d949b4cc20776436a9c9ba0eeedcbc9c650daa536df63f0ff111bb920"}, ] [[package]] @@ -2887,6 +3042,109 @@ files = [ attrs = ">=22.2.0" rpds-py = ">=0.7.0" +[[package]] +name = "regex" +version = "2024.9.11" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.8" +files = [ + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, + {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, + {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, + {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, + {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, + {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, + {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, + {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, + {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"}, + {file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"}, + {file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, + {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, + {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, + {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, +] + [[package]] name = "requests" version = "2.32.3" @@ -3346,6 +3604,53 @@ drf = ["adrf", "django (>=4)", "django-cors-headers (==3.11.0)", "django-stubs ( fastapi = ["Fastapi", "python-dotenv (==0.19.2)", "respx (==0.19.2)", "uvicorn (==0.18.2)"] flask = ["Flask", "flask-cors", "python-dotenv (==0.19.2)"] +[[package]] +name = "tiktoken" +version = "0.8.0" +description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" +optional = false +python-versions = ">=3.9" +files = [ + {file = "tiktoken-0.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b07e33283463089c81ef1467180e3e00ab00d46c2c4bbcef0acab5f771d6695e"}, + {file = "tiktoken-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9269348cb650726f44dd3bbb3f9110ac19a8dcc8f54949ad3ef652ca22a38e21"}, + {file = "tiktoken-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e13f37bc4ef2d012731e93e0fef21dc3b7aea5bb9009618de9a4026844e560"}, + {file = "tiktoken-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f13d13c981511331eac0d01a59b5df7c0d4060a8be1e378672822213da51e0a2"}, + {file = "tiktoken-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6b2ddbc79a22621ce8b1166afa9f9a888a664a579350dc7c09346a3b5de837d9"}, + {file = "tiktoken-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d8c2d0e5ba6453a290b86cd65fc51fedf247e1ba170191715b049dac1f628005"}, + {file = "tiktoken-0.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d622d8011e6d6f239297efa42a2657043aaed06c4f68833550cac9e9bc723ef1"}, + {file = "tiktoken-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2efaf6199717b4485031b4d6edb94075e4d79177a172f38dd934d911b588d54a"}, + {file = "tiktoken-0.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5637e425ce1fc49cf716d88df3092048359a4b3bbb7da762840426e937ada06d"}, + {file = "tiktoken-0.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fb0e352d1dbe15aba082883058b3cce9e48d33101bdaac1eccf66424feb5b47"}, + {file = "tiktoken-0.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:56edfefe896c8f10aba372ab5706b9e3558e78db39dd497c940b47bf228bc419"}, + {file = "tiktoken-0.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:326624128590def898775b722ccc327e90b073714227175ea8febbc920ac0a99"}, + {file = "tiktoken-0.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:881839cfeae051b3628d9823b2e56b5cc93a9e2efb435f4cf15f17dc45f21586"}, + {file = "tiktoken-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fe9399bdc3f29d428f16a2f86c3c8ec20be3eac5f53693ce4980371c3245729b"}, + {file = "tiktoken-0.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a58deb7075d5b69237a3ff4bb51a726670419db6ea62bdcd8bd80c78497d7ab"}, + {file = "tiktoken-0.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2908c0d043a7d03ebd80347266b0e58440bdef5564f84f4d29fb235b5df3b04"}, + {file = "tiktoken-0.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:294440d21a2a51e12d4238e68a5972095534fe9878be57d905c476017bff99fc"}, + {file = "tiktoken-0.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:d8f3192733ac4d77977432947d563d7e1b310b96497acd3c196c9bddb36ed9db"}, + {file = "tiktoken-0.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:02be1666096aff7da6cbd7cdaa8e7917bfed3467cd64b38b1f112e96d3b06a24"}, + {file = "tiktoken-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94ff53c5c74b535b2cbf431d907fc13c678bbd009ee633a2aca269a04389f9a"}, + {file = "tiktoken-0.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b231f5e8982c245ee3065cd84a4712d64692348bc609d84467c57b4b72dcbc5"}, + {file = "tiktoken-0.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4177faa809bd55f699e88c96d9bb4635d22e3f59d635ba6fd9ffedf7150b9953"}, + {file = "tiktoken-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5376b6f8dc4753cd81ead935c5f518fa0fbe7e133d9e25f648d8c4dabdd4bad7"}, + {file = "tiktoken-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:18228d624807d66c87acd8f25fc135665617cab220671eb65b50f5d70fa51f69"}, + {file = "tiktoken-0.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e17807445f0cf1f25771c9d86496bd8b5c376f7419912519699f3cc4dc5c12e"}, + {file = "tiktoken-0.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:886f80bd339578bbdba6ed6d0567a0d5c6cfe198d9e587ba6c447654c65b8edc"}, + {file = "tiktoken-0.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6adc8323016d7758d6de7313527f755b0fc6c72985b7d9291be5d96d73ecd1e1"}, + {file = "tiktoken-0.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b591fb2b30d6a72121a80be24ec7a0e9eb51c5500ddc7e4c2496516dd5e3816b"}, + {file = "tiktoken-0.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:845287b9798e476b4d762c3ebda5102be87ca26e5d2c9854002825d60cdb815d"}, + {file = "tiktoken-0.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:1473cfe584252dc3fa62adceb5b1c763c1874e04511b197da4e6de51d6ce5a02"}, + {file = "tiktoken-0.8.0.tar.gz", hash = "sha256:9ccbb2740f24542534369c5635cfd9b2b3c2490754a78ac8831d99f89f94eeb2"}, +] + +[package.dependencies] +regex = ">=2022.1.18" +requests = ">=2.26.0" + +[package.extras] +blobfile = ["blobfile (>=2)"] + [[package]] name = "tldextract" version = "3.1.0" @@ -3363,6 +3668,123 @@ idna = "*" requests = ">=2.1.0" requests-file = ">=1.4" +[[package]] +name = "tokenizers" +version = "0.20.1" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tokenizers-0.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:439261da7c0a5c88bda97acb284d49fbdaf67e9d3b623c0bfd107512d22787a9"}, + {file = "tokenizers-0.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03dae629d99068b1ea5416d50de0fea13008f04129cc79af77a2a6392792d93c"}, + {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b61f561f329ffe4b28367798b89d60c4abf3f815d37413b6352bc6412a359867"}, + {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec870fce1ee5248a10be69f7a8408a234d6f2109f8ea827b4f7ecdbf08c9fd15"}, + {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d388d1ea8b7447da784e32e3b86a75cce55887e3b22b31c19d0b186b1c677800"}, + {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:299c85c1d21135bc01542237979bf25c32efa0d66595dd0069ae259b97fb2dbe"}, + {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e96f6c14c9752bb82145636b614d5a78e9cde95edfbe0a85dad0dd5ddd6ec95c"}, + {file = "tokenizers-0.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc9e95ad49c932b80abfbfeaf63b155761e695ad9f8a58c52a47d962d76e310f"}, + {file = "tokenizers-0.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f22dee205329a636148c325921c73cf3e412e87d31f4d9c3153b302a0200057b"}, + {file = "tokenizers-0.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2ffd9a8895575ac636d44500c66dffaef133823b6b25067604fa73bbc5ec09d"}, + {file = "tokenizers-0.20.1-cp310-none-win32.whl", hash = "sha256:2847843c53f445e0f19ea842a4e48b89dd0db4e62ba6e1e47a2749d6ec11f50d"}, + {file = "tokenizers-0.20.1-cp310-none-win_amd64.whl", hash = "sha256:f9aa93eacd865f2798b9e62f7ce4533cfff4f5fbd50c02926a78e81c74e432cd"}, + {file = "tokenizers-0.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4a717dcb08f2dabbf27ae4b6b20cbbb2ad7ed78ce05a829fae100ff4b3c7ff15"}, + {file = "tokenizers-0.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f84dad1ff1863c648d80628b1b55353d16303431283e4efbb6ab1af56a75832"}, + {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:929c8f3afa16a5130a81ab5079c589226273ec618949cce79b46d96e59a84f61"}, + {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d10766473954397e2d370f215ebed1cc46dcf6fd3906a2a116aa1d6219bfedc3"}, + {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9300fac73ddc7e4b0330acbdda4efaabf74929a4a61e119a32a181f534a11b47"}, + {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ecaf7b0e39caeb1aa6dd6e0975c405716c82c1312b55ac4f716ef563a906969"}, + {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5170be9ec942f3d1d317817ced8d749b3e1202670865e4fd465e35d8c259de83"}, + {file = "tokenizers-0.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f1ae08fa9aea5891cbd69df29913e11d3841798e0bfb1ff78b78e4e7ea0a4"}, + {file = "tokenizers-0.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ee86d4095d3542d73579e953c2e5e07d9321af2ffea6ecc097d16d538a2dea16"}, + {file = "tokenizers-0.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:86dcd08da163912e17b27bbaba5efdc71b4fbffb841530fdb74c5707f3c49216"}, + {file = "tokenizers-0.20.1-cp311-none-win32.whl", hash = "sha256:9af2dc4ee97d037bc6b05fa4429ddc87532c706316c5e11ce2f0596dfcfa77af"}, + {file = "tokenizers-0.20.1-cp311-none-win_amd64.whl", hash = "sha256:899152a78b095559c287b4c6d0099469573bb2055347bb8154db106651296f39"}, + {file = "tokenizers-0.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:407ab666b38e02228fa785e81f7cf79ef929f104bcccf68a64525a54a93ceac9"}, + {file = "tokenizers-0.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f13a2d16032ebc8bd812eb8099b035ac65887d8f0c207261472803b9633cf3e"}, + {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e98eee4dca22849fbb56a80acaa899eec5b72055d79637dd6aa15d5e4b8628c9"}, + {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47c1bcdd61e61136087459cb9e0b069ff23b5568b008265e5cbc927eae3387ce"}, + {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:128c1110e950534426e2274837fc06b118ab5f2fa61c3436e60e0aada0ccfd67"}, + {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2e2d47a819d2954f2c1cd0ad51bb58ffac6f53a872d5d82d65d79bf76b9896d"}, + {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bdd67a0e3503a9a7cf8bc5a4a49cdde5fa5bada09a51e4c7e1c73900297539bd"}, + {file = "tokenizers-0.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:689b93d2e26d04da337ac407acec8b5d081d8d135e3e5066a88edd5bdb5aff89"}, + {file = "tokenizers-0.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0c6a796ddcd9a19ad13cf146997cd5895a421fe6aec8fd970d69f9117bddb45c"}, + {file = "tokenizers-0.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3ea919687aa7001a8ff1ba36ac64f165c4e89035f57998fa6cedcfd877be619d"}, + {file = "tokenizers-0.20.1-cp312-none-win32.whl", hash = "sha256:6d3ac5c1f48358ffe20086bf065e843c0d0a9fce0d7f0f45d5f2f9fba3609ca5"}, + {file = "tokenizers-0.20.1-cp312-none-win_amd64.whl", hash = "sha256:b0874481aea54a178f2bccc45aa2d0c99cd3f79143a0948af6a9a21dcc49173b"}, + {file = "tokenizers-0.20.1-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:96af92e833bd44760fb17f23f402e07a66339c1dcbe17d79a9b55bb0cc4f038e"}, + {file = "tokenizers-0.20.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:65f34e5b731a262dfa562820818533c38ce32a45864437f3d9c82f26c139ca7f"}, + {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17f98fccb5c12ab1ce1f471731a9cd86df5d4bd2cf2880c5a66b229802d96145"}, + {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8c0fc3542cf9370bf92c932eb71bdeb33d2d4aeeb4126d9fd567b60bd04cb30"}, + {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b39356df4575d37f9b187bb623aab5abb7b62c8cb702867a1768002f814800c"}, + {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfdad27b0e50544f6b838895a373db6114b85112ba5c0cefadffa78d6daae563"}, + {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:094663dd0e85ee2e573126918747bdb40044a848fde388efb5b09d57bc74c680"}, + {file = "tokenizers-0.20.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e4cf033a2aa207d7ac790e91adca598b679999710a632c4a494aab0fc3a1b2"}, + {file = "tokenizers-0.20.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9310951c92c9fb91660de0c19a923c432f110dbfad1a2d429fbc44fa956bf64f"}, + {file = "tokenizers-0.20.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:05e41e302c315bd2ed86c02e917bf03a6cf7d2f652c9cee1a0eb0d0f1ca0d32c"}, + {file = "tokenizers-0.20.1-cp37-none-win32.whl", hash = "sha256:212231ab7dfcdc879baf4892ca87c726259fa7c887e1688e3f3cead384d8c305"}, + {file = "tokenizers-0.20.1-cp37-none-win_amd64.whl", hash = "sha256:896195eb9dfdc85c8c052e29947169c1fcbe75a254c4b5792cdbd451587bce85"}, + {file = "tokenizers-0.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:741fb22788482d09d68e73ece1495cfc6d9b29a06c37b3df90564a9cfa688e6d"}, + {file = "tokenizers-0.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:10be14ebd8082086a342d969e17fc2d6edc856c59dbdbddd25f158fa40eaf043"}, + {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:514cf279b22fa1ae0bc08e143458c74ad3b56cd078b319464959685a35c53d5e"}, + {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a647c5b7cb896d6430cf3e01b4e9a2d77f719c84cefcef825d404830c2071da2"}, + {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cdf379219e1e1dd432091058dab325a2e6235ebb23e0aec8d0508567c90cd01"}, + {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ba72260449e16c4c2f6f3252823b059fbf2d31b32617e582003f2b18b415c39"}, + {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:910b96ed87316e4277b23c7bcaf667ce849c7cc379a453fa179e7e09290eeb25"}, + {file = "tokenizers-0.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e53975a6694428a0586534cc1354b2408d4e010a3103117f617cbb550299797c"}, + {file = "tokenizers-0.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:07c4b7be58da142b0730cc4e5fd66bb7bf6f57f4986ddda73833cd39efef8a01"}, + {file = "tokenizers-0.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b605c540753e62199bf15cf69c333e934077ef2350262af2ccada46026f83d1c"}, + {file = "tokenizers-0.20.1-cp38-none-win32.whl", hash = "sha256:88b3bc76ab4db1ab95ead623d49c95205411e26302cf9f74203e762ac7e85685"}, + {file = "tokenizers-0.20.1-cp38-none-win_amd64.whl", hash = "sha256:d412a74cf5b3f68a90c615611a5aa4478bb303d1c65961d22db45001df68afcb"}, + {file = "tokenizers-0.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a25dcb2f41a0a6aac31999e6c96a75e9152fa0127af8ece46c2f784f23b8197a"}, + {file = "tokenizers-0.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a12c3cebb8c92e9c35a23ab10d3852aee522f385c28d0b4fe48c0b7527d59762"}, + {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02e18da58cf115b7c40de973609c35bde95856012ba42a41ee919c77935af251"}, + {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f326a1ac51ae909b9760e34671c26cd0dfe15662f447302a9d5bb2d872bab8ab"}, + {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b4872647ea6f25224e2833b044b0b19084e39400e8ead3cfe751238b0802140"}, + {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce6238a3311bb8e4c15b12600927d35c267b92a52c881ef5717a900ca14793f7"}, + {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57b7a8880b208866508b06ce365dc631e7a2472a3faa24daa430d046fb56c885"}, + {file = "tokenizers-0.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a908c69c2897a68f412aa05ba38bfa87a02980df70f5a72fa8490479308b1f2d"}, + {file = "tokenizers-0.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:da1001aa46f4490099c82e2facc4fbc06a6a32bf7de3918ba798010954b775e0"}, + {file = "tokenizers-0.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:42c097390e2f0ed0a5c5d569e6669dd4e9fff7b31c6a5ce6e9c66a61687197de"}, + {file = "tokenizers-0.20.1-cp39-none-win32.whl", hash = "sha256:3d4d218573a3d8b121a1f8c801029d70444ffb6d8f129d4cca1c7b672ee4a24c"}, + {file = "tokenizers-0.20.1-cp39-none-win_amd64.whl", hash = "sha256:37d1e6f616c84fceefa7c6484a01df05caf1e207669121c66213cb5b2911d653"}, + {file = "tokenizers-0.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48689da7a395df41114f516208d6550e3e905e1239cc5ad386686d9358e9cef0"}, + {file = "tokenizers-0.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:712f90ea33f9bd2586b4a90d697c26d56d0a22fd3c91104c5858c4b5b6489a79"}, + {file = "tokenizers-0.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:359eceb6a620c965988fc559cebc0a98db26713758ec4df43fb76d41486a8ed5"}, + {file = "tokenizers-0.20.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d3caf244ce89d24c87545aafc3448be15870096e796c703a0d68547187192e1"}, + {file = "tokenizers-0.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03b03cf8b9a32254b1bf8a305fb95c6daf1baae0c1f93b27f2b08c9759f41dee"}, + {file = "tokenizers-0.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:218e5a3561561ea0f0ef1559c6d95b825308dbec23fb55b70b92589e7ff2e1e8"}, + {file = "tokenizers-0.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f40df5e0294a95131cc5f0e0eb91fe86d88837abfbee46b9b3610b09860195a7"}, + {file = "tokenizers-0.20.1-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:08aaa0d72bb65058e8c4b0455f61b840b156c557e2aca57627056624c3a93976"}, + {file = "tokenizers-0.20.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:998700177b45f70afeb206ad22c08d9e5f3a80639dae1032bf41e8cbc4dada4b"}, + {file = "tokenizers-0.20.1-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62f7fbd3c2c38b179556d879edae442b45f68312019c3a6013e56c3947a4e648"}, + {file = "tokenizers-0.20.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31e87fca4f6bbf5cc67481b562147fe932f73d5602734de7dd18a8f2eee9c6dd"}, + {file = "tokenizers-0.20.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:956f21d359ae29dd51ca5726d2c9a44ffafa041c623f5aa33749da87cfa809b9"}, + {file = "tokenizers-0.20.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1fbbaf17a393c78d8aedb6a334097c91cb4119a9ced4764ab8cfdc8d254dc9f9"}, + {file = "tokenizers-0.20.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ebe63e31f9c1a970c53866d814e35ec2ec26fda03097c486f82f3891cee60830"}, + {file = "tokenizers-0.20.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:81970b80b8ac126910295f8aab2d7ef962009ea39e0d86d304769493f69aaa1e"}, + {file = "tokenizers-0.20.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130e35e76f9337ed6c31be386e75d4925ea807055acf18ca1a9b0eec03d8fe23"}, + {file = "tokenizers-0.20.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd28a8614f5c82a54ab2463554e84ad79526c5184cf4573bbac2efbbbcead457"}, + {file = "tokenizers-0.20.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9041ee665d0fa7f5c4ccf0f81f5e6b7087f797f85b143c094126fc2611fec9d0"}, + {file = "tokenizers-0.20.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:62eb9daea2a2c06bcd8113a5824af8ef8ee7405d3a71123ba4d52c79bb3d9f1a"}, + {file = "tokenizers-0.20.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f861889707b54a9ab1204030b65fd6c22bdd4a95205deec7994dc22a8baa2ea4"}, + {file = "tokenizers-0.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:89d5c337d74ea6e5e7dc8af124cf177be843bbb9ca6e58c01f75ea103c12c8a9"}, + {file = "tokenizers-0.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0b7f515c83397e73292accdbbbedc62264e070bae9682f06061e2ddce67cacaf"}, + {file = "tokenizers-0.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e0305fc1ec6b1e5052d30d9c1d5c807081a7bd0cae46a33d03117082e91908c"}, + {file = "tokenizers-0.20.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc611e6ac0fa00a41de19c3bf6391a05ea201d2d22b757d63f5491ec0e67faa"}, + {file = "tokenizers-0.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5ffe0d7f7bfcfa3b2585776ecf11da2e01c317027c8573c78ebcb8985279e23"}, + {file = "tokenizers-0.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e7edb8ec12c100d5458d15b1e47c0eb30ad606a05641f19af7563bc3d1608c14"}, + {file = "tokenizers-0.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:de291633fb9303555793cc544d4a86e858da529b7d0b752bcaf721ae1d74b2c9"}, + {file = "tokenizers-0.20.1.tar.gz", hash = "sha256:84edcc7cdeeee45ceedb65d518fffb77aec69311c9c8e30f77ad84da3025f002"}, +] + +[package.dependencies] +huggingface-hub = ">=0.16.4,<1.0" + +[package.extras] +dev = ["tokenizers[testing]"] +docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] +testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests", "ruff"] + [[package]] name = "toml" version = "0.10.2" @@ -3730,103 +4152,109 @@ files = [ [[package]] name = "yarl" -version = "1.14.0" +version = "1.15.2" description = "Yet another URL library" optional = false python-versions = ">=3.8" files = [ - {file = "yarl-1.14.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1bfc25aa6a7c99cf86564210f79a0b7d4484159c67e01232b116e445b3036547"}, - {file = "yarl-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0cf21f46a15d445417de8fc89f2568852cf57fe8ca1ab3d19ddb24d45c0383ae"}, - {file = "yarl-1.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1dda53508df0de87b6e6b0a52d6718ff6c62a5aca8f5552748404963df639269"}, - {file = "yarl-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:587c3cc59bc148a9b1c07a019346eda2549bc9f468acd2f9824d185749acf0a6"}, - {file = "yarl-1.14.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3007a5b75cb50140708420fe688c393e71139324df599434633019314ceb8b59"}, - {file = "yarl-1.14.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:06ff23462398333c78b6f4f8d3d70410d657a471c2c5bbe6086133be43fc8f1a"}, - {file = "yarl-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:689a99a42ee4583fcb0d3a67a0204664aa1539684aed72bdafcbd505197a91c4"}, - {file = "yarl-1.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0547ab1e9345dc468cac8368d88ea4c5bd473ebc1d8d755347d7401982b5dd8"}, - {file = "yarl-1.14.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:742aef0a99844faaac200564ea6f5e08facb285d37ea18bd1a5acf2771f3255a"}, - {file = "yarl-1.14.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:176110bff341b6730f64a1eb3a7070e12b373cf1c910a9337e7c3240497db76f"}, - {file = "yarl-1.14.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46a9772a1efa93f9cd170ad33101c1817c77e0e9914d4fe33e2da299d7cf0f9b"}, - {file = "yarl-1.14.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ee2c68e4f2dd1b1c15b849ba1c96fac105fca6ffdb7c1e8be51da6fabbdeafb9"}, - {file = "yarl-1.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:047b258e00b99091b6f90355521f026238c63bd76dcf996d93527bb13320eefd"}, - {file = "yarl-1.14.0-cp310-cp310-win32.whl", hash = "sha256:0aa92e3e30a04f9462a25077db689c4ac5ea9ab6cc68a2e563881b987d42f16d"}, - {file = "yarl-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:d9baec588f015d0ee564057aa7574313c53a530662ffad930b7886becc85abdf"}, - {file = "yarl-1.14.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:07f9eaf57719d6721ab15805d85f4b01a5b509a0868d7320134371bcb652152d"}, - {file = "yarl-1.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c14b504a74e58e2deb0378b3eca10f3d076635c100f45b113c18c770b4a47a50"}, - {file = "yarl-1.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:16a682a127930f3fc4e42583becca6049e1d7214bcad23520c590edd741d2114"}, - {file = "yarl-1.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73bedd2be05f48af19f0f2e9e1353921ce0c83f4a1c9e8556ecdcf1f1eae4892"}, - {file = "yarl-1.14.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3ab950f8814f3b7b5e3eebc117986f817ec933676f68f0a6c5b2137dd7c9c69"}, - {file = "yarl-1.14.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b693c63e7e64b524f54aa4888403c680342d1ad0d97be1707c531584d6aeeb4f"}, - {file = "yarl-1.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85cb3e40eaa98489f1e2e8b29f5ad02ee1ee40d6ce6b88d50cf0f205de1d9d2c"}, - {file = "yarl-1.14.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f24f08b6c9b9818fd80612c97857d28f9779f0d1211653ece9844fc7b414df2"}, - {file = "yarl-1.14.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:29a84a46ec3ebae7a1c024c055612b11e9363a8a23238b3e905552d77a2bc51b"}, - {file = "yarl-1.14.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5cd5dad8366e0168e0fd23d10705a603790484a6dbb9eb272b33673b8f2cce72"}, - {file = "yarl-1.14.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a152751af7ef7b5d5fa6d215756e508dd05eb07d0cf2ba51f3e740076aa74373"}, - {file = "yarl-1.14.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:3d569f877ed9a708e4c71a2d13d2940cb0791da309f70bd970ac1a5c088a0a92"}, - {file = "yarl-1.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6a615cad11ec3428020fb3c5a88d85ce1b5c69fd66e9fcb91a7daa5e855325dd"}, - {file = "yarl-1.14.0-cp311-cp311-win32.whl", hash = "sha256:bab03192091681d54e8225c53f270b0517637915d9297028409a2a5114ff4634"}, - {file = "yarl-1.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:985623575e5c4ea763056ffe0e2d63836f771a8c294b3de06d09480538316b13"}, - {file = "yarl-1.14.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fc2c80bc87fba076e6cbb926216c27fba274dae7100a7b9a0983b53132dd99f2"}, - {file = "yarl-1.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:55c144d363ad4626ca744556c049c94e2b95096041ac87098bb363dcc8635e8d"}, - {file = "yarl-1.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b03384eed107dbeb5f625a99dc3a7de8be04fc8480c9ad42fccbc73434170b20"}, - {file = "yarl-1.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f72a0d746d38cb299b79ce3d4d60ba0892c84bbc905d0d49c13df5bace1b65f8"}, - {file = "yarl-1.14.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8648180b34faaea4aa5b5ca7e871d9eb1277033fa439693855cf0ea9195f85f1"}, - {file = "yarl-1.14.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9557c9322aaa33174d285b0c1961fb32499d65ad1866155b7845edc876c3c835"}, - {file = "yarl-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f50eb3837012a937a2b649ec872b66ba9541ad9d6f103ddcafb8231cfcafd22"}, - {file = "yarl-1.14.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8892fa575ac9b1b25fae7b221bc4792a273877b9b56a99ee2d8d03eeb3dbb1d2"}, - {file = "yarl-1.14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e6a2c5c5bb2556dfbfffffc2bcfb9c235fd2b566d5006dfb2a37afc7e3278a07"}, - {file = "yarl-1.14.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ab3abc0b78a5dfaa4795a6afbe7b282b6aa88d81cf8c1bb5e394993d7cae3457"}, - {file = "yarl-1.14.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:47eede5d11d669ab3759b63afb70d28d5328c14744b8edba3323e27dc52d298d"}, - {file = "yarl-1.14.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fe4d2536c827f508348d7b40c08767e8c7071614250927233bf0c92170451c0a"}, - {file = "yarl-1.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0fd7b941dd1b00b5f0acb97455fea2c4b7aac2dd31ea43fb9d155e9bc7b78664"}, - {file = "yarl-1.14.0-cp312-cp312-win32.whl", hash = "sha256:99ff3744f5fe48288be6bc402533b38e89749623a43208e1d57091fc96b783b9"}, - {file = "yarl-1.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:1ca3894e9e9f72da93544f64988d9c052254a338a9f855165f37f51edb6591de"}, - {file = "yarl-1.14.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5d02d700705d67e09e1f57681f758f0b9d4412eeb70b2eb8d96ca6200b486db3"}, - {file = "yarl-1.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:30600ba5db60f7c0820ef38a2568bb7379e1418ecc947a0f76fd8b2ff4257a97"}, - {file = "yarl-1.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e85d86527baebb41a214cc3b45c17177177d900a2ad5783dbe6f291642d4906f"}, - {file = "yarl-1.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37001e5d4621cef710c8dc1429ca04e189e572f128ab12312eab4e04cf007132"}, - {file = "yarl-1.14.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4f4547944d4f5cfcdc03f3f097d6f05bbbc915eaaf80a2ee120d0e756de377d"}, - {file = "yarl-1.14.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75ff4c819757f9bdb35de049a509814d6ce851fe26f06eb95a392a5640052482"}, - {file = "yarl-1.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68ac1a09392ed6e3fd14be880d39b951d7b981fd135416db7d18a6208c536561"}, - {file = "yarl-1.14.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96952f642ac69075e44c7d0284528938fdff39422a1d90d3e45ce40b72e5e2d9"}, - {file = "yarl-1.14.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a56fbe3d7f3bce1d060ea18d2413a2ca9ca814eea7cedc4d247b5f338d54844e"}, - {file = "yarl-1.14.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7e2637d75e92763d1322cb5041573279ec43a80c0f7fbbd2d64f5aee98447b17"}, - {file = "yarl-1.14.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9abe80ae2c9d37c17599557b712e6515f4100a80efb2cda15f5f070306477cd2"}, - {file = "yarl-1.14.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:217a782020b875538eebf3948fac3a7f9bbbd0fd9bf8538f7c2ad7489e80f4e8"}, - {file = "yarl-1.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9cfef3f14f75bf6aba73a76caf61f9d00865912a04a4393c468a7ce0981b519"}, - {file = "yarl-1.14.0-cp313-cp313-win32.whl", hash = "sha256:d8361c7d04e6a264481f0b802e395f647cd3f8bbe27acfa7c12049efea675bd1"}, - {file = "yarl-1.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:bc24f968b82455f336b79bf37dbb243b7d76cd40897489888d663d4e028f5069"}, - {file = "yarl-1.14.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:91d875f75fabf76b3018c5f196bf3d308ed2b49ddcb46c1576d6b075754a1393"}, - {file = "yarl-1.14.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4009def9be3a7e5175db20aa2d7307ecd00bbf50f7f0f989300710eee1d0b0b9"}, - {file = "yarl-1.14.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:582cedde49603f139be572252a318b30dc41039bc0b8165f070f279e5d12187f"}, - {file = "yarl-1.14.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbd9ff43a04f8ffe8a959a944c2dca10d22f5f99fc6a459f49c3ebfb409309d9"}, - {file = "yarl-1.14.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9f805e37ed16cc212fdc538a608422d7517e7faf539bedea4fe69425bc55d76"}, - {file = "yarl-1.14.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95e16e9eaa2d7f5d87421b8fe694dd71606aa61d74b824c8d17fc85cc51983d1"}, - {file = "yarl-1.14.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:816d24f584edefcc5ca63428f0b38fee00b39fe64e3c5e558f895a18983efe96"}, - {file = "yarl-1.14.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd2660c01367eb3ef081b8fa0a5da7fe767f9427aa82023a961a5f28f0d4af6c"}, - {file = "yarl-1.14.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:94b2bb9bcfd5be9d27004ea4398fb640373dd0c1a9e219084f42c08f77a720ab"}, - {file = "yarl-1.14.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c2089a9afef887664115f7fa6d3c0edd6454adaca5488dba836ca91f60401075"}, - {file = "yarl-1.14.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2192f718db4a8509f63dd6d950f143279211fa7e6a2c612edc17d85bf043d36e"}, - {file = "yarl-1.14.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:8385ab36bf812e9d37cf7613999a87715f27ef67a53f0687d28c44b819df7cb0"}, - {file = "yarl-1.14.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b4c1ecba93e7826dc71ddba75fb7740cdb52e7bd0be9f03136b83f54e6a1f511"}, - {file = "yarl-1.14.0-cp38-cp38-win32.whl", hash = "sha256:e749af6c912a7bb441d105c50c1a3da720474e8acb91c89350080dd600228f0e"}, - {file = "yarl-1.14.0-cp38-cp38-win_amd64.whl", hash = "sha256:147e36331f6f63e08a14640acf12369e041e0751bb70d9362df68c2d9dcf0c87"}, - {file = "yarl-1.14.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a9f917966d27f7ce30039fe8d900f913c5304134096554fd9bea0774bcda6d1"}, - {file = "yarl-1.14.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a2f8fb7f944bcdfecd4e8d855f84c703804a594da5123dd206f75036e536d4d"}, - {file = "yarl-1.14.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f4e475f29a9122f908d0f1f706e1f2fc3656536ffd21014ff8a6f2e1b14d1d8"}, - {file = "yarl-1.14.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8089d4634d8fa2b1806ce44fefa4979b1ab2c12c0bc7ef3dfa45c8a374811348"}, - {file = "yarl-1.14.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b16f6c75cffc2dc0616ea295abb0e1967601bd1fb1e0af6a1de1c6c887f3439"}, - {file = "yarl-1.14.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498b3c55087b9d762636bca9b45f60d37e51d24341786dc01b81253f9552a607"}, - {file = "yarl-1.14.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3f8bfc1db82589ef965ed234b87de30d140db8b6dc50ada9e33951ccd8ec07a"}, - {file = "yarl-1.14.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:625f207b1799e95e7c823f42f473c1e9dbfb6192bd56bba8695656d92be4535f"}, - {file = "yarl-1.14.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:781e2495e408a81e4eaeedeb41ba32b63b1980dddf8b60dbbeff6036bcd35049"}, - {file = "yarl-1.14.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:659603d26d40dd4463200df9bfbc339fbfaed3fe32e5c432fe1dc2b5d4aa94b4"}, - {file = "yarl-1.14.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4e0d45ebf975634468682c8bec021618b3ad52c37619e5c938f8f831fa1ac5c0"}, - {file = "yarl-1.14.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a2e4725a08cb2b4794db09e350c86dee18202bb8286527210e13a1514dc9a59a"}, - {file = "yarl-1.14.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:19268b4fec1d7760134f2de46ef2608c2920134fb1fa61e451f679e41356dc55"}, - {file = "yarl-1.14.0-cp39-cp39-win32.whl", hash = "sha256:337912bcdcf193ade64b9aae5a4017a0a1950caf8ca140362e361543c6773f21"}, - {file = "yarl-1.14.0-cp39-cp39-win_amd64.whl", hash = "sha256:b6d0147574ce2e7b812c989e50fa72bbc5338045411a836bd066ce5fc8ac0bce"}, - {file = "yarl-1.14.0-py3-none-any.whl", hash = "sha256:c8ed4034f0765f8861620c1f2f2364d2e58520ea288497084dae880424fc0d9f"}, - {file = "yarl-1.14.0.tar.gz", hash = "sha256:88c7d9d58aab0724b979ab5617330acb1c7030b79379c8138c1c8c94e121d1b3"}, + {file = "yarl-1.15.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e4ee8b8639070ff246ad3649294336b06db37a94bdea0d09ea491603e0be73b8"}, + {file = "yarl-1.15.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a7cf963a357c5f00cb55b1955df8bbe68d2f2f65de065160a1c26b85a1e44172"}, + {file = "yarl-1.15.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:43ebdcc120e2ca679dba01a779333a8ea76b50547b55e812b8b92818d604662c"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3433da95b51a75692dcf6cc8117a31410447c75a9a8187888f02ad45c0a86c50"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38d0124fa992dbacd0c48b1b755d3ee0a9f924f427f95b0ef376556a24debf01"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ded1b1803151dd0f20a8945508786d57c2f97a50289b16f2629f85433e546d47"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace4cad790f3bf872c082366c9edd7f8f8f77afe3992b134cfc810332206884f"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c77494a2f2282d9bbbbcab7c227a4d1b4bb829875c96251f66fb5f3bae4fb053"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b7f227ca6db5a9fda0a2b935a2ea34a7267589ffc63c8045f0e4edb8d8dcf956"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:31561a5b4d8dbef1559b3600b045607cf804bae040f64b5f5bca77da38084a8a"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3e52474256a7db9dcf3c5f4ca0b300fdea6c21cca0148c8891d03a025649d935"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0e1af74a9529a1137c67c887ed9cde62cff53aa4d84a3adbec329f9ec47a3936"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:15c87339490100c63472a76d87fe7097a0835c705eb5ae79fd96e343473629ed"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:74abb8709ea54cc483c4fb57fb17bb66f8e0f04438cff6ded322074dbd17c7ec"}, + {file = "yarl-1.15.2-cp310-cp310-win32.whl", hash = "sha256:ffd591e22b22f9cb48e472529db6a47203c41c2c5911ff0a52e85723196c0d75"}, + {file = "yarl-1.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:1695497bb2a02a6de60064c9f077a4ae9c25c73624e0d43e3aa9d16d983073c2"}, + {file = "yarl-1.15.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9fcda20b2de7042cc35cf911702fa3d8311bd40055a14446c1e62403684afdc5"}, + {file = "yarl-1.15.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0545de8c688fbbf3088f9e8b801157923be4bf8e7b03e97c2ecd4dfa39e48e0e"}, + {file = "yarl-1.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fbda058a9a68bec347962595f50546a8a4a34fd7b0654a7b9697917dc2bf810d"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1ac2bc069f4a458634c26b101c2341b18da85cb96afe0015990507efec2e417"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd126498171f752dd85737ab1544329a4520c53eed3997f9b08aefbafb1cc53b"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3db817b4e95eb05c362e3b45dafe7144b18603e1211f4a5b36eb9522ecc62bcf"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:076b1ed2ac819933895b1a000904f62d615fe4533a5cf3e052ff9a1da560575c"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f8cfd847e6b9ecf9f2f2531c8427035f291ec286c0a4944b0a9fce58c6446046"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:32b66be100ac5739065496c74c4b7f3015cef792c3174982809274d7e51b3e04"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:34a2d76a1984cac04ff8b1bfc939ec9dc0914821264d4a9c8fd0ed6aa8d4cfd2"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0afad2cd484908f472c8fe2e8ef499facee54a0a6978be0e0cff67b1254fd747"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c68e820879ff39992c7f148113b46efcd6ec765a4865581f2902b3c43a5f4bbb"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:98f68df80ec6ca3015186b2677c208c096d646ef37bbf8b49764ab4a38183931"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56ec1eacd0a5d35b8a29f468659c47f4fe61b2cab948ca756c39b7617f0aa5"}, + {file = "yarl-1.15.2-cp311-cp311-win32.whl", hash = "sha256:eedc3f247ee7b3808ea07205f3e7d7879bc19ad3e6222195cd5fbf9988853e4d"}, + {file = "yarl-1.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:0ccaa1bc98751fbfcf53dc8dfdb90d96e98838010fc254180dd6707a6e8bb179"}, + {file = "yarl-1.15.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:82d5161e8cb8f36ec778fd7ac4d740415d84030f5b9ef8fe4da54784a1f46c94"}, + {file = "yarl-1.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fa2bea05ff0a8fb4d8124498e00e02398f06d23cdadd0fe027d84a3f7afde31e"}, + {file = "yarl-1.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:99e12d2bf587b44deb74e0d6170fec37adb489964dbca656ec41a7cd8f2ff178"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:243fbbbf003754fe41b5bdf10ce1e7f80bcc70732b5b54222c124d6b4c2ab31c"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:856b7f1a7b98a8c31823285786bd566cf06226ac4f38b3ef462f593c608a9bd6"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:553dad9af802a9ad1a6525e7528152a015b85fb8dbf764ebfc755c695f488367"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30c3ff305f6e06650a761c4393666f77384f1cc6c5c0251965d6bfa5fbc88f7f"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:353665775be69bbfc6d54c8d134bfc533e332149faeddd631b0bc79df0897f46"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f4fe99ce44128c71233d0d72152db31ca119711dfc5f2c82385ad611d8d7f897"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9c1e3ff4b89cdd2e1a24c214f141e848b9e0451f08d7d4963cb4108d4d798f1f"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:711bdfae4e699a6d4f371137cbe9e740dc958530cb920eb6f43ff9551e17cfbc"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4388c72174868884f76affcdd3656544c426407e0043c89b684d22fb265e04a5"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f0e1844ad47c7bd5d6fa784f1d4accc5f4168b48999303a868fe0f8597bde715"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a5cafb02cf097a82d74403f7e0b6b9df3ffbfe8edf9415ea816314711764a27b"}, + {file = "yarl-1.15.2-cp312-cp312-win32.whl", hash = "sha256:156ececdf636143f508770bf8a3a0498de64da5abd890c7dbb42ca9e3b6c05b8"}, + {file = "yarl-1.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:435aca062444a7f0c884861d2e3ea79883bd1cd19d0a381928b69ae1b85bc51d"}, + {file = "yarl-1.15.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:416f2e3beaeae81e2f7a45dc711258be5bdc79c940a9a270b266c0bec038fb84"}, + {file = "yarl-1.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:173563f3696124372831007e3d4b9821746964a95968628f7075d9231ac6bb33"}, + {file = "yarl-1.15.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9ce2e0f6123a60bd1a7f5ae3b2c49b240c12c132847f17aa990b841a417598a2"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eaea112aed589131f73d50d570a6864728bd7c0c66ef6c9154ed7b59f24da611"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4ca3b9f370f218cc2a0309542cab8d0acdfd66667e7c37d04d617012485f904"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23ec1d3c31882b2a8a69c801ef58ebf7bae2553211ebbddf04235be275a38548"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75119badf45f7183e10e348edff5a76a94dc19ba9287d94001ff05e81475967b"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78e6fdc976ec966b99e4daa3812fac0274cc28cd2b24b0d92462e2e5ef90d368"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8657d3f37f781d987037f9cc20bbc8b40425fa14380c87da0cb8dfce7c92d0fb"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:93bed8a8084544c6efe8856c362af08a23e959340c87a95687fdbe9c9f280c8b"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:69d5856d526802cbda768d3e6246cd0d77450fa2a4bc2ea0ea14f0d972c2894b"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ccad2800dfdff34392448c4bf834be124f10a5bc102f254521d931c1c53c455a"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a880372e2e5dbb9258a4e8ff43f13888039abb9dd6d515f28611c54361bc5644"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c998d0558805860503bc3a595994895ca0f7835e00668dadc673bbf7f5fbfcbe"}, + {file = "yarl-1.15.2-cp313-cp313-win32.whl", hash = "sha256:533a28754e7f7439f217550a497bb026c54072dbe16402b183fdbca2431935a9"}, + {file = "yarl-1.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:5838f2b79dc8f96fdc44077c9e4e2e33d7089b10788464609df788eb97d03aad"}, + {file = "yarl-1.15.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fbbb63bed5fcd70cd3dd23a087cd78e4675fb5a2963b8af53f945cbbca79ae16"}, + {file = "yarl-1.15.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2e93b88ecc8f74074012e18d679fb2e9c746f2a56f79cd5e2b1afcf2a8a786b"}, + {file = "yarl-1.15.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af8ff8d7dc07ce873f643de6dfbcd45dc3db2c87462e5c387267197f59e6d776"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66f629632220a4e7858b58e4857927dd01a850a4cef2fb4044c8662787165cf7"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:833547179c31f9bec39b49601d282d6f0ea1633620701288934c5f66d88c3e50"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2aa738e0282be54eede1e3f36b81f1e46aee7ec7602aa563e81e0e8d7b67963f"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a13a07532e8e1c4a5a3afff0ca4553da23409fad65def1b71186fb867eeae8d"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c45817e3e6972109d1a2c65091504a537e257bc3c885b4e78a95baa96df6a3f8"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:670eb11325ed3a6209339974b276811867defe52f4188fe18dc49855774fa9cf"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:d417a4f6943112fae3924bae2af7112562285848d9bcee737fc4ff7cbd450e6c"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:bc8936d06cd53fddd4892677d65e98af514c8d78c79864f418bbf78a4a2edde4"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:954dde77c404084c2544e572f342aef384240b3e434e06cecc71597e95fd1ce7"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:5bc0df728e4def5e15a754521e8882ba5a5121bd6b5a3a0ff7efda5d6558ab3d"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b71862a652f50babab4a43a487f157d26b464b1dedbcc0afda02fd64f3809d04"}, + {file = "yarl-1.15.2-cp38-cp38-win32.whl", hash = "sha256:63eab904f8630aed5a68f2d0aeab565dcfc595dc1bf0b91b71d9ddd43dea3aea"}, + {file = "yarl-1.15.2-cp38-cp38-win_amd64.whl", hash = "sha256:2cf441c4b6e538ba0d2591574f95d3fdd33f1efafa864faa077d9636ecc0c4e9"}, + {file = "yarl-1.15.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a32d58f4b521bb98b2c0aa9da407f8bd57ca81f34362bcb090e4a79e9924fefc"}, + {file = "yarl-1.15.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:766dcc00b943c089349d4060b935c76281f6be225e39994c2ccec3a2a36ad627"}, + {file = "yarl-1.15.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bed1b5dbf90bad3bfc19439258c97873eab453c71d8b6869c136346acfe497e7"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed20a4bdc635f36cb19e630bfc644181dd075839b6fc84cac51c0f381ac472e2"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d538df442c0d9665664ab6dd5fccd0110fa3b364914f9c85b3ef9b7b2e157980"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c6cf1d92edf936ceedc7afa61b07e9d78a27b15244aa46bbcd534c7458ee1b"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce44217ad99ffad8027d2fde0269ae368c86db66ea0571c62a000798d69401fb"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47a6000a7e833ebfe5886b56a31cb2ff12120b1efd4578a6fcc38df16cc77bd"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e52f77a0cd246086afde8815039f3e16f8d2be51786c0a39b57104c563c5cbb0"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:f9ca0e6ce7774dc7830dc0cc4bb6b3eec769db667f230e7c770a628c1aa5681b"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:136f9db0f53c0206db38b8cd0c985c78ded5fd596c9a86ce5c0b92afb91c3a19"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:173866d9f7409c0fb514cf6e78952e65816600cb888c68b37b41147349fe0057"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:6e840553c9c494a35e449a987ca2c4f8372668ee954a03a9a9685075228e5036"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:458c0c65802d816a6b955cf3603186de79e8fdb46d4f19abaec4ef0a906f50a7"}, + {file = "yarl-1.15.2-cp39-cp39-win32.whl", hash = "sha256:5b48388ded01f6f2429a8c55012bdbd1c2a0c3735b3e73e221649e524c34a58d"}, + {file = "yarl-1.15.2-cp39-cp39-win_amd64.whl", hash = "sha256:81dadafb3aa124f86dc267a2168f71bbd2bfb163663661ab0038f6e4b8edb810"}, + {file = "yarl-1.15.2-py3-none-any.whl", hash = "sha256:0d3105efab7c5c091609abacad33afff33bdff0035bece164c98bcf5a85ef90a"}, + {file = "yarl-1.15.2.tar.gz", hash = "sha256:a39c36f4218a5bb668b4f06874d676d35a035ee668e6e7e3538835c703634b84"}, ] [package.dependencies] @@ -3834,7 +4262,26 @@ idna = ">=2.0" multidict = ">=4.0" propcache = ">=0.2.0" +[[package]] +name = "zipp" +version = "3.20.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "70a25dabadc35bc8f4015a8d01a969c0d33087889c222d81ac6ebf7d96f06bdf" +content-hash = "d1da021aa0ba3fb36a3c9a6a3f21c3bc2b9afbca0ac60fe4cc60144e2e494c1c" diff --git a/agenta-backend/pyproject.toml b/agenta-backend/pyproject.toml index c3f656dbf9..2ab5c17463 100644 --- a/agenta-backend/pyproject.toml +++ b/agenta-backend/pyproject.toml @@ -44,6 +44,7 @@ autoevals = "^0.0.83" supertokens-python = "^0.24.2" protobuf = "^4.25.4" +litellm ="^1.48.0" [tool.poetry.group.dev.dependencies] pytest = "^7.3.1" From 45216ae0f878e9cea8f94221fee184fbc1ee6aea Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 16 Oct 2024 13:10:06 +0200 Subject: [PATCH 078/305] bump prerelease --- agenta-cli/agenta/sdk/tracing/inline.py | 1 - agenta-cli/pyproject.toml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py index 7945ae4d64..33f5dfd185 100644 --- a/agenta-cli/agenta/sdk/tracing/inline.py +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -1282,7 +1282,6 @@ def _parse_to_legacy_span(span: SpanDTO) -> CreateSpan: def calculate_cost(span_idx: Dict[str, SpanCreateDTO]): for span in span_idx.values(): if span.node.type.name.lower() in PAYING_TYPES and span.meta and span.metrics: - try: costs = cost_calculator.cost_per_token( model=span.meta.get("response.model"), diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index 6084efb65c..1dcaa23c63 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.25.4a2" +version = "0.25.4a3" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] From 339f3c3ae772f6d7fbcef49da9864a705887cee7 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 16 Oct 2024 15:48:25 +0200 Subject: [PATCH 079/305] clean up inline --- agenta-cli/agenta/sdk/tracing/inline.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py index 33f5dfd185..e2490a9985 100644 --- a/agenta-cli/agenta/sdk/tracing/inline.py +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -960,9 +960,9 @@ def parse_to_agenta_span_dto( if span_dto.data: span_dto.data = _unmarshal_attributes(span_dto.data) - if "outputs" in span_dto.data: - if "__default__" in span_dto.data["outputs"]: - span_dto.data["outputs"] = span_dto.data["outputs"]["__default__"] + # if "outputs" in span_dto.data: + # if "__default__" in span_dto.data["outputs"]: + # span_dto.data["outputs"] = span_dto.data["outputs"]["__default__"] # METRICS if span_dto.metrics: @@ -1206,14 +1206,15 @@ class CreateSpan(BaseModel): def _parse_to_legacy_span(span: SpanDTO) -> CreateSpan: attributes = None if span.otel: - attributes = span.otel.attributes + attributes = span.otel.attributes or {} - for event in span.otel.events: - if event.name == "exception": - attributes.update(**event.attributes) + if span.otel.events: + for event in span.otel.events: + if event.name == "exception": + attributes.update(**event.attributes) legacy_span = CreateSpan( - id=span.node.id.hex, + id=span.node.id.hex[:24], spankind=span.node.type, name=span.node.name, # @@ -1222,7 +1223,7 @@ def _parse_to_legacy_span(span: SpanDTO) -> CreateSpan: start_time=span.time.start, end_time=span.time.end, # - parent_span_id=span.parent.id.hex if span.parent else None, + parent_span_id=span.parent.id.hex[:24] if span.parent else None, # inputs=span.data.get("inputs") if span.data else {}, internals=span.data.get("internals") if span.data else {}, @@ -1253,7 +1254,7 @@ def _parse_to_legacy_span(span: SpanDTO) -> CreateSpan: ), # app_id=( - span.refs.get("application", {}).get("id", "missing-app-id") + span.refs.get("application_id", "missing-app-id") if span.refs else "missing-app-id" ), From 5e92804a2b00efeca4afd26ea0465a0905bb75c4 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 16 Oct 2024 16:35:31 +0200 Subject: [PATCH 080/305] remove mock data --- .../apis/fastapi/observability/opentelemetry/otlp.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py index 7621606fbe..fe02e3df26 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py @@ -64,8 +64,6 @@ def _parse_timestamp( def parse_otlp_stream( otlp_stream: bytes, ) -> List[OTelSpanDTO]: - # otlp_stream = b"\n\x94$\n6\n\x18\n\x0cservice.name\x12\x08\n\x06agenta\n\x1a\n\x0fservice.version\x12\x07\n\x050.1.0\x12\x95\x1d\n1\n'opentelemetry.instrumentation.openai.v1\x12\x060.30.0\x12\xa3\x04\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\xd9N#YhoL\n\"\x08\x1dh\x04X\xa0\xf4\xf8P*\x11openai.embeddings0\x039\x00\x90\xa2\tWP\xfa\x17A\x08\xb2 WP\xfa\x17J\x1f\n\x10llm.request.type\x12\x0b\n\tembeddingJ9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307J\x19\n\rgen_ai.system\x12\x08\n\x06OpenAIJ0\n\x14gen_ai.request.model\x12\x18\n\x16text-embedding-ada-002J\x15\n\x0bllm.headers\x12\x06\n\x04NoneJ\x16\n\x10llm.is_streaming\x12\x02\x10\x00JI\n\x17gen_ai.prompt.0.content\x12.\n,Movies about witches in the genre of comedy.J6\n\x16gen_ai.openai.api_base\x12\x1c\n\x1ahttps://api.openai.com/v1/J1\n\x15gen_ai.response.model\x12\x18\n\x16text-embedding-ada-002J\x1c\n\x16llm.usage.total_tokens\x12\x02\x18\tJ \n\x1agen_ai.usage.prompt_tokens\x12\x02\x18\tz\x00\x85\x01\x00\x01\x00\x00\x12\xb9\x18\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\x1b\x05'\x8e\xb4\xaar\xda\"\x08\xac\t\x88\x86\x96\x87\xce\xb4*\x0bopenai.chat0\x039\x10h\x16&WP\xfa\x17A\x10{0qWP\xfa\x17J\x1a\n\x10llm.request.type\x12\x06\n\x04chatJ9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307J\x19\n\rgen_ai.system\x12\x08\n\x06OpenAIJ'\n\x14gen_ai.request.model\x12\x0f\n\rgpt-3.5-turboJ'\n\x1agen_ai.request.temperature\x12\t!\x9a\x99\x99\x99\x99\x99\xe9?J\x15\n\x0bllm.headers\x12\x06\n\x04NoneJ\x16\n\x10llm.is_streaming\x12\x02\x10\x00J6\n\x16gen_ai.openai.api_base\x12\x1c\n\x1ahttps://api.openai.com/v1/J \n\x14gen_ai.prompt.0.role\x12\x08\n\x06systemJ\x84\x11\n\x17gen_ai.prompt.0.content\x12\xe8\x10\n\xe5\x10Given the following list of suggested movies:\n\nThe Craft (1996) in ['Drama', 'Fantasy', 'Horror']: A newcomer to a Catholic prep high school falls in with a trio of outcast teenage girls who practice witchcraft and they all soon conjure up various spells and curses against those who even slightly anger them.\nWicked Stepmother (1989) in ['Comedy', 'Fantasy']: A mother/daughter pair of witches descend on a yuppie family's home and cause havoc, one at a time since they share one body & the other must live in a cat the rest of the time. Now it's up...\nOz the Great and Powerful (2013) in ['Adventure', 'Family', 'Fantasy']: A small-time magician is swept away to an enchanted land and is forced into a power struggle between three witches.\nInto the Woods (2014) in ['Adventure', 'Fantasy', 'Musical']: A witch tasks a childless baker and his wife with procuring magical items from classic fairy tales to reverse the curse put on their family tree.\nSnow White: A Tale of Terror (1997) in ['Fantasy', 'Horror']: In this dark take on the fairy tale, the growing hatred of a noblewoman, secretly a practitioner of the dark arts, for her stepdaughter, and the witch's horrifying attempts to kill her.\nBedknobs and Broomsticks (1971) in ['Adventure', 'Family', 'Fantasy']: An apprentice witch, three kids and a cynical conman search for the missing component to a magic spell useful to the defense of Britain.\nMy Neighbor Totoro (1988) in ['Animation', 'Family', 'Fantasy']: When two girls move to the country to be near their ailing mother, they have adventures with the wonderous forest spirits who live nearby.\nHocus Pocus (1993) in ['Comedy', 'Family', 'Fantasy']: After three centuries, three witch sisters are resurrected in Salem Massachusetts on Halloween night, and it is up to two teen-agers, a young girl, and an immortal cat to put an end to the witches' reign of terror once and for all.\nPractical Magic (1998) in ['Comedy', 'Fantasy', 'Romance']: The wry, comic romantic tale follows the Owens sisters, Sally and Gillian, as they struggle to use their hereditary gift for practical magic to overcome the obstacles in discovering true love.J\x1e\n\x14gen_ai.prompt.1.role\x12\x06\n\x04userJ]\n\x17gen_ai.prompt.1.content\x12B\n@Provide a list of 3 movies about witches in the genre of comedy.J-\n\x15gen_ai.response.model\x12\x14\n\x12gpt-3.5-turbo-0125J\x1d\n\x16llm.usage.total_tokens\x12\x03\x18\xc2\x04J$\n\x1egen_ai.usage.completion_tokens\x12\x02\x18*J!\n\x1agen_ai.usage.prompt_tokens\x12\x03\x18\x98\x04J+\n!gen_ai.completion.0.finish_reason\x12\x06\n\x04stopJ'\n\x18gen_ai.completion.0.role\x12\x0b\n\tassistantJ\xa7\x01\n\x1bgen_ai.completion.0.content\x12\x87\x01\n\x84\x01Here are 3 movies about witches in the genre of comedy:\n\n1. Wicked Stepmother (1989)\n2. Hocus Pocus (1993)\n3. Practical Magic (1998)z\x00\x85\x01\x00\x01\x00\x00\x12\xc1\x06\n\x0f\n\ragenta.tracer\x12\x86\x01\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\x1dh\x04X\xa0\xf4\xf8P\"\x08\xac\xb2\x8e\xff\xb2\xe8\xea\x14*\x05embed0\x019@\xc1\x9f\tWP\xfa\x17A\xb0H- WP\xfa\x17J9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307z\x02\x18\x01\x85\x01\x00\x01\x00\x00\x12\x87\x01\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\xcaD;\xdf \xbb\x13|\"\x08\xac\xb2\x8e\xff\xb2\xe8\xea\x14*\x06search0\x019`\xf0/ WP\xfa\x17A\xc0\x16\x03&WP\xfa\x17J9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307z\x02\x18\x01\x85\x01\x00\x01\x00\x00\x12\x8a\x01\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\xac\xb2\x8e\xff\xb2\xe8\xea\x14\"\x08\x9f\xad5\xa6\\\xc9\xf9\xeb*\tretriever0\x019\xc8o\x9b\tWP\xfa\x17A\xd8w\x07&WP\xfa\x17J9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307z\x02\x18\x01\x85\x01\x00\x01\x00\x00\x12\x85\x01\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\xac\t\x88\x86\x96\x87\xce\xb4\"\x08\xf8T\xa3\xe9\x07;\x90\x86*\x04chat0\x019\x08\xbd\x0f&WP\xfa\x17Ax\xfe:qWP\xfa\x17J9\n\x0fag.extra.app_id\x12&\n$0191cb41-ecf9-7112-87fa-5e4a9ce72307z\x02\x18\x01\x85\x01\x00\x01\x00\x00\x12\x89\x01\n\x10\xa6a\x98\x18A\x7fIj\x06D\xdaLp\xd0\xa2\xe0\x12\x08\xf8T\xa3\xe9\x07;\x90\x86\"\x08\x9f\xad5\xa6\\\xc9\xf9\xeb*\x08reporter0\x019\xb8\x17\n&WP\xfa\x17A0\x81 Date: Wed, 16 Oct 2024 16:39:10 +0200 Subject: [PATCH 081/305] remove todo list, add calculate cost --- .../apis/fastapi/observability/router.py | 28 -------------- .../core/observability/service.py | 3 ++ .../core/observability/utils.py | 37 +++++++++++++++++++ 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py index 71e6a5fd5c..1035016746 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py @@ -119,34 +119,6 @@ async def query_traces( Query traces, with optional grouping, filtering, (sorting,) and pagination. """ - ### TODO -------------------------------------- # - # - [x] Allows for scoping by: - # - [x] 'project_id' - # - [x] Allows for grouping by: - # - [x] 'scenario' i.e. root - # - [x] 'trace' i.e. tree - # - [x] 'span' i.e. node - # - [x] Allows for windowing by: - # - [x] 'earliest' and 'latest' - # - [x] Allows for filtering by: - # - [x] nested conditions - # - [x] single conditions - # - [x] Allows for pagination by: - # - [x] 'page' and 'size' - # - [x] Allows for formatting by: - # - [x] 'custom' - # - [x] 'opentelemetry' - # All using: - # - [x] query params - # - [x] sane defaults - # - [x] error handling - # - [x] clean DTO types - # - [x] clean DBA types - # - [x] semconv mapping - ### ------------------------------------------- # - - ##### ADD FETCH ONE (BY PID / NID) WITH FORMAT - if ( format == "opentelemetry" and query_dto.grouping diff --git a/agenta-backend/agenta_backend/core/observability/service.py b/agenta-backend/agenta_backend/core/observability/service.py index 6a2d024d8d..cf2b3dd68f 100644 --- a/agenta-backend/agenta_backend/core/observability/service.py +++ b/agenta-backend/agenta_backend/core/observability/service.py @@ -6,6 +6,7 @@ from agenta_backend.core.observability.utils import ( parse_span_dtos_to_span_idx, parse_span_idx_to_span_id_tree, + calculate_costs, cumulate_costs, cumulate_tokens, connect_children, @@ -55,6 +56,8 @@ async def ingest( span_id_tree = parse_span_idx_to_span_id_tree(span_idx) + calculate_costs(span_idx) + cumulate_costs(span_id_tree, span_idx) cumulate_tokens(span_id_tree, span_idx) diff --git a/agenta-backend/agenta_backend/core/observability/utils.py b/agenta-backend/agenta_backend/core/observability/utils.py index 05af93c3de..52e2f2e038 100644 --- a/agenta-backend/agenta_backend/core/observability/utils.py +++ b/agenta-backend/agenta_backend/core/observability/utils.py @@ -1,5 +1,7 @@ from typing import List, Dict, OrderedDict +from litellm import cost_calculator + from agenta_backend.core.observability.dtos import SpanCreateDTO, SpanDTO @@ -186,3 +188,38 @@ def _connect_tree_dfs( if len(parent_span.nodes) == 0: parent_span.nodes = None + + +PAYING_TYPES = [ + "embedding", + "query", + "completion", + "chat", + "rerank", +] + + +def calculate_costs(span_idx: Dict[str, SpanCreateDTO]): + for span in span_idx.values(): + if span.node.type.name.lower() in PAYING_TYPES and span.meta and span.metrics: + try: + costs = cost_calculator.cost_per_token( + model=span.meta.get("response.model"), + prompt_tokens=span.metrics.get("unit.tokens.prompt", 0.0), + completion_tokens=span.metrics.get("unit.tokens.completion", 0.0), + call_type=span.node.type.name.lower(), + response_time_ms=span.time.span // 1_000, + ) + + if not costs: + continue + + prompt_cost, completion_cost = costs + total_cost = prompt_cost + completion_cost + + span.metrics["unit.costs.prompt"] = prompt_cost + span.metrics["unit.costs.completion"] = completion_cost + span.metrics["unit.costs.total"] = total_cost + + except: + pass From 0bcc96fb5371d4eafd5ad93b564001597084c6a4 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 16 Oct 2024 17:55:27 +0200 Subject: [PATCH 082/305] update links and project_id --- .../apis/fastapi/observability/router.py | 5 ++- .../apis/fastapi/observability/utils.py | 14 ++---- .../core/observability/service.py | 6 +-- .../dbs/postgres/observability/mappings.py | 44 ++++++++++++------- 4 files changed, 36 insertions(+), 33 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py index 1035016746..e063062760 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py @@ -242,7 +242,7 @@ async def otlp_collect_traces( res = self.legacy_receiver(otlp_stream) ### LEGACY ### - project_id = request.headers.get("AG-PROJECT-ID") + project_id = request.headers.get("AG-PROJECT-ID") or request.state.project_id app_id = request.headers.get("AG-APP-ID") otel_span_dtos = parse_otlp_stream(otlp_stream) @@ -252,7 +252,8 @@ async def otlp_collect_traces( for otel_span_dto in otel_span_dtos ] - background_tasks.add_task(self.service.ingest, span_dtos=span_dtos) + # background_tasks.add_task(self.service.ingest, span_dtos=span_dtos) + await self.service.ingest(span_dtos=span_dtos) ### LEGACY ### if res: diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py index 33998263f1..c0f81fd741 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py @@ -451,15 +451,6 @@ def _parse_from_semconv( def _parse_from_links( otel_span_dto: OTelSpanDTO, ) -> dict: - # TESTING - otel_span_dto.links = [ - OTelLinkDTO( - context=otel_span_dto.context, - attributes={"ag.type.link": "testcase"}, - ) - ] - # ------- - # LINKS links = None otel_links = None @@ -805,8 +796,9 @@ def parse_to_agenta_span_dto( if span_dto.refs: span_dto.refs = _unmarshal_attributes(span_dto.refs) - for link in span_dto.links: - link.tree_id = None + if span_dto.links: + for link in span_dto.links: + link.tree_id = None if span_dto.nodes: for v in span_dto.nodes.values(): diff --git a/agenta-backend/agenta_backend/core/observability/service.py b/agenta-backend/agenta_backend/core/observability/service.py index cf2b3dd68f..ead1d4044b 100644 --- a/agenta-backend/agenta_backend/core/observability/service.py +++ b/agenta-backend/agenta_backend/core/observability/service.py @@ -51,7 +51,7 @@ async def ingest( self, *, span_dtos: List[SpanCreateDTO], - ) -> SpanDTO: + ) -> None: span_idx = parse_span_dtos_to_span_idx(span_dtos) span_id_tree = parse_span_idx_to_span_id_tree(span_idx) @@ -62,9 +62,7 @@ async def ingest( cumulate_tokens(span_id_tree, span_idx) - span_ids = await self.observability_dao.create_many(span_dtos=span_idx.values()) - - return span_ids + await self.observability_dao.create_many(span_dtos=span_idx.values()) async def create( self, diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py index b260a2fc93..935d80562d 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py @@ -61,14 +61,18 @@ def map_span_dbe_to_dto(span: InvocationSpanDBE) -> SpanDTO: tags=span.tags, refs=span.refs, # ---------- - links=[ - LinkDTO( - type=link.split(":")[0], - tree_id=link.split(":")[1] + UUID(id).hex[:16], - id=id, - ) - for id, link in span.links.items() - ], + links=( + [ + LinkDTO( + type=link.split(":")[0], + tree_id=link.split(":")[1] + UUID(id).hex[:16], + id=id, + ) + for id, link in span.links.items() + ] + if span.links + else None + ), otel=OTelExtraDTO(**span.otel), ) @@ -106,10 +110,14 @@ def map_span_create_dto_to_dbe( tags=span_create_dto.tags, refs=span_create_dto.refs, # LINKS - links={ - str(link.id): f"{link.type}:{link.tree_id.hex[:16]}" - for link in span_create_dto.links - }, + links=( + { + str(link.id): f"{link.type}:{link.tree_id.hex[:16]}" + for link in span_create_dto.links + } + if span_create_dto.links + else None + ), # OTEL otel=span_create_dto.otel.model_dump(exclude_none=True), ) @@ -152,10 +160,14 @@ def map_span_dto_to_dbe( tags=span_dto.tags, refs=span_dto.refs, # LINKS - links={ - str(link.id): f"{link.type}:{link.tree_id.hex[:16]}" - for link in span_dto.links - }, + links=( + { + str(link.id): f"{link.type}:{link.tree_id.hex[:16]}" + for link in span_dto.links + } + if span_dto.links + else None + ), # OTEL otel=span_dto.otel.model_dump(exclude_none=True), ) From 207c1d36779695c22585d026c793320c9e99cf44 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 16 Oct 2024 18:30:48 +0200 Subject: [PATCH 083/305] fix import --- agenta-backend/agenta_backend/cloud | 1 + agenta-backend/agenta_backend/commons | 1 + agenta-backend/agenta_backend/main.py | 2 ++ 3 files changed, 4 insertions(+) create mode 120000 agenta-backend/agenta_backend/cloud create mode 120000 agenta-backend/agenta_backend/commons diff --git a/agenta-backend/agenta_backend/cloud b/agenta-backend/agenta_backend/cloud new file mode 120000 index 0000000000..773b7806f1 --- /dev/null +++ b/agenta-backend/agenta_backend/cloud @@ -0,0 +1 @@ +/app/cloud_backend/cloud \ No newline at end of file diff --git a/agenta-backend/agenta_backend/commons b/agenta-backend/agenta_backend/commons new file mode 120000 index 0000000000..4eaf9bafd2 --- /dev/null +++ b/agenta-backend/agenta_backend/commons @@ -0,0 +1 @@ +/app/commons_backend/commons \ No newline at end of file diff --git a/agenta-backend/agenta_backend/main.py b/agenta-backend/agenta_backend/main.py index ed9c86145c..32aa88711a 100644 --- a/agenta-backend/agenta_backend/main.py +++ b/agenta-backend/agenta_backend/main.py @@ -119,6 +119,8 @@ async def lifespan(application: FastAPI, cache=True): observability_legacy_receiver = None if isCloudEE(): + import agenta_backend.cloud.main as cloud + observability_legacy_receiver = cloud.observability_legacy_receiver observability = ObservabilityRouter( From 74594db1b3240dae6f06a468f468ae158fd7b0de Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 17 Oct 2024 08:31:00 +0200 Subject: [PATCH 084/305] fix simlinks --- agenta-backend/agenta_backend/cloud | 1 - agenta-backend/agenta_backend/commons | 1 - agenta-backend/agenta_backend/ee | 1 - 3 files changed, 3 deletions(-) delete mode 120000 agenta-backend/agenta_backend/cloud delete mode 120000 agenta-backend/agenta_backend/commons delete mode 120000 agenta-backend/agenta_backend/ee diff --git a/agenta-backend/agenta_backend/cloud b/agenta-backend/agenta_backend/cloud deleted file mode 120000 index 773b7806f1..0000000000 --- a/agenta-backend/agenta_backend/cloud +++ /dev/null @@ -1 +0,0 @@ -/app/cloud_backend/cloud \ No newline at end of file diff --git a/agenta-backend/agenta_backend/commons b/agenta-backend/agenta_backend/commons deleted file mode 120000 index 4eaf9bafd2..0000000000 --- a/agenta-backend/agenta_backend/commons +++ /dev/null @@ -1 +0,0 @@ -/app/commons_backend/commons \ No newline at end of file diff --git a/agenta-backend/agenta_backend/ee b/agenta-backend/agenta_backend/ee deleted file mode 120000 index 567193fbc8..0000000000 --- a/agenta-backend/agenta_backend/ee +++ /dev/null @@ -1 +0,0 @@ -../ee_backend/ee \ No newline at end of file From 3c91137c76960590d900a498607c62529cf0aa50 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Thu, 17 Oct 2024 08:58:31 +0200 Subject: [PATCH 085/305] fix(frontend): removed duplicate node IDs in mock response --- agenta-web/src/lib/test_trace.json | 40 +++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/agenta-web/src/lib/test_trace.json b/agenta-web/src/lib/test_trace.json index b4e5fa71d7..5ccb125225 100644 --- a/agenta-web/src/lib/test_trace.json +++ b/agenta-web/src/lib/test_trace.json @@ -2795,7 +2795,7 @@ "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" }, "node": { - "id": "3283749b-be79-15d6-3839-39eb32070bbc", + "id": "376l749b-be79-15d6-3839-39eb32070bbc", "type": "workflow", "name": "rag" }, @@ -2867,7 +2867,7 @@ "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" }, "node": { - "id": "3283749b-be79-15d6-ef78-9bdcad4f3199", + "id": "376l749b-be79-15d6-ef78-9bdcad4f3199", "type": "task", "name": "retriever" }, @@ -2950,7 +2950,7 @@ "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" }, "node": { - "id": "3283749b-be79-15d6-94b9-72820ad2ef90", + "id": "376l749b-be79-15d6-94b9-72820ad2ef90", "type": "embedding", "name": "openai.embeddings" }, @@ -3029,7 +3029,7 @@ "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" }, "node": { - "id": "3283749b-be79-15d6-a84a-c487893ed246", + "id": "376l749b-be79-15d6-a84a-c487893ed246", "type": "query", "name": "search" }, @@ -3674,7 +3674,7 @@ "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" }, "node": { - "id": "3283749b-be79-15d6-048d-1fa32d25288b", + "id": "376l749b-be79-15d6-048d-1fa32d25288b", "type": "task", "name": "reporter" }, @@ -3747,7 +3747,7 @@ "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" }, "node": { - "id": "3283749b-be79-15d6-7647-7397bc185832", + "id": "376l749b-be79-15d6-7647-7397bc185832", "type": "chat", "name": "openai.chat" }, @@ -3845,7 +3845,7 @@ "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" }, "node": { - "id": "3283749b-be79-15d6-2cb7-0d88553280b1", + "id": "376l749b-be79-15d6-2cb7-0d88553280b1", "type": "task", "name": "summarizer" }, @@ -3907,7 +3907,7 @@ "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" }, "node": { - "id": "3283749b-be79-15d6-e5d2-54ae9f3b0529", + "id": "376l749b-be79-15d6-e5d2-54ae9f3b0529", "type": "chat", "name": "openai.chat" }, @@ -4004,7 +4004,7 @@ "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" }, "node": { - "id": "3283749b-be79-15d6-4cdb-4f10ec83e5c4", + "id": "376l749b-be79-15d6-4cdb-4f10ec83e5c4", "type": "task", "name": "summarizer" }, @@ -4066,7 +4066,7 @@ "id": "a5b2f3dc-2de8-d069-3283-749bbe7915d6" }, "node": { - "id": "3283749b-be79-15d6-2420-816ed54fd495", + "id": "376l749b-be79-15d6-2420-816ed54fd495", "type": "chat", "name": "openai.chat" }, @@ -4180,7 +4180,7 @@ "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" }, "node": { - "id": "43c39569-3e04-49aa-2d60-d51d0c7dfede", + "id": "43c39569-3e04-49aa-2d60-d51d0c7dfasd", "type": "workflow", "name": "rag" }, @@ -4252,7 +4252,7 @@ "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" }, "node": { - "id": "43c39569-3e04-49aa-44cc-6ddc96192c8c", + "id": "43c39569-3e04-49aa-44cc-6ddc96192lop", "type": "task", "name": "retriever" }, @@ -4335,7 +4335,7 @@ "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" }, "node": { - "id": "43c39569-3e04-49aa-1652-1b7965d27817", + "id": "43c39569-3e04-49aa-1652-1b7965d27098", "type": "embedding", "name": "openai.embeddings" }, @@ -4414,7 +4414,7 @@ "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" }, "node": { - "id": "43c39569-3e04-49aa-254c-eff4fcfe2c18", + "id": "43c39569-3e04-49aa-254c-eff4fcfe2kjh", "type": "query", "name": "search" }, @@ -5059,7 +5059,7 @@ "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" }, "node": { - "id": "43c39569-3e04-49aa-f638-ccc37deb35c6", + "id": "43c39569-3e04-49aa-f638-ccc3lkub35c6", "type": "task", "name": "reporter" }, @@ -5132,7 +5132,7 @@ "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" }, "node": { - "id": "43c39569-3e04-49aa-2ebe-b45375e00a9c", + "id": "43c39569-3e04-49aa-2ebe-b4537598fa9c", "type": "chat", "name": "openai.chat" }, @@ -5230,7 +5230,7 @@ "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" }, "node": { - "id": "43c39569-3e04-49aa-e2a6-0ea40ffabc06", + "id": "43c39569-76f4-49aa-e2a6-0ea40ffabc06", "type": "task", "name": "summarizer" }, @@ -5292,7 +5292,7 @@ "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" }, "node": { - "id": "43c39569-3e04-49aa-54f6-bd98032e1a49", + "id": "43c39569-76f4-49aa-54f6-bd98032e1a49", "type": "chat", "name": "openai.chat" }, @@ -5389,7 +5389,7 @@ "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" }, "node": { - "id": "43c39569-3e04-49aa-e106-ba0319c6e51f", + "id": "43c39569-76f4-49aa-e106-ba0319c6e51f", "type": "task", "name": "summarizer" }, @@ -5451,7 +5451,7 @@ "id": "e8835c10-43f1-e9ac-43c3-95693e0449aa" }, "node": { - "id": "43c39569-3e04-49aa-5699-abba488eec45", + "id": "43c39569-76f4-49aa-5699-abba488eec45", "type": "chat", "name": "openai.chat" }, From 89758dfaa1932f139c92560ff185c3db3b908b87 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Thu, 17 Oct 2024 09:00:34 +0200 Subject: [PATCH 086/305] fix(frontend): added helper to transform response to appropriate structure and populated data in table --- .../apps/[app_id]/observability/index.tsx | 84 +++++++++++++++++-- .../src/services/observability/core/index.ts | 21 ++++- 2 files changed, 96 insertions(+), 9 deletions(-) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index b7df3bf9ba..e9ae542de1 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -2,13 +2,16 @@ import GenericDrawer from "@/components/GenericDrawer" import TraceContent from "@/components/pages/observability/drawer/TraceContent" import TraceHeader from "@/components/pages/observability/drawer/TraceHeader" import TraceTree from "@/components/pages/observability/drawer/TraceTree" +import ResultTag from "@/components/ResultTag/ResultTag" import {useQueryParam} from "@/hooks/useQuery" import {findTraceNodeById} from "@/lib/helpers/observability_helpers" import {useTraces} from "@/lib/hooks/useTraces" import {JSSTheme} from "@/lib/Types" -import {AgentaNodeDTO, AgentaRootsDTO} from "@/services/observability/types" +import {observabilityTransformer} from "@/services/observability/core" +import {AgentaNodeDTO} from "@/services/observability/types" import {Table, Typography} from "antd" import {ColumnsType} from "antd/es/table" +import dayjs from "dayjs" import React, {useCallback, useMemo, useState} from "react" import {createUseStyles} from "react-jss" @@ -51,27 +54,90 @@ const ObservabilityDashboard = ({}: Props) => { [activeTrace], ) - const columns: ColumnsType = [ + const columns: ColumnsType< + Omit & { + key: string + } + > = [ { - title: "Trace Id", - dataIndex: "key", + title: "ID", + dataIndex: ["key"], key: "key", - width: 200, + onHeaderCell: () => ({ + style: {minWidth: 200}, + }), + fixed: "left", render: (_, record) => { - return
{record.root.id}
+ return }, }, + { + title: "Timestamp", + key: "timestamp", + dataIndex: ["time", "start"], + onHeaderCell: () => ({ + style: {minWidth: 200}, + }), + render: (_, record) => { + return
{dayjs(record.time.start).format("HH:mm:ss DD MMM YYYY")}
+ }, + }, + { + title: "Inputs", + key: "inputs", + onHeaderCell: () => ({ + style: {minWidth: 350}, + }), + }, + { + title: "Outputs", + key: "outputs", + onHeaderCell: () => ({ + style: {minWidth: 350}, + }), + }, + { + title: "Status", + key: "status", + dataIndex: ["status", "code"], + onHeaderCell: () => ({ + style: {minWidth: 160}, + }), + }, + { + title: "Latency", + key: "latency", + dataIndex: ["time", "span"], + onHeaderCell: () => ({ + style: {minWidth: 80}, + }), + }, + { + title: "Usage", + key: "usage", + dataIndex: ["metrics", "acc", "tokens", "total"], + onHeaderCell: () => ({ + style: {minWidth: 80}, + }), + }, + { + title: "Total cost", + key: "total_cost", + dataIndex: ["metrics", "acc", "costs", "total"], + onHeaderCell: () => ({ + style: {minWidth: 80}, + }), + }, ] return (
Observability -
Observability Table
{traces && (
observabilityTransformer(item.trees[0]))} bordered style={{cursor: "pointer"}} onRow={(record) => ({ @@ -79,6 +145,8 @@ const ObservabilityDashboard = ({}: Props) => { setSelectedTraceId(record.root.id) }, })} + pagination={false} + scroll={{x: "max-content"}} /> )} diff --git a/agenta-web/src/services/observability/core/index.ts b/agenta-web/src/services/observability/core/index.ts index 11aa5d90a7..cc168e0709 100644 --- a/agenta-web/src/services/observability/core/index.ts +++ b/agenta-web/src/services/observability/core/index.ts @@ -1,6 +1,6 @@ import {delay} from "@/lib/helpers/utils" import data from "@/lib/test_trace.json" -import {AgentaRootsResponse} from "../types" +import {AgentaNodeDTO, AgentaRootsResponse, AgentaTreeDTO} from "../types" //Prefix convention: // - fetch: GET single entity from server @@ -9,6 +9,25 @@ import {AgentaRootsResponse} from "../types" // - update: PUT data to server // - delete: DELETE data from server +export const observabilityTransformer = (item: AgentaTreeDTO | AgentaNodeDTO): any => { + if (item.nodes) { + return Object.entries(item.nodes).flatMap(([_, value]) => { + if (value && Array.isArray(value)) { + return value.flatMap((item) => observabilityTransformer(item)) + } + + if (value && !Array.isArray(value)) { + const {nodes, ...node} = value + return { + ...node, + key: node.node.id, + ...(value.nodes ? {children: observabilityTransformer(value)} : null), + } + } + }) + } +} + export const fetchAllTraces = async () => { await delay(1000) return data as AgentaRootsResponse From 4011078086c832afd1344e29e14b2fc568213087 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Thu, 17 Oct 2024 11:58:51 +0200 Subject: [PATCH 087/305] fix(frontend): added transform mock type and improved logic --- agenta-web/src/lib/hooks/useTraces.ts | 6 +-- .../apps/[app_id]/observability/index.tsx | 53 ++++++++++--------- .../src/services/observability/core/index.ts | 39 ++++++++------ .../src/services/observability/types/index.ts | 5 ++ 4 files changed, 59 insertions(+), 44 deletions(-) diff --git a/agenta-web/src/lib/hooks/useTraces.ts b/agenta-web/src/lib/hooks/useTraces.ts index 57cbc70d75..23d9855ca2 100644 --- a/agenta-web/src/lib/hooks/useTraces.ts +++ b/agenta-web/src/lib/hooks/useTraces.ts @@ -1,9 +1,9 @@ import {fetchAllTraces} from "@/services/observability/core" -import {AgentaRootsResponse} from "@/services/observability/types" +import {_AgentaRootsResponse} from "@/services/observability/types" import {useEffect, useState} from "react" export const useTraces = () => { - const [traces, setTraces] = useState(null) + const [traces, setTraces] = useState<_AgentaRootsResponse[]>([]) const [isLoadingTraces, setIsLoadingTraces] = useState(true) const fetchTraces = async () => { @@ -23,7 +23,7 @@ export const useTraces = () => { }, []) return { - traces: traces?.roots, + traces: traces, isLoadingTraces, } } diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index e9ae542de1..67b37d6c8d 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -29,30 +29,31 @@ const ObservabilityDashboard = ({}: Props) => { const classes = useStyles() const [selectedTraceId, setSelectedTraceId] = useQueryParam("trace", "") const {traces} = useTraces() + console.log(traces) - const activeTrace = useMemo( - () => traces?.find((item) => item.root.id === selectedTraceId) ?? null, - [selectedTraceId, traces], - ) + // const activeTrace = useMemo( + // () => traces?.find((item) => item.root.id === selectedTraceId) ?? null, + // [selectedTraceId, traces], + // ) - const defaultSelectedTraceKey = useMemo(() => { - if (!activeTrace || !activeTrace.trees.length) return undefined - const firstNodeKey = Object.keys(activeTrace.trees[0].nodes)[0] - return activeTrace.trees[0].nodes[firstNodeKey].node.id - }, [activeTrace]) + // const defaultSelectedTraceKey = useMemo(() => { + // if (!activeTrace || !activeTrace.trees.length) return undefined + // const firstNodeKey = Object.keys(activeTrace.trees[0].nodes)[0] + // return activeTrace.trees[0].nodes[firstNodeKey].node.id + // }, [activeTrace]) - const [selectedKeys, setSelectedKeys] = useState([]) - const [selectedItem, setSelectedItem] = useState(null) + // const [selectedKeys, setSelectedKeys] = useState([]) + // const [selectedItem, setSelectedItem] = useState(null) - const onSelect = useCallback( - (keys: React.Key[]) => { - const selectedId = keys[0] as string - setSelectedKeys([selectedId]) - const foundItem = findTraceNodeById(activeTrace?.trees[0].nodes, selectedId) - setSelectedItem(foundItem) - }, - [activeTrace], - ) + // const onSelect = useCallback( + // (keys: React.Key[]) => { + // const selectedId = keys[0] as string + // setSelectedKeys([selectedId]) + // const foundItem = findTraceNodeById(activeTrace?.trees[0].nodes, selectedId) + // setSelectedItem(foundItem) + // }, + // [activeTrace], + // ) const columns: ColumnsType< Omit & { @@ -134,23 +135,23 @@ const ObservabilityDashboard = ({}: Props) => {
Observability - {traces && ( + {traces?.length ? (
observabilityTransformer(item.trees[0]))} + dataSource={traces} bordered style={{cursor: "pointer"}} onRow={(record) => ({ onClick: () => { - setSelectedTraceId(record.root.id) + setSelectedTraceId(record.key) }, })} pagination={false} scroll={{x: "max-content"}} /> - )} + ) : null} - {activeTrace && traces?.length && ( + {/* {activeTrace && traces?.length && ( setSelectedTraceId("")} @@ -173,7 +174,7 @@ const ObservabilityDashboard = ({}: Props) => { /> } /> - )} + )} */} ) } diff --git a/agenta-web/src/services/observability/core/index.ts b/agenta-web/src/services/observability/core/index.ts index cc168e0709..0250a47d9a 100644 --- a/agenta-web/src/services/observability/core/index.ts +++ b/agenta-web/src/services/observability/core/index.ts @@ -1,6 +1,6 @@ import {delay} from "@/lib/helpers/utils" import data from "@/lib/test_trace.json" -import {AgentaNodeDTO, AgentaRootsResponse, AgentaTreeDTO} from "../types" +import {_AgentaRootsResponse, AgentaNodeDTO, AgentaTreeDTO} from "../types" //Prefix convention: // - fetch: GET single entity from server @@ -9,26 +9,35 @@ import {AgentaNodeDTO, AgentaRootsResponse, AgentaTreeDTO} from "../types" // - update: PUT data to server // - delete: DELETE data from server -export const observabilityTransformer = (item: AgentaTreeDTO | AgentaNodeDTO): any => { +export const observabilityTransformer = ( + item: AgentaTreeDTO | AgentaNodeDTO, +): _AgentaRootsResponse[] => { if (item.nodes) { - return Object.entries(item.nodes).flatMap(([_, value]) => { - if (value && Array.isArray(value)) { - return value.flatMap((item) => observabilityTransformer(item)) - } + return Object.entries(item.nodes) + .flatMap(([_, value]) => { + if (value && Array.isArray(value)) { + return value.flatMap((item) => observabilityTransformer(item)) + } - if (value && !Array.isArray(value)) { - const {nodes, ...node} = value - return { - ...node, - key: node.node.id, - ...(value.nodes ? {children: observabilityTransformer(value)} : null), + if (value && !Array.isArray(value)) { + const {nodes, ...node} = value + return { + ...node, + key: node.node.id, + ...(value.nodes ? {children: observabilityTransformer(value)} : null), + } } - } - }) + }) + .filter((item): item is _AgentaRootsResponse => item !== null && item !== undefined) } + + return [] } export const fetchAllTraces = async () => { await delay(1000) - return data as AgentaRootsResponse + return data.roots.flatMap((item) => + // @ts-ignore + observabilityTransformer(item.trees[0]), + ) as _AgentaRootsResponse[] } diff --git a/agenta-web/src/services/observability/types/index.ts b/agenta-web/src/services/observability/types/index.ts index ff9f42f069..a44c0a4909 100644 --- a/agenta-web/src/services/observability/types/index.ts +++ b/agenta-web/src/services/observability/types/index.ts @@ -1,3 +1,8 @@ +export interface _AgentaRootsResponse extends Omit { + children: _AgentaRootsResponse[] + key: string +} + export interface AgentaRootsResponse { version: string roots: AgentaRootsDTO[] From 2748e023eac218ef0b37f33dedcc2cd0e7e1913b Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Thu, 17 Oct 2024 13:12:39 +0200 Subject: [PATCH 088/305] fix(frontend): improve type names --- .../src/services/observability/types/index.ts | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/agenta-web/src/services/observability/types/index.ts b/agenta-web/src/services/observability/types/index.ts index a44c0a4909..64615a85ac 100644 --- a/agenta-web/src/services/observability/types/index.ts +++ b/agenta-web/src/services/observability/types/index.ts @@ -30,28 +30,28 @@ export interface AgentaNodeDTO extends NodeDTO {} export interface NodeDTO { scope: ProjectScopeDTO - lifecycle: LifecycleDTO + lifecycle: NodeLifecycleDTO root: RootContextDTO tree: TreeContextDTO node: NodeContextDTO parent?: ParentContextDTO | null - time: TimeDTO - status: StatusDTO - data?: Data | null - metrics?: Metrics | null - meta?: Metadata | null - tags?: Tags | null - refs?: Refs | null - links?: LinkDTO[] | null + time: NodeTimeDTO + status: NodeStatusDTO + data?: NodeData | null + metrics?: NodeMetrics | null + meta?: NodeMetadata | null + tags?: NodeTags | null + refs?: NodeRefs | null + links?: NodeLinkDTO[] | null nodes?: Record | null } -type Data = Record -type Metrics = Record -type Metadata = Record -type Tags = Record -type Refs = Record -type LinkDTO = { +type NodeData = Record +type NodeMetrics = Record +type NodeMetadata = Record +type NodeTags = Record +type NodeRefs = Record +type NodeLinkDTO = { type: string id: string tree_id?: string | null @@ -61,7 +61,7 @@ interface ProjectScopeDTO { project_id: string } -interface LifecycleDTO { +interface NodeLifecycleDTO { created_at: string updated_at?: string | null updated_by_id?: string | null @@ -86,7 +86,7 @@ enum NodeType { RERANK = "rerank", } -enum StatusCode { +export enum NodeStatusCode { UNSET = "UNSET", OK = "OK", ERROR = "ERROR", @@ -96,14 +96,14 @@ interface ParentContextDTO { id: string } -interface TimeDTO { +interface NodeTimeDTO { start: string end: string span: number } -interface StatusDTO { - code: StatusCode +export interface NodeStatusDTO { + code: NodeStatusCode message?: string | null stacktrace?: string | null } From 1faa3f830cd490852edb888f914462dc23739cd9 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Thu, 17 Oct 2024 13:13:37 +0200 Subject: [PATCH 089/305] fix(frontend): populated latency, cost, status, and usage columns --- .../components/StatusRenderer.tsx | 42 +++++++++++++++++++ .../apps/[app_id]/observability/index.tsx | 16 ++++--- 2 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 agenta-web/src/components/pages/observability/components/StatusRenderer.tsx diff --git a/agenta-web/src/components/pages/observability/components/StatusRenderer.tsx b/agenta-web/src/components/pages/observability/components/StatusRenderer.tsx new file mode 100644 index 0000000000..d7e484ff1c --- /dev/null +++ b/agenta-web/src/components/pages/observability/components/StatusRenderer.tsx @@ -0,0 +1,42 @@ +import {_AgentaRootsResponse, NodeStatusCode, NodeStatusDTO} from "@/services/observability/types" +import {InfoCircleOutlined} from "@ant-design/icons" +import {Space, Tag, Tooltip} from "antd" +import React from "react" + +export const statusMapper = (status: NodeStatusCode) => { + switch (status) { + case NodeStatusCode.UNSET: + return { + label: "initiated", + color: "processing", + } + case NodeStatusCode.ERROR: + return { + label: "failed", + color: "error", + } + default: + return { + label: "success", + color: "success", + } + } +} + +const StatusRenderer = (status: NodeStatusDTO) => { + const {label, color} = statusMapper(status.code) + const errorMsg = status.code === NodeStatusCode.ERROR ? status.message : null + + return ( + + {label} + {errorMsg && ( + + + + )} + + ) +} + +export default StatusRenderer diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index 67b37d6c8d..57b75321e6 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -1,14 +1,16 @@ import GenericDrawer from "@/components/GenericDrawer" +import StatusRenderer from "@/components/pages/observability/components/StatusRenderer" import TraceContent from "@/components/pages/observability/drawer/TraceContent" import TraceHeader from "@/components/pages/observability/drawer/TraceHeader" import TraceTree from "@/components/pages/observability/drawer/TraceTree" import ResultTag from "@/components/ResultTag/ResultTag" import {useQueryParam} from "@/hooks/useQuery" +import {formatCurrency, formatLatency, formatTokenUsage} from "@/lib/helpers/formatters" import {findTraceNodeById} from "@/lib/helpers/observability_helpers" import {useTraces} from "@/lib/hooks/useTraces" import {JSSTheme} from "@/lib/Types" import {observabilityTransformer} from "@/services/observability/core" -import {AgentaNodeDTO} from "@/services/observability/types" +import {_AgentaRootsResponse, AgentaNodeDTO} from "@/services/observability/types" import {Table, Typography} from "antd" import {ColumnsType} from "antd/es/table" import dayjs from "dayjs" @@ -55,11 +57,7 @@ const ObservabilityDashboard = ({}: Props) => { // [activeTrace], // ) - const columns: ColumnsType< - Omit & { - key: string - } - > = [ + const columns: ColumnsType<_AgentaRootsResponse> = [ { title: "ID", dataIndex: ["key"], @@ -104,6 +102,7 @@ const ObservabilityDashboard = ({}: Props) => { onHeaderCell: () => ({ style: {minWidth: 160}, }), + render: (_, record) => StatusRenderer(record.status), }, { title: "Latency", @@ -112,6 +111,7 @@ const ObservabilityDashboard = ({}: Props) => { onHeaderCell: () => ({ style: {minWidth: 80}, }), + render: (_, record) =>
{formatLatency(record.time.span / 1000000)}
, }, { title: "Usage", @@ -120,6 +120,9 @@ const ObservabilityDashboard = ({}: Props) => { onHeaderCell: () => ({ style: {minWidth: 80}, }), + render: (_, record) => ( +
{formatTokenUsage(record.metrics?.acc?.tokens?.total)}
+ ), }, { title: "Total cost", @@ -128,6 +131,7 @@ const ObservabilityDashboard = ({}: Props) => { onHeaderCell: () => ({ style: {minWidth: 80}, }), + render: (_, record) =>
{formatCurrency(record.metrics?.acc?.costs?.total)}
, }, ] From 562d177f8298ac79168259de990535bfac8a8bbf Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Thu, 17 Oct 2024 13:15:22 +0200 Subject: [PATCH 090/305] remove console log(frontend) --- agenta-web/src/pages/apps/[app_id]/observability/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index 57b75321e6..4703c1c645 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -31,7 +31,6 @@ const ObservabilityDashboard = ({}: Props) => { const classes = useStyles() const [selectedTraceId, setSelectedTraceId] = useQueryParam("trace", "") const {traces} = useTraces() - console.log(traces) // const activeTrace = useMemo( // () => traces?.find((item) => item.root.id === selectedTraceId) ?? null, From 24a78aef6826644ea520a53cce85fd0a62e82a68 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Thu, 17 Oct 2024 20:52:25 +0600 Subject: [PATCH 091/305] feat(frontend): implemented filters in observability --- .../observability/TableHeader/Filters.tsx | 153 ++++++++++++++++++ .../pages/observability/TableHeader/Sort.tsx | 43 +++++ agenta-web/src/lib/Types.ts | 6 + agenta-web/src/lib/helpers/utils.ts | 94 +++++++++++ .../apps/[app_id]/observability/index.tsx | 72 ++++++++- 5 files changed, 364 insertions(+), 4 deletions(-) create mode 100644 agenta-web/src/components/pages/observability/TableHeader/Filters.tsx create mode 100644 agenta-web/src/components/pages/observability/TableHeader/Sort.tsx diff --git a/agenta-web/src/components/pages/observability/TableHeader/Filters.tsx b/agenta-web/src/components/pages/observability/TableHeader/Filters.tsx new file mode 100644 index 0000000000..50ec47f6ef --- /dev/null +++ b/agenta-web/src/components/pages/observability/TableHeader/Filters.tsx @@ -0,0 +1,153 @@ +import React, {useState} from "react" +import {Filter, JSSTheme} from "@/lib/Types" +import {ArrowCounterClockwise, CaretDown, Funnel, X} from "@phosphor-icons/react" +import {Button, Divider, Input, Popover, Select, Space, Typography} from "antd" +import {createUseStyles} from "react-jss" + +const useStyles = createUseStyles((theme: JSSTheme) => ({ + popover: { + "& .ant-popover-inner": { + width: "600px !important", + padding: `0px ${theme.paddingXS}px ${theme.paddingXS}px ${theme.padding}px`, + }, + }, + filterHeading: { + fontSize: theme.fontSizeHeading5, + lineHeight: theme.lineHeightHeading5, + fontWeight: theme.fontWeightMedium, + }, + filterContainer: { + padding: 8, + gap: 8, + borderRadius: theme.borderRadius, + backgroundColor: "#f5f7fa", + marginTop: 8, + }, +})) + +type Props = { + setFilterValue: React.Dispatch> + columns: {column: string; mapping: string}[] +} + +const Filters: React.FC = ({setFilterValue, columns}) => { + const classes = useStyles() + const [filter, setFilter] = useState({} as Filter) + const [isFilterOpen, setIsFilterOpen] = useState(false) + + const conditions = [ + "contains", + "does not contain", + "starts with", + "ends with", + "exists", + "does not exist", + "=", + ">", + "<", + ">=", + "<=", + ] + + const clearFilter = () => { + setFilter({} as Filter) + setFilterValue({} as Filter) + } + + return ( + setIsFilterOpen(false)} + open={isFilterOpen} + placement="bottomLeft" + content={ +
+
+ Filter + + +
+ +
+ +
+ +
+ + Where + + } + onChange={(value) => + setFilter({...filter, condition: value}) + } + popupMatchSelectWidth={250} + value={filter.condition} + options={conditions.map((item) => { + return {value: item, label: item.toUpperCase()} + })} + /> + + + setFilter({...filter, keyword: e.target.value}) + } + /> + + )} + +
+ +
+ +
+
+ } + > + +
+ ) +} + +export default Filters diff --git a/agenta-web/src/components/pages/observability/TableHeader/Sort.tsx b/agenta-web/src/components/pages/observability/TableHeader/Sort.tsx new file mode 100644 index 0000000000..3e50b8de15 --- /dev/null +++ b/agenta-web/src/components/pages/observability/TableHeader/Sort.tsx @@ -0,0 +1,43 @@ +import React, {useState} from "react" +import {Hourglass} from "@phosphor-icons/react" +import {Select} from "antd" + +type Props = { + setSort: React.Dispatch> + sort?: string +} + +const Sort: React.FC = ({setSort, sort}) => { + return ( +
({ From 76a27b2945d32e19ebddec3311d28f27cf79033e Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 17 Oct 2024 21:41:10 +0200 Subject: [PATCH 092/305] Fix missing await for the legacy receiver, Add APi grouping for Observability v1 --- .../apis/fastapi/observability/router.py | 19 ++++++++----------- agenta-backend/agenta_backend/main.py | 4 +++- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py index e063062760..872b29b9be 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py @@ -228,7 +228,7 @@ async def query_traces( async def otlp_collect_traces( self, request: Request, - background_tasks: BackgroundTasks, + # background_tasks: BackgroundTasks, ): """ Collect traces via OTLP. @@ -236,15 +236,17 @@ async def otlp_collect_traces( otlp_stream = await request.body() + project_id = request.headers.get("AG-PROJECT-ID") or request.state.project_id + app_id = request.headers.get("AG-APP-ID") + ### LEGACY ### - res = None if self.legacy_receiver: - res = self.legacy_receiver(otlp_stream) + await self.legacy_receiver( + project_id=project_id, + otlp_stream=otlp_stream, + ) ### LEGACY ### - project_id = request.headers.get("AG-PROJECT-ID") or request.state.project_id - app_id = request.headers.get("AG-APP-ID") - otel_span_dtos = parse_otlp_stream(otlp_stream) span_dtos = [ @@ -255,9 +257,4 @@ async def otlp_collect_traces( # background_tasks.add_task(self.service.ingest, span_dtos=span_dtos) await self.service.ingest(span_dtos=span_dtos) - ### LEGACY ### - if res: - return res - ### LEGACY ### - return CollectStatusResponse(version=self.VERSION, status="processing") diff --git a/agenta-backend/agenta_backend/main.py b/agenta-backend/agenta_backend/main.py index 32aa88711a..ae30c91122 100644 --- a/agenta-backend/agenta_backend/main.py +++ b/agenta-backend/agenta_backend/main.py @@ -128,7 +128,9 @@ async def lifespan(application: FastAPI, cache=True): observability_legacy_receiver=observability_legacy_receiver, ) -app.include_router(router=observability.router, prefix="/observability/v1") +app.include_router( + router=observability.router, prefix="/observability/v1", tags=["Observability [v1]"] +) if isCloudEE(): import agenta_backend.cloud.main as cloud From fdf4fdd4ab0438eb82c5ae1fa6acb26bfca028be Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Fri, 18 Oct 2024 09:05:02 +0200 Subject: [PATCH 093/305] fix(frontend): Refactor observabilityTransformer to follow tree node structure pattern --- .../src/services/observability/core/index.ts | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/agenta-web/src/services/observability/core/index.ts b/agenta-web/src/services/observability/core/index.ts index 0250a47d9a..6415281a0c 100644 --- a/agenta-web/src/services/observability/core/index.ts +++ b/agenta-web/src/services/observability/core/index.ts @@ -12,23 +12,32 @@ import {_AgentaRootsResponse, AgentaNodeDTO, AgentaTreeDTO} from "../types" export const observabilityTransformer = ( item: AgentaTreeDTO | AgentaNodeDTO, ): _AgentaRootsResponse[] => { + const buildData = (node: AgentaNodeDTO) => { + const key = node.node.id + const hasChildren = node.nodes && Object.keys(node.nodes).length > 0 + + return { + ...node, + key, + ...(hasChildren ? {children: observabilityTransformer(node)} : undefined), + } + } + if (item.nodes) { return Object.entries(item.nodes) .flatMap(([_, value]) => { - if (value && Array.isArray(value)) { - return value.flatMap((item) => observabilityTransformer(item)) - } - - if (value && !Array.isArray(value)) { - const {nodes, ...node} = value - return { - ...node, - key: node.node.id, - ...(value.nodes ? {children: observabilityTransformer(value)} : null), - } + if (Array.isArray(value)) { + return value.map((item, index) => + buildData({ + ...item, + node: {...item.node, name: `${item.node.name}[${index}]`}, + }), + ) + } else { + return buildData(value) } }) - .filter((item): item is _AgentaRootsResponse => item !== null && item !== undefined) + .filter((node): node is _AgentaRootsResponse => node !== null && node !== undefined) } return [] From 7e6a7c720c7e183f1da93639783ad1feaa559224 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Fri, 18 Oct 2024 14:48:14 +0600 Subject: [PATCH 094/305] refactor(frontend): observability filter code --- .../TableHeader => Filters}/Filters.tsx | 0 .../TableHeader => Filters}/Sort.tsx | 2 +- agenta-web/src/lib/Types.ts | 17 +++++- agenta-web/src/lib/helpers/utils.ts | 61 +++++-------------- .../apps/[app_id]/observability/index.tsx | 7 +-- 5 files changed, 33 insertions(+), 54 deletions(-) rename agenta-web/src/components/{pages/observability/TableHeader => Filters}/Filters.tsx (100%) rename agenta-web/src/components/{pages/observability/TableHeader => Filters}/Sort.tsx (97%) diff --git a/agenta-web/src/components/pages/observability/TableHeader/Filters.tsx b/agenta-web/src/components/Filters/Filters.tsx similarity index 100% rename from agenta-web/src/components/pages/observability/TableHeader/Filters.tsx rename to agenta-web/src/components/Filters/Filters.tsx diff --git a/agenta-web/src/components/pages/observability/TableHeader/Sort.tsx b/agenta-web/src/components/Filters/Sort.tsx similarity index 97% rename from agenta-web/src/components/pages/observability/TableHeader/Sort.tsx rename to agenta-web/src/components/Filters/Sort.tsx index 3e50b8de15..bf3e0e897d 100644 --- a/agenta-web/src/components/pages/observability/TableHeader/Sort.tsx +++ b/agenta-web/src/components/Filters/Sort.tsx @@ -1,4 +1,4 @@ -import React, {useState} from "react" +import React from "react" import {Hourglass} from "@phosphor-icons/react" import {Select} from "antd" diff --git a/agenta-web/src/lib/Types.ts b/agenta-web/src/lib/Types.ts index 5a1d251569..2fb5baa57f 100644 --- a/agenta-web/src/lib/Types.ts +++ b/agenta-web/src/lib/Types.ts @@ -664,7 +664,20 @@ export interface HumanEvaluationListTableDataType { } export type Filter = { - condition: string + condition: FilterConditions column: string keyword: string -} \ No newline at end of file +} + +export type FilterConditions = + | "contains" + | "does not contain" + | "starts with" + | "ends with" + | "exists" + | "does not exist" + | "=" + | ">" + | "<" + | ">=" + | "<=" diff --git a/agenta-web/src/lib/helpers/utils.ts b/agenta-web/src/lib/helpers/utils.ts index 2a7f9f6807..7c83073551 100644 --- a/agenta-web/src/lib/helpers/utils.ts +++ b/agenta-web/src/lib/helpers/utils.ts @@ -1,6 +1,6 @@ import {v4 as uuidv4} from "uuid" import {EvaluationType} from "../enums" -import {GenericObject} from "../Types" +import {FilterConditions, GenericObject} from "../Types" import promiseRetry from "promise-retry" import {getErrorMessage} from "./errorHandler" import dayjs from "dayjs" @@ -360,53 +360,20 @@ export const formatVariantIdWithHash = (variantId: string) => { return `# ${variantId.split("-")[0]}` } -export const getTimeRangeUnit = (time: string) => { - let unit: dayjs.UnitType - let duration: number - - switch (time) { - case "30 mins": - unit = "minute" - duration = 30 - break - case "1 hour": - unit = "hour" - duration = 1 - break - case "6 hour": - unit = "hour" - duration = 6 - break - case "24 hour": - unit = "hour" - duration = 24 - break - case "3 days": - unit = "day" - duration = 3 - break - case "7 days": - unit = "day" - duration = 7 - break - case "14 days": - unit = "day" - duration = 14 - break - case "1 month": - unit = "month" - duration = 1 - break - case "3 month": - unit = "month" - duration = 3 - break - default: - unit = "year" - duration = Infinity +export const getTimeRangeUnit = (time: string): {duration: number; unit: dayjs.ManipulateType} => { + const timeRanges: {[key: string]: {duration: number; unit: dayjs.ManipulateType}} = { + "30 mins": {duration: 30, unit: "minute"}, + "1 hour": {duration: 1, unit: "hour"}, + "6 hour": {duration: 6, unit: "hour"}, + "24 hour": {duration: 24, unit: "hour"}, + "3 days": {duration: 3, unit: "day"}, + "7 days": {duration: 7, unit: "day"}, + "14 days": {duration: 14, unit: "day"}, + "1 month": {duration: 1, unit: "month"}, + "3 month": {duration: 3, unit: "month"}, } - return {duration, unit} + return timeRanges[time] || {duration: Infinity, unit: "year"} } export const getNestedProperties = (obj: any, path: string) => { @@ -419,7 +386,7 @@ export const matcheFilterCriteria = ({ keyword, }: { data: any - condition: string + condition: FilterConditions keyword: string | number }) => { if (data === undefined) return false diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index df2bde90db..5afda64223 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -3,8 +3,8 @@ import StatusRenderer from "@/components/pages/observability/components/StatusRe import TraceContent from "@/components/pages/observability/drawer/TraceContent" import TraceHeader from "@/components/pages/observability/drawer/TraceHeader" import TraceTree from "@/components/pages/observability/drawer/TraceTree" -import Filters from "@/components/pages/observability/TableHeader/Filters" -import Sort from "@/components/pages/observability/TableHeader/Sort" +import Filters from "@/components/Filters/Filters" +import Sort from "@/components/Filters/Sort" import ResultTag from "@/components/ResultTag/ResultTag" import {useQueryParam} from "@/hooks/useQuery" import {formatCurrency, formatLatency, formatTokenUsage} from "@/lib/helpers/formatters" @@ -88,13 +88,12 @@ const ObservabilityDashboard = ({}: Props) => { const now = dayjs() const {duration, unit} = getTimeRangeUnit(sortTrace) - if (duration == Infinity) { + if (duration === Infinity) { return filtered } filtered = filtered?.filter((item) => { const itemDate = dayjs(item.lifecycle.created_at) - return itemDate.isAfter(now.subtract(duration, unit)) }) } From e8de649a46d159eaa7aa1c576c81e10f8f0ebd5e Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Fri, 18 Oct 2024 15:52:52 +0200 Subject: [PATCH 095/305] refactor(frontend): cleanup --- .../observability/drawer/TraceContent.tsx | 127 ++++++++---------- .../observability/drawer/TraceHeader.tsx | 15 +-- .../pages/observability/drawer/TraceTree.tsx | 88 +++++------- .../src/lib/helpers/observability_helpers.ts | 54 ++++---- .../apps/[app_id]/observability/index.tsx | 65 ++++----- 5 files changed, 158 insertions(+), 191 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx index 5d36c9d0ef..65d758e381 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx @@ -6,7 +6,7 @@ import {Button, Collapse, CollapseProps, Divider, Space, Tabs, TabsProps, Typogr import React, {useState} from "react" import {createUseStyles} from "react-jss" import {IBM_Plex_Mono} from "next/font/google" -import {AgentaNodeDTO} from "@/services/observability/types" +import {_AgentaRootsResponse} from "@/services/observability/types" import dayjs from "dayjs" import {getStringOrJson} from "@/lib/helpers/utils" @@ -16,7 +16,7 @@ const ibm_plex_mono = IBM_Plex_Mono({ }) interface TraceContentProps { - activeTrace: AgentaNodeDTO + activeTrace: _AgentaRootsResponse } const useStyles = createUseStyles((theme: JSSTheme) => ({ @@ -94,69 +94,6 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { const classes = useStyles() const [tab, setTab] = useState("overview") - const generateAccordionItems = ( - obj: Record | string, - parentKey = "", - ): CollapseProps["items"] => { - if (typeof obj !== "object") { - const key = parentKey || "value" - - return [ - { - key, - label: key, - children:
{getStringOrJson(obj)}
, - extra: , - }, - ] - } - - return Object.entries(obj) - .flatMap(([key, value]) => { - const currentPath = parentKey ? `${parentKey}.${key}` : key - - if (Array.isArray(value)) { - return value.map((item, index) => ({ - key: `${currentPath}[${index}]`, - label: `${currentPath}[${index}]`, - children: ( -
- {typeof item === "object" - ? getStringOrJson(item) - : getStringOrJson(item)} -
- ), - extra: ( - - ), - })) - } - - if (typeof value === "object" && value !== null) { - return generateAccordionItems(value, currentPath) || [] - } - - return { - key: currentPath, - label: currentPath, - children:
{value}
, - extra: ( - - ), - } - }) - .filter(Boolean) - } - const items: TabsProps["items"] = [ { key: "overview", @@ -182,7 +119,25 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { Inputs + {getStringOrJson(data.inputs)} + + ), + extra: ( + + ), + }, + ]} className={classes.collapseContainer} bordered={false} /> @@ -195,7 +150,25 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { Internals + {getStringOrJson(data.internals)} + + ), + extra: ( + + ), + }, + ]} className={classes.collapseContainer} bordered={false} /> @@ -206,7 +179,25 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { Outputs + {getStringOrJson(data.outputs)} + + ), + extra: ( + + ), + }, + ]} className={classes.collapseContainer} bordered={false} /> diff --git a/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx b/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx index 4d2f7f9392..996efe8d3c 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx @@ -1,5 +1,5 @@ import {JSSTheme} from "@/lib/Types" -import {AgentaRootsDTO} from "@/services/observability/types" +import {_AgentaRootsResponse, AgentaRootsDTO} from "@/services/observability/types" import {DeleteOutlined} from "@ant-design/icons" import {CaretDown, CaretUp} from "@phosphor-icons/react" import {Button, Space, Tag, Typography} from "antd" @@ -7,10 +7,10 @@ import React, {useMemo, useCallback} from "react" import {createUseStyles} from "react-jss" interface TraceHeaderProps { - activeTrace: AgentaRootsDTO - selectedTraceId: string - traces: AgentaRootsDTO[] + activeTrace: _AgentaRootsResponse + traces: _AgentaRootsResponse[] setSelectedTraceId: (val: string) => void + activeTraceIndex: number } const useStyles = createUseStyles((theme: JSSTheme) => ({ @@ -23,17 +23,12 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ const TraceHeader = ({ activeTrace, - selectedTraceId, traces, setSelectedTraceId, + activeTraceIndex, }: TraceHeaderProps) => { const classes = useStyles() - const activeTraceIndex = useMemo( - () => traces?.findIndex((item) => item.root.id === selectedTraceId), - [selectedTraceId, traces], - ) - const handleNextTrace = useCallback(() => { if (activeTraceIndex !== undefined && activeTraceIndex < traces.length - 1) { setSelectedTraceId(traces[activeTraceIndex + 1].root.id) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx index 6f054efd69..d2b07a8dfd 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx @@ -1,16 +1,21 @@ import {formatLatency} from "@/lib/helpers/formatters" import {JSSTheme} from "@/lib/Types" -import {AgentaNodeDTO} from "@/services/observability/types" +import {_AgentaRootsResponse} from "@/services/observability/types" import {Coins, PlusCircle, Timer, TreeStructure} from "@phosphor-icons/react" -import {Avatar, Space, Tree, TreeDataNode, Typography} from "antd" +import {Avatar, Space, Tree, Typography} from "antd" import React from "react" import {createUseStyles} from "react-jss" interface TraceTreeProps { - activeTrace: Record - selectedKeys: string[] - onSelect: (keys: React.Key[]) => void - defaultSelectedTraceKey: string | undefined + activeTrace: _AgentaRootsResponse + selected: string + setSelected: React.Dispatch> +} + +interface NodeTreeChildren { + title: React.ReactElement + key: string + children?: NodeTreeChildren[] } const useStyles = createUseStyles((theme: JSSTheme) => ({ @@ -53,8 +58,8 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ }, })) -const TreeContent = ({nodeValue}: {nodeValue: AgentaNodeDTO}) => { - const {node, time} = nodeValue +const TreeContent = ({value}: {value: _AgentaRootsResponse}) => { + const {node, time} = value const classes = useStyles() return ( @@ -86,62 +91,37 @@ const TreeContent = ({nodeValue}: {nodeValue: AgentaNodeDTO}) => { ) } -const buildTreeData = ( - nodes: Record, - expandedKeys: string[], -): TreeDataNode[] => { - const createTreeNode = (node: AgentaNodeDTO): TreeDataNode => { - const hasChildren = node.nodes && Object.keys(node.nodes).length > 0 - const key = node.node.id - expandedKeys.push(key) - - return { - key: key, - title: , - children: hasChildren - ? buildTreeData( - node.nodes as Record, - expandedKeys, - ) - : undefined, - } - } - - return Object.entries(nodes).flatMap(([_, value]) => { - if (Array.isArray(value)) { - return value.map((item, index) => - createTreeNode({ - ...item, - node: {...item.node, name: `${item.node.name}[${index}]`}, - }), - ) - } else { - return createTreeNode(value) - } - }) +const buildTreeData = (spans: _AgentaRootsResponse[]): NodeTreeChildren[] => { + return spans.map((span) => ({ + title: , + key: span.node.id, + children: span.children ? buildTreeData(span.children) : undefined, + })) } -const TraceTree = ({ - activeTrace, - selectedKeys, - onSelect, - defaultSelectedTraceKey, -}: TraceTreeProps) => { +const TraceTree = ({activeTrace, selected, setSelected}: TraceTreeProps) => { const classes = useStyles() - const expandedKeys: string[] = [] - const treeData = buildTreeData(activeTrace, expandedKeys) return ( { + setSelected(keys[0]?.toString() || activeTrace.node.id) + }} + treeData={[ + { + title: , + key: activeTrace.node.id, + children: activeTrace.children + ? buildTreeData(activeTrace.children) + : undefined, + }, + ]} className={classes.tree} defaultExpandAll - onSelect={onSelect} defaultExpandParent - expandedKeys={expandedKeys} - selectedKeys={selectedKeys} /> ) } diff --git a/agenta-web/src/lib/helpers/observability_helpers.ts b/agenta-web/src/lib/helpers/observability_helpers.ts index 6bb1025f9d..523c22c3ec 100644 --- a/agenta-web/src/lib/helpers/observability_helpers.ts +++ b/agenta-web/src/lib/helpers/observability_helpers.ts @@ -1,35 +1,35 @@ -import {AgentaNodeDTO} from "@/services/observability/types" +import {_AgentaRootsResponse} from "@/services/observability/types" -export const findTraceNodeById = ( - nodes: Record | undefined, +export const getNodeById = ( + nodes: _AgentaRootsResponse[] | _AgentaRootsResponse, id: string, -): AgentaNodeDTO | null => { - for (const key in nodes) { - const node = nodes[key] +): _AgentaRootsResponse | null => { + if (nodes && !Array.isArray(nodes) && nodes.key === id) { + return nodes + } - if (Array.isArray(node)) { - for (const childNode of node) { - if (childNode.node.id === id) { - return childNode - } + if (nodes) { + for (const value of Object.values(nodes)) { + if (Array.isArray(value)) { + for (const node of value) { + if (node.key === id) { + return node + } - const found = findTraceNodeById( - childNode.nodes as Record, - id, - ) - if (found) return found - } - } else { - if (node.node.id === id) { - return node - } + if (node.children) { + const foundNode = getNodeById(node.children, id) + if (foundNode) return foundNode + } + } + } else { + if (value.key === id) { + return value + } - if (node.nodes) { - const found = findTraceNodeById( - node.nodes as Record, - id, - ) - if (found) return found + if (value.children) { + const foundNode = getNodeById(value.children, id) + if (foundNode) return foundNode + } } } } diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index 4703c1c645..946f74f285 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -6,15 +6,14 @@ import TraceTree from "@/components/pages/observability/drawer/TraceTree" import ResultTag from "@/components/ResultTag/ResultTag" import {useQueryParam} from "@/hooks/useQuery" import {formatCurrency, formatLatency, formatTokenUsage} from "@/lib/helpers/formatters" -import {findTraceNodeById} from "@/lib/helpers/observability_helpers" +import {getNodeById} from "@/lib/helpers/observability_helpers" import {useTraces} from "@/lib/hooks/useTraces" import {JSSTheme} from "@/lib/Types" -import {observabilityTransformer} from "@/services/observability/core" -import {_AgentaRootsResponse, AgentaNodeDTO} from "@/services/observability/types" +import {_AgentaRootsResponse} from "@/services/observability/types" import {Table, Typography} from "antd" import {ColumnsType} from "antd/es/table" import dayjs from "dayjs" -import React, {useCallback, useMemo, useState} from "react" +import React, {useEffect, useMemo, useState} from "react" import {createUseStyles} from "react-jss" const useStyles = createUseStyles((theme: JSSTheme) => ({ @@ -32,29 +31,29 @@ const ObservabilityDashboard = ({}: Props) => { const [selectedTraceId, setSelectedTraceId] = useQueryParam("trace", "") const {traces} = useTraces() - // const activeTrace = useMemo( - // () => traces?.find((item) => item.root.id === selectedTraceId) ?? null, - // [selectedTraceId, traces], - // ) + const activeTraceIndex = useMemo( + () => traces?.findIndex((item) => item.root.id === selectedTraceId), + [selectedTraceId, traces], + ) + + const activeTrace = useMemo(() => traces[activeTraceIndex] ?? null, [activeTraceIndex, traces]) - // const defaultSelectedTraceKey = useMemo(() => { - // if (!activeTrace || !activeTrace.trees.length) return undefined - // const firstNodeKey = Object.keys(activeTrace.trees[0].nodes)[0] - // return activeTrace.trees[0].nodes[firstNodeKey].node.id - // }, [activeTrace]) + const [selected, setSelected] = useState(activeTrace?.key) - // const [selectedKeys, setSelectedKeys] = useState([]) - // const [selectedItem, setSelectedItem] = useState(null) + const [selectedItem, setSelectedItem] = useState<_AgentaRootsResponse | null>( + getNodeById(traces, selected), + ) - // const onSelect = useCallback( - // (keys: React.Key[]) => { - // const selectedId = keys[0] as string - // setSelectedKeys([selectedId]) - // const foundItem = findTraceNodeById(activeTrace?.trees[0].nodes, selectedId) - // setSelectedItem(foundItem) - // }, - // [activeTrace], - // ) + useEffect(() => { + setSelected(activeTrace?.key) + }, [activeTrace]) + + const handleTreeNodeClick = (nodeId: string) => { + const selectedNode = activeTrace ? getNodeById(activeTrace, nodeId) : null + if (selectedNode) { + setSelectedItem(selectedNode) + } + } const columns: ColumnsType<_AgentaRootsResponse> = [ { @@ -146,7 +145,7 @@ const ObservabilityDashboard = ({}: Props) => { style={{cursor: "pointer"}} onRow={(record) => ({ onClick: () => { - setSelectedTraceId(record.key) + setSelectedTraceId(record.root.id) }, })} pagination={false} @@ -154,7 +153,7 @@ const ObservabilityDashboard = ({}: Props) => { /> ) : null} - {/* {activeTrace && traces?.length && ( + {activeTrace && !!traces?.length && ( setSelectedTraceId("")} @@ -162,22 +161,24 @@ const ObservabilityDashboard = ({}: Props) => { headerExtra={ } mainContent={selectedItem ? : null} sideContent={ { + setSelected(nodeId) + handleTreeNodeClick(nodeId.toString()) + }} /> } /> - )} */} + )} ) } From cb3981ce20dcc888bfb27383838458fdae7dc0c6 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Sat, 19 Oct 2024 11:23:36 +0200 Subject: [PATCH 096/305] fix errors in review --- .../agenta_backend/core/observability/dtos.py | 2 +- agenta-cli/agenta/sdk/agenta_init.py | 4 ++-- agenta-cli/agenta/sdk/decorators/tracing.py | 20 ++----------------- 3 files changed, 5 insertions(+), 21 deletions(-) diff --git a/agenta-backend/agenta_backend/core/observability/dtos.py b/agenta-backend/agenta_backend/core/observability/dtos.py index c84ba2fb9b..642b5834e9 100644 --- a/agenta-backend/agenta_backend/core/observability/dtos.py +++ b/agenta-backend/agenta_backend/core/observability/dtos.py @@ -139,7 +139,7 @@ class OTelContextDTO(DisplayBase): class OTelEventDTO(DisplayBase): name: str - timestamp: datetime + timestamp: str attributes: Optional[Attributes] = None diff --git a/agenta-cli/agenta/sdk/agenta_init.py b/agenta-cli/agenta/sdk/agenta_init.py index a4fa82a42e..a8dfbc2553 100644 --- a/agenta-cli/agenta/sdk/agenta_init.py +++ b/agenta-cli/agenta/sdk/agenta_init.py @@ -86,12 +86,12 @@ def init( self.project_id = ( project_id - or config.get("project_id") or os.environ.get("AGENTA_PROJECT_ID") + or config.get("project_id") ) self.api_key = ( - api_key or config.get("api_key") or os.environ.get("AGENTA_API_KEY") + api_key or os.environ.get("AGENTA_API_KEY") or config.get("api_key") ) self.tracing = Tracing( diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 5a6f507f1a..1b639f4ca1 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -137,16 +137,7 @@ async def wrapped_func(*args, **kwargs): max_depth=self.max_depth, ) - try: - result = await func(*args, **kwargs) - except Exception as e: - traceback.print_exc() - - span.record_exception(e) - - span.set_status("ERROR") - - raise e + result = await func(*args, **kwargs) with suppress(): cost = None @@ -240,14 +231,7 @@ def wrapped_func(*args, **kwargs): max_depth=self.max_depth, ) - try: - result = func(*args, **kwargs) - except Exception as e: - span.record_exception(e) - - span.set_status("ERROR") - - raise e + result = func(*args, **kwargs) with suppress(): cost = None From 0b8eb2413d7458c3bd50c88c759266108fb6243d Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sat, 19 Oct 2024 16:01:34 +0200 Subject: [PATCH 097/305] fix(frontend): Refactor conditional rendering for inputs, outputs, and internals --- .../observability/drawer/TraceContent.tsx | 250 ++++++++++++------ 1 file changed, 168 insertions(+), 82 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx index 65d758e381..c521ae43ed 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx @@ -2,7 +2,7 @@ import CopyButton from "@/components/CopyButton/CopyButton" import ResultTag from "@/components/ResultTag/ResultTag" import {JSSTheme} from "@/lib/Types" import {ArrowRight, Database, PlusCircle, Rocket, Sparkle, Timer} from "@phosphor-icons/react" -import {Button, Collapse, CollapseProps, Divider, Space, Tabs, TabsProps, Typography} from "antd" +import {Button, Collapse, Divider, Space, Tabs, TabsProps, Typography} from "antd" import React, {useState} from "react" import {createUseStyles} from "react-jss" import {IBM_Plex_Mono} from "next/font/google" @@ -62,11 +62,9 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ collapseContainer: { backgroundColor: "unset", "& .ant-collapse-item": { - marginBottom: 24, background: theme.colorFillAlter, borderRadius: `${theme.borderRadiusLG}px !important`, border: `1px solid ${theme.colorBorder}`, - borderBottom: "in", }, "& .ant-collapse-item:last-child": { borderBottom: `1px solid ${theme.colorBorder}`, @@ -115,92 +113,180 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { )} - {data && data.inputs && ( + {data && data?.inputs ? ( - Inputs - - {getStringOrJson(data.inputs)} - - ), - extra: ( - - ), - }, - ]} - className={classes.collapseContainer} - bordered={false} - /> + {node.type !== "chat" ? ( + + {getStringOrJson(data.inputs)} + + ), + extra: ( + + ), + }, + ]} + className={classes.collapseContainer} + bordered={false} + /> + ) : ( + Object.values(data.inputs).map((item) => + Array.isArray(item) + ? item.map((param, index) => + param.role !== "tool" ? ( + + {getStringOrJson( + param.content, + )} + + ), + extra: ( + + ), + }, + ]} + className={classes.collapseContainer} + bordered={false} + /> + ) : ( + "TO_DO" + ), + ) + : null, + ) + )} - )} + ) : null} - {data && data.internals && ( + {data && data?.outputs ? ( - - Internals - - - {getStringOrJson(data.internals)} - - ), - extra: ( - - ), - }, - ]} - className={classes.collapseContainer} - bordered={false} - /> + {node.type !== "chat" ? ( + + {getStringOrJson(data.outputs)} + + ), + extra: ( + + ), + }, + ]} + className={classes.collapseContainer} + bordered={false} + /> + ) : ( + Object.values(data.outputs).map((item) => + Array.isArray(item) + ? item.map((param, index) => + !!param.content && + !Array.isArray(param.tool_calls) ? ( + + {getStringOrJson( + param.content, + )} + + ), + extra: ( + + ), + }, + ]} + className={classes.collapseContainer} + bordered={false} + /> + ) : ( + "TO_DO" + ), + ) + : null, + ) + )} - )} + ) : null} - {data && data.outputs && ( + {data && data?.internals && ( - Outputs - - {getStringOrJson(data.outputs)} - - ), - extra: ( - - ), - }, - ]} - className={classes.collapseContainer} - bordered={false} - /> + {node.type !== "chat" && ( + + {getStringOrJson(data.internals)} + + ), + extra: ( + + ), + }, + ]} + className={classes.collapseContainer} + bordered={false} + /> + )} )} From 873f86a74e981427c789cf5945b0a64076ab8906 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sat, 19 Oct 2024 17:46:27 +0200 Subject: [PATCH 098/305] feat(frontend): added AvatarTreeContent and populated cost and usage data in trace tree --- .../components/AvatarTreeContent.tsx | 108 ++++++++++++++++++ .../pages/observability/drawer/TraceTree.tsx | 32 +++--- .../src/services/observability/types/index.ts | 2 +- 3 files changed, 125 insertions(+), 17 deletions(-) create mode 100644 agenta-web/src/components/pages/observability/components/AvatarTreeContent.tsx diff --git a/agenta-web/src/components/pages/observability/components/AvatarTreeContent.tsx b/agenta-web/src/components/pages/observability/components/AvatarTreeContent.tsx new file mode 100644 index 0000000000..40594e205e --- /dev/null +++ b/agenta-web/src/components/pages/observability/components/AvatarTreeContent.tsx @@ -0,0 +1,108 @@ +import {_AgentaRootsResponse, NodeStatusDTO, NodeType} from "@/services/observability/types" +import { + ArrowsCounterClockwise, + ArrowsDownUp, + Download, + GearFine, + LineSegments, + ListDashes, + Sparkle, + StarFour, + TreeStructure, + Wrench, +} from "@phosphor-icons/react" +import {Avatar} from "antd" +import React from "react" + +type AvatarTreeContentProps = { + value: _AgentaRootsResponse +} & React.ComponentProps + +const nodeTypeStyles = { + [NodeType.AGENT]: { + bgColor: "#F6F6FD", + color: "#7D7DDB", + icon: TreeStructure, + }, + [NodeType.WORKFLOW]: { + bgColor: "#FFF8F5", + color: "#F6875A", + icon: LineSegments, + }, + [NodeType.CHAIN]: { + bgColor: "#F5F1FE", + color: "#9D79E8", + icon: ListDashes, + }, + [NodeType.TASK]: { + bgColor: "#F0FAF0", + color: "#228B22", + icon: GearFine, + }, + [NodeType.TOOL]: { + bgColor: "#FEF5FB", + color: "#E175BD", + icon: Wrench, + }, + [NodeType.EMBEDDING]: { + bgColor: "#F0FDFD", + color: "#008080", + icon: ArrowsCounterClockwise, + }, + [NodeType.COMPLETION]: { + bgColor: "#EDF8FD", + color: "#63ADCB", + icon: StarFour, + }, + [NodeType.QUERY]: { + bgColor: "#FCFAEE", + color: "#D6B507", + icon: Download, + }, + [NodeType.CHAT]: { + bgColor: "#EAFDEA", + color: "#36D16A", + icon: Sparkle, + }, + [NodeType.RERANK]: { + bgColor: "#F9F9FC", + color: "#8C92A3", + icon: ArrowsDownUp, + }, + default: { + bgColor: "#F9F9FC", + color: "#8C92A3", + icon: TreeStructure, + }, +} + +export const statusMapper = (node: NodeType | null | undefined, status: NodeStatusDTO) => { + const {code} = status + const {bgColor, color, icon: Icon} = nodeTypeStyles[node ?? "default"] + return { + bgColor, + color, + icon: , + } +} + +const AvatarTreeContent = ({value, ...props}: AvatarTreeContentProps) => { + const {node, status} = value + const {icon, bgColor, color} = statusMapper(node.type, status) + + return ( + + ) +} + +export default AvatarTreeContent diff --git a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx index d2b07a8dfd..f389aa7871 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx @@ -1,10 +1,11 @@ -import {formatLatency} from "@/lib/helpers/formatters" +import {formatCurrency, formatLatency, formatTokenUsage} from "@/lib/helpers/formatters" import {JSSTheme} from "@/lib/Types" import {_AgentaRootsResponse} from "@/services/observability/types" -import {Coins, PlusCircle, Timer, TreeStructure} from "@phosphor-icons/react" -import {Avatar, Space, Tree, Typography} from "antd" +import {Coins, PlusCircle, Timer} from "@phosphor-icons/react" +import {Space, Tree, Typography} from "antd" import React from "react" import {createUseStyles} from "react-jss" +import AvatarTreeContent from "../components/AvatarTreeContent" interface TraceTreeProps { activeTrace: _AgentaRootsResponse @@ -59,32 +60,31 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ })) const TreeContent = ({value}: {value: _AgentaRootsResponse}) => { - const {node, time} = value + const {node, time, metrics} = value const classes = useStyles() return (
- } - /> +
{node.name}
- {formatLatency(time?.span / 1000000)} + {formatLatency(time.span / 1000000)}
+
- $0.002 -
-
- - 72 + {formatCurrency(metrics?.acc?.costs?.total)}
+ + {!!metrics?.acc?.tokens?.total && ( +
+ + {formatTokenUsage(metrics?.acc?.tokens?.total)} +
+ )}
diff --git a/agenta-web/src/services/observability/types/index.ts b/agenta-web/src/services/observability/types/index.ts index 64615a85ac..9f3a244d0c 100644 --- a/agenta-web/src/services/observability/types/index.ts +++ b/agenta-web/src/services/observability/types/index.ts @@ -73,7 +73,7 @@ interface NodeContextDTO { name: string } -enum NodeType { +export enum NodeType { AGENT = "agent", WORKFLOW = "workflow", CHAIN = "chain", From dd0d37da1d7b930eb1ce101d0ce5a67bf5e5d95f Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sat, 19 Oct 2024 22:26:32 +0200 Subject: [PATCH 099/305] feat(frontend): added icon to observability status renderer --- .../components/StatusRenderer.tsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/agenta-web/src/components/pages/observability/components/StatusRenderer.tsx b/agenta-web/src/components/pages/observability/components/StatusRenderer.tsx index d7e484ff1c..ceec7684e8 100644 --- a/agenta-web/src/components/pages/observability/components/StatusRenderer.tsx +++ b/agenta-web/src/components/pages/observability/components/StatusRenderer.tsx @@ -1,5 +1,10 @@ import {_AgentaRootsResponse, NodeStatusCode, NodeStatusDTO} from "@/services/observability/types" -import {InfoCircleOutlined} from "@ant-design/icons" +import { + CheckCircleOutlined, + ClockCircleOutlined, + CloseCircleOutlined, + InfoCircleOutlined, +} from "@ant-design/icons" import {Space, Tag, Tooltip} from "antd" import React from "react" @@ -8,28 +13,33 @@ export const statusMapper = (status: NodeStatusCode) => { case NodeStatusCode.UNSET: return { label: "initiated", - color: "processing", + color: "blue", + icon: , } case NodeStatusCode.ERROR: return { label: "failed", color: "error", + icon: , } default: return { label: "success", color: "success", + icon: , } } } const StatusRenderer = (status: NodeStatusDTO) => { - const {label, color} = statusMapper(status.code) + const {label, color, icon} = statusMapper(status.code) const errorMsg = status.code === NodeStatusCode.ERROR ? status.message : null return ( - {label} + + {label} + {errorMsg && ( From 7990516f9afa79b35d45ab9911034b25819e3330 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sat, 19 Oct 2024 22:34:23 +0200 Subject: [PATCH 100/305] fix(frontend): conditionally removed open in playground button for child nodes, added node type tag and status tag --- .../observability/drawer/TraceContent.tsx | 52 ++++++++++++------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx index c521ae43ed..aba853aa41 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx @@ -1,7 +1,7 @@ import CopyButton from "@/components/CopyButton/CopyButton" import ResultTag from "@/components/ResultTag/ResultTag" import {JSSTheme} from "@/lib/Types" -import {ArrowRight, Database, PlusCircle, Rocket, Sparkle, Timer} from "@phosphor-icons/react" +import {ArrowRight, Database, PlusCircle, Rocket, Timer} from "@phosphor-icons/react" import {Button, Collapse, Divider, Space, Tabs, TabsProps, Typography} from "antd" import React, {useState} from "react" import {createUseStyles} from "react-jss" @@ -9,6 +9,9 @@ import {IBM_Plex_Mono} from "next/font/google" import {_AgentaRootsResponse} from "@/services/observability/types" import dayjs from "dayjs" import {getStringOrJson} from "@/lib/helpers/utils" +import {statusMapper} from "../components/AvatarTreeContent" +import {formatCurrency, formatLatency, formatTokenUsage} from "@/lib/helpers/formatters" +import StatusRenderer from "../components/StatusRenderer" const ibm_plex_mono = IBM_Plex_Mono({ subsets: ["latin"], @@ -88,9 +91,10 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ })) const TraceContent = ({activeTrace}: TraceContentProps) => { - const {node, time, meta, data} = activeTrace + const {node, time, meta, data, status, metrics, parent} = activeTrace const classes = useStyles() const [tab, setTab] = useState("overview") + const {icon, bgColor, color} = statusMapper(node.type, status) const items: TabsProps["items"] = [ { @@ -307,10 +311,12 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { {node.name} - + {!parent && ( + + )}
({ + ...col, + hidden: editColumns.includes(col.key as string), + }))} + dataSource={traces} bordered style={{cursor: "pointer"}} onRow={(record) => ({ From a1dbd3f3eafbe326a246a16fe70e02350cefdf2f Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Tue, 22 Oct 2024 23:16:39 +0200 Subject: [PATCH 137/305] openai example observability --- .../integrations/langchain/requirements.txt | 7 +++ .../integrations/langchain/simple_chain.py | 48 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt create mode 100644 agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain.py diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt b/agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt new file mode 100644 index 0000000000..ab803b39b9 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt @@ -0,0 +1,7 @@ +agenta==0.27.0a0 +# opentelemetry.instrumentation.openai==0.31.2 +openai +opentelemetry-instrumentation-langchain +langchain +langchain_community +langchain_openai \ No newline at end of file diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain.py b/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain.py new file mode 100644 index 0000000000..a22155b168 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain.py @@ -0,0 +1,48 @@ +from langchain.schema import SystemMessage, HumanMessage +from langchain.prompts import ChatPromptTemplate, HumanMessagePromptTemplate +from langchain_community.chat_models import ChatOpenAI +from langchain.chains import LLMChain, SequentialChain, TransformChain +from opentelemetry.instrumentation.langchain import LangchainInstrumentor + +import agenta as ag + +ag.init(project_id="0192813f-60d5-7a65-8a75-6dda36b79267", + host="https://cloud.beta.agenta.ai", + api_key="NYWy4We0.17ce0e85db4840a39ca9ee7b00e8817b22b60d7e152407a5a4cc98c5284f2e0e", + app_id="0192b552-ad65-7c61-a8dc-fedf3608b7a5") + +LangchainInstrumentor().instrument() + + +def langchain_app(): + chat = ChatOpenAI(temperature=0) + + transform = TransformChain( + input_variables=["subject"], + output_variables=["prompt"], + transform=lambda subject: {"prompt": f"Tell me a joke about {subject}."}, + ) + + first_prompt_messages = [ + SystemMessage(content="You are a funny sarcastic nerd."), + HumanMessage(content="{prompt}"), + ] + first_prompt_template = ChatPromptTemplate.from_messages(first_prompt_messages) + first_chain = LLMChain(llm=chat, prompt=first_prompt_template, output_key="joke") + + second_prompt_messages = [ + SystemMessage(content="You are an Elf."), + HumanMessagePromptTemplate.from_template( + "Translate the joke below into Sindarin language:\n {joke}" + ), + ] + second_prompt_template = ChatPromptTemplate.from_messages(second_prompt_messages) + second_chain = LLMChain(llm=chat, prompt=second_prompt_template) + + workflow = SequentialChain( + chains=[transform, first_chain, second_chain], input_variables=["subject"] + ) + print(workflow({"subject": "OpenTelemetry"})) + + +langchain_app() \ No newline at end of file From ea03ae1d02459afa433c75d8c0dc790904fe7825 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Wed, 23 Oct 2024 10:01:16 +0200 Subject: [PATCH 138/305] feat(frontend): enabled feature to resizable observability table width --- .../apps/[app_id]/observability/index.tsx | 81 +++++++++++++------ 1 file changed, 58 insertions(+), 23 deletions(-) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index 4567412b0f..c8f427f1f9 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -5,13 +5,14 @@ import TraceContent from "@/components/pages/observability/drawer/TraceContent" import TraceHeader from "@/components/pages/observability/drawer/TraceHeader" import TraceTree from "@/components/pages/observability/drawer/TraceTree" import ResultTag from "@/components/ResultTag/ResultTag" +import {ResizableTitle} from "@/components/ServerTable/components" import {useQueryParam} from "@/hooks/useQuery" import {formatCurrency, formatLatency, formatTokenUsage} from "@/lib/helpers/formatters" import {getNodeById} from "@/lib/helpers/observability_helpers" import {useTraces} from "@/lib/hooks/useTraces" import {JSSTheme} from "@/lib/Types" import {_AgentaRootsResponse} from "@/services/observability/types" -import {Space, Table, Typography} from "antd" +import {Space, Table, TableColumnType, Typography} from "antd" import {ColumnsType} from "antd/es/table" import dayjs from "dayjs" import React, {useEffect, useMemo, useState} from "react" @@ -31,30 +32,12 @@ const ObservabilityDashboard = ({}: Props) => { const classes = useStyles() const [selectedTraceId, setSelectedTraceId] = useQueryParam("trace", "") const {traces} = useTraces() - - const activeTraceIndex = useMemo( - () => traces?.findIndex((item) => item.root.id === selectedTraceId), - [selectedTraceId, traces], - ) - - const activeTrace = useMemo(() => traces[activeTraceIndex] ?? null, [activeTraceIndex, traces]) - - const [selected, setSelected] = useState(activeTrace?.key) - - const selectedItem = useMemo( - () => (traces?.length ? getNodeById(traces, selected) : null), - [selected, traces], - ) - - useEffect(() => { - setSelected(activeTrace?.key) - }, [activeTrace]) - - const columns: ColumnsType<_AgentaRootsResponse> = [ + const [columns, setColumns] = useState>([ { title: "ID", dataIndex: ["key"], key: "key", + width: 200, onHeaderCell: () => ({ style: {minWidth: 200}, }), @@ -78,6 +61,7 @@ const ObservabilityDashboard = ({}: Props) => { title: "Timestamp", key: "timestamp", dataIndex: ["time", "start"], + width: 200, onHeaderCell: () => ({ style: {minWidth: 200}, }), @@ -88,6 +72,7 @@ const ObservabilityDashboard = ({}: Props) => { { title: "Inputs", key: "inputs", + width: 350, onHeaderCell: () => ({ style: {minWidth: 350}, }), @@ -95,6 +80,7 @@ const ObservabilityDashboard = ({}: Props) => { { title: "Outputs", key: "outputs", + width: 350, onHeaderCell: () => ({ style: {minWidth: 350}, }), @@ -103,6 +89,7 @@ const ObservabilityDashboard = ({}: Props) => { title: "Status", key: "status", dataIndex: ["status", "code"], + width: 160, onHeaderCell: () => ({ style: {minWidth: 160}, }), @@ -112,6 +99,7 @@ const ObservabilityDashboard = ({}: Props) => { title: "Latency", key: "latency", dataIndex: ["time", "span"], + width: 80, onHeaderCell: () => ({ style: {minWidth: 80}, }), @@ -121,6 +109,7 @@ const ObservabilityDashboard = ({}: Props) => { title: "Usage", key: "usage", dataIndex: ["metrics", "acc", "tokens", "total"], + width: 80, onHeaderCell: () => ({ style: {minWidth: 80}, }), @@ -132,12 +121,53 @@ const ObservabilityDashboard = ({}: Props) => { title: "Total cost", key: "total_cost", dataIndex: ["metrics", "acc", "costs", "total"], + width: 80, onHeaderCell: () => ({ style: {minWidth: 80}, }), render: (_, record) =>
{formatCurrency(record.metrics?.acc?.costs?.total)}
, }, - ] + ]) + + const activeTraceIndex = useMemo( + () => traces?.findIndex((item) => item.root.id === selectedTraceId), + [selectedTraceId, traces], + ) + + const activeTrace = useMemo(() => traces[activeTraceIndex] ?? null, [activeTraceIndex, traces]) + + const [selected, setSelected] = useState(activeTrace?.key) + + const selectedItem = useMemo( + () => (traces?.length ? getNodeById(traces, selected) : null), + [selected, traces], + ) + + useEffect(() => { + setSelected(activeTrace?.key) + }, [activeTrace]) + + const handleResize = + (key: string) => + (_: any, {size}: {size: {width: number}}) => { + setColumns((cols) => { + return cols.map((col) => ({ + ...col, + width: col.key === key ? size.width : col.width, + })) + }) + } + + const mergedColumns = useMemo(() => { + return columns.map((col) => ({ + ...col, + width: col.width || 200, + onHeaderCell: (column: TableColumnType<_AgentaRootsResponse>) => ({ + width: column.width, + onResize: handleResize(column.key?.toString()!), + }), + })) + }, [columns]) return (
@@ -145,7 +175,7 @@ const ObservabilityDashboard = ({}: Props) => { {traces?.length ? (
[]} dataSource={traces} bordered style={{cursor: "pointer"}} @@ -154,6 +184,11 @@ const ObservabilityDashboard = ({}: Props) => { setSelectedTraceId(record.root.id) }, })} + components={{ + header: { + cell: ResizableTitle, + }, + }} pagination={false} scroll={{x: "max-content"}} /> From 5eb672c28e691f834cdde8e7fc1f107288fdb6c8 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 23 Oct 2024 10:07:11 +0200 Subject: [PATCH 139/305] clean up filtering dto --- .../apis/fastapi/observability/router.py | 4 +-- .../apis/fastapi/observability/utils.py | 21 ++++++---------- .../agenta_backend/core/observability/dtos.py | 25 +------------------ 3 files changed, 11 insertions(+), 39 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py index c26101d1ea..35fc7f9733 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Union, Optional, Callable +from typing import Dict, List, Union, Optional, Callable, Literal from fastapi import ( APIRouter, @@ -112,7 +112,7 @@ async def query_traces( self, project_id: str, query_dto: QueryDTO = Depends(parse_query_dto), - format: str = Query("agenta"), + format: Literal["opentelemetry", "agenta"] = Query("agenta"), ): """ Query traces, with optional grouping, filtering, (sorting,) and pagination. diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py index 3dfa7eb225..aeeda7938b 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py @@ -217,8 +217,11 @@ def _parse_pagination( def parse_query_dto( - # SCOPING - project_id: str, + # GROUPING + # - Option 1: Single query parameter as JSON + grouping: Optional[str] = Query(None), + # - Option 2: Flat query parameters + focus: Optional[str] = Query(None), # WINDOWING # - Option 1: Single query parameter as JSON windowing: Optional[str] = Query(None), @@ -235,11 +238,6 @@ def parse_query_dto( operator: Optional[str] = Query(None), exact_match: Optional[bool] = Query(False), case_sensitive: Optional[bool] = Query(False), - # GROUPING - # - Option 1: Single query parameter as JSON - grouping: Optional[str] = Query(None), - # - Option 2: Flat query parameters - focus: Optional[str] = Query(None), # PAGINATION # - Option 1: Single query parameter as JSON pagination: Optional[str] = Query(None), @@ -248,8 +246,9 @@ def parse_query_dto( size: Optional[int] = Query(None), ) -> QueryDTO: return QueryDTO( - scoping=_parse_scoping( - project_id=project_id, + grouping=_parse_grouping( + grouping=grouping, + focus=focus, ), windowing=_parse_windowing( windowing=windowing, @@ -265,10 +264,6 @@ def parse_query_dto( exact_match=exact_match, case_sensitive=case_sensitive, ), - grouping=_parse_grouping( - grouping=grouping, - focus=focus, - ), pagination=_parse_pagination( pagination=pagination, page=page, diff --git a/agenta-backend/agenta_backend/core/observability/dtos.py b/agenta-backend/agenta_backend/core/observability/dtos.py index 0d5d037ca0..3de8755d16 100644 --- a/agenta-backend/agenta_backend/core/observability/dtos.py +++ b/agenta-backend/agenta_backend/core/observability/dtos.py @@ -322,19 +322,6 @@ class Config: arbitrary_types_allowed = True -class DirectionType(Enum): - ASCENDING = "ascending" - DESCENDING = "descending" - - -class SortingDTO(DisplayBase): - field: str - - key: Optional[str] = None - - direction: DirectionType = "descending" - - class Focus(Enum): ROOT = "root" # SCENARIO TREE = "tree" # TRACE @@ -346,23 +333,13 @@ class GroupingDTO(DisplayBase): # SET TO ROOT ? TO TREE ? TO NODE ? -class Standard(Enum): - OPENTELEMETRY = "opentelemetry" - AGENTA = "agenta" - - class PaginationDTO(DisplayBase): page: int size: int -class FormattingDTO(DisplayBase): - standard: Standard = "agenta" - - class QueryDTO(DisplayBase): + grouping: Optional[GroupingDTO] = None windowing: Optional[WindowingDTO] = None filtering: Optional[FilteringDTO] = None - sorting: Optional[SortingDTO] = None - grouping: Optional[GroupingDTO] = None pagination: Optional[PaginationDTO] = None From 4e100f07ce897a2205b0d542dd942432c4a893f9 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Wed, 23 Oct 2024 10:18:16 +0200 Subject: [PATCH 140/305] fix(frontend): added conditional in StatusRenderer to show tooltip --- .../pages/observability/components/StatusRenderer.tsx | 10 ++++++++-- .../pages/observability/drawer/TraceContent.tsx | 2 +- .../src/pages/apps/[app_id]/observability/index.tsx | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/agenta-web/src/components/pages/observability/components/StatusRenderer.tsx b/agenta-web/src/components/pages/observability/components/StatusRenderer.tsx index ceec7684e8..c4b0038a85 100644 --- a/agenta-web/src/components/pages/observability/components/StatusRenderer.tsx +++ b/agenta-web/src/components/pages/observability/components/StatusRenderer.tsx @@ -31,7 +31,13 @@ export const statusMapper = (status: NodeStatusCode) => { } } -const StatusRenderer = (status: NodeStatusDTO) => { +const StatusRenderer = ({ + status, + showMore = false, +}: { + status: NodeStatusDTO + showMore?: boolean +}) => { const {label, color, icon} = statusMapper(status.code) const errorMsg = status.code === NodeStatusCode.ERROR ? status.message : null @@ -40,7 +46,7 @@ const StatusRenderer = (status: NodeStatusDTO) => { {label} - {errorMsg && ( + {showMore && errorMsg && ( diff --git a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx index 6647822856..c9b6cdc029 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx @@ -237,7 +237,7 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { } /> - + diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index c8f427f1f9..2aca13c173 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -93,7 +93,7 @@ const ObservabilityDashboard = ({}: Props) => { onHeaderCell: () => ({ style: {minWidth: 160}, }), - render: (_, record) => StatusRenderer(record.status), + render: (_, record) => StatusRenderer({status: record.status, showMore: true}), }, { title: "Latency", From d2f64a4b2323789be29768bc51fd9d00e208430a Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 23 Oct 2024 10:40:44 +0200 Subject: [PATCH 141/305] fix suppress in SDK, add log.error() in SDK, apply black --- agenta-cli/agenta/sdk/decorators/routing.py | 26 +++++++++------- agenta-cli/agenta/sdk/utils/exceptions.py | 11 ++++--- .../sanity_check/app_errors.py | 30 +++++++++++++++++-- .../sanity_check/app_new_sdk.py | 29 ++++++++++++++++-- .../sanity_check/app_old_sdk.py | 30 +++++++++++++++++-- .../sanity_check/sanity_sdk.py | 3 +- 6 files changed, 107 insertions(+), 22 deletions(-) diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index 33a2ee8ee5..8cd5e4f8a4 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -1,8 +1,3 @@ -"""The code for the Agenta SDK""" - -import os -import sys -import time import json import inspect import argparse @@ -12,7 +7,6 @@ from pathlib import Path from tempfile import NamedTemporaryFile from typing import Any, Callable, Dict, Optional, Tuple, List -from importlib.metadata import version from fastapi.middleware.cors import CORSMiddleware from fastapi import Body, FastAPI, UploadFile, HTTPException @@ -44,8 +38,8 @@ from annotated_types import Ge, Le, Gt, Lt from pydantic import BaseModel, HttpUrl -import contextvars -from contextlib import contextmanager + +from traceback import format_exc app = FastAPI() @@ -394,9 +388,11 @@ async def execute_function( ) data = self.patch_result(result) except Exception as e: - log.error(f"Agenta SDK - Routing Exception") - - traceback.print_exc() + log.error("Agenta SDK - handling application exception below:") + log.error("--------------------------------------------------") + log.error(format_exc().strip("\n")) + log.error("--------------------------------------------------") + log.error("\n") self.handle_exception(e) @@ -426,6 +422,10 @@ async def execute_function( response = BaseResponse(data=data, trace=trace) + log.info(f"Agenta SDK - exiting successfully: 200") + log.info(f"----------------------------------") + log.info("\n") + return response def handle_exception(self, e: Exception): @@ -434,6 +434,10 @@ def handle_exception(self, e: Exception): stacktrace = traceback.format_exception(e, value=e, tb=e.__traceback__) # type: ignore detail = {"message": message, "stacktrace": stacktrace} + log.error(f"Agenta SDK - exiting with HTTPException: {status_code}") + log.error(f"----------------------------------------") + log.error("\n") + raise HTTPException( status_code=status_code, detail=detail, diff --git a/agenta-cli/agenta/sdk/utils/exceptions.py b/agenta-cli/agenta/sdk/utils/exceptions.py index 9eea8f1f21..d5a6a54d12 100644 --- a/agenta-cli/agenta/sdk/utils/exceptions.py +++ b/agenta-cli/agenta/sdk/utils/exceptions.py @@ -12,8 +12,11 @@ def __enter__(self): def __exit__(self, exc_type, exc_value, exc_tb): if exc_type is None: - return + return True else: - print("Agenta SDK - Tracing Exception") - log.error(f"{exc_type.__name__}: {exc_value}\n{format_exc()}") - return + log.error("Agenta SDK - suppressing tracing exception below:") + log.error("-------------------------------------------------") + log.error(format_exc().strip("\n")) + log.error("-------------------------------------------------") + log.error("\n") + return True diff --git a/agenta-cli/tests/observability_sdk/sanity_check/app_errors.py b/agenta-cli/tests/observability_sdk/sanity_check/app_errors.py index dca4f3b5df..54328d4b1b 100644 --- a/agenta-cli/tests/observability_sdk/sanity_check/app_errors.py +++ b/agenta-cli/tests/observability_sdk/sanity_check/app_errors.py @@ -13,15 +13,18 @@ prompt_system=ag.TextParam("MY_SYSTEM_PROMPT"), ) + # Pydantic models class InputData(BaseModel): text: str value: int + class OutputData(BaseModel): result: str count: int + # Function with ignored outputs @ag.instrument( spankind="EMBEDDING", @@ -35,6 +38,7 @@ def ignore_some_outputs_embedding(description: str): "cost": 15, } + # Function with all outputs ignored @ag.instrument(spankind="AGENT", ignore_outputs=True) def ignore_all_outputs_agent(query: str): @@ -44,36 +48,42 @@ def ignore_all_outputs_agent(query: str): "confidence": 0.9, } + # Function with ignored inputs @ag.instrument(spankind="IGNORE_INPUTS", ignore_inputs=["secret"]) def function_with_ignored_inputs(public_data: str, secret: str): print("function with ignored inputs") return f"Public: {public_data}, Secret: {secret}" + # Function with all inputs ignored @ag.instrument(spankind="IGNORE_ALL_INPUTS", ignore_inputs=True) def function_with_all_ignored_inputs(data: str): print("function with all ignored inputs") return f"Data: {data}" + # Function using dict inputs/outputs @ag.instrument(spankind="DICT_IO") def dict_function(input_dict: dict) -> dict: print("dict function") return {"output_data": input_dict.get("key", None)} + # Function using Pydantic models @ag.instrument(spankind="PYDANTIC") def pydantic_function(input_data: InputData) -> OutputData: print("pydantic function") return OutputData(result=input_data.text.upper(), count=input_data.value + 1) + # Function with None output @ag.instrument(spankind="NONE_OUTPUT") def none_output_function(): print("none output function") return None + # Nested function calls @ag.instrument(spankind="NESTED") def nested_function(value: int): @@ -81,67 +91,81 @@ def nested_function(value: int): inner_result = inner_function(value) return f"Nested result: {inner_result}" + @ag.instrument(spankind="INNER") def inner_function(value: int): print("inner function") return value * 2 + # Function called multiple times @ag.instrument(spankind="MULTIPLE_CALLS") def multiple_calls_function(counter: int): print(f"multiple calls function call {counter}") return f"Call number: {counter}" + # Existing functions @ag.instrument(spankind="CHAIN") def chain_function(input_data: str): print("chain") return f"Processed: {input_data}" + @ag.instrument(spankind="TASK") def task_function(task: str): print("task") return f"Completed task: {task}" + @ag.instrument(spankind="TOOL") def tool_function(tool_input: str): print("tool") return f"Tool output: {tool_input}" + @ag.instrument(spankind="QUERY") def query_function(query: str): print("query") return f"Query result: {query}" + @ag.instrument(spankind="COMPLETION") def completion_function(prompt: str): print("completion") return f"Completed: {prompt}" + @ag.instrument(spankind="CHAT") async def chat_function(message: str): print("chat") return f"Chat response: {message}" + @ag.instrument(spankind="RERANK") def rerank_function(documents: list): print("rerank") return sorted(documents, reverse=True) + @ag.instrument(spankind="WRONGKIND") def wrong_kind_function(input_data: str): print("wrong kind") return f"Processed with wrong kind: {input_data}" + @ag.instrument(spankind="GENERATOR", ignore_inputs=True) async def summarizer(topic: str, genre: str, report: dict) -> dict: print("summarizer") return {"report": "mya"} + @ag.instrument(spankind="CHAT") async def exception_func(): raise Exception("This is an exception") return "dummy" + + @ag.entrypoint @ag.instrument(spankind="WORKFLOW") async def errors(topic: str, genre: str, count: int = 5): @@ -165,7 +189,9 @@ async def errors(topic: str, genre: str, count: int = 5): pydantic_result = pydantic_function(pydantic_input) none_output_result = none_output_function() nested_result = nested_function(5) - multiple_calls_results = [multiple_calls_function(i) for i in range(3)] # Called multiple times + multiple_calls_results = [ + multiple_calls_function(i) for i in range(3) + ] # Called multiple times exception_result = await exception_func() return f"""Results: Embedding: {result} @@ -186,4 +212,4 @@ async def errors(topic: str, genre: str, count: int = 5): None Output Function: {none_output_result} Nested Function: {nested_result} Multiple Calls Function: {multiple_calls_results} -app_old_sdk""" \ No newline at end of file +app_old_sdk""" diff --git a/agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk.py b/agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk.py index dab02f8ca6..046f62e630 100644 --- a/agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk.py +++ b/agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk.py @@ -5,6 +5,7 @@ ag.init() + class MyConfig(BaseModel): temperature: float = Field(default=0.2, le=1, ge=0) model: Annotated[str, ag.MultipleChoice(choices=supported_llm_models)] = Field( @@ -22,10 +23,12 @@ class InputData(BaseModel): text: str value: int + class OutputData(BaseModel): result: str count: int + # Function with ignored outputs @ag.instrument( spankind="EMBEDDING", @@ -39,6 +42,7 @@ def ignore_some_outputs_embedding(description: str): "cost": 15, } + # Function with all outputs ignored @ag.instrument(spankind="AGENT", ignore_outputs=True) def ignore_all_outputs_agent(query: str): @@ -48,36 +52,42 @@ def ignore_all_outputs_agent(query: str): "confidence": 0.9, } + # Function with ignored inputs @ag.instrument(spankind="IGNORE_INPUTS", ignore_inputs=["secret"]) def function_with_ignored_inputs(public_data: str, secret: str): print("function with ignored inputs") return f"Public: {public_data}, Secret: {secret}" + # Function with all inputs ignored @ag.instrument(spankind="IGNORE_ALL_INPUTS", ignore_inputs=True) def function_with_all_ignored_inputs(data: str): print("function with all ignored inputs") return f"Data: {data}" + # Function using dict inputs/outputs @ag.instrument(spankind="DICT_IO") def dict_function(input_dict: dict) -> dict: print("dict function") return {"output_data": input_dict.get("key", None)} + # Function using Pydantic models @ag.instrument(spankind="PYDANTIC") def pydantic_function(input_data: InputData) -> OutputData: print("pydantic function") return OutputData(result=input_data.text.upper(), count=input_data.value + 1) + # Function with None output @ag.instrument(spankind="NONE_OUTPUT") def none_output_function(): print("none output function") return None + # Nested function calls @ag.instrument(spankind="NESTED") def nested_function(value: int): @@ -85,68 +95,81 @@ def nested_function(value: int): inner_result = inner_function(value) return f"Nested result: {inner_result}" + @ag.instrument(spankind="INNER") def inner_function(value: int): print("inner function") return value * 2 + # Function called multiple times @ag.instrument(spankind="MULTIPLE_CALLS") def multiple_calls_function(counter: int): print(f"multiple calls function call {counter}") return f"Call number: {counter}" + # Existing functions @ag.instrument(spankind="CHAIN") def chain_function(input_data: str): print("chain") return f"Processed: {input_data}" + @ag.instrument(spankind="TASK") def task_function(task: str): print("task") return f"Completed task: {task}" + @ag.instrument(spankind="TOOL") def tool_function(tool_input: str): print("tool") return f"Tool output: {tool_input}" + @ag.instrument(spankind="QUERY") def query_function(query: str): print("query") return f"Query result: {query}" + @ag.instrument(spankind="COMPLETION") def completion_function(prompt: str): print("completion") return f"Completed: {prompt}" + @ag.instrument(spankind="CHAT") async def chat_function(message: str): print("chat") return f"Chat response: {message}" + @ag.instrument(spankind="RERANK") def rerank_function(documents: list): print("rerank") return sorted(documents, reverse=True) + @ag.instrument(spankind="WRONGKIND") def wrong_kind_function(input_data: str): print("wrong kind") return f"Processed with wrong kind: {input_data}" + @ag.instrument(spankind="GENERATOR", ignore_inputs=True) async def summarizer(topic: str, genre: str, report: dict) -> dict: print("summarizer") return {"report": "mya"} + @ag.instrument(spankind="CHAT") async def exception_func(): raise Exception("This is an exception") return "dummy" + @ag.route("/", config_schema=MyConfig) @ag.instrument(spankind="WORKFLOW") async def newsdk(topic: str, genre: str, count: int = 5): @@ -170,7 +193,9 @@ async def newsdk(topic: str, genre: str, count: int = 5): pydantic_result = pydantic_function(pydantic_input) none_output_result = none_output_function() nested_result = nested_function(5) - multiple_calls_results = [multiple_calls_function(i) for i in range(3)] # Called multiple times + multiple_calls_results = [ + multiple_calls_function(i) for i in range(3) + ] # Called multiple times # exception_result = await exception_func() return f"""Results: Embedding: {result} @@ -191,4 +216,4 @@ async def newsdk(topic: str, genre: str, count: int = 5): None Output Function: {none_output_result} Nested Function: {nested_result} Multiple Calls Function: {multiple_calls_results} -app_old_sdk""" \ No newline at end of file +app_old_sdk""" diff --git a/agenta-cli/tests/observability_sdk/sanity_check/app_old_sdk.py b/agenta-cli/tests/observability_sdk/sanity_check/app_old_sdk.py index e3fb00f206..3ae0ec4e3c 100644 --- a/agenta-cli/tests/observability_sdk/sanity_check/app_old_sdk.py +++ b/agenta-cli/tests/observability_sdk/sanity_check/app_old_sdk.py @@ -13,15 +13,18 @@ prompt_system=ag.TextParam("MY_SYSTEM_PROMPT"), ) + # Pydantic models class InputData(BaseModel): text: str value: int + class OutputData(BaseModel): result: str count: int + # Function with ignored outputs @ag.instrument( spankind="EMBEDDING", @@ -35,6 +38,7 @@ def ignore_some_outputs_embedding(description: str): "cost": 15, } + # Function with all outputs ignored @ag.instrument(spankind="AGENT", ignore_outputs=True) def ignore_all_outputs_agent(query: str): @@ -44,36 +48,42 @@ def ignore_all_outputs_agent(query: str): "confidence": 0.9, } + # Function with ignored inputs @ag.instrument(spankind="IGNORE_INPUTS", ignore_inputs=["secret"]) def function_with_ignored_inputs(public_data: str, secret: str): print("function with ignored inputs") return f"Public: {public_data}, Secret: {secret}" + # Function with all inputs ignored @ag.instrument(spankind="IGNORE_ALL_INPUTS", ignore_inputs=True) def function_with_all_ignored_inputs(data: str): print("function with all ignored inputs") return f"Data: {data}" + # Function using dict inputs/outputs @ag.instrument(spankind="DICT_IO") def dict_function(input_dict: dict) -> dict: print("dict function") return {"output_data": input_dict.get("key", None)} + # Function using Pydantic models @ag.instrument(spankind="PYDANTIC") def pydantic_function(input_data: InputData) -> OutputData: print("pydantic function") return OutputData(result=input_data.text.upper(), count=input_data.value + 1) + # Function with None output @ag.instrument(spankind="NONE_OUTPUT") def none_output_function(): print("none output function") return None + # Nested function calls @ag.instrument(spankind="NESTED") def nested_function(value: int): @@ -81,67 +91,81 @@ def nested_function(value: int): inner_result = inner_function(value) return f"Nested result: {inner_result}" + @ag.instrument(spankind="INNER") def inner_function(value: int): print("inner function") return value * 2 + # Function called multiple times @ag.instrument(spankind="MULTIPLE_CALLS") def multiple_calls_function(counter: int): print(f"multiple calls function call {counter}") return f"Call number: {counter}" + # Existing functions @ag.instrument(spankind="CHAIN") def chain_function(input_data: str): print("chain") return f"Processed: {input_data}" + @ag.instrument(spankind="TASK") def task_function(task: str): print("task") return f"Completed task: {task}" + @ag.instrument(spankind="TOOL") def tool_function(tool_input: str): print("tool") return f"Tool output: {tool_input}" + @ag.instrument(spankind="QUERY") def query_function(query: str): print("query") return f"Query result: {query}" + @ag.instrument(spankind="COMPLETION") def completion_function(prompt: str): print("completion") return f"Completed: {prompt}" + @ag.instrument(spankind="CHAT") async def chat_function(message: str): print("chat") return f"Chat response: {message}" + @ag.instrument(spankind="RERANK") def rerank_function(documents: list): print("rerank") return sorted(documents, reverse=True) + @ag.instrument(spankind="WRONGKIND") def wrong_kind_function(input_data: str): print("wrong kind") return f"Processed with wrong kind: {input_data}" + @ag.instrument(spankind="GENERATOR", ignore_inputs=True) async def summarizer(topic: str, genre: str, report: dict) -> dict: print("summarizer") return {"report": "mya"} + @ag.instrument(spankind="CHAT") async def exception_func(): raise Exception("This is an exception") return "dummy" + + @ag.entrypoint @ag.instrument(spankind="WORKFLOW") async def rag(topic: str, genre: str, count: int = 5): @@ -165,7 +189,9 @@ async def rag(topic: str, genre: str, count: int = 5): pydantic_result = pydantic_function(pydantic_input) none_output_result = none_output_function() nested_result = nested_function(5) - multiple_calls_results = [multiple_calls_function(i) for i in range(3)] # Called multiple times + multiple_calls_results = [ + multiple_calls_function(i) for i in range(3) + ] # Called multiple times # exception_result = await exception_func() return f"""Results: Embedding: {result} @@ -186,4 +212,4 @@ async def rag(topic: str, genre: str, count: int = 5): None Output Function: {none_output_result} Nested Function: {nested_result} Multiple Calls Function: {multiple_calls_results} -app_old_sdk""" \ No newline at end of file +app_old_sdk""" diff --git a/agenta-cli/tests/observability_sdk/sanity_check/sanity_sdk.py b/agenta-cli/tests/observability_sdk/sanity_check/sanity_sdk.py index e121b0582d..ae1f4d273d 100644 --- a/agenta-cli/tests/observability_sdk/sanity_check/sanity_sdk.py +++ b/agenta-cli/tests/observability_sdk/sanity_check/sanity_sdk.py @@ -12,6 +12,7 @@ prompt_system=ag.TextParam("MY_SYSTEM_PROMPT"), ) + @ag.entrypoint @ag.instrument( spankind="EMBEDDING", @@ -24,4 +25,4 @@ def embed(description: str): "ignored": "ignored", "cost": 15, "usage": 20, - } \ No newline at end of file + } From 32ef1f57c5227ede4a972ba57ee2d2df30be4279 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 23 Oct 2024 10:45:55 +0200 Subject: [PATCH 142/305] allow usage as (int, float) --- agenta-cli/agenta/sdk/decorators/tracing.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 1b639f4ca1..268c287529 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -147,6 +147,9 @@ async def wrapped_func(*args, **kwargs): cost = result.get("cost", None) usage = result.get("usage", {}) + if isinstance(usage, (int, float)): + usage = {"total_tokens": usage} + span.set_attributes( attributes={"total": cost}, namespace="metrics.unit.costs", @@ -240,6 +243,9 @@ def wrapped_func(*args, **kwargs): cost = result.get("cost", None) usage = result.get("usage", {}) + if isinstance(usage, (int, float)): + usage = {"total_tokens": usage} + span.set_attributes( attributes={"total": cost}, namespace="metrics.unit.costs", From 54dab926b01c24da64400fd1baf61cdfdc8bfab9 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Wed, 23 Oct 2024 14:54:10 +0600 Subject: [PATCH 143/305] enhance(frontend): added search query filter --- .../src/components/Filters/EditColumns.tsx | 8 +-- agenta-web/src/lib/helpers/utils.ts | 2 +- .../apps/[app_id]/observability/index.tsx | 58 +++++++++++-------- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/agenta-web/src/components/Filters/EditColumns.tsx b/agenta-web/src/components/Filters/EditColumns.tsx index 507f7a3c3e..2e4eb894e6 100644 --- a/agenta-web/src/components/Filters/EditColumns.tsx +++ b/agenta-web/src/components/Filters/EditColumns.tsx @@ -1,5 +1,5 @@ -import {Button, Dropdown, Space, Checkbox} from "antd" import React, {useState} from "react" +import {Button, Dropdown, Space, Checkbox} from "antd" import {createUseStyles} from "react-jss" import {Columns} from "@phosphor-icons/react" import {ColumnsType} from "antd/es/table" @@ -30,7 +30,7 @@ const useStyles = createUseStyles((theme) => ({ interface EditColumnsProps { isOpen: boolean handleOpenChange: (open: boolean) => void - selectedKeys: string[] // Default selected column keys + selectedKeys: string[] columns: ColumnsType onChange: (key: string) => void excludes?: string[] // Array of column keys to exclude @@ -51,7 +51,7 @@ const EditColumns: React.FC = ({ const handleDropdownChange = (newOpen: boolean) => { setOpen(newOpen) - if (!newOpen) handleOpenChange(newOpen) // Only trigger close action if dropdown is closing + if (!newOpen) handleOpenChange(newOpen) } const generateEditItems = (): MenuProps["items"] => { @@ -78,7 +78,7 @@ const EditColumns: React.FC = ({ e.stopPropagation()}> onChange(child.key as string)} /> {child.title as string} diff --git a/agenta-web/src/lib/helpers/utils.ts b/agenta-web/src/lib/helpers/utils.ts index d89dee399a..01fac1df35 100644 --- a/agenta-web/src/lib/helpers/utils.ts +++ b/agenta-web/src/lib/helpers/utils.ts @@ -1,6 +1,6 @@ import {v4 as uuidv4} from "uuid" import {EvaluationType} from "../enums" -import {FilterConditions, GenericObject} from "../Types" +import {GenericObject} from "../Types" import promiseRetry from "promise-retry" import {getErrorMessage} from "./errorHandler" import dayjs from "dayjs" diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index a7b0ff9ba5..368ee38e9c 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -14,7 +14,7 @@ import {getNodeById} from "@/lib/helpers/observability_helpers" import {useTraces} from "@/lib/hooks/useTraces" import {Filter, JSSTheme, SortTypes} from "@/lib/Types" import {_AgentaRootsResponse} from "@/services/observability/types" -import {Button, Radio, RadioChangeEvent, Space, Table, Typography} from "antd" +import {Button, Input, Radio, RadioChangeEvent, Space, Table, Typography} from "antd" import {ColumnsType} from "antd/es/table" import dayjs from "dayjs" import React, {useEffect, useMemo, useState} from "react" @@ -36,6 +36,7 @@ interface Props {} const ObservabilityDashboard = ({}: Props) => { const classes = useStyles() const [selectedTraceId, setSelectedTraceId] = useQueryParam("trace", "") + const [searchQuery, setSearchQuery] = useState("") const [traceTabs, setTraceTabs] = useState("root calls") const [editColumns, setEditColumns] = useState([]) const [isExportLoading, setIsExportLoading] = useState(false) @@ -215,6 +216,8 @@ const ObservabilityDashboard = ({}: Props) => { const onSortApply = (sortData: SortTypes) => {} + const onSearchQueryAppy = async () => {} + const handleToggleColumnVisibility = (key: string) => { setEditColumns((prev) => prev.includes(key) ? prev.filter((item) => item !== key) : [...prev, key], @@ -227,39 +230,48 @@ const ObservabilityDashboard = ({}: Props) => { {traces?.length ? (
-
+
+ setSearchQuery(e.target.value)} + onPressEnter={onSearchQueryAppy} + className="w-[320px]" + allowClear + /> - - + + +
Root calls Generation All runs - - - - - + + + + +
Date: Wed, 23 Oct 2024 11:05:03 +0200 Subject: [PATCH 144/305] fix null check --- .../agenta_backend/core/observability/utils.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/agenta-backend/agenta_backend/core/observability/utils.py b/agenta-backend/agenta_backend/core/observability/utils.py index 52e2f2e038..4b0e2eeedd 100644 --- a/agenta-backend/agenta_backend/core/observability/utils.py +++ b/agenta-backend/agenta_backend/core/observability/utils.py @@ -190,7 +190,7 @@ def _connect_tree_dfs( parent_span.nodes = None -PAYING_TYPES = [ +TYPES_WITH_COSTS = [ "embedding", "query", "completion", @@ -201,7 +201,12 @@ def _connect_tree_dfs( def calculate_costs(span_idx: Dict[str, SpanCreateDTO]): for span in span_idx.values(): - if span.node.type.name.lower() in PAYING_TYPES and span.meta and span.metrics: + if ( + span.node.type + and span.node.type.name.lower() in TYPES_WITH_COSTS + and span.meta + and span.metrics + ): try: costs = cost_calculator.cost_per_token( model=span.meta.get("response.model"), From 50c0e27be4960b5f06b5edbed6af912d79ea0f27 Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Wed, 23 Oct 2024 11:13:30 +0200 Subject: [PATCH 145/305] many tests --- .../langchain/simple_chain_openinference.py | 48 +++++ ..._chain.py => simple_chain_openllmetery.py} | 0 .../integrations/openai/app.py | 40 ++++ .../integrations/openai/app_image.py | 68 ++++++ .../integrations/openai/app_no_workflow.py | 39 ++++ .../integrations/openai/requirements.txt | 3 + .../sanity_check/app_local.py | 195 ++++++++++++++++++ .../sanity_check/app_new_sdk_noinstrument.py | 170 +++++++++++++++ .../sanity_check/app_sync.py | 41 ++++ 9 files changed, 604 insertions(+) create mode 100644 agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py rename agenta-cli/tests/observability_sdk/integrations/langchain/{simple_chain.py => simple_chain_openllmetery.py} (100%) create mode 100644 agenta-cli/tests/observability_sdk/integrations/openai/app.py create mode 100644 agenta-cli/tests/observability_sdk/integrations/openai/app_image.py create mode 100644 agenta-cli/tests/observability_sdk/integrations/openai/app_no_workflow.py create mode 100644 agenta-cli/tests/observability_sdk/integrations/openai/requirements.txt create mode 100644 agenta-cli/tests/observability_sdk/sanity_check/app_local.py create mode 100644 agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk_noinstrument.py create mode 100644 agenta-cli/tests/observability_sdk/sanity_check/app_sync.py diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py b/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py new file mode 100644 index 0000000000..1ff318551d --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py @@ -0,0 +1,48 @@ +from langchain.schema import SystemMessage, HumanMessage +from langchain.prompts import ChatPromptTemplate, HumanMessagePromptTemplate +from langchain_community.chat_models import ChatOpenAI +from langchain.chains import LLMChain, SequentialChain, TransformChain +from openinference.instrumentation.langchain import LangChainInstrumentor + +import agenta as ag + +ag.init(project_id="0192813f-60d5-7a65-8a75-6dda36b79267", + host="http://localhost/", + api_key="NYWy4We0.17ce0e85db4840a39ca9ee7b00e8817b22b60d7e152407a5a4cc98c5284f2e0e", + app_id="0192b552-ad65-7c61-a8dc-fedf3608b7a5") + +LangChainInstrumentor().instrument() + + +def langchain_app(): + chat = ChatOpenAI(temperature=0) + + transform = TransformChain( + input_variables=["subject"], + output_variables=["prompt"], + transform=lambda subject: {"prompt": f"Tell me a joke about {subject}."}, + ) + + first_prompt_messages = [ + SystemMessage(content="You are a funny sarcastic nerd."), + HumanMessage(content="{prompt}"), + ] + first_prompt_template = ChatPromptTemplate.from_messages(first_prompt_messages) + first_chain = LLMChain(llm=chat, prompt=first_prompt_template, output_key="joke") + + second_prompt_messages = [ + SystemMessage(content="You are an Elf."), + HumanMessagePromptTemplate.from_template( + "Translate the joke below into Sindarin language:\n {joke}" + ), + ] + second_prompt_template = ChatPromptTemplate.from_messages(second_prompt_messages) + second_chain = LLMChain(llm=chat, prompt=second_prompt_template) + + workflow = SequentialChain( + chains=[transform, first_chain, second_chain], input_variables=["subject"] + ) + print(workflow({"subject": "OpenTelemetry"})) + + +langchain_app() \ No newline at end of file diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain.py b/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openllmetery.py similarity index 100% rename from agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain.py rename to agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openllmetery.py diff --git a/agenta-cli/tests/observability_sdk/integrations/openai/app.py b/agenta-cli/tests/observability_sdk/integrations/openai/app.py new file mode 100644 index 0000000000..db540f1af8 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/openai/app.py @@ -0,0 +1,40 @@ +import agenta as ag +from pydantic import BaseModel, Field +from agenta.sdk.assets import supported_llm_models +from typing import Annotated +from opentelemetry.instrumentation.openai import OpenAIInstrumentor +from openai import OpenAI + +ag.init() + + +openai = OpenAI() + + +OpenAIInstrumentor().instrument() + +class MyConfig(BaseModel): + temperature: float = Field(default=0.2, le=1, ge=0) + model: Annotated[str, ag.MultipleChoice(choices=supported_llm_models)] = Field( + default="gpt-3.5-turbo" + ) + max_tokens: int = Field(default=-1, ge=-1, le=4000) + prompt_system: str = Field(default="system prompt") + multiselect: Annotated[str, ag.MultipleChoice(choices=["a", "b", "c"])] = Field( + default="a" + ) + + + +@ag.entrypoint +@ag.instrument(spankind="WORKFLOW") +async def rag(topic: str, genre: str): + response = openai.chat.completions.create( + model="gpt-3.5-turbo", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": f"Write a short {genre} story about {topic}."} + ], + ) + + return response.choices[0].message.content diff --git a/agenta-cli/tests/observability_sdk/integrations/openai/app_image.py b/agenta-cli/tests/observability_sdk/integrations/openai/app_image.py new file mode 100644 index 0000000000..ea735574ce --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/openai/app_image.py @@ -0,0 +1,68 @@ +import agenta as ag +from pydantic import BaseModel, Field +from agenta.sdk.assets import supported_llm_models +from typing import Annotated +from opentelemetry.instrumentation.openai import OpenAIInstrumentor +from openai import OpenAI + +ag.init() + + +openai = OpenAI() + + +OpenAIInstrumentor().instrument() + +class MyConfig(BaseModel): + temperature: float = Field(default=0.2, le=1, ge=0) + model: Annotated[str, ag.MultipleChoice(choices=supported_llm_models)] = Field( + default="gpt-3.5-turbo" + ) + max_tokens: int = Field(default=-1, ge=-1, le=4000) + prompt_system: str = Field(default="system prompt") + multiselect: Annotated[str, ag.MultipleChoice(choices=["a", "b", "c"])] = Field( + default="a" + ) + +base64="data:image/jpeg;base64,/9j/4QC8RXhpZgAASUkqAAgAAAAGABIBAwABAAAAAQAAABoBBQABAAAAVgAAABsBBQABAAAAXgAAACgBAwABAAAAAgAAABMCAwABAAAAAQAAAGmHBAABAAAAZgAAAAAAAABgAAAAAQAAAGAAAAABAAAABgAAkAcABAAAADAyMTABkQcABAAAAAECAwAAoAcABAAAADAxMDABoAMAAQAAAP//AAACoAQAAQAAALAEAAADoAQAAQAAALAEAAAAAAAA/+EOLmh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8APD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc+CjxyZGY6UkRGIHhtbG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyc+CgogPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9JycKICB4bWxuczpBdHRyaWI9J2h0dHA6Ly9ucy5hdHRyaWJ1dGlvbi5jb20vYWRzLzEuMC8nPgogIDxBdHRyaWI6QWRzPgogICA8cmRmOlNlcT4KICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0nUmVzb3VyY2UnPgogICAgIDxBdHRyaWI6Q3JlYXRlZD4yMDIyLTA5LTE0PC9BdHRyaWI6Q3JlYXRlZD4KICAgICA8QXR0cmliOkV4dElkPmU3ZTM3MmU5LTAwZjgtNGJiYi1iNDNiLTFjMzlkMTllMTllMDwvQXR0cmliOkV4dElkPgogICAgIDxBdHRyaWI6RmJJZD41MjUyNjU5MTQxNzk1ODA8L0F0dHJpYjpGYklkPgogICAgIDxBdHRyaWI6VG91Y2hUeXBlPjI8L0F0dHJpYjpUb3VjaFR5cGU+CiAgICA8L3JkZjpsaT4KICAgPC9yZGY6U2VxPgogIDwvQXR0cmliOkFkcz4KIDwvcmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PScnCiAgeG1sbnM6ZGM9J2h0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvJz4KICA8ZGM6dGl0bGU+CiAgIDxyZGY6QWx0PgogICAgPHJkZjpsaSB4bWw6bGFuZz0neC1kZWZhdWx0Jz5SRU1PVkUtQkctQiZhbXA7Vy1XLUlMTFVTVFJBVElPTi1hbnRsZXItdGVhbS1wcm9maWxlLWltYWdlcyAoMTIwMCDDlyAxMjAwcHgpPC9yZGY6bGk+CiAgIDwvcmRmOkFsdD4KICA8L2RjOnRpdGxlPgogPC9yZGY6RGVzY3JpcHRpb24+CgogPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9JycKICB4bWxuczpwZGY9J2h0dHA6Ly9ucy5hZG9iZS5jb20vcGRmLzEuMy8nPgogIDxwZGY6QXV0aG9yPkphY2sgTWNEb25hbGQ8L3BkZjpBdXRob3I+CiA8L3JkZjpEZXNjcmlwdGlvbj4KCiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0nJwogIHhtbG5zOnhtcD0naHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyc+CiAgPHhtcDpDcmVhdG9yVG9vbD5DYW52YTwveG1wOkNyZWF0b3JUb29sPgogPC9yZGY6RGVzY3JpcHRpb24+CjwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9J3cnPz7/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCASwBLADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwCvRRRXjH6QFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAPhiaaZY16scCvSNJ8B2Fxp8UlyHErD5sGuW8I6X9v1NXIOIiDXsaIEQKBgCuzD0k1dnzucY6dOSp03Y5L/hXmkf9NP8Avqj/AIV5pH/TT/vquvorp9lDseJ9fxP87OQ/4V5pH/TT/vqj/hXmkf8ATT/vquvoo9lDsH1/E/zs5D/hXmkf9NP++qP+FeaR/wBNP++q6+ij2UOwfX8T/OzkP+FeaR/00/76o/4V5pH/AE0/76rr6KPZQ7B9fxP87OQ/4V5pH/TT/vqj/hXmkf8ATT/vquvoo9lDsH1/E/zs5D/hXmkf9NP++qP+FeaR/wBNP++q6+ij2UOwfX8T/OzkP+FeaR/00/76rjfFXh1NGuMwA+Tjv617DXPeLdLXUNKcY+Zfm/Ks6tGLjojrwWY1o1l7SV0eM0U6RSkjKRjBxTa84+xTurhRRRQMKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAClVSzBR1NJWt4esGvtVhUDKqwLVUVd2M6tRU4OT6HovgnSvsmmpcMuGkXmusqG2gW3gWJBhQOlTV6sI8sbHwOIrOtUc31CiiiqMQooooAKKKKACiiigAooooAKKKKACmTIJImQ9xin0UAnY8X8V6YdP1V1C4Q81gV6r470n7Vp/nRLmQEEn2rysjBIPUV5leHLI+3yzEe2oJ9UJRRRWJ6IUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFek+ANK8qI3jrxIvFcFplqbu/hiCkgsAa9t0qySwsI4EGAorqw0LvmPCzrE8lP2a3ZeooorvPlAooooAKKKKACiiigAooooAKKKKACiiigAooooAgu4RPbSIRnKkV4jrlg2nanJCR717rXnnxA0nIF6i5YkA4rnxEOaNz2MmxHs63I9med0UUV5x9gFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFS20RnuI4h1ZsUITdldna+AtI82ZrqRcrjK16aOlY3hvThp2kwxEYYDmtmvUpQ5Y2PhcfiHWruXQKKKK1OIKKKKACiiigAooooAKKKKACiiigAooooAKKKKACs/WLJL2wkRhnCnH1rQpGG4EHpSaurFQk4SUkeBX1q9pdvE4wQTVau18eaT5F2bxVwrYFcVXlVI8srH3uErqtSU0FFFFQdIUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXUeC9J+36nvdfljwwJrmY0MkioOpOBXr/g7TBZaTE7LiRhzW9CHNI8zNcT7Gg0t2dKo2qBS0UV6R8UFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAYfijTl1HSnQjOwFhXi8sbRSMjDBB6V9BSIJI2Q9CMV494x0w2WrSOq4jOMVx4qH2j6LI8TZukzm6KKK4j6YKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiigAk4HWgDb8Mac2oasgxkIQxr2iGNYo1VRgAVxvgPSRBZreMuHcYNdtXpYeHLG58Zm+J9rW5Vsgooorc8oKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK5XxppIvtO3ovzqdxNdVUN1CJ7d4z/ECKmceZWNsPVdKoprofP7DaxB7Ula/iPTjpurSQgfL1BrIrypKzsffUqiqQUl1CiiipNAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAq9pFm95qMUaKWwwz9Ko16D8PtK+Zr1xlWGBmtKUOaVjjx1dUKLkd7YWqWlokSDAAqzR7UV6iVj4SUnJ3YUUUUxBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBwfj/SvNt1uYky+fmI9K80r3vU7VbuxljIySpArxDVLJrC/kt26rXBiYWd0fV5Jieen7N7op0UUVynuhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAE9nA1xdRxqM5YA17boWnrp2mxwqMd6878DaSbq/M0i/uwMg+9erKNqgeld+GhZcx8rneJ5pqkugtFFFdR4IUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFACEZFea+PtJ8qUXiL984Nel1keItPW/0uVWGSqkis6seaNjtwGI9hXUuh4fRUk8LW8zROMMOtR15T0Puk01dBRRRQMKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACnxIZJUQdWYCmV0HhLS/7S1ZVYHamG/KqjHmdjGvVVKm5voekeFNM/s/So1ZcP1rfpkaBI1UdhT69WKsrHwNao6k3N9QoooqjMKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAprqHQqRkGnUUAeR+NtKNnqT3IXEchwK5SvY/F+lDUNMZscxgtXjrKVOD1rzcRDlkfaZTifbUEnuhKKKKwPUCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigBVBZgo6k4r1fwNpX2XT1mkTEhzXneg6cdR1KOMfwkN+te2WsKwW6IoxgCuvDQ15j5/O8TaKpLqT0UUV3Hy4UUUUAFFFFABRRRQAUUUUAFFFFABWZreprpenS3HGVGQDWnXmfj/Vi862sbZXBDVnVnyRudmBw/t6yh0O+0vUE1G0WVDnI5q9Xnfw+1QgPau2SzcZr0SinPmjcnG4f2FZwCiiitDlCiiigAooooAKKKKACiiigAooooAjniE0Lxt0YYrxjxTpxsNYmAXEZPy17XXF+O9J+1WizovKZY1hiIc0bnq5TifZV+V7M8sooorzT7MKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKOpoqzY2z3V3HGgySwz9M00ruxM5KMW2d98P9J2xfbnHJyvNegVQ0iySxsI4kGOATV+vVpx5Y2Pg8ZXdas5hRRRVnKFFFFABRRRQAUUUUAFFFFABRRRQBV1C7Wys5J2PCjNeIatdtealPKTkFyRXonjzVvs9n9lU/61a8t6muHEzu+U+qyTDcsHVfU0tC1A6dqkU+eB1Fe22VwLm0jlB+8oNeAA4Oa9X8DasbywaOQ/Mh2gUYadnyk53huaKqrodhRRRXcfLhRRRQAUUUUAFFFFABRRRQAUUUUAFVr+2F3ZSwkfeGKs0UPUcZOLujwnWrE6fqk0GOFPFZ1eheP9JI2XUS8k5Y157XlVYcsrH3eBrqtRUgooorM7AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACu38A6WZLv7Wy5TBHNcZBGZp0QAnLAV7V4b0xdN0xIwOvNdGHhzSuePnGJ9lR5FuzYAwMUtFFeifHhRRRQAUUUUAFFFFABRRRQAUUUUAFRzSrDEzscACpK5vxhqQstJkQNiR1+WplLlVzWhSdWooLqeb+JtSa/1WUE5VGIWsSnSOZJGdupOTTa8qT5nc++o01TgooK6DwlqRsdXiDNiM9a5+nIxRwynBFEJcruFekqtNwfU+gonEkSuOhGafWH4X1NdR0xCDygC1uV60XdXPgKtN05uD6BRRRTMwooooAKKKKACiiigAooooAKKKKAM3XLNbzS54yMsV4rxG8tmtLp4WGCpxXv5AIwa8q8daT9lvRcqOJW5rlxMLrmPeyTE8s3SfU46iiiuA+qCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoopUXc6qO5xQDdjpfBmmG91VHdcxj+devooRAo6AYrmPBelfYdMBdcOxzXU16dCHLE+JzTEe2ru2yCiiitjzQooooAKKKKACiiigAooooAKKKKAEJwCTXk3jjVhe34gRj+6JBr0bX74WGlzS5wwXIrxO8nN1dyznq5zXJiZ2XKe/kmG5puq+hBRRRXCfUhRRRQB2fgTVvs94LNmwHJNepqQQCO9eA2Vy9pdpMhwwNe36PeJeafE6nJ2jNd+GndWPlM7w3JNVF1NCiiiuo8IKKKKACiiigAooooAKKKKACiiigArB8VaYl/pcjFcuikrW9TJEEkbK3Q0pK6saUajpzU10Pn2SNopCjDDDqKbXReL9Max1eWTGEkbK1zteTKPK7H31CqqtNTXUKKKKk2CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACtnw1pn9p6skRB2j5s1jAZOK9O8A6V5NqbmRcPnjPpWtGHNI4MxxHsaDfU7WCIQwIgGNqgVJRRXqHw7d3cKKKKBBRRRQAUUUUAFFFFABRRRQAUUVWvrlbW0kmY4CjNDdhxi5OyOC8f6vlktom45DivPq0NavDeapcSlsqXJFZ9eVVnzSufd4GgqFFRCiiiszsCiiigAr0f4f6tmJrSRvmJyK84rU0C/OnarHMDjtWtKfLK5xY/DqvQcep7nRUFpMJ7ZJAQcqDU9eoj4VqzswooooEFFFFABRRRQAUUUUAFFFFABRRRQByPjfSVvLA3AXLRAnivJiCDgjBr6Au4Bc2zxMOGGK8T1+xay1WZCuF3ccVw4mFnzH0+R4m8XSfQy6KKK5D6EKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAu6VZPf38cKDnOa9v062W1s441GMKM1wPgDSC8n25xjaSOa9JAxXoYaFo3Pks6xPtKvs1sgooorpPECiiigAooooAKKKKACiiigAooooAK43x1qgtbIW6tzKCDiuvmkWKJnY4AFeNeK9Sa+1WVM5RG+WsK8+WJ6mU4b2tdN7IwKKKK80+0CiiigAooooAKVTtYH0NJRQG56z4H1T7Zp2x2+dTgCuurxzwfqf2HVkV2xGa9hjcOisO4zXpUJ80T4rNcP7Gu2tmOooorc8wKKKKACiiigAooooAKKKKACiiigArgfH+lB4UuY1+7ksa76qGr2S3+nSwH+IYqKkeaNjqwdd0aymeD0Va1G1NlfzQEcI2BVWvKas7H3kJKUVJBRRRSKCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAqW3heedY0BJJ6Coq63wPpRutSW5YZjTINXCPNKxz4qsqNJzZ6NoVgthp0aKuMgE/WtSkVQqgDoKWvVSsrHwVSbnJyfUKKKKZAUUUUAFFFFABRRRQAUUUUAFFFITgE0Ac/4t1JbHSZFzh3XC143I7SuXY5Y9a6/x3qv2q9Fsp4iauOrzsRPmlY+yyjD+yocz3YUUUVznrBRRRQAUUUUAFFFFAD4nMcqsDjBr2nwzqY1LTEcHO35fyrxOu38A6o0N2LMthDlq6MPPllY8jOMN7WjzLdHqFFIDkZFLXonxwUUUUAFFFFABRRRQAUUUUAFFFFABQelFFAHl/j3SfIuUuI14fJYiuIr27xJp4v9KmULl9vFeLXEJt7h4m6ocGvOxEOWVz7DJ8T7WjyvdEVFFFc57AUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAORC7hQMkmvZPCelJp2mLhcF8NXnHhPTTqGrxhh+7HU17JDGIoVQdFGK7cLD7R83nmJ2pIkooorsPmwooooAKKKKACiiigAooooAKKKKACs7Wr0WWmTSbsMFyK0a88+IGrEeXbRNzkhgKzqS5Y3OvBUHWrKJwd9dNeXkk7nLOcmq9FFeW3dn3cYqKSQUUUUigooooAKKKKACiiigAq1p13JZ3kcsbYOQM1Vozg5pp2dyZxUouLPetKu1vLGORTn5RmrtcF8P8AVd9ubN2y+Sfwrva9WnLmjc+DxlB0azgFFFFWcoUUUUAFFFFABRRRQAUUUUAFFFFADWUMpB715D4z0r7DqbSqvEpLV7BXL+M9LF5pckwXLxr8orGvDmielleI9jXV9meQUUrqUcqwwR1FJXmH2qdwooooGFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUdTRWloennUtUit8cNTSu7EVJqEXJ9D0TwLpX2WxaaRPmYgqTXY1XsrcW1pHEP4VAqxXqwjyxSPgcVWdaq5sKKKKswCiiigAooooAKKKKACiiigAooooAgvJ1trWSUnhRmvE9fvjf6tNKGyhbivRfG+r/YrARKcmXKmvJic1w4md3yn1GR4a0XVfUKKKK5D6AKKKKACiiigAooooAKKKKACiiigDX8OaidO1RJM8H5a9rtpRNAjqc5ANfPyttdWHY5r1zwXqv27TFVz84OMV2YWf2T53PMNdKqjqqKKK7T5kKKKKACiiigAooooAKKKKACiiigAqKeJZoWjYZBqWigadndHiXiXTX0/VJcjCuxK1i16f4+0vzrT7Wq/6sc15gRivLrQ5ZH3GXYj29BPqFFFFZHeFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAV6L4A0nCNdyJhgflJrgrG1a9u0gTq1e36PZrZ6fFGowdozXVhoXlc8TOsTyUvZrdmhRRRXefJBRRRQAUUUUAFFFFABRRRQAUUUUAFMlcRxs7dAKfWH4o1FbDSpcnDOpApSdlc0pU3UmoLqebeL9Ta91aSMHKI3Fc7T5JGlkLucse5pleTOXM7n31CkqVNQXQKKKKk2CiiigAooooAKKKKACiiigAooooAK6fwXqn2HVVWRv3bDGPeuYqSCQxTxuDjawP61UJcsrmGIpKrScH1PoFGDoGHcZp1Y/hzUBqOlpKDntWxXrRd1c+BqQdObi+gUUUUyAooooAKKKKACiiigAooooAKKKKAKmo2i3tlJA4yGFeI6taNZ6jNGRgBjiveeorzbx/pJWRbqNMKASxFc2JheNz2slxPs6vs3szgaKKK88+uCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoop8cbzOEQZY9BQJuyuzsfAmkG5vBdsuRG1epAADA6VieGNNWw0uPAwXUE1uV6lGHLE+GzHEe3rt9EFFFFanCFFFFABRRRQAUUUUAFFFFABRRRQAhIAya8u8ear9ouvsiN/q25r0PV7xLPT5ZGbBCnFeI6hdte3rzt1auXEzsrHu5Jhueo6j6FWiiiuA+rCiiigAooooAKKKKACiiigAooooAKKKKACiiigDu/AGreXcfY3bCYyK9LByMivBdMu2s76ORTj5hn6Zr2/TLtLyyjlQ5GK9DDTvGx8lnWG5KntFsy5RRRXSeIFFFFABRRRQAUUUUAFFFFABRRRQAVl69p66jpc0GOWGM1qUhGQRSaurF05uElJdDwC7gNtdywn+BiKgrrfHGlG01ASxp8j5LGuSrypx5ZWPvcLWVakpoKKKKg6AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArpvBmmm81ZJSuY0PNc0Bk4Fet+CdJFjp3msOZcNW9CHNI83NMR7Gg7bs6mNAiBQOBTqKK9I+J3CiiigAooooAKKKKACiiigAooooAKKKhuphBbSSn+EZoGld2Rw/j/VdkSW0bfNn5hXm1aviG/OoavNMGypPArKry6s+aVz7nL8P7Ggo9QooorI7gooooAKKKKACiiigAooooAKKKKACiiigAooooAM4Oa9O+H+qCWz+xs2XXJ5rzGtnw1qTadqiMDgOQta0Z8sjgzHD+3oNdT26io4ZBJErA5yKkr1D4dqzsFFFFAgooooAKKKKACiiigAooooAKKKKAOc8X6Z9u0mUqMyAcV49KhilZD1U4r6BkQSRlWGQRXjXizTTp2qkYwJCWFceKh9o+kyPE70WYFFFFcR9IFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAa/hzT21DVoUK5TPzV7VawC3tkiXooxXF+AtJ8m3a4kXlsFTXdV6OHhyxufHZvifa1uVbIKKKK6DyAooooAKKKKACiiigAooooAKKKKACuV8a6r9g07Yp5fK11EjiNGY9AK8g8ZambzVpIlbManisa8+WJ6WV4f21dX2RzROSSaSiivMPtkFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAU5GKSK46g5ptFAmrqx7B4N1MXulortmQda6evIvBGp/Y9U2O2EYYAr1xTlQfWvToT5onxOZ4f2Nd22YtFFFbHnBRRRQAUUUUAFFFFABRRRQAUUUUAFcb470kXOntdKMyIMCuyqve26XVs8TjIIqJx5o2N8NWdGqpo8BIKkg9RSVo61YvY6lKjDALEj6VnV5TVnY++pzU4qS6hRRRSLCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAq5pdmb7UIrcDO84qnXd+AdJ82Z7qRfuMCtaU480kjlxtdUaLkegaZaCz0+GEAAqoFXKBwKK9RKyPg5Scm2wooopkhRRRQAUUUUAFFFFABRRRQAUUUhOBmgDH8S6gLHSpCTgsCBXiksjSyM7Eknua7fx9q/nT/AGJT9wg8VwtediJ80rH2GT4f2dHme7Ciiiuc9gKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAmtZjBdRygkbWBr2zw/qI1HTI5s8mvDa77wBq5EzWchwirkV04adpWPGznDe0pc63R6TRQORRXoHyAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB574/0jcv21V4QYOK86r3fWLFb/T5IWHUV4hfW7W13LGwxhjiuDEwtK59bkuJ56Xs3uivRRRXKe2FFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAE1rbtc3CQoMsxwK9q8PaethpcK4w5X5q868EaWbzUhOR8sRzzXraqFUAdBXdhoWXMfL53ieaSpLoLRRRXWfPhRRRQAUUUUAFFFFABRRRQAUUUUAFU9TultbGWQnBCnFXK4Xx9qxhtltomw+eR7VFSXLG504Sg61ZQR57qV499fSTv1PFVKDyaK8pu7ufewioxUUFFFFIoKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKvaTdvZ6hFIpx8wz9Ko0A4OR1pp2dyJwU4uLPfdPulu7RJUOQRVquJ8Bar59iLRmyyDPNdtXqwlzRufBYqi6NVwYUUUVZzhRRRQAUUUUAFFFFABRRRQAUUUUAIRkEeteW+PNK8i+FxGuI8cn3r1OsPxPpg1LSpIwPm61lWhzRO7LsQ6FdPozxSipJ4zFO8ZH3WIqOvLPuU7q6CiiigYUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABSqpdgqjJNJW54X01r7VYmxlEb5qqK5nYyrVFTg5Poej+D9LWy0uOXbhpFBNdJUcESwwqijAAqSvVjHlVj4GvVdWo5vqFFFFUZBRRRQAUUUUAFFFFABRRRQAUUUUARXEohgeQn7oJrxbxNqJ1LWJJQTtxjFekeMtUGn6WcH5nO3ivH2bc5b1Oa4sVP7J9LkeG0dViUUUVxn0YUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAG54X1M6dqicnEhC17RFIssasDkEV8/ROY5VcdVOa9h8IamL3SIlY5kA5rtws/snzeeYbaqjo6KKK7D5sKKKKACiiigAooooAKKKKACiiigAprrvQr6inUUAeO+MdKOn6oSo+V8tmuar1vxvpn2vTGkRcyLXkrDaxU9QcV5leHLI+2yvEe2oK+6EooorE9IKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAxmvVPAmki1svtDLzKAa870aya91OGIDILYNe3WFsLOzjhA4RcV14WF3zHgZ3ieWCpLqWaKKK7j5YKKKKACiiigAooooAKKKKACiiigAprsEQsegGadWT4g1BdP0ySRjjcCBSbsrl04Oc1FdTzjxrqn2zVHiRsxriuVqW4maed5GOSSairypy5pXPvsNSVKkoIKKKKg3CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK67wNqn2TUTE7fK+ABXI1PZTm2vIpgfuNmrpy5ZXOfFUVWpODPfwcjNLWZoV+NR0yKfPLCtOvVTurnwU4OEnF9AooopkBRRRQAUUUUAFFFFABRRRQAUUUUARXEQmgdCM5BrxPxDprabqbxsPvEsPzr3GuE8faSJbc3qrl1wOK58RDmjc9fJ8T7KtyvZnmdFHQ0V5x9iFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRViytzdXkUAH32xTSuTKSim2d14A0n79zKnXBU16LWbodkLHS4YcYKrg1pV6lKPLGx8Jja7rVnIKKKK0OQKKKKACiiigAooooAKKKKACiiigAPFebfEDVRI/wBhByAQ1d/qFwttZyOzAYU4zXh+q3rX1/JK5yckVzYmdo2PayXDe0q+0eyKVFFFeefXBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB6F8P8AVclrSRvlUDaK9FrwjRbxrPUoXBwNwzXt9jcrd2iTIchhXoYad42PkM5w3s6vOtmWKKKK6TxgooooAKKKKACiiigAooooAKKKKACqepWiXlnJE4yCKuUEZGKTV0VGTi00eB6lZvZXskT9dxI/Oqld14+0kx3H2xF+XAFcLXl1I8srH3mDrqtRUwooorM6gooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArsfAmki7vjPInEeGU1yMMTTTLGvVjgV7L4U0wWGlRZXEhX5jXRh4c0rnk5vifZUeVbs3gMCloor0T40KKKKACiiigAooooAKKKKACiiigAoopk0gjiZyegzQCV9Di/H2qiGz+zI2JCQT9K8vJySfWt3xVqP9o6u8inKgbfyrCry60+aR9xluH9jQS6sKKKKyPQCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigABwcivVfAeq/adPW1J5iWvKq6DwlqZ0/VEQHAlYA1tRnyyPOzPD+2oNLdHs9FMjcSIGU5Bp9emfEPQKKKKACiiigAooooAKKKKACiiigAooooAyPEOnLqWmPER0Ga8UuImhuJI2BBDEc19AOoZCp7ivJPG+lmz1QyouIyB+dcmJhdcx9BkmJ5ZOk+pytFFFcJ9QFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUoBYgAZJoA6HwjpbX+ppJjKxMCa9jjQIgVRgCuT8DaWLXS0uCMPKvIrrq9KhDlifFZrifbV2lsgooorc8wKKKKACiiigAooooAKKKKACiiigArnPF+qf2fpbFT8x4xXRM21ST2FeTeOdUa61Iwo2YwOnvWNafLE9DLMP7aur7I5R23yM3qSabRRXmH26VlYKKKKBhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAU+KRopVkU4ZTkUyigTV1Y9n8J6iL7SIQzZkC/NXQV5T4F1U2t8YHbiTAUV6sORXqUZ80T4fMsP7Gu10YUUUVqcAUUUUAFFFFABRRRQAUUUUAFFFFABXM+MtK/tDSztHzKd2fpXTVHPGJYXQ9wRUyjzKxrQqulUU10Pn51KOynscU2tzxRph07VnRV+Q85rDrypR5XY++o1FUpqa6hRRRUmoUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFamg2D32pxKqkqrAt9Ky69J8AaT5cRvHXIkXitaUOaVjizDEKhRcup3FrbrbW6RIMBRU1FFeofCttu7CiiigQUUUUAFFFFABRRRQAUUUUAFFFHSgDM12+Ww02SVjjjFeI3U73FzJI7ZJY4rvPH+q5P2FWyCM157Xn4md5WPrsmw3s6XO92FFFFcx7QUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBZ0+5NpfQzg/cbNe3aNfLf6bFNnJZcmvCK9G8AatvV7WQ4CgBc11YadnY8TOsNz0vaLdHoNFFFd58kFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAcV490rz7D7RGmZARnHpXlpGCQa9+vrdbi0ljYZypA/KvENYsW0/UZIG6g5rhxMLPmPqckxPNB0n0KFFFFch74UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAXNMtWvNQhiAyGYA17dpVithYRwKOFFee+AtKMt01zIvyYypNen9BXfhoWjc+TzrE89RU1sgooorqPDCiiigAooooAKKKKACiiigAooooAKr3twltavI5wADViuN8d6p9nsDbI2HbmonLljc6MLRdaqoI861m/fUNQkkfsxA+maz6UnJJPc0leU3d3PvacFCKigooopFhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABWloV81jqkDhsLv+as2lBIORwRTi7O5FSCnBxfU9/s7hbq1jmXowzU9cl4H1UXmmiAtlohg11tetCXNG58DiaTpVXB9AoooqjAKKKKACiiigAooooAKKKKACiiigAPIrznx/pAX/TUX5iQDXo1Zut2C3+nSIw6KSKzqw5o2OvA13QrKR4VRU95bvbXTxOMEGoK8tqx93GSkroKKKKRQUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUtvEZ7iOIdWbFRV1PgrSvt2ol3X5Uwwq4R5pWMMTWVGk5s9F8N6cNP0iKIj5gOTWzTVUKoA7U6vVirKx8DVm6k3J9QooopkBRRRQAUUUUAFFFFABRRRQAUUUUAMkcJGzZ6CvGvFmqf2jqrFW+VMrivSPFep/2fpMjK3z9MV43K5kldz1Yk1x4qf2T6PI8NvVYyiiiuI+lCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA6PwhqZsdUSPdhZGwa9ijdZEDKcg18+wytDKsi/eU5Fez+FtRW+0qIbsuq/NXbhZ/ZPmc8w1mqqN2iiiuw+dCiiigAooooAKKKKACiiigAooooAKRgGUg9DS0UAeU+O9KNtqBu1XEb4H41x1e0eKtLXUdLYEcoC1eMyIUcqeoNediIcsrn2WUYn2tHle6G0UUVznrBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAORDI6oOpOBXr3gzShZaVHI64kYc15x4a05tR1VFUZ2EMa9qgiEUKoowAK7MLD7R85nmJslSRJRRRXafNBRRRQAUUUUAFFFFABRRRQAUUUUAFITgE0tZ+s3y6fp0k7HgCk3ZXKhFzkorqec+OtV+06gIYnzGBz9a46p7yc3F3JITnLEioK8qpLmlc++wlFUaSggoooqDoCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK7XwFqot7trZ2/1hAXNcVVrTrs2V9HODjYc1pTlyyuc2MoKtRcGe+jkUVR0i6F5psM2cllyavV6id1c+CnFxk4voFFFFMkKKKKACiiigAooooAKKKKACiiigBksYkiZD0IxXjni7TDY6vIVXERxivZq5LxvpQvNO8xB8yHcSKwrw5onp5VifY10nszyWig8GivNPtQooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKAMnAoq/o9k97qMUaLnDAn6U0ruxFSahFyZ6D4C0lYbNb1h87jFdvVWwtUtLRIkGAB0q1XqwjyxsfBYqs61VzYUUUVZzhRRRQAUUUUAFFFFABRRRQAUUUUAFefeP9X2L9hVvvjNd1dzrb2zyMcACvEtdv3v9SkdjkKxA+lc+Inyxsexk+G9pW53sjMooorzj7AKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAPSfAGqmWKS3kb7pAUV31eHeHr42OrQuWITd81e12s63FskqnIYZr0cPPmjY+OzjDeyrc62ZNRRRXQeQFFFFABRRRQAUUUUAFFFFABRRRQAVBeQC5tZIiM7lIqeih6jTs7o8M1/TjpuqSwYwB0rLr0jx/pJaJbqJcuT830rzevLqw5ZWPusBiPb0FLqFFFFZHaFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFd/4A0nMn21hwRgZrh7OBri6jjUZ3MBXtmhaeunadHCBjjNdOGheVzxc5xPs6XIt2alFFFegfIhRRRQAUUUUAFFFFABRRRQAUUUUAFFFNdgqEntQByXjrUhb6W9urYd+RivKCSxJPU10PjDUzqGqkBjiPK4rna8yvPmkfbZXh/Y0FfdhRRRWJ6QUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAKCQcg4NeueCtVF7poiJ5iAWvIq6bwZqj2eqxwZxHI3NbUJ8sjzM0w3tqDtuj2Gimo4dAw6GnV6Z8UFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAUdWtFu7CWMjJKnFeIajZtY3rwOMFa98IyK8w8faV5N19sVf9YccVy4mF1c9zJcTyVHTezOIooorgPrAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiinRoZJFQdWIFAm7K51vgXSTd6gZpFzGBwfevV1G1QB2rn/COm/2fpCKy4YnNdDXqUYcsT4jMsR7au30QUUUVqeeFFFFABRRRQAUUUUAFFFFABRRRQAVheKdSGn6TK4bD44rdPAry3x5qvn3iwRt8gBDfWsq0+WJ3Zdh/bV0uiOOnlM07yHqxzUdFFeWfcpWVgooooGFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUkE728yyp95elR0UITSasz27w3qK32lxENllUbvrWzXmPgDVPJuWtGb/WHjNenA5FepSnzRufC5hh/Y13HoFFFFanEFFFFABRRRQAUUUUAFFFFABRRRQAVi+JNOW/0yTK5ZVJFbVNdQ6FT0NKSurGlKo6c1JdD5+mheCUxuMMOtR10/jTS2s9VknC4jc8VzFeTOPLKx97h6qq01NBRRRUm4UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFdB4S0salqqq6navzVgKpZgo6k4r1fwPpP2TT1nkXEh71tQhzSPOzPE+xoO27OsiQRxqoHQYp9FFemfEN3CiiigAooooAKKKKACiiigAooooAKKKKAKOrXosNPknJxtFeIahcNdXssrHO5iRXoHj/VfLh+xq3+sXnFea1wYmd3Y+syXDclP2j3YUUUVynuBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAFzTL02F/HcAn5T2r2/TLlbqwhlByWUE14JXpvgLVxNbtbyN8wOFrqw07PlPCzvDc9NVFujuqKKK7z5QKKKKACiiigAooooAKKKKACiiigAooooA5jxnpgvtLL7eYgWrx8gg4Iwa+grmFbi3eJhkMMV4v4m082GrTKFwhPFcWKh9pH0uR4nR0mYtFFFcZ9GFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAGlodg2oanHEozggn869utIFt7dEUYAArhfh/pICfbmXDcjmvQa9HDwtG58fnGJ9pW5FsgoooroPHCiiigAooooAKKKKACiiigAooooAKiuZ1t4Gkc4AFS1yfjfVBaaW8CnEki8VM5cqubYek6tRQXU868Q6g9/qkpY5VWIWsmlZizFieT1pK8mTu7n31KCpwUV0CiiikaBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABWz4a1A6fq8UhbCZ5rGoHFVF2dzOtTVSDg+p9BW0wnt0kB4YZqWuZ8G6oL/SwpPMYC101erGXMrnwNek6VRwfQKKKKoxCiiigAooooAKKKKACiiigAooooAK4bx5pPn2y3Ea8qSWNdzVTUbUXdjLCQPmXFRUjzRsdOErujVU0eB0Ve1eyNhqU1uR9w4qjXlNWdj7yE1OKkuoUUUUiwooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACrNhavd3kcaDJ3DP51Wrt/AOledd/a3GUGRV0480rHNjK6o0XM9B0iySxsI40GOATV+kAwMDtS16qVlY+CnJyk5MKKKKZIUUUUAFFFFABRRRQAUUUUAFFFFACMwVST0ryDxpqov8AU/LXOIiVr0fxJqAsNJmkDYcLxXi1zMbi4klPVzmuTFT05T6HI8NeTqsiooorhPpwooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAOq8E6obTVEt2bEbnJr1tGDqGHQ18/28zQTLIhwRXtnh6/S+0yNlOSqgGu7DTuuU+XzvDcslVRrUUUV1nz4UUUUAFFFFABRRRQAUUUUAFFFFABRRRQB5r4/0ny5UuokyXOWIrgq9y1+xF7pcyYy23ivE7q3a1uXhf7ynBrz8TC0rn1+TYn2lLke6IaKKK5j2QooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAJIYzLMiKMkkCvafDOmLpulogAy3zfnXnHg3TDe6qrOmYwOvvXr8ahEVR0AxXdhoacx8xnmJu1SQ6iiius+eCiiigAooooAKKKKACiiigAooooAKDxRVPU7tbKxlmY42jNJuyuVGLlJRR574+1Uy3CW8bcDIYVw1XdUujeajPMTkM5IqlXl1Jc0rn3mDoKjRUQooorM6gooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACu9+H+qeXK1mzffORXBVe0i+bT9RjuF6jitKUuWVzkxtBVqLie89RRVTTrkXVnHIDnKgmrdeondHwcouLaYUUUUxBRRRQAUUUUAFFFFABRRRQAUUUUAIw3KQeleT+OdKFpf/aFXiVua9ZrnfF2lrf6VI+MtGuVrGtDmiehluI9jXTezPGqKdIjRuUYYI602vMPt07q4UUUUDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAClVdzhfU4pK2PDenf2jq0cZXK9c1UVd2M61RU4OT6Ho/gvSvsGlguvzsd2frXUVFbxCGBEAxgAVLXqwjyqx8DXqurUc31CiiiqMQooooAKKKKACiiigAooooAKKKKACuG8far5Fstqhz5gIOK7S4lWCFpGOABXiviS/a+1abLZRW+WufET5Y2PWyjDe1rcz2Rj0UUV5x9kFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFHQ5oooA9R8BaqZ7JoJXy4OBn0rtq8T8MaibDWInZsJ3r2iGQSwo4/iGa9LDz5o2PjM3w/sq/MtmSUUUVueUFFFFABRRRQAUUUUAFFFFABRRRQAUyaNZYmRhkEU+igE7anivirTWsNWlbGEdvlrCr1bxzpP2ux+0KvMQJrykjBwa8ytDlkfcZbiPbUE+qCiiisT0AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAAycV6b4C0jy7Y3Ugw+ePpXn+lWT39/HCgySc17fptqtpZRxquPlGa6sNC75jws7xPJT9kt2W6KKK7z5QKKKKACiiigAooooAKKKKACiiigAoopGYKpJPAoA5nxnqYstKeIHDyAhTXkDMXYsxyT1NdT421Q3momANkRNiuVrza8+aR9plWH9jQTe7CiiisD1AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAVW2sCOxr2Twjqo1HTBuPzJ8uK8arrfA+qm11FbZjhGya3w8+WR5WbYb2tBtbo9aopFYMoYdCKWvSPjAooooAKKKKACiiigAooooAKKKKACiiigCve263dpJCw4YYrxLXLFrDVZ4tuFDYWvda898f6TuCXUa/dBLEVz4iF43PZybE+zq8j2Z51RRRXnH14UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRU1tA9zcLGgySelNK4pNRV2dv8P8ASvMlN8w+6dtelAYFZPh/T00/TY0VcFgCfrWtXqUocsbHwmPxHt67l0CiiitDjCiiigAooooAKKKKACiiigAooooAKyfEF8LLSpn3YbbxWtXmvj7V/MlS1ibG0kMBWdWfLG52YCg69dROIup2ubl5mOSxzUNFFeW3c+7iklZBRRRSGFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABU1rO1vcJIhIINQ0U07ClFSVme56Ffrf6dG6noADWpXnPw/wBWwTYs2SSSK9Gr1KUuaNz4THUPYVnEKKKK0OMKKKKACiiigAooooAKKKKACiiigArO1qxF/pk0OOWXANaNIeRSaurFQm4SUl0PAr+2NnfSwHqjYqtXaeO9J+zXS3EacOSWNcXXlVI8srH3uErqtSU0FFFFQdIUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFdR4Lso5dTS4lPyrkYNcvUsV1PB/qpWT6GqhJRd2YYinKrTcIu1z3hbu3VQocYFO+2wf3xXhf9p33/P1J+dH9p33/P1J+ddf1ryPB/sF/wAx7p9tg/vij7bB/wA9BXhf9p33/P1J+dH9p33/AD9SfnR9a8g/sF/zHun22D++KPtsH/PQV4X/AGnff8/Un50f2nff8/Un50fWvIP7Bf8AMe6fbYP74o+2wf8APQV4X/ad9/z9SfnR/ad9/wA/Un50fWvIP7Bf8x7p9tg/vij7bB/z0FeF/wBp33/P1J+dH9p33/P1J+dH1ryD+wX/ADHun22D++KPtsH/AD0FeF/2nff8/Un50f2nff8AP1J+dH1ryD+wX/Me6fbYP74o+2wf89BXhf8Aad9/z9SfnR/ad9/z9SfnR9a8g/sF/wAx7ZfanBb2csocfKua8W1e8N9qc8+ch2yKjbUbx0KtcSFT1BNVaxq1ufQ9HL8uWFbbd2wooorA9QKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA0NGvm0/UY5lOOcV7RaalbzW0biUHKjNeD9KspqN4i7VuJAPQGt6Vbk0PLx+WrFNSTsz3b7bB/z0FH22D/noK8L/tO+/wCfqT86P7Tvv+fqT862+teR539gv+Y90+2wf3xR9tg/56CvC/7Tvv8An6k/Oj+077/n6k/Oj615B/YL/mPdPtsH98UfbYP+egrwv+077/n6k/Oj+077/n6k/Oj615B/YL/mPdPtsH98UfbYP+egrwv+077/AJ+pPzo/tO+/5+pPzo+teQf2C/5j3T7bB/fFH22D/noK8L/tO+/5+pPzo/tO+/5+pPzo+teQf2C/5j3T7bB/fFH22D/noK8L/tO+/wCfqT86P7Tvv+fqT86PrXkH9gv+Y90+2wf3xR9tg/56CvC/7Tvv+fqT86P7Tvv+fqT86PrXkH9gv+Y9X8Ux29/pEqhgXA+WvIJYzFKyH+E4qdtSvWGDcyEfWqxJYkk5JrCrUU3c9XAYOWFi4t3EooorE9AKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP//Z" + +@ag.entrypoint +async def image(none:str): + response = openai.chat.completions.create( + model="gpt-4o-mini", + messages=[ + { + "role": "user", + "content": [ + {"type": "text", "text": "What’s in this image?"}, + { + "type": "image_url", + "image_url": { + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg", + }, + }, + ], + } + ], + ) + + response = openai.chat.completions.create( + model="gpt-4o-mini", + messages=[ + { + "role": "user", + "content": [ + {"type": "text", "text": "What’s in this image?"}, + { + "type": "image_url", + "image_url": { + "url": base64, + }, + }, + ], + } + ], + ) + + + return response.choices[0].message.content diff --git a/agenta-cli/tests/observability_sdk/integrations/openai/app_no_workflow.py b/agenta-cli/tests/observability_sdk/integrations/openai/app_no_workflow.py new file mode 100644 index 0000000000..9b42d554bc --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/openai/app_no_workflow.py @@ -0,0 +1,39 @@ +import agenta as ag +from pydantic import BaseModel, Field +from agenta.sdk.assets import supported_llm_models +from typing import Annotated +from opentelemetry.instrumentation.openai import OpenAIInstrumentor +from openai import OpenAI + +ag.init() + + +openai = OpenAI() + + +OpenAIInstrumentor().instrument() + +class MyConfig(BaseModel): + temperature: float = Field(default=0.2, le=1, ge=0) + model: Annotated[str, ag.MultipleChoice(choices=supported_llm_models)] = Field( + default="gpt-3.5-turbo" + ) + max_tokens: int = Field(default=-1, ge=-1, le=4000) + prompt_system: str = Field(default="system prompt") + multiselect: Annotated[str, ag.MultipleChoice(choices=["a", "b", "c"])] = Field( + default="a" + ) + + + +@ag.entrypoint +async def rag(topic: str, genre: str): + response = openai.chat.completions.create( + model="gpt-3.5-turbo", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": f"Write a short {genre} story about {topic}."} + ], + ) + + return response.choices[0].message.content diff --git a/agenta-cli/tests/observability_sdk/integrations/openai/requirements.txt b/agenta-cli/tests/observability_sdk/integrations/openai/requirements.txt new file mode 100644 index 0000000000..3dd64bb756 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/openai/requirements.txt @@ -0,0 +1,3 @@ +agenta==0.27.0a0 +opentelemetry.instrumentation.openai==0.31.2 +openai \ No newline at end of file diff --git a/agenta-cli/tests/observability_sdk/sanity_check/app_local.py b/agenta-cli/tests/observability_sdk/sanity_check/app_local.py new file mode 100644 index 0000000000..d1ef912f18 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/sanity_check/app_local.py @@ -0,0 +1,195 @@ +import agenta as ag +from pydantic import BaseModel +from agenta.sdk.assets import supported_llm_models + +ag.init(project_id="0192813f-60d5-7a65-8a75-6dda36b79267", + host="https://cloud.beta.agenta.ai", + api_key="NYWy4We0.17ce0e85db4840a39ca9ee7b00e8817b22b60d7e152407a5a4cc98c5284f2e0e", + app_id="0192b51a-bfc8-7a91-9276-470c3b35597f") + +ag.config.default( + temperature=ag.FloatParam(0.2), + model=ag.GroupedMultipleChoiceParam( + default="gpt-3.5-turbo", choices=supported_llm_models + ), + max_tokens=ag.IntParam(-1, -1, 4000), + prompt_system=ag.TextParam("MY_SYSTEM_PROMPT"), +) + +# Pydantic models +class InputData(BaseModel): + text: str + value: int + +class OutputData(BaseModel): + result: str + count: int + +# Function with ignored outputs +@ag.instrument( + spankind="EMBEDDING", + ignore_outputs=["ignored", "cost", "usage"], +) +def ignore_some_outputs_embedding(description: str): + print("embed") + return { + "embedding": "somedata", + "ignored": "ignored", + "cost": 15, + } + +# Function with all outputs ignored +@ag.instrument(spankind="AGENT", ignore_outputs=True) +def ignore_all_outputs_agent(query: str): + print("agent") + return { + "result": "agent result", + "confidence": 0.9, + } + +# Function with ignored inputs +@ag.instrument(spankind="CHAIN", ignore_inputs=["secret"]) +def function_with_ignored_inputs(public_data: str, secret: str): + print("function with ignored inputs") + return f"Public: {public_data}, Secret: {secret}" + +# Function with all inputs ignored +@ag.instrument(spankind="CHAIN", ignore_inputs=True) +def function_with_all_ignored_inputs(data: str): + print("function with all ignored inputs") + return f"Data: {data}" + +# Function using dict inputs/outputs +@ag.instrument(spankind="CHAIN") +def dict_function(input_dict: dict) -> dict: + print("dict function") + return {"output_data": input_dict.get("key", None)} + +# Function using Pydantic models +@ag.instrument(spankind="CHAIN") +def pydantic_function(input_data: InputData) -> OutputData: + print("pydantic function") + return OutputData(result=input_data.text.upper(), count=input_data.value + 1) + +# Function with None output +@ag.instrument(spankind="CHAIN") +def none_output_function(): + print("none output function") + return None + +# Nested function calls +@ag.instrument(spankind="CHAIN") +def nested_function(value: int): + print("nested function") + inner_result = inner_function(value) + return f"Nested result: {inner_result}" + +@ag.instrument(spankind="CHAIN") +def inner_function(value: int): + print("inner function") + return value * 2 + +# Function called multiple times +@ag.instrument(spankind="CHAIN") +def multiple_calls_function(counter: int): + print(f"multiple calls function call {counter}") + return f"Call number: {counter}" + +# Existing functions +@ag.instrument(spankind="CHAIN") +def chain_function(input_data: str): + print("chain") + return f"Processed: {input_data}" + +@ag.instrument(spankind="TASK") +def task_function(task: str): + print("task") + return f"Completed task: {task}" + +@ag.instrument(spankind="TOOL") +def tool_function(tool_input: str): + print("tool") + return f"Tool output: {tool_input}" + +@ag.instrument(spankind="QUERY") +def query_function(query: str): + print("query") + return f"Query result: {query}" + +@ag.instrument(spankind="COMPLETION") +def completion_function(prompt: str): + print("completion") + return f"Completed: {prompt}" + +@ag.instrument(spankind="CHAT") +async def chat_function(message: str): + print("chat") + return f"Chat response: {message}" + +@ag.instrument(spankind="RERANK") +def rerank_function(documents: list): + print("rerank") + return sorted(documents, reverse=True) + +# @ag.instrument(spankind="WRONGKIND") +# def wrong_kind_function(input_data: str): +# print("wrong kind") +# return f"Processed with wrong kind: {input_data}" + +@ag.instrument(spankind="COMPLETION", ignore_inputs=True) +async def summarizer(topic: str, genre: str, report: dict) -> dict: + print("summarizer") + return {"report": "mya"} + +@ag.instrument(spankind="CHAT") +async def exception_func(): + raise Exception("This is an exception") + return "dummy" + + +@ag.instrument(spankind="WORKFLOW") +async def main2(topic: str, genre: str, count: int = 5): + result = ignore_some_outputs_embedding("something") + agent_result = ignore_all_outputs_agent("agent query") + chain_result1 = chain_function("chain input 1") + chain_result2 = chain_function("chain input 2") + chain_result3 = chain_function("chain input 3") # Called multiple times + task_result = task_function("important task") + tool_result = tool_function("tool input") + query_result = query_function("search query") + completion_result = completion_function("complete this") + chat_result = await chat_function("Hello, AI!") + rerank_result = rerank_function([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]) + summarizer_result = await summarizer("topic", "genre", {"content": "report"}) + ignored_input_result = function_with_ignored_inputs("public info", "secret info") + all_ignored_input_result = function_with_all_ignored_inputs("some data") + dict_result = dict_function({"key": "value"}) + pydantic_input = InputData(text="hello", value=42) + pydantic_result = pydantic_function(pydantic_input) + none_output_result = none_output_function() + nested_result = nested_function(5) + multiple_calls_results = [multiple_calls_function(i) for i in range(3)] # Called multiple times + return f"""Results: + Embedding: {result} + Agent: {agent_result} + Chain Results: {chain_result1}, {chain_result2}, {chain_result3} + Task: {task_result} + Tool: {tool_result} + Query: {query_result} + Completion: {completion_result} + Chat: {chat_result} + Rerank: {rerank_result} + Summarizer: {summarizer_result} + Ignored Inputs: {ignored_input_result} + All Ignored Inputs: {all_ignored_input_result} + Dict Function: {dict_result} + Pydantic Function: {pydantic_result} + None Output Function: {none_output_result} + Nested Function: {nested_result} + Multiple Calls Function: {multiple_calls_results} + app_old_sdk""" + return "x" + +if __name__ == "__main__": + import asyncio + asyncio.run(main2(topic="df", genre="d", count=1)) \ No newline at end of file diff --git a/agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk_noinstrument.py b/agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk_noinstrument.py new file mode 100644 index 0000000000..3fb4e610ab --- /dev/null +++ b/agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk_noinstrument.py @@ -0,0 +1,170 @@ +import agenta as ag +from pydantic import BaseModel, Field +from agenta.sdk.assets import supported_llm_models +from typing import Annotated + +ag.init() + +class MyConfig(BaseModel): + temperature: float = Field(default=0.2, le=1, ge=0) + model: Annotated[str, ag.MultipleChoice(choices=supported_llm_models)] = Field( + default="gpt-3.5-turbo" + ) + max_tokens: int = Field(default=-1, ge=-1, le=4000) + prompt_system: str = Field(default="system prompt") + multiselect: Annotated[str, ag.MultipleChoice(choices=["a", "b", "c"])] = Field( + default="a" + ) + + +# Pydantic models +class InputData(BaseModel): + text: str + value: int + +class OutputData(BaseModel): + result: str + count: int + +# Function with ignored outputs +def ignore_some_outputs_embedding(description: str): + print("embed") + return { + "embedding": "somedata", + "ignored": "ignored", + "cost": 15, + } + +# Function with all outputs ignored +def ignore_all_outputs_agent(query: str): + print("agent") + return { + "result": "agent result", + "confidence": 0.9, + } + +# Function with ignored inputs +def function_with_ignored_inputs(public_data: str, secret: str): + print("function with ignored inputs") + return f"Public: {public_data}, Secret: {secret}" + +# Function with all inputs ignored +def function_with_all_ignored_inputs(data: str): + print("function with all ignored inputs") + return f"Data: {data}" + +# Function using dict inputs/outputs +def dict_function(input_dict: dict) -> dict: + print("dict function") + return {"output_data": input_dict.get("key", None)} + +# Function using Pydantic models +def pydantic_function(input_data: InputData) -> OutputData: + print("pydantic function") + return OutputData(result=input_data.text.upper(), count=input_data.value + 1) + +# Function with None output +def none_output_function(): + print("none output function") + return None + +# Nested function calls +def nested_function(value: int): + print("nested function") + inner_result = inner_function(value) + return f"Nested result: {inner_result}" + +def inner_function(value: int): + print("inner function") + return value * 2 + +# Function called multiple times +def multiple_calls_function(counter: int): + print(f"multiple calls function call {counter}") + return f"Call number: {counter}" + +# Existing functions +def chain_function(input_data: str): + print("chain") + return f"Processed: {input_data}" + +def task_function(task: str): + print("task") + return f"Completed task: {task}" + +def tool_function(tool_input: str): + print("tool") + return f"Tool output: {tool_input}" + +def query_function(query: str): + print("query") + return f"Query result: {query}" + +def completion_function(prompt: str): + print("completion") + return f"Completed: {prompt}" + +async def chat_function(message: str): + print("chat") + return f"Chat response: {message}" + +def rerank_function(documents: list): + print("rerank") + return sorted(documents, reverse=True) + +def wrong_kind_function(input_data: str): + print("wrong kind") + return f"Processed with wrong kind: {input_data}" + +async def summarizer(topic: str, genre: str, report: dict) -> dict: + print("summarizer") + return {"report": "mya"} + +async def exception_func(): + raise Exception("This is an exception") + return "dummy" + +@ag.route("/", config_schema=MyConfig) +async def newsdk(topic: str, genre: str, count: int = 5): + result = ignore_some_outputs_embedding("something") + agent_result = ignore_all_outputs_agent("agent query") + chain_result1 = chain_function("chain input 1") + chain_result2 = chain_function("chain input 2") + chain_result3 = chain_function("chain input 3") # Called multiple times + task_result = task_function("important task") + tool_result = tool_function("tool input") + query_result = query_function("search query") + completion_result = completion_function("complete this") + chat_result = await chat_function("Hello, AI!") + rerank_result = rerank_function([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]) + wrong_kind_result = wrong_kind_function("test wrong kind") + summarizer_result = await summarizer("topic", "genre", {"content": "report"}) + ignored_input_result = function_with_ignored_inputs("public info", "secret info") + all_ignored_input_result = function_with_all_ignored_inputs("some data") + dict_result = dict_function({"key": "value"}) + pydantic_input = InputData(text="hello", value=42) + pydantic_result = pydantic_function(pydantic_input) + none_output_result = none_output_function() + nested_result = nested_function(5) + multiple_calls_results = [multiple_calls_function(i) for i in range(3)] # Called multiple times + # exception_result = await exception_func() + return f"""Results: +Embedding: {result} +Agent: {agent_result} +Chain Results: {chain_result1}, {chain_result2}, {chain_result3} +Task: {task_result} +Tool: {tool_result} +Query: {query_result} +Completion: {completion_result} +Chat: {chat_result} +Rerank: {rerank_result} +Wrong Kind: {wrong_kind_result} +Summarizer: {summarizer_result} +Ignored Inputs: {ignored_input_result} +All Ignored Inputs: {all_ignored_input_result} +Dict Function: {dict_result} +Pydantic Function: {pydantic_result} +None Output Function: {none_output_result} +Nested Function: {nested_result} +Multiple Calls Function: {multiple_calls_results} +app_old_sdk""" \ No newline at end of file diff --git a/agenta-cli/tests/observability_sdk/sanity_check/app_sync.py b/agenta-cli/tests/observability_sdk/sanity_check/app_sync.py new file mode 100644 index 0000000000..12df510ef6 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/sanity_check/app_sync.py @@ -0,0 +1,41 @@ + +import agenta as ag +from pydantic import BaseModel, Field + +ag.init() + +class MyConfig(BaseModel): + prompt: str = Field(default="somevalue") + +@ag.instrument( + spankind="EMBEDDING", + ignore_outputs=["ignored", "cost", "usage"], +) +def embed(description: str): + print("embed") + return { + "embedding": "somedata", + "ignored": "ignored", + "cost": 15, + "usage": 20, + } + + +@ag.instrument(spankind="GENERATOR", ignore_inputs=True) +async def summarizer(topic: str, genre: str, report: dict) -> dict: + + return {"report": report} + + +@ag.entrypoint +@ag.instrument(spankind="WORKFLOW") +async def rag(topic: str, genre: str, count: int = 5): + count = int(count) + + result = await embed("something") + + + result = await summarizer("topic", "genre", "report") + + + return result["report"] From cf8f87f3e018ff898608e8220ef8697fe6f1f5aa Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 23 Oct 2024 11:46:53 +0200 Subject: [PATCH 146/305] add some openinference semconvs --- .../fastapi/observability/opentelemetry/semconv.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py index a0344a4dcb..9567a08724 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py @@ -1,6 +1,7 @@ VERSION = "0.4.1" V_0_4_1_ATTRIBUTES_EXACT = [ + # OPENLLMETRY ("gen_ai.system", "ag.meta.system"), ("gen_ai.request.base_url", "ag.meta.request.base_url"), ("gen_ai.request.endpoint", "ag.meta.request.endpoint"), @@ -25,10 +26,23 @@ ("db.vector.query.top_k", "ag.meta.request.top_k"), ("pinecone.query.top_k", "ag.meta.request.top_k"), ("traceloop.span.kind", "ag.type.node"), + # OPENINFERENCE + ("output.value", "ag.data.outputs"), + ("input.value", "ag.data.inputs"), + ("embedding.model_name", "ag.meta.request.model"), + ("llm.invocation_parameters", "ag.meta.request"), + ("llm.model_name", "ag.meta.request.model"), + ("llm.provider", "ag.meta.provider"), + ("llm.system", "ag.meta.system"), ] V_0_4_1_ATTRIBUTES_PREFIX = [ + # OPENLLMETRY ("gen_ai.prompt", "ag.data.inputs.prompt"), ("gen_ai.completion", "ag.data.outputs.completion"), + # OPENINFERENCE + ("llm.token_count", "ag.metrics.unit.tokens"), + ("llm.input_messages", "ag.data.inputs.prompt"), + ("llm.output_messages", "ag.data.outputs.completion"), ] V_0_4_1_MAPS = { From 663b4b2d17c669b01d0dccd04367ab67c59858ed Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Wed, 23 Oct 2024 12:06:04 +0200 Subject: [PATCH 147/305] fix(frontend): expanded all accordions by default, updated nodes types and updated UI to handle error state --- .../components/AccordionTreePanel.tsx | 1 + .../components/AvatarTreeContent.tsx | 20 ++--- .../observability/drawer/TraceContent.tsx | 85 ++++++++++++------- .../pages/observability/drawer/TraceTree.tsx | 14 ++- .../src/services/observability/types/index.ts | 13 ++- 5 files changed, 81 insertions(+), 52 deletions(-) diff --git a/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx b/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx index 42b6f84e85..3d66b51f96 100644 --- a/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx +++ b/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx @@ -88,6 +88,7 @@ const AccordionTreePanel = ({ return ( { - const {code} = status +export const statusMapper = (node: NodeType | null | undefined) => { const {bgColor, color, icon: Icon} = nodeTypeStyles[node ?? "default"] return { bgColor, color, - icon: , + icon: , } } const AvatarTreeContent = ({value, ...props}: AvatarTreeContentProps) => { - const {node, status} = value - const {icon, bgColor, color} = statusMapper(node.type, status) + const {node} = value + const {icon, bgColor, color} = statusMapper(node.type) return ( { shape="square" size={"large"} style={{ - backgroundColor: status.code === NodeStatusCode.ERROR ? "#FBE7E7" : bgColor, + backgroundColor: bgColor, width: 32, - border: `1px solid ${status.code === NodeStatusCode.ERROR ? "#D61010" : color}`, + border: `1px solid ${color}`, }} icon={icon} /> diff --git a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx index c9b6cdc029..6ed6fa813e 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx @@ -4,7 +4,7 @@ import {ArrowRight, Database, PlusCircle, Rocket, Timer} from "@phosphor-icons/r import {Button, Divider, Space, Tabs, TabsProps, Typography} from "antd" import React, {useState} from "react" import {createUseStyles} from "react-jss" -import {_AgentaRootsResponse, NodeStatusCode} from "@/services/observability/types" +import {_AgentaRootsResponse} from "@/services/observability/types" import dayjs from "dayjs" import {getStringOrJson} from "@/lib/helpers/utils" import {statusMapper} from "../components/AvatarTreeContent" @@ -75,11 +75,10 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ })) const TraceContent = ({activeTrace}: TraceContentProps) => { - const {node, time, meta, data, status, metrics, parent, key, children, ...filteredTrace} = - activeTrace + const {data, key, children, ...filteredTrace} = activeTrace const classes = useStyles() const [tab, setTab] = useState("overview") - const {icon, bgColor, color} = statusMapper(node.type, status) + const {icon, bgColor, color} = statusMapper(activeTrace.node.type) const items: TabsProps["items"] = [ { @@ -87,26 +86,28 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { label: "Overview", children: ( - {meta && meta.request && ( + {activeTrace.meta && activeTrace.meta.request && ( Meta Data - {Object.entries(meta.request).map(([key, value], index) => ( - - ))} + {Object.entries(activeTrace.meta.request).map( + ([key, value], index) => ( + + ), + )} )} {data && data?.inputs ? ( - - {node.type !== "chat" ? ( + + {activeTrace.node.type !== "chat" ? ( { ) : null} {data && data?.outputs ? ( - - {node.type !== "chat" ? ( + + {activeTrace.node.type !== "chat" ? ( { ) : null} {data && data?.internals && ( - - {node.type !== "chat" && ( + + {activeTrace.node.type !== "chat" && ( { )} )} + + {activeTrace.exception && ( + + + + )} ), }, @@ -192,7 +204,7 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { children: ( @@ -205,7 +217,9 @@ const TraceContent = ({activeTrace}: TraceContentProps) => {
- {node.name} + + {activeTrace.node.name} + {!parent && ( @@ -225,23 +239,22 @@ const TraceContent = ({activeTrace}: TraceContentProps) => {
- {icon} {node.type} + {icon} {activeTrace.node.type} } /> - + - {formatLatency(time.span / 1000000)} + {formatLatency(activeTrace.time.span / 1000000)} } /> @@ -249,18 +262,24 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { value1={ <> - {formatTokenUsage(metrics?.acc?.tokens?.total)} /{" "} - {formatCurrency(metrics?.acc?.costs?.total)} + {formatTokenUsage(activeTrace.metrics?.acc?.tokens?.total)} /{" "} + {formatCurrency(activeTrace.metrics?.acc?.costs?.total)} } popoverContent={ -
{formatTokenUsage(metrics?.acc?.tokens?.prompt)}
+
+ {formatTokenUsage(activeTrace.metrics?.acc?.tokens?.prompt)} +
Prompt tokens
-
{formatTokenUsage(metrics?.acc?.tokens?.completion)}
+
+ {formatTokenUsage( + activeTrace.metrics?.acc?.tokens?.completion, + )} +
Completion tokens
@@ -269,9 +288,9 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { - {dayjs(time.start).format("DD/MM/YYYY, hh:mm:ss A")} + {dayjs(activeTrace.time.start).format("DD/MM/YYYY, hh:mm:ss A")} {" "} - {dayjs(time.end).format("DD/MM/YYYY, hh:mm:ss A")} + {dayjs(activeTrace.time.end).format("DD/MM/YYYY, hh:mm:ss A")} } /> diff --git a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx index 7151c4c54d..e38910b25b 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx @@ -1,6 +1,6 @@ import {formatCurrency, formatLatency, formatTokenUsage} from "@/lib/helpers/formatters" import {JSSTheme} from "@/lib/Types" -import {_AgentaRootsResponse} from "@/services/observability/types" +import {_AgentaRootsResponse, NodeStatusCode} from "@/services/observability/types" import {Coins, PlusCircle, Timer} from "@phosphor-icons/react" import {Space, Tree, Typography} from "antd" import React, {useEffect, useState} from "react" @@ -67,7 +67,7 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ })) const TreeContent = ({value}: {value: _AgentaRootsResponse}) => { - const {node, time, metrics} = value + const {node, time, metrics, status} = value const classes = useStyles() return ( @@ -76,7 +76,15 @@ const TreeContent = ({value}: {value: _AgentaRootsResponse}) => {
- {node.name} + + {node.name} +
diff --git a/agenta-web/src/services/observability/types/index.ts b/agenta-web/src/services/observability/types/index.ts index 9f3a244d0c..4d0a02688d 100644 --- a/agenta-web/src/services/observability/types/index.ts +++ b/agenta-web/src/services/observability/types/index.ts @@ -1,4 +1,4 @@ -export interface _AgentaRootsResponse extends Omit { +export interface _AgentaRootsResponse extends AgentaNodeDTO { children: _AgentaRootsResponse[] key: string } @@ -19,7 +19,7 @@ interface RootContextDTO { export interface AgentaTreeDTO { tree: TreeContextDTO - nodes: Record + nodes: AgentaNodeDTO[] } interface TreeContextDTO { @@ -44,6 +44,7 @@ export interface NodeDTO { refs?: NodeRefs | null links?: NodeLinkDTO[] | null nodes?: Record | null + exception?: NodeExceptionDTO | null } type NodeData = Record @@ -56,6 +57,13 @@ type NodeLinkDTO = { id: string tree_id?: string | null } +interface NodeExceptionDTO { + timestamp: string + type: string + message?: string | null + stacktrace?: string | null + attributes?: Record | null +} interface ProjectScopeDTO { project_id: string @@ -105,5 +113,4 @@ interface NodeTimeDTO { export interface NodeStatusDTO { code: NodeStatusCode message?: string | null - stacktrace?: string | null } From 8532c3358c003da1e19b26aea32c0771f8619e8d Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Wed, 23 Oct 2024 12:27:21 +0200 Subject: [PATCH 148/305] fix(frontend): changed duplicate node ids --- agenta-web/src/lib/test_trace.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/agenta-web/src/lib/test_trace.json b/agenta-web/src/lib/test_trace.json index f06fc54263..4499f84aa2 100644 --- a/agenta-web/src/lib/test_trace.json +++ b/agenta-web/src/lib/test_trace.json @@ -1901,7 +1901,7 @@ "id": "8eb22aff-ed8a-40ca-ce8d-257bc727d465" }, "node": { - "id": "ce8d257b-c727-d465-822c-c1211610a45e", + "id": "ce8d257b-c727-d465-822c-c1211610aoyh", "type": "task", "name": "reporter" }, @@ -1985,7 +1985,7 @@ "name": "chat" }, "parent": { - "id": "ce8d257b-c727-d465-822c-c1211610a45e" + "id": "ce8d257b-c727-d465-822c-c1211610aoyh" }, "time": { "start": "2024-10-22T09:15:40.353996", @@ -2070,7 +2070,7 @@ "id": "8eb22aff-ed8a-40ca-ce8d-341bc727d465" }, "node": { - "id": "ce8d257b-c727-d465-404c-e0b524fb236f", + "id": "ce8d257b-c727-d465-404c-e0b524fb2spo", "type": "workflow", "name": "rag" }, @@ -2150,12 +2150,12 @@ "id": "8eb22aff-ed8a-40ca-ce8d-341bc727d465" }, "node": { - "id": "ce8d257b-c727-d465-6cb6-77876280d508", + "id": "ce8d257b-c727-d465-6cb6-77876280dswl", "type": "task", "name": "retriever" }, "parent": { - "id": "ce8d257b-c727-d465-404c-e0b524fb236f" + "id": "ce8d257b-c727-d465-404c-e0b524fb2spo" }, "time": { "start": "2024-10-22T09:15:39.766929", @@ -2221,12 +2221,12 @@ "id": "8eb22aff-ed8a-40ca-ce8d-341bc727d465" }, "node": { - "id": "ce8d257b-c727-d465-744e-f4f096671fce", + "id": "ce8d257b-c727-d465-744e-f4f096671sdc", "type": "embedding", "name": "embed" }, "parent": { - "id": "ce8d257b-c727-d465-6cb6-77876280d508" + "id": "ce8d257b-c727-d465-6cb6-77876280dswl" }, "time": { "start": "2024-10-22T09:15:39.767077", @@ -3060,12 +3060,12 @@ "id": "8eb22aff-ed8a-40ca-ce8d-341bc727d465" }, "node": { - "id": "ce8d257b-c727-d465-9b1b-55bfe9d5d8d4", + "id": "ce8d257b-c727-d465-9b1b-55bfe9d5dlos", "type": "query", "name": "search" }, "parent": { - "id": "ce8d257b-c727-d465-6cb6-77876280d508" + "id": "ce8d257b-c727-d465-6cb6-77876280dswl" }, "time": { "start": "2024-10-22T09:15:40.272296", @@ -3947,12 +3947,12 @@ "id": "8eb22aff-ed8a-40ca-ce8d-341bc727d465" }, "node": { - "id": "ce8d257b-c727-d465-822c-c1211610a45e", + "id": "ce8d257b-c727-d465-822c-c1211610aepo", "type": "task", "name": "reporter" }, "parent": { - "id": "ce8d257b-c727-d465-404c-e0b524fb236f" + "id": "ce8d257b-c727-d465-404c-e0b524fb2spo" }, "time": { "start": "2024-10-22T09:15:40.353662", @@ -4026,12 +4026,12 @@ "id": "8eb22aff-ed8a-40ca-ce8d-341bc727d465" }, "node": { - "id": "ce8d257b-c727-d465-6e98-fe9cf979877a", + "id": "ce8d257b-c727-d465-6e98-fe9cf9798ydk", "type": "completion", "name": "chat" }, "parent": { - "id": "ce8d257b-c727-d465-822c-c1211610a45e" + "id": "ce8d257b-c727-d465-822c-c1211610aoyh" }, "time": { "start": "2024-10-22T09:15:40.353996", From 6176854d1b94a11ae3ef9f30e1cf4248aae0c292 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 23 Oct 2024 13:36:21 +0200 Subject: [PATCH 149/305] only print trace is available --- agenta-cli/agenta/sdk/decorators/routing.py | 43 +++++++++++---------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index 8cd5e4f8a4..4a786f93cc 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -662,27 +662,28 @@ def handle_terminal_run( SHOW_DATA = False SHOW_TRACE = False - log.info("\n========= Result =========\n") - - log.info(f"trace_id: {result.trace['trace_id']}") - if SHOW_DETAILS: - log.info(f"latency: {result.trace.get('latency')}") - log.info(f"cost: {result.trace.get('cost')}") - log.info(f"usage: {list(result.trace.get('usage', {}).values())}") - - if SHOW_DATA: - log.info(" ") - log.info(f"data:") - log.info(json.dumps(result.data, indent=2)) - - if SHOW_TRACE: - log.info(" ") - log.info(f"trace:") - log.info(f"----------------") - log.info(json.dumps(result.trace.get("spans", []), indent=2)) - log.info(f"----------------") - - log.info("\n==========================\n") + if result.trace: + log.info("\n========= Result =========\n") + + log.info(f"trace_id: {result.trace['trace_id']}") + if SHOW_DETAILS: + log.info(f"latency: {result.trace.get('latency')}") + log.info(f"cost: {result.trace.get('cost')}") + log.info(f"usage: {list(result.trace.get('usage', {}).values())}") + + if SHOW_DATA: + log.info(" ") + log.info(f"data:") + log.info(json.dumps(result.data, indent=2)) + + if SHOW_TRACE: + log.info(" ") + log.info(f"trace:") + log.info(f"----------------") + log.info(json.dumps(result.trace.get("spans", []), indent=2)) + log.info(f"----------------") + + log.info("\n==========================\n") def override_config_in_schema( self, From 55032fdb6b53290474cb39ce5a8d9f81099881d4 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 23 Oct 2024 14:09:34 +0200 Subject: [PATCH 150/305] fix retry/timeout and prints --- agenta-cli/agenta/sdk/agenta_init.py | 13 +++++++------ agenta-cli/agenta/sdk/decorators/routing.py | 12 ++++++------ agenta-cli/agenta/sdk/tracing/exporters.py | 3 +++ agenta-cli/agenta/sdk/tracing/processors.py | 15 +++++++++++++-- agenta-cli/agenta/sdk/utils/exceptions.py | 2 +- 5 files changed, 30 insertions(+), 15 deletions(-) diff --git a/agenta-cli/agenta/sdk/agenta_init.py b/agenta-cli/agenta/sdk/agenta_init.py index a8dfbc2553..0d6a960aaf 100644 --- a/agenta-cli/agenta/sdk/agenta_init.py +++ b/agenta-cli/agenta/sdk/agenta_init.py @@ -37,9 +37,9 @@ def init( # app_id: Optional[str] = None, ) -> None: - log.info(f"\n--------------------------------") - log.info(f"Using Agenta Python SDK version: {version('agenta')}") - log.info(f"--------------------------------\n") + log.info(f"---------------------------") + log.info(f"Agenta SDK - using version: {version('agenta')}") + log.info(f"---------------------------") """Main function to initialize the singleton. @@ -126,9 +126,10 @@ def __init__( self.base_id = base_id if self.base_id is None: - print( - "Warning: Your configuration will not be saved permanently since base_id is not provided.\n" - ) + # print( + # "Warning: Your configuration will not be saved permanently since base_id is not provided.\n" + # ) + pass if base_id is None or host is None: self.persist = False diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index 4a786f93cc..7d4953bf38 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -363,11 +363,11 @@ async def execute_function( *args, **func_params, ): - log.info(f"\n--------------------------") + log.info(f"---------------------------") log.info( - f"Running application route: {repr(self.route_path if self.route_path != '' else '/')}" + f"Agenta SDK - running route: {repr(self.route_path if self.route_path != '' else '/')}" ) - log.info(f"--------------------------\n") + log.info(f"---------------------------") tracing_context.set(routing_context.get()) @@ -388,11 +388,11 @@ async def execute_function( ) data = self.patch_result(result) except Exception as e: + log.error("--------------------------------------------------") log.error("Agenta SDK - handling application exception below:") log.error("--------------------------------------------------") log.error(format_exc().strip("\n")) log.error("--------------------------------------------------") - log.error("\n") self.handle_exception(e) @@ -422,9 +422,9 @@ async def execute_function( response = BaseResponse(data=data, trace=trace) + log.info(f"----------------------------------") log.info(f"Agenta SDK - exiting successfully: 200") log.info(f"----------------------------------") - log.info("\n") return response @@ -434,9 +434,9 @@ def handle_exception(self, e: Exception): stacktrace = traceback.format_exception(e, value=e, tb=e.__traceback__) # type: ignore detail = {"message": message, "stacktrace": stacktrace} + log.error(f"----------------------------------------") log.error(f"Agenta SDK - exiting with HTTPException: {status_code}") log.error(f"----------------------------------------") - log.error("\n") raise HTTPException( status_code=status_code, diff --git a/agenta-cli/agenta/sdk/tracing/exporters.py b/agenta-cli/agenta/sdk/tracing/exporters.py index 26cae81284..430f04428b 100644 --- a/agenta-cli/agenta/sdk/tracing/exporters.py +++ b/agenta-cli/agenta/sdk/tracing/exporters.py @@ -48,6 +48,9 @@ def fetch(self, trace_id: int) -> List[ReadableSpan]: return trace +OTLPSpanExporter._MAX_RETRY_TIMEOUT = 2 + + ConsoleExporter = ConsoleSpanExporter InlineExporter = InlineTraceExporter OTLPExporter = OTLPSpanExporter diff --git a/agenta-cli/agenta/sdk/tracing/processors.py b/agenta-cli/agenta/sdk/tracing/processors.py index d40c3baa95..51150dc5e3 100644 --- a/agenta-cli/agenta/sdk/tracing/processors.py +++ b/agenta-cli/agenta/sdk/tracing/processors.py @@ -1,4 +1,5 @@ -from typing import Optional, Any, Dict +from typing import Optional, Dict +from traceback import format_exc from opentelemetry.context import Context from opentelemetry.sdk.trace import Span @@ -10,6 +11,8 @@ _DEFAULT_MAX_QUEUE_SIZE, ) +from agenta.sdk.utils.logging import log + # LOAD CONTEXT, HERE @@ -28,7 +31,7 @@ def __init__( _DEFAULT_MAX_QUEUE_SIZE, 60 * 60 * 1000, # 1 hour _DEFAULT_MAX_QUEUE_SIZE, - _DEFAULT_EXPORT_TIMEOUT_MILLIS, + 500, # < 1 second (0.5 seconds) ) self._registry = dict() @@ -54,6 +57,14 @@ def on_end(self, span: ReadableSpan): if self.is_ready(span.get_span_context().trace_id): self.force_flush() + def force_flush(self, timeout_millis: int = None) -> bool: + ret = super().force_flush(timeout_millis) + + if not ret: + log.error("--------------------------------------------") + log.error("Agenta SDK - skipping export due to timeout.") + log.error("--------------------------------------------") + def is_ready(self, trace_id: Optional[int] = None) -> bool: is_ready = not len(self._registry.get(trace_id, {})) diff --git a/agenta-cli/agenta/sdk/utils/exceptions.py b/agenta-cli/agenta/sdk/utils/exceptions.py index d5a6a54d12..319d75645d 100644 --- a/agenta-cli/agenta/sdk/utils/exceptions.py +++ b/agenta-cli/agenta/sdk/utils/exceptions.py @@ -14,9 +14,9 @@ def __exit__(self, exc_type, exc_value, exc_tb): if exc_type is None: return True else: + log.error("-------------------------------------------------") log.error("Agenta SDK - suppressing tracing exception below:") log.error("-------------------------------------------------") log.error(format_exc().strip("\n")) log.error("-------------------------------------------------") - log.error("\n") return True From c15d7d3a285d14705760bd0c0f1f7b1932f726bb Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 23 Oct 2024 15:08:44 +0200 Subject: [PATCH 151/305] missing migration for oss --- .../7d8643d3d672_cast_data_to_string.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 agenta-backend/agenta_backend/migrations/postgres/versions/7d8643d3d672_cast_data_to_string.py diff --git a/agenta-backend/agenta_backend/migrations/postgres/versions/7d8643d3d672_cast_data_to_string.py b/agenta-backend/agenta_backend/migrations/postgres/versions/7d8643d3d672_cast_data_to_string.py new file mode 100644 index 0000000000..2940b3aea3 --- /dev/null +++ b/agenta-backend/agenta_backend/migrations/postgres/versions/7d8643d3d672_cast_data_to_string.py @@ -0,0 +1,43 @@ +"""cast_data_to_string + +Revision ID: 7d8643d3d672 +Revises: ddf8f6e431e6 +Create Date: 2024-10-22 10:21:52.468451 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision: str = "7d8643d3d672" +down_revision: Union[str, None] = "ddf8f6e431e6" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "invocation_span", + "data", + existing_type=postgresql.JSON(astext_type=sa.Text()), + type_=sa.String(), + existing_nullable=True, + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "invocation_span", + "data", + existing_type=sa.String(), + type_=postgresql.JSON(astext_type=sa.Text()), + existing_nullable=True, + ) + # ### end Alembic commands ### From 38d93ffa2524f3550edb8af222b9982a35bb25db Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Wed, 23 Oct 2024 16:10:18 +0200 Subject: [PATCH 152/305] fix node parsing to agenta format --- .../apis/fastapi/observability/utils.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py index aeeda7938b..abc98fdd13 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py @@ -857,12 +857,21 @@ def parse_to_agenta_span_dto( span_dto.exception.attributes ) + # LINKS if span_dto.links: for link in span_dto.links: link.tree_id = None + # NODES if span_dto.nodes: - for node in span_dto.nodes: - parse_to_agenta_span_dto(node) + for node in span_dto.nodes.values(): + if isinstance(node, list): + for span in node: + parse_to_agenta_span_dto(span) + else: + parse_to_agenta_span_dto(node) + + # TAGS + span_dto.tags = None return span_dto From 8787acdbe3b59d6421902ed19268d8e405c9f1c6 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Thu, 24 Oct 2024 13:37:43 +0200 Subject: [PATCH 153/305] updated Node types(frontend) --- .../src/services/observability/types/index.ts | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/agenta-web/src/services/observability/types/index.ts b/agenta-web/src/services/observability/types/index.ts index 4d0a02688d..81853f0ccf 100644 --- a/agenta-web/src/services/observability/types/index.ts +++ b/agenta-web/src/services/observability/types/index.ts @@ -22,29 +22,32 @@ export interface AgentaTreeDTO { nodes: AgentaNodeDTO[] } +enum NodeTreeType { + INVOCATION = "invocation", +} + interface TreeContextDTO { id: string + type?: NodeTreeType | null } - -export interface AgentaNodeDTO extends NodeDTO {} - -export interface NodeDTO { +export interface AgentaNodeDTO { scope: ProjectScopeDTO lifecycle: NodeLifecycleDTO - root: RootContextDTO - tree: TreeContextDTO - node: NodeContextDTO - parent?: ParentContextDTO | null time: NodeTimeDTO status: NodeStatusDTO + exception?: NodeExceptionDTO | null data?: NodeData | null metrics?: NodeMetrics | null meta?: NodeMetadata | null - tags?: NodeTags | null refs?: NodeRefs | null + root: RootContextDTO + tree: TreeContextDTO + node: NodeContextDTO + parent?: ParentContextDTO | null links?: NodeLinkDTO[] | null - nodes?: Record | null - exception?: NodeExceptionDTO | null + otel?: NodeOTelExtraDTO | null + tags?: NodeTags | null + nodes?: Record | null } type NodeData = Record @@ -55,7 +58,6 @@ type NodeRefs = Record type NodeLinkDTO = { type: string id: string - tree_id?: string | null } interface NodeExceptionDTO { timestamp: string @@ -64,7 +66,26 @@ interface NodeExceptionDTO { stacktrace?: string | null attributes?: Record | null } +type NodeOTelExtraDTO = { + kind?: string | null + attributes?: Record | null + events?: NodeOTelEventDTO[] | null + links?: NodeOTelLinkDTO[] | null +} + +type NodeOTelEventDTO = { + name: string + timestamp: string + attributes?: Record | null +} +type NodeOTelLinkDTO = { + context: { + trace_id: string + span_id: string + } + attributes?: Record | null +} interface ProjectScopeDTO { project_id: string } From e0095f08c8aede7bfb6f078f84a918e1b61ec65e Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 24 Oct 2024 14:16:30 +0200 Subject: [PATCH 154/305] fixes filtering (same interface for the request and response), + other chores --- .../observability/opentelemetry/otlp.py | 92 +------ .../apis/fastapi/observability/router.py | 33 +-- .../apis/fastapi/observability/utils.py | 249 ++++-------------- .../agenta_backend/core/observability/dtos.py | 169 +++++------- .../core/observability/interfaces.py | 18 +- .../core/observability/service.py | 27 +- .../core/observability/utils.py | 31 +-- .../agenta_backend/core/shared/dtos.py | 7 - .../dbs/postgres/observability/dao.py | 167 +++++++----- .../dbs/postgres/observability/dbas.py | 88 ++----- .../dbs/postgres/observability/mappings.py | 91 +------ ...392da9_clean_up_observability_interface.py | 106 ++++++++ agenta-cli/agenta/sdk/tracing/inline.py | 72 ++--- 13 files changed, 446 insertions(+), 704 deletions(-) create mode 100644 agenta-backend/agenta_backend/migrations/postgres/versions/7693ab392da9_clean_up_observability_interface.py diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py index fe02e3df26..683bf46cbe 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py @@ -1,9 +1,5 @@ from typing import List -from json import dumps -from uuid import UUID -from hashlib import shake_128 from datetime import datetime -from collections import OrderedDict import agenta_backend.apis.fastapi.observability.opentelemetry.traces_proto as Trace_Proto @@ -37,122 +33,55 @@ ] -def _parse_attribute( - attribute, -): - key = attribute.key +def _parse_attribute(attribute): raw_value = attribute.value value_type = list(MessageToDict(raw_value).keys())[0].replace("V", "_v") clean_value = getattr(raw_value, value_type) - return (key, clean_value) + return (attribute.key, clean_value) -def _parse_timestamp( - timestamp_ns: int, -) -> str: - return datetime.fromtimestamp( - timestamp_ns / 1_000_000_000, - ).isoformat( - timespec="microseconds", - ) +def _parse_timestamp(timestamp_ns: int) -> str: + timestamp = timestamp_ns / 1_000_000_000 + return datetime.fromtimestamp(timestamp).isoformat(timespec="microseconds") -LJUST = 20 - -def parse_otlp_stream( - otlp_stream: bytes, -) -> List[OTelSpanDTO]: +def parse_otlp_stream(otlp_stream: bytes) -> List[OTelSpanDTO]: proto = Trace_Proto.TracesData() proto.ParseFromString(otlp_stream) - resources = [] otel_span_dtos = [] for resource_span in proto.resource_spans: - # print("---", "resource", "---") - - resource = { - "attributes": { - k: v - for k, v in [ - _parse_attribute(attribute) - for attribute in resource_span.resource.attributes - ] - } - } - - keys = OrderedDict( - {k: v for k, v in resource["attributes"].items() if not k.startswith("ag.")} - ) - - resource["resource_id"] = str( - UUID( - shake_128(bytearray(dumps(keys, sort_keys=True), "utf-8")).hexdigest( - RESOURCE_ID_SIZE - ) - ) - ) - - # print(resource) - - resources.append(resource) - for scope_span in resource_span.scope_spans: - # print("---", "scope", "---") - - # print({"name": scope_span.scope.name, "version": scope_span.scope.version}) - for span in scope_span.spans: - # print("---", "span", "---") s_trace_id = "0x" + span.trace_id.hex() - # print("s_trace_id".ljust(LJUST), s_trace_id) - s_span_id = "0x" + span.span_id.hex() - # print("s_span_id".ljust(LJUST), s_span_id) - - s_context = OTelContextDTO( - trace_id=s_trace_id, - span_id=s_span_id, - ) + s_context = OTelContextDTO(trace_id=s_trace_id, span_id=s_span_id) s_parent_id = span.parent_span_id.hex() s_parent_id = "0x" + s_parent_id if s_parent_id else None - # print("s_parent_id".ljust(LJUST), s_parent_id) - p_context = ( - OTelContextDTO( - trace_id=s_trace_id, - span_id=s_parent_id, - ) + OTelContextDTO(trace_id=s_trace_id, span_id=s_parent_id) if s_parent_id else None ) s_name = span.name - # print("s_name".ljust(LJUST), s_name) s_kind = SPAN_KINDS[span.kind] - # print("s_kind".ljust(LJUST), s_kind) s_start_time = _parse_timestamp(span.start_time_unix_nano) - # print("s_start_time".ljust(LJUST), s_start_time) - s_end_time = _parse_timestamp(span.end_time_unix_nano) - # print("s_end_time".ljust(LJUST), s_end_time) s_status_code = SPAN_STATUS_CODES[ span.status.code if span.status.code else 0 ] - # print("s_status_code".ljust(LJUST), s_status_code) - s_status_message = ( span.status.message if span.status.message != "" else None ) - # print("s_status_message") - # (# print(s_status_message) if s_status_message else None) s_attributes = { k: v @@ -160,9 +89,6 @@ def parse_otlp_stream( _parse_attribute(attribute) for attribute in span.attributes ] } - s_attributes["ag.refs.resource_id"] = resource["resource_id"] - # print("s_attributes") - # print(list(s_attributes.keys())) s_events = [ OTelEventDTO( @@ -179,7 +105,6 @@ def parse_otlp_stream( for event in span.events ] s_events = s_events if len(s_events) > 0 else None - # print("s_events".ljust(LJUST), s_events) s_links = [ OTelLinkDTO( @@ -198,7 +123,6 @@ def parse_otlp_stream( for link in span.links ] s_links = s_links if len(s_links) > 0 else None - # print("s_links".ljust(LJUST), s_links) otel_span_dto = OTelSpanDTO( context=s_context, diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py index 35fc7f9733..a63e1468b1 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py @@ -1,13 +1,7 @@ from typing import Dict, List, Union, Optional, Callable, Literal +from uuid import UUID -from fastapi import ( - APIRouter, - Request, - Depends, - Query, - status, - HTTPException, -) +from fastapi import APIRouter, Request, Depends, Query, status, HTTPException from agenta_backend.core.observability.service import ObservabilityService from agenta_backend.core.observability.dtos import QueryDTO @@ -31,13 +25,10 @@ AgentaNodeDTO, AgentaTreeDTO, AgentaRootDTO, - SpanDTO, TreeDTO, RootDTO, ) -VERSION = "1.0.0" - class ObservabilityRouter: VERSION = "1.0.0" @@ -68,7 +59,7 @@ def __init__( ### QUERIES self.router.add_api_route( - "/{project_id}/traces", + "/traces/search", self.query_traces, methods=["GET"], operation_id="query_traces", @@ -129,7 +120,7 @@ async def query_traces( ) span_dtos = await self.service.query( - project_id=project_id, + project_id=UUID(project_id), query_dto=query_dto, ) @@ -163,7 +154,7 @@ async def query_traces( if query_dto.grouping.focus.value == "tree": return AgentaTreesResponse( - version=VERSION, + version=self.VERSION, trees=[ AgentaTreeDTO( tree=TreeDTO( @@ -189,7 +180,7 @@ async def query_traces( _nodes_by_root[nodes[0].root.id].append( AgentaTreeDTO( - version=VERSION, + version=self.VERSION, tree=TreeDTO( id=tree_id, type=_types_by_tree[tree_id], @@ -201,7 +192,7 @@ async def query_traces( ) return AgentaRootsResponse( - version=VERSION, + version=self.VERSION, roots=[ AgentaRootDTO( root=RootDTO(id=root_id), @@ -212,7 +203,7 @@ async def query_traces( ) return AgentaNodesResponse( - version=VERSION, + version=self.VERSION, nodes=[AgentaNodeDTO(**span.model_dump()) for span in spans], ) @@ -243,10 +234,12 @@ async def otlp_collect_traces( otel_span_dtos = parse_otlp_stream(otlp_stream) span_dtos = [ - parse_from_otel_span_dto(project_id or app_id, otel_span_dto) - for otel_span_dto in otel_span_dtos + parse_from_otel_span_dto(otel_span_dto) for otel_span_dto in otel_span_dtos ] - await self.service.ingest(span_dtos=span_dtos) + await self.service.ingest( + project_id=UUID(project_id or app_id), + span_dtos=span_dtos, + ) return CollectStatusResponse(version=self.VERSION, status="processing") diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py index abc98fdd13..4b56a47775 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py @@ -1,4 +1,4 @@ -from typing import Optional, Union, Tuple, Any, List, Dict +from typing import Optional, Tuple, Any, List, Dict from uuid import UUID from collections import OrderedDict from json import loads, JSONDecodeError, dumps @@ -8,8 +8,6 @@ from agenta_backend.apis.fastapi.observability.opentelemetry.semconv import CODEX -from agenta_backend.core.shared.dtos import ProjectScopeDTO -from agenta_backend.core.observability.dtos import SpanCreateDTO, SpanDTO from agenta_backend.core.observability.dtos import ( TimeDTO, StatusDTO, @@ -20,6 +18,7 @@ LinkDTO, ExceptionDTO, Attributes, + SpanDTO, OTelExtraDTO, OTelEventDTO, OTelSpanDTO, @@ -27,61 +26,25 @@ OTelLinkDTO, ) from agenta_backend.core.observability.dtos import ( - ScopingDTO, + GroupingDTO, WindowingDTO, FilteringDTO, - ConditionDTO, - TextOptionsDTO, - GroupingDTO, PaginationDTO, QueryDTO, ) - -def _parse_scoping( - *, - project_id: str, -) -> ScopingDTO: - return ScopingDTO( - project_id=project_id, - ) +# --- PARSE QUERY DTO --- def _parse_windowing( *, - windowing: Optional[str] = None, - # earliest: Optional[str] = None, latest: Optional[str] = None, ) -> Optional[WindowingDTO]: - # Parse JSON windowing - windowing_json_dto = None - if windowing: - try: - windowing_json_data = loads(windowing) - windowing_json_dto = WindowingDTO(**windowing_json_data) - except JSONDecodeError as e: - raise HTTPException( - status_code=400, - detail=f"Invalid JSON windowing provided: {str(e)}", - ) + _windowing = None - # Parse flat windowing - windowing_flat_dto = None if earliest or latest: - windowing_flat_dto = WindowingDTO( - earliest=earliest, - latest=latest, - ) - - # Check for windowing conflict - if windowing_json_dto and windowing_flat_dto: - raise HTTPException( - status_code=400, - detail="Both flat and JSON windowing data provided", - ) - - _windowing = windowing_json_dto or windowing_flat_dto + _windowing = WindowingDTO(earliest=earliest, latest=latest) return _windowing @@ -89,189 +52,75 @@ def _parse_windowing( def _parse_filtering( *, filtering: Optional[str] = None, - # - field: Optional[str] = None, - key: Optional[str] = None, - value: Optional[Union[str, int, float, bool]] = None, - operator: Optional[str] = None, - exact_match: Optional[bool] = None, - case_sensitive: Optional[bool] = None, ) -> Optional[FilteringDTO]: # Parse JSON filtering - filtering_json_dto = None + _filtering = None + if filtering: try: filtering_json_data = loads(filtering) - filtering_json_dto = FilteringDTO(**filtering_json_data) + _filtering = FilteringDTO(**filtering_json_data) except JSONDecodeError as e: raise HTTPException( status_code=400, detail=f"Invalid JSON filtering provided: {str(e)}", ) - # Parse flat filtering - filtering_flat_dto = None - if field: - filtering_flat_dto = FilteringDTO( - operator="and", - conditions=[ - ConditionDTO( - field=field, - key=key, - value=value, - operator=operator, - options=TextOptionsDTO( - case_sensitive=case_sensitive, - exact_match=exact_match, - ), - ) - ], - ) - - # Check for filtering conflict - if filtering_json_dto and filtering_flat_dto: - raise HTTPException( - status_code=400, - detail="Both flat and JSON filtering data provided", - ) - - _filtering = filtering_json_dto or filtering_flat_dto - return _filtering def _parse_grouping( *, - grouping: Optional[str] = None, - # focus: Optional[str] = None, ) -> Optional[GroupingDTO]: - # Parse JSON grouping - grouping_json_dto = None - if grouping: - try: - grouping_json_data = loads(grouping) - grouping_json_dto = GroupingDTO(**grouping_json_data) - except JSONDecodeError as e: - raise HTTPException( - status_code=400, - detail=f"Invalid JSON grouping provided: {str(e)}", - ) - - # Parse flat grouping - grouping_flat_dto = None - if focus: - grouping_flat_dto = GroupingDTO( - focus=focus, - ) - - # Check for grouping conflict - if grouping_json_dto and grouping_flat_dto: - raise HTTPException( - status_code=400, - detail="Both flat and JSON grouping data provided", - ) + _grouping = None - _grouping = grouping_json_dto or grouping_flat_dto + if focus != "node": + _grouping = GroupingDTO(focus=focus or "tree") return _grouping def _parse_pagination( *, - pagination: Optional[str] = None, - # page: Optional[int] = None, size: Optional[int] = None, ) -> Optional[PaginationDTO]: - # Parse JSON pagination - pagination_json_dto = None - if pagination: - try: - pagination_json_data = loads(pagination) - pagination_json_dto = PaginationDTO(**pagination_json_data) - except JSONDecodeError as e: - raise HTTPException( - status_code=400, - detail=f"Invalid JSON pagination provided: {str(e)}", - ) + _pagination = None - # Parse flat pagination - pagination_flat_dto = None if page and size: - pagination_flat_dto = PaginationDTO( - page=page, - size=size, - ) - - # Check for pagination conflict - if pagination_json_dto and pagination_flat_dto: - raise HTTPException( - status_code=400, - detail="Both flat and JSON pagination data provided", - ) - - _pagination = pagination_json_dto or pagination_flat_dto + _pagination = PaginationDTO(page=page, size=size) return _pagination def parse_query_dto( # GROUPING - # - Option 1: Single query parameter as JSON - grouping: Optional[str] = Query(None), # - Option 2: Flat query parameters focus: Optional[str] = Query(None), # WINDOWING - # - Option 1: Single query parameter as JSON - windowing: Optional[str] = Query(None), # - Option 2: Flat query parameters earliest: Optional[str] = Query(None), latest: Optional[str] = Query(None), # FILTERING # - Option 1: Single query parameter as JSON filtering: Optional[str] = Query(None), - # - Option 2: Flat query parameters (single condition with 'and' operator) - field: Optional[str] = Query(None), - key: Optional[str] = Query(None), - value: Optional[Union[str, int, float, bool]] = Query(None), - operator: Optional[str] = Query(None), - exact_match: Optional[bool] = Query(False), - case_sensitive: Optional[bool] = Query(False), # PAGINATION - # - Option 1: Single query parameter as JSON - pagination: Optional[str] = Query(None), # - Option 2: Flat query parameters page: Optional[int] = Query(None), size: Optional[int] = Query(None), ) -> QueryDTO: return QueryDTO( - grouping=_parse_grouping( - grouping=grouping, - focus=focus, - ), - windowing=_parse_windowing( - windowing=windowing, - earliest=earliest, - latest=latest, - ), - filtering=_parse_filtering( - filtering=filtering, - field=field, - key=key, - value=value, - operator=operator, - exact_match=exact_match, - case_sensitive=case_sensitive, - ), - pagination=_parse_pagination( - pagination=pagination, - page=page, - size=size, - ), + grouping=_parse_grouping(focus=focus), + windowing=_parse_windowing(earliest=earliest, latest=latest), + filtering=_parse_filtering(filtering=filtering), + pagination=_parse_pagination(page=page, size=size), ) +# --- PARSE SPAN DTO --- + + def _unmarshal_attributes( marshalled: Dict[str, Any], ) -> Dict[str, Any]: @@ -514,14 +363,6 @@ def _parse_from_attributes( # _meta = _unmarshal_attributes(_meta) _meta = _meta if _meta else None - # TAGS - _tags = _get_attributes(otel_span_dto.attributes, "tags") - - for key in _tags.keys(): - del otel_span_dto.attributes[_encode_key("tags", key)] - - _tags = _tags if _tags else None - # REFS _refs = _get_attributes(otel_span_dto.attributes, "refs") @@ -533,7 +374,7 @@ def _parse_from_attributes( if len(otel_span_dto.attributes.keys()) < 1: otel_span_dto.attributes = None - return _data, _metrics, _meta, _tags, _refs + return _data, _metrics, _meta, _refs def _parse_from_events( @@ -567,11 +408,8 @@ def _parse_from_events( def parse_from_otel_span_dto( - project_id: str, otel_span_dto: OTelSpanDTO, -) -> SpanCreateDTO: - scope = ProjectScopeDTO(project_id=UUID(project_id)) - +) -> SpanDTO: _parse_from_semconv(otel_span_dto.attributes) types = _parse_from_types(otel_span_dto) @@ -608,12 +446,9 @@ def parse_from_otel_span_dto( else None ) - duration = (otel_span_dto.end_time - otel_span_dto.start_time).total_seconds() - time = TimeDTO( start=otel_span_dto.start_time, end=otel_span_dto.end_time, - span=round(duration * 1_000_000), # microseconds ) status = StatusDTO( @@ -623,12 +458,18 @@ def parse_from_otel_span_dto( links = _parse_from_links(otel_span_dto) - data, metrics, meta, tags, refs = _parse_from_attributes(otel_span_dto) + data, metrics, meta, refs = _parse_from_attributes(otel_span_dto) + + duration = (otel_span_dto.end_time - otel_span_dto.start_time).total_seconds() + + if metrics is None: + metrics = dict() + + metrics["acc.duration.total"] = round(duration * 1_000, 3) # milliseconds exception = _parse_from_events(otel_span_dto) - # TODO: TURN DEFAULT VALUE INTO A RND UUID PER TRACE ! - root_id = refs.get("scenario_id", "70befa7f3cf24485839673c8a361f900") + root_id = refs.get("scenario_id", tree.id.hex) root = RootDTO(id=UUID(root_id)) @@ -639,8 +480,7 @@ def parse_from_otel_span_dto( links=otel_span_dto.links, ) - span_dto = SpanCreateDTO( - scope=scope, + span_dto = SpanDTO( root=root, tree=tree, node=node, @@ -651,7 +491,6 @@ def parse_from_otel_span_dto( data=data, metrics=metrics, meta=meta, - tags=tags, refs=refs, links=links, otel=otel, @@ -688,11 +527,6 @@ def _parse_to_attributes( for key, value in _meta.items(): attributes[_encode_key("meta", key)] = _encode_value(value) - # TAGS - if span_dto.tags: - for key, value in span_dto.tags.items(): - attributes[_encode_key("tags", key)] = _encode_value(value) - # REFS if span_dto.refs: for key, value in span_dto.refs.items(): @@ -807,6 +641,10 @@ def parse_to_otel_span_dto( links = links if links else None + # MASK LINKS FOR NOW + links = None + # ------------------ + otel_span_dto = OTelSpanDTO( context=context, parent=parent, @@ -843,10 +681,6 @@ def parse_to_agenta_span_dto( if span_dto.meta: span_dto.meta = _unmarshal_attributes(span_dto.meta) - # TAGS - if span_dto.tags: - span_dto.tags = _unmarshal_attributes(span_dto.tags) - # REFS if span_dto.refs: span_dto.refs = _unmarshal_attributes(span_dto.refs) @@ -871,7 +705,12 @@ def parse_to_agenta_span_dto( else: parse_to_agenta_span_dto(node) - # TAGS - span_dto.tags = None + # MASK LINKS FOR NOW + span_dto.links = None + # ------------------ + + # MASK LIFECYCLE FOR NOW + span_dto.lifecycle = None + # ---------------------- return span_dto diff --git a/agenta-backend/agenta_backend/core/observability/dtos.py b/agenta-backend/agenta_backend/core/observability/dtos.py index 3de8755d16..557703b33d 100644 --- a/agenta-backend/agenta_backend/core/observability/dtos.py +++ b/agenta-backend/agenta_backend/core/observability/dtos.py @@ -5,68 +5,14 @@ from uuid import UUID from agenta_backend.core.shared.dtos import DisplayBase -from agenta_backend.core.shared.dtos import ProjectScopeDTO, LifecycleDTO +from agenta_backend.core.shared.dtos import LifecycleDTO -AttributeValueType = Any # -""" -AttributeValueType = Union[ - str, - bool, - int, - float, - Sequence[Union[str, bool, int, float]], -] -""" -Attributes = Dict[str, AttributeValueType] +## --- SUB-ENTITIES --- ## -## --- TIME --- ## - - -class TimeDTO(DisplayBase): - start: datetime - end: datetime - span: int - - -## --- STATUS --- ## - - -class StatusCode(Enum): - UNSET = "UNSET" - OK = "OK" - ERROR = "ERROR" - - -class StatusDTO(DisplayBase): - code: StatusCode - message: Optional[str] = None - - -## --- EXCEPTIONS --- ## - - -class ExceptionDTO(DisplayBase): - timestamp: str - type: str - message: Optional[str] = None - stacktrace: Optional[str] = None - attributes: Optional[Attributes] = None - - -## --- ATTRIBUTES --- ## - - -class AttributesDTO(DisplayBase): - data: Optional[Attributes] = None - metrics: Optional[Attributes] = None - meta: Optional[Attributes] = None - tags: Optional[Attributes] = None - semconv: Optional[Attributes] = None - - -## --- HIERARCHICAL STRUCTURE --- ## +class RootDTO(DisplayBase): + id: UUID class TreeType(Enum): @@ -75,6 +21,11 @@ class TreeType(Enum): # --- VARIANTS --- # +class TreeDTO(DisplayBase): + id: UUID + type: Optional[TreeType] = None + + class NodeType(Enum): # --- VARIANTS --- # ## SPAN_KIND_SERVER @@ -93,36 +44,62 @@ class NodeType(Enum): # --- VARIANTS --- # -class RootDTO(DisplayBase): +class NodeDTO(DisplayBase): id: UUID + name: str + type: Optional[NodeType] = None -class TreeDTO(DisplayBase): +class ParentDTO(DisplayBase): id: UUID - type: Optional[TreeType] = None -class NodeDTO(DisplayBase): - id: UUID - type: Optional[NodeType] = None - name: str +class TimeDTO(DisplayBase): + start: datetime + end: datetime + + +class StatusCode(Enum): + UNSET = "UNSET" + OK = "OK" + ERROR = "ERROR" + + +class StatusDTO(DisplayBase): + code: StatusCode + message: Optional[str] = None + + class Config: + use_enum_values = True + + +Attributes = Dict[str, Any] + + +class ExceptionDTO(DisplayBase): + timestamp: datetime + type: str + message: Optional[str] = None + stacktrace: Optional[str] = None + attributes: Optional[Attributes] = None + + class Config: + json_encoders = {datetime: lambda dt: dt.isoformat()} Data = Dict[str, Any] Metrics = Dict[str, Any] Metadata = Dict[str, Any] -Tags = Dict[str, str] Refs = Dict[str, str] class LinkDTO(DisplayBase): - type: str + type: TreeType # Yes, this is correct id: UUID tree_id: Optional[UUID] = None - -class ParentDTO(DisplayBase): - id: UUID + class Config: + use_enum_values = True class OTelSpanKind(Enum): @@ -172,10 +149,8 @@ class OTelExtraDTO(DisplayBase): ## --- ENTITIES --- ## -class SpanDTO(DisplayBase): # DBE - scope: ProjectScopeDTO # DBA - - lifecycle: LifecycleDTO +class SpanDTO(DisplayBase): + lifecycle: Optional[LifecycleDTO] = None root: RootDTO tree: TreeDTO @@ -191,7 +166,6 @@ class SpanDTO(DisplayBase): # DBE data: Optional[Data] = None metrics: Optional[Metrics] = None meta: Optional[Metadata] = None - tags: Optional[Tags] = None refs: Optional[Refs] = None links: Optional[List[LinkDTO]] = None @@ -201,31 +175,6 @@ class SpanDTO(DisplayBase): # DBE nodes: Optional[Dict[str, Union["SpanDTO", List["SpanDTO"]]]] = None -class SpanCreateDTO(DisplayBase): # DBE - scope: ProjectScopeDTO # DBA - - root: RootDTO - tree: TreeDTO - node: NodeDTO - - parent: Optional[ParentDTO] = None - - time: TimeDTO - status: StatusDTO - - exception: Optional[ExceptionDTO] = None - - data: Optional[Data] = None - metrics: Optional[Metrics] = None - meta: Optional[Metadata] = None - tags: Optional[Tags] = None - refs: Optional[Refs] = None - - links: Optional[List[LinkDTO]] = None - - otel: Optional[OTelExtraDTO] = None - - class OTelSpanDTO(DisplayBase): context: OTelContextDTO @@ -248,10 +197,6 @@ class OTelSpanDTO(DisplayBase): ## --- QUERY --- ## -class ScopingDTO(ProjectScopeDTO): - pass - - class WindowingDTO(DisplayBase): earliest: Optional[datetime] = None latest: Optional[datetime] = None @@ -263,6 +208,11 @@ class LogicalOperator(Enum): NOT = "not" +class ComparisonOperator(Enum): + IS = "is" + IS_NOT = "is_not" + + class NumericOperator(Enum): EQ = "eq" NEQ = "neq" @@ -270,7 +220,7 @@ class NumericOperator(Enum): LT = "lt" GTE = "gte" LTE = "lte" - BETWEEN = "between" + BETWEEN = "btwn" class StringOperator(Enum): @@ -296,19 +246,22 @@ class TextOptionsDTO(DisplayBase): class ConditionDTO(DisplayBase): - field: str # column + # column/field in a[.b[.c]] format + # where a is the column name, and + # b[.c] is the optional, and optionally nested, field name + key: str - key: Optional[str] = None value: Optional[Union[str, int, float, bool]] = None operator: Optional[ Union[ + ComparisonOperator, NumericOperator, StringOperator, ListOperator, ExistenceOperator, ] - ] = None + ] = ComparisonOperator.IS options: Optional[TextOptionsDTO] = None diff --git a/agenta-backend/agenta_backend/core/observability/interfaces.py b/agenta-backend/agenta_backend/core/observability/interfaces.py index d6ff244f8d..0f309fb022 100644 --- a/agenta-backend/agenta_backend/core/observability/interfaces.py +++ b/agenta-backend/agenta_backend/core/observability/interfaces.py @@ -1,7 +1,7 @@ from typing import List +from uuid import UUID -from agenta_backend.core.observability.dtos import QueryDTO -from agenta_backend.core.observability.dtos import SpanDTO, SpanCreateDTO +from agenta_backend.core.observability.dtos import QueryDTO, SpanDTO class ObservabilityDAOInterface: @@ -13,7 +13,7 @@ def __init__(self): async def query( self, *, - project_id: str, + project_id: UUID, # query_dto: QueryDTO, ) -> List[SpanDTO]: @@ -24,21 +24,21 @@ async def query( async def create_one( self, *, - span_dto: SpanCreateDTO, + span_dto: SpanDTO, ) -> None: raise NotImplementedError async def create_many( self, *, - span_dtos: List[SpanCreateDTO], + span_dtos: List[SpanDTO], ) -> None: raise NotImplementedError async def read_one( self, *, - project_id: str, + project_id: UUID, # node_id: str, ) -> SpanDTO: @@ -47,7 +47,7 @@ async def read_one( async def read_many( self, *, - project_id: str, + project_id: UUID, # node_ids: List[str], ) -> List[SpanDTO]: @@ -56,7 +56,7 @@ async def read_many( async def delete_one( self, *, - project_id: str, + project_id: UUID, # node_id: str, ) -> None: @@ -65,7 +65,7 @@ async def delete_one( async def delete_many( self, *, - project_id: str, + project_id: UUID, # node_ids: List[str], ) -> None: diff --git a/agenta-backend/agenta_backend/core/observability/service.py b/agenta-backend/agenta_backend/core/observability/service.py index 1cfe376e9d..af6b0817a7 100644 --- a/agenta-backend/agenta_backend/core/observability/service.py +++ b/agenta-backend/agenta_backend/core/observability/service.py @@ -1,8 +1,8 @@ from typing import List, Optional +from uuid import UUID from agenta_backend.core.observability.interfaces import ObservabilityDAOInterface -from agenta_backend.core.observability.dtos import QueryDTO -from agenta_backend.core.observability.dtos import SpanDTO, SpanCreateDTO +from agenta_backend.core.observability.dtos import QueryDTO, SpanDTO from agenta_backend.core.observability.utils import ( parse_span_dtos_to_span_idx, parse_span_idx_to_span_id_tree, @@ -23,7 +23,7 @@ def __init__( async def query( self, *, - project_id: str, + project_id: UUID, # query_dto: QueryDTO, ) -> List[SpanDTO]: @@ -50,7 +50,9 @@ async def query( async def ingest( self, *, - span_dtos: List[SpanCreateDTO], + project_id: UUID, + # + span_dtos: List[SpanDTO], ) -> None: span_idx = parse_span_dtos_to_span_idx(span_dtos) @@ -62,28 +64,35 @@ async def ingest( cumulate_tokens(span_id_tree, span_idx) - await self.observability_dao.create_many(span_dtos=span_idx.values()) + await self.observability_dao.create_many( + project_id=project_id, + span_dtos=span_idx.values(), + ) async def create( self, *, - span_dto: Optional[SpanCreateDTO] = None, - span_dtos: Optional[List[SpanCreateDTO]] = None, + project_id: UUID, + # + span_dto: Optional[SpanDTO] = None, + span_dtos: Optional[List[SpanDTO]] = None, ) -> SpanDTO: if span_dto: return await self.observability_dao.create_one( + project_id=project_id, span_dto=span_dto, ) if span_dtos: return await self.observability_dao.create_many( + project_id=project_id, span_dtos=span_dtos, ) async def read( self, *, - project_id: str, + project_id: UUID, # node_id: Optional[str] = None, node_ids: Optional[List[str]] = None, @@ -103,7 +112,7 @@ async def read( async def delete( self, *, - project_id: str, + project_id: UUID, # node_id: Optional[str] = None, node_ids: Optional[List[str]] = None, diff --git a/agenta-backend/agenta_backend/core/observability/utils.py b/agenta-backend/agenta_backend/core/observability/utils.py index 4b0e2eeedd..68d2e9c5e3 100644 --- a/agenta-backend/agenta_backend/core/observability/utils.py +++ b/agenta-backend/agenta_backend/core/observability/utils.py @@ -2,24 +2,24 @@ from litellm import cost_calculator -from agenta_backend.core.observability.dtos import SpanCreateDTO, SpanDTO +from agenta_backend.core.observability.dtos import SpanDTO def parse_span_dtos_to_span_idx( - span_dtos: List[SpanCreateDTO], -) -> Dict[str, SpanCreateDTO]: + span_dtos: List[SpanDTO], +) -> Dict[str, SpanDTO]: span_idx = {span_dto.node.id: span_dto for span_dto in span_dtos} return span_idx def parse_span_idx_to_span_id_tree( - span_idx: Dict[str, SpanCreateDTO], + span_idx: Dict[str, SpanDTO], ) -> OrderedDict: span_id_tree = OrderedDict() index = {} - def push(span_dto: SpanCreateDTO) -> None: + def push(span_dto: SpanDTO) -> None: if span_dto.parent is None: span_id_tree[span_dto.node.id] = OrderedDict() index[span_dto.node.id] = span_id_tree[span_dto.node.id] @@ -35,15 +35,16 @@ def push(span_dto: SpanCreateDTO) -> None: def cumulate_costs( spans_id_tree: OrderedDict, - spans_idx: Dict[str, SpanCreateDTO], + spans_idx: Dict[str, SpanDTO], ) -> None: - def _get_unit(span: SpanCreateDTO): + + def _get_unit(span: SpanDTO): if span.metrics is not None: return span.metrics.get("unit.costs.total", 0.0) return 0.0 - def _get_acc(span: SpanCreateDTO): + def _get_acc(span: SpanDTO): if span.metrics is not None: return span.metrics.get("acc.costs.total", 0.0) @@ -52,7 +53,7 @@ def _get_acc(span: SpanCreateDTO): def _acc(a: float, b: float): return a + b - def _set(span: SpanCreateDTO, cost: float): + def _set(span: SpanDTO, cost: float): if span.metrics is None: span.metrics = {} @@ -66,7 +67,8 @@ def cumulate_tokens( spans_id_tree: OrderedDict, spans_idx: Dict[str, dict], ) -> None: - def _get_unit(span: SpanCreateDTO): + + def _get_unit(span: SpanDTO): _tokens = { "prompt": 0.0, "completion": 0.0, @@ -82,7 +84,7 @@ def _get_unit(span: SpanCreateDTO): return _tokens - def _get_acc(span: SpanCreateDTO): + def _get_acc(span: SpanDTO): _tokens = { "prompt": 0.0, "completion": 0.0, @@ -105,7 +107,7 @@ def _acc(a: dict, b: dict): "total": a.get("total", 0.0) + b.get("total", 0.0), } - def _set(span: SpanCreateDTO, tokens: dict): + def _set(span: SpanDTO, tokens: dict): if span.metrics is None: span.metrics = {} @@ -127,7 +129,7 @@ def _set(span: SpanCreateDTO, tokens: dict): def _cumulate_tree_dfs( spans_id_tree: OrderedDict, - spans_idx: Dict[str, SpanCreateDTO], + spans_idx: Dict[str, SpanDTO], get_unit_metric, get_acc_metric, accumulate_metric, @@ -199,7 +201,7 @@ def _connect_tree_dfs( ] -def calculate_costs(span_idx: Dict[str, SpanCreateDTO]): +def calculate_costs(span_idx: Dict[str, SpanDTO]): for span in span_idx.values(): if ( span.node.type @@ -213,7 +215,6 @@ def calculate_costs(span_idx: Dict[str, SpanCreateDTO]): prompt_tokens=span.metrics.get("unit.tokens.prompt", 0.0), completion_tokens=span.metrics.get("unit.tokens.completion", 0.0), call_type=span.node.type.name.lower(), - response_time_ms=span.time.span // 1_000, ) if not costs: diff --git a/agenta-backend/agenta_backend/core/shared/dtos.py b/agenta-backend/agenta_backend/core/shared/dtos.py index 1702afe939..13582e39bf 100644 --- a/agenta-backend/agenta_backend/core/shared/dtos.py +++ b/agenta-backend/agenta_backend/core/shared/dtos.py @@ -81,13 +81,6 @@ def __repr__(self): return _repr(self) -## --- SCOPE --- ## - - -class ProjectScopeDTO(DisplayBase): - project_id: UUID - - ## --- LIFECYCLE --- ## diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py index 683c7c5323..ff35e0404e 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py @@ -1,34 +1,31 @@ from typing import Optional, List +from datetime import datetime from sqlalchemy import and_, or_, not_, distinct, Column, cast -from sqlalchemy import UUID, String, Integer, Float, Boolean +from sqlalchemy import UUID, String, Float, Boolean, TIMESTAMP, Enum from sqlalchemy.dialects.postgresql import HSTORE, JSON, JSONB from sqlalchemy.future import select +from sqlalchemy.dialects import postgresql from agenta_backend.dbs.postgres.shared.engine import engine from agenta_backend.dbs.postgres.observability.dbes import InvocationSpanDBE from agenta_backend.dbs.postgres.observability.mappings import ( - map_span_create_dto_to_dbe, + map_span_dto_to_dbe, map_span_dbe_to_dto, ) from agenta_backend.core.observability.interfaces import ObservabilityDAOInterface -from agenta_backend.core.observability.dtos import QueryDTO +from agenta_backend.core.observability.dtos import QueryDTO, SpanDTO from agenta_backend.core.observability.dtos import ( FilteringDTO, ConditionDTO, LogicalOperator, + ComparisonOperator, NumericOperator, StringOperator, ListOperator, ExistenceOperator, ) -from agenta_backend.core.observability.dtos import ( - SpanDTO, - SpanCreateDTO, -) - -from sqlalchemy.dialects import postgresql class ObservabilityDAO(ObservabilityDAOInterface): @@ -38,7 +35,7 @@ def __init__(self): async def query( self, *, - project_id: str, + project_id: UUID, # query_dto: QueryDTO, ) -> List[SpanDTO]: @@ -135,9 +132,14 @@ async def query( async def create_one( self, *, - span_dto: SpanCreateDTO, + project_id: UUID, + # + span_dto: SpanDTO, ) -> None: - span_dbe = map_span_create_dto_to_dbe(span_dto) + span_dbe = map_span_dto_to_dbe( + project_id=project_id, + span_dto=span_dto, + ) async with engine.session() as session: session.add(span_dbe) @@ -146,9 +148,17 @@ async def create_one( async def create_many( self, *, - span_dtos: List[SpanCreateDTO], + project_id: UUID, + # + span_dtos: List[SpanDTO], ) -> None: - span_dbes = [map_span_create_dto_to_dbe(span_dto) for span_dto in span_dtos] + span_dbes = [ + map_span_dto_to_dbe( + project_id=project_id, + span_dto=span_dto, + ) + for span_dto in span_dtos + ] async with engine.session() as session: for span_dbe in span_dbes: @@ -159,7 +169,7 @@ async def create_many( async def read_one( self, *, - project_id: str, + project_id: UUID, node_id: str, to_dto: bool = True, ) -> Optional[SpanDTO]: @@ -185,7 +195,7 @@ async def read_one( async def read_many( self, *, - project_id: str, + project_id: UUID, node_ids: List[str], to_dto: bool = True, ) -> List[SpanDTO]: @@ -210,7 +220,7 @@ async def read_many( async def delete_one( self, *, - project_id: str, + project_id: UUID, node_id: str, ) -> None: span_dbe = self.read_one( @@ -227,7 +237,7 @@ async def delete_one( async def delete_many( self, *, - project_id: str, + project_id: UUID, node_ids: List[str], ) -> None: span_dbes = self.read_many( @@ -258,6 +268,19 @@ def _combine( raise ValueError(f"Unknown operator: {operator}") +_FLAT_KEYS = { + "time.start": "time_start", + "time.end": "time_end", + "root.id": "root_id", + "tree.id": "tree_id", + "tree.type": "tree_type", + "node.id": "node_id", + "node.type": "node_type", + "node.name": "node_name", + "parent.id": "parent_id", +} + + def _filters(filtering: FilteringDTO) -> list: _conditions = [] @@ -273,61 +296,83 @@ def _filters(filtering: FilteringDTO) -> list: ) elif isinstance(condition, ConditionDTO): - column: Column = getattr(InvocationSpanDBE, condition.field) + key = condition.key + value = condition.value - # Handle JSON/JSONB/HSTORE key-paths - # Assumption: JSON/JSONB/HSTORE columns are stored flat even when nested - if condition.key: - if isinstance(column.type, (JSON, JSONB, HSTORE)): - if isinstance(column.type, HSTORE): - column = column[condition.key] + # MAP FLAT KEYS + if key in _FLAT_KEYS: + key = _FLAT_KEYS[key] - condition.value = str(condition.value) + # SPLIT FIELD AND KEY + _split = key.split(".", 1) + field = _split[0] + key = _split[1] if len(_split) > 1 else None + # GET COLUMN AS ATTRIBUTE + attribute: Column = getattr(InvocationSpanDBE, field) + + # Handle JSON/JSONB/HSTORE key-paths + # Assumption: JSON/JSONB/HSTORE columns are stored flat even when nested + if key: + if isinstance(attribute.type, (JSON, JSONB, HSTORE)): + if isinstance(attribute.type, HSTORE): + attribute = attribute[key] + value = str(value) else: - column = column[condition.key].astext - - if isinstance(condition.value, UUID): - column = cast(column, UUID) - elif isinstance(condition.value, str): - column = cast(column, String) - condition.value = f'"{condition.value}"' - elif isinstance(condition.value, int): - column = cast(column, Float) # Yes, Float - elif isinstance(condition.value, float): - column = cast(column, Float) - elif isinstance(condition.value, bool): - column = cast(column, Boolean) + attribute = attribute[key].astext + + if isinstance(value, UUID): + attribute = cast(attribute, UUID) + elif isinstance(value, str): + attribute = cast(attribute, String) + value = f'"{value}"' + elif isinstance(value, int): + attribute = cast(attribute, Float) # Yes, Float + elif isinstance(value, float): + attribute = cast(attribute, Float) + elif isinstance(value, bool): + attribute = cast(attribute, Boolean) + + if isinstance(attribute.type, TIMESTAMP): + value = datetime.fromisoformat(value) + + if isinstance(attribute.type, Enum): + value = str(value).upper() + + # COMPARISON OPERATORS + if isinstance(condition.operator, ComparisonOperator): + if condition.operator == ComparisonOperator.IS: + _conditions.append(attribute == value) + elif condition.operator == ComparisonOperator.IS_NOT: + _conditions.append(attribute != value) # NUMERIC OPERATORS - if isinstance(condition.operator, NumericOperator): + elif isinstance(condition.operator, NumericOperator): if condition.operator == NumericOperator.EQ: - _conditions.append(column == condition.value) + _conditions.append(attribute == value) elif condition.operator == NumericOperator.NEQ: - _conditions.append(column != condition.value) + _conditions.append(attribute != value) elif condition.operator == NumericOperator.GT: - _conditions.append(column > condition.value) + _conditions.append(attribute > value) elif condition.operator == NumericOperator.LT: - _conditions.append(column < condition.value) + _conditions.append(attribute < value) elif condition.operator == NumericOperator.GTE: - _conditions.append(column >= condition.value) + _conditions.append(attribute >= value) elif condition.operator == NumericOperator.LTE: - _conditions.append(column <= condition.value) + _conditions.append(attribute <= value) elif condition.operator == NumericOperator.BETWEEN: - _conditions.append( - column.between(condition.value[0], condition.value[1]) - ) + _conditions.append(attribute.between(value[0], value[1])) # STRING OPERATORS elif isinstance(condition.operator, StringOperator): if condition.operator == StringOperator.STARTSWITH: - _conditions.append(column.startswith(condition.value)) + _conditions.append(attribute.startswith(value)) elif condition.operator == StringOperator.ENDSWITH: - _conditions.append(column.endswith(condition.value)) + _conditions.append(attribute.endswith(value)) elif condition.operator == StringOperator.CONTAINS: - _conditions.append(column.contains(condition.value)) + _conditions.append(attribute.contains(value)) elif condition.operator == StringOperator.LIKE: - _conditions.append(column.like(condition.value)) + _conditions.append(attribute.like(value)) elif condition.operator == StringOperator.MATCHES: if condition.options: case_sensitive = condition.options.case_sensitive @@ -338,26 +383,26 @@ def _filters(filtering: FilteringDTO) -> list: if exact_match: if case_sensitive: - _conditions.append(column.like(condition.value)) + _conditions.append(attribute.like(value)) else: - _conditions.append(column.ilike(condition.value)) + _conditions.append(attribute.ilike(value)) else: - pattern = f"%{condition.value}%" + pattern = f"%{value}%" if case_sensitive: - _conditions.append(column.like(pattern)) + _conditions.append(attribute.like(pattern)) else: - _conditions.append(column.ilike(pattern)) + _conditions.append(attribute.ilike(pattern)) # LIST OPERATORS elif isinstance(condition.operator, ListOperator): if condition.operator == ListOperator.IN: - _conditions.append(column.in_(condition.value)) + _conditions.append(attribute.in_(value)) # EXISTENCE OPERATORS elif isinstance(condition.operator, ExistenceOperator): if condition.operator == ExistenceOperator.EXISTS: - _conditions.append(column.isnot(None)) + _conditions.append(attribute.isnot(None)) elif condition.operator == ExistenceOperator.NOT_EXISTS: - _conditions.append(column.is_(None)) + _conditions.append(attribute.is_(None)) return _conditions diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py index d3cf5bad8b..2ea9772270 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py @@ -1,99 +1,66 @@ -from sqlalchemy import Column, UUID, TIMESTAMP, Enum as SQLEnum, String, BigInteger -from sqlalchemy.dialects.postgresql import HSTORE, JSON, JSONB +from sqlalchemy import Column, UUID, TIMESTAMP, Enum as SQLEnum, String +from sqlalchemy.dialects.postgresql import HSTORE, JSONB -from agenta_backend.core.observability.dtos import StatusCode, TreeType, NodeType +from agenta_backend.core.observability.dtos import TreeType, NodeType from agenta_backend.dbs.postgres.shared.dbas import DisplayBase from agenta_backend.dbs.postgres.shared.dbas import ProjectScopeDBA, LifecycleDBA -## --- TIME (DBA) --- ## - - -class TimeDBA(DisplayBase): # TBD - __abstract__ = True - - time_start = Column(TIMESTAMP, nullable=False) - time_end = Column(TIMESTAMP, nullable=False) - time_span = Column(BigInteger, nullable=True) - - -## --- STATUS (DBA) --- ## - - -class StatusDBA(DisplayBase): +class RootDBA(DisplayBase): __abstract__ = True - status_code = Column(SQLEnum(StatusCode), nullable=False) - status_message = Column(String, nullable=True) - - -## --- EXCEPTIONS (DBA) --- ## + root_id = Column(UUID(as_uuid=True), nullable=False) -class ExceptionDBA(DisplayBase): +class TreeDBA(DisplayBase): __abstract__ = True - exception = Column(JSON, nullable=True) - - -## --- ATTRIBUTES (DBA) --- ## + tree_id = Column(UUID(as_uuid=True), nullable=False) + tree_type = Column(SQLEnum(TreeType), nullable=True) -class AttributesDBA(DisplayBase): +class NodeDBA(DisplayBase): __abstract__ = True - # inputs, internals, outputs, etc. - data = Column(String, nullable=True) - # scores, costs, tokens, durations, etc. - metrics = Column(JSON, nullable=True) - # configs, resources, etc. - meta = Column(JSON, nullable=True) - # tags, etc. - tags = Column(HSTORE, nullable=True) - # references, etc. - refs = Column(HSTORE, nullable=True) - - -## --- HIERARCHICAL STRUCTURE --- ## + node_id = Column(UUID(as_uuid=True), nullable=False) + node_name = Column(String, nullable=False) + node_type = Column(SQLEnum(NodeType), nullable=True) -class RootDBA(DisplayBase): +class ParentDBA(DisplayBase): __abstract__ = True - root_id = Column(UUID(as_uuid=True), nullable=False) + parent_id = Column(UUID(as_uuid=True), nullable=True) -class TreeDBA(DisplayBase): +class TimeDBA(DisplayBase): __abstract__ = True - tree_id = Column(UUID(as_uuid=True), nullable=False) - - tree_type = Column(SQLEnum(TreeType), nullable=True) + time_start = Column(TIMESTAMP, nullable=False) + time_end = Column(TIMESTAMP, nullable=False) -class NodeDBA(DisplayBase): +class StatusDBA(DisplayBase): __abstract__ = True - node_id = Column(UUID(as_uuid=True), nullable=False) - node_name = Column(String, nullable=False) - - node_type = Column(SQLEnum(NodeType), nullable=True) + status = Column(JSONB, nullable=True) -class LinksDBA(DisplayBase): +class ExceptionDBA(DisplayBase): __abstract__ = True - links = Column(HSTORE, nullable=True) + exception = Column(JSONB, nullable=True) -class ParentDBA(DisplayBase): +class AttributesDBA(DisplayBase): __abstract__ = True - parent_id = Column(UUID(as_uuid=True), nullable=True) - - -## --- TABLES --- ## + data = Column(String, nullable=True) # STRING for full-text search + metrics = Column(JSONB, nullable=True) + meta = Column(JSONB, nullable=True) + refs = Column(HSTORE, nullable=True) # HSTORE for fast querying + links = Column(HSTORE, nullable=True) # HSTORE for fast querying class OTelDBA(DisplayBase): @@ -113,7 +80,6 @@ class SpanDBA( StatusDBA, ExceptionDBA, AttributesDBA, - LinksDBA, OTelDBA, ): __abstract__ = True diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py index e954c64811..1ea79e35e0 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py @@ -2,8 +2,7 @@ from agenta_backend.dbs.postgres.observability.dbes import InvocationSpanDBE -from agenta_backend.core.shared.dtos import ProjectScopeDTO, LifecycleDTO -from agenta_backend.core.observability.dtos import SpanDTO, SpanCreateDTO +from agenta_backend.core.shared.dtos import LifecycleDTO from agenta_backend.core.observability.dtos import ( RootDTO, TreeDTO, @@ -14,6 +13,7 @@ ExceptionDTO, LinkDTO, OTelExtraDTO, + SpanDTO, ) from json import dumps, loads @@ -21,9 +21,6 @@ def map_span_dbe_to_dto(span: InvocationSpanDBE) -> SpanDTO: return SpanDTO( - scope=ProjectScopeDTO( - project_id=span.project_id.hex, - ), lifecycle=LifecycleDTO( created_at=span.created_at, updated_at=span.updated_at, @@ -51,16 +48,15 @@ def map_span_dbe_to_dto(span: InvocationSpanDBE) -> SpanDTO: time=TimeDTO( start=span.time_start, end=span.time_end, - span=span.time_span, ), status=StatusDTO( - code=span.status_code, - message=span.status_message, + code=span.status.get("code"), + message=span.status.get("message"), ), exception=( ExceptionDTO( - timestamp=span.exception["timestamp"], - type=span.exception["type"], + timestamp=span.exception.get("timestamp"), + type=span.exception.get("type"), message=span.exception.get("message"), stacktrace=span.exception.get("stacktrace"), attributes=span.exception.get("attributes"), @@ -72,7 +68,6 @@ def map_span_dbe_to_dto(span: InvocationSpanDBE) -> SpanDTO: data=loads(span.data), metrics=span.metrics, meta=span.meta, - tags=span.tags, refs=span.refs, # ---------- links=( @@ -91,70 +86,17 @@ def map_span_dbe_to_dto(span: InvocationSpanDBE) -> SpanDTO: ) -def map_span_create_dto_to_dbe( - span_create_dto: SpanCreateDTO, -) -> InvocationSpanDBE: - span_dbe = InvocationSpanDBE( - # SCOPE - project_id=span_create_dto.scope.project_id, - # LIFECYCLE - # --------- - # ROOT - root_id=span_create_dto.root.id, - # TREE - tree_id=span_create_dto.tree.id, - tree_type=(span_create_dto.tree.type), - # NODE - node_id=span_create_dto.node.id, - node_type=(span_create_dto.node.type), - node_name=span_create_dto.node.name, - # PARENT - parent_id=span_create_dto.parent.id if span_create_dto.parent else None, - # TIME - time_start=span_create_dto.time.start, - time_end=span_create_dto.time.end, - time_span=span_create_dto.time.span, - # STATUS - status_code=span_create_dto.status.code, - status_message=span_create_dto.status.message, - # EXCEPTION - exception=( - span_create_dto.exception.model_dump(exclude_none=True) - if span_create_dto.exception - else None - ), - # ATTRIBUTES - data=dumps(span_create_dto.data), - metrics=span_create_dto.metrics, - meta=span_create_dto.meta, - tags=span_create_dto.tags, - refs=span_create_dto.refs, - # LINKS - links=( - { - str(link.id): f"{link.type}:{link.tree_id.hex[:16]}" - for link in span_create_dto.links - } - if span_create_dto.links - else None - ), - # OTEL - otel=span_create_dto.otel.model_dump(exclude_none=True), - ) - - return span_dbe - - def map_span_dto_to_dbe( + project_id: str, span_dto: SpanDTO, ) -> InvocationSpanDBE: span_dbe = InvocationSpanDBE( # SCOPE - project_id=span_dto.scope.project_id, + project_id=project_id, # LIFECYCLE - created_at=span_dto.lifecycle.created_at, - updated_at=span_dto.lifecycle.updated_at, - updated_by_id=span_dto.lifecycle.updated_by_id, + created_at=span_dto.lifecycle.created_at if span_dto.lifecycle else None, + updated_at=span_dto.lifecycle.updated_at if span_dto.lifecycle else None, + updated_by_id=span_dto.lifecycle.updated_by_id if span_dto.lifecycle else None, # ROOT root_id=span_dto.root.id, # TREE @@ -169,10 +111,10 @@ def map_span_dto_to_dbe( # TIME time_start=span_dto.time.start, time_end=span_dto.time.end, - time_span=span_dto.time.span, # STATUS - status_code=span_dto.status.code, - status_message=span_dto.status.message, + status=( + span_dto.status.model_dump(exclude_none=True) if span_dto.status else None + ), # EXCEPTION exception=( span_dto.exception.model_dump(exclude_none=True) @@ -183,7 +125,6 @@ def map_span_dto_to_dbe( data=dumps(span_dto.data), metrics=span_dto.metrics, meta=span_dto.meta, - tags=span_dto.tags, refs=span_dto.refs, # LINKS links=( @@ -199,7 +140,3 @@ def map_span_dto_to_dbe( ) return span_dbe - - -def map_span_dbe_to_dict(dbe: InvocationSpanDBE) -> dict: - return {c.name: getattr(dbe, c.name) for c in dbe.__table__.columns} diff --git a/agenta-backend/agenta_backend/migrations/postgres/versions/7693ab392da9_clean_up_observability_interface.py b/agenta-backend/agenta_backend/migrations/postgres/versions/7693ab392da9_clean_up_observability_interface.py new file mode 100644 index 0000000000..7662f584b4 --- /dev/null +++ b/agenta-backend/agenta_backend/migrations/postgres/versions/7693ab392da9_clean_up_observability_interface.py @@ -0,0 +1,106 @@ +"""clean_up_observability_interface + +Revision ID: 7693ab392da9 +Revises: 7d8643d3d672 +Create Date: 2024-10-24 08:46:46.212987 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision: str = "7693ab392da9" +down_revision: Union[str, None] = "7d8643d3d672" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "invocation_span", + sa.Column("status", postgresql.JSONB(astext_type=sa.Text()), nullable=True), + ) + op.alter_column( + "invocation_span", + "exception", + existing_type=postgresql.JSON(astext_type=sa.Text()), + type_=postgresql.JSONB(astext_type=sa.Text()), + existing_nullable=True, + ) + op.alter_column( + "invocation_span", + "metrics", + existing_type=postgresql.JSON(astext_type=sa.Text()), + type_=postgresql.JSONB(astext_type=sa.Text()), + existing_nullable=True, + ) + op.alter_column( + "invocation_span", + "meta", + existing_type=postgresql.JSON(astext_type=sa.Text()), + type_=postgresql.JSONB(astext_type=sa.Text()), + existing_nullable=True, + ) + op.drop_column("invocation_span", "status_code") + op.drop_column("invocation_span", "status_message") + op.drop_column("invocation_span", "tags") + op.drop_column("invocation_span", "time_span") + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "invocation_span", + sa.Column("time_span", sa.BIGINT(), autoincrement=False, nullable=True), + ) + op.add_column( + "invocation_span", + sa.Column( + "tags", + postgresql.HSTORE(text_type=sa.Text()), + autoincrement=False, + nullable=True, + ), + ) + op.add_column( + "invocation_span", + sa.Column("status_message", sa.VARCHAR(), autoincrement=False, nullable=True), + ) + op.add_column( + "invocation_span", + sa.Column( + "status_code", + postgresql.ENUM("UNSET", "OK", "ERROR", name="statuscode"), + autoincrement=False, + nullable=False, + ), + ) + op.alter_column( + "invocation_span", + "meta", + existing_type=postgresql.JSONB(astext_type=sa.Text()), + type_=postgresql.JSON(astext_type=sa.Text()), + existing_nullable=True, + ) + op.alter_column( + "invocation_span", + "metrics", + existing_type=postgresql.JSONB(astext_type=sa.Text()), + type_=postgresql.JSON(astext_type=sa.Text()), + existing_nullable=True, + ) + op.alter_column( + "invocation_span", + "exception", + existing_type=postgresql.JSONB(astext_type=sa.Text()), + type_=postgresql.JSON(astext_type=sa.Text()), + existing_nullable=True, + ) + op.drop_column("invocation_span", "status") + # ### end Alembic commands ### diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py index e2490a9985..679074cab5 100644 --- a/agenta-cli/agenta/sdk/tracing/inline.py +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -131,14 +131,6 @@ class StatusDTO(DisplayBase): Attributes = Dict[str, AttributeValueType] -class AttributesDTO(DisplayBase): - data: Optional[Attributes] = None - metrics: Optional[Attributes] = None - meta: Optional[Attributes] = None - tags: Optional[Attributes] = None - semconv: Optional[Attributes] = None - - class TreeType(Enum): # --- VARIANTS --- # INVOCATION = "invocation" @@ -242,7 +234,7 @@ class OTelExtraDTO(DisplayBase): class SpanDTO(DisplayBase): scope: ProjectScopeDTO - lifecycle: LifecycleDTO + lifecycle: Optional[LifecycleDTO] = None root: RootDTO tree: TreeDTO @@ -266,29 +258,6 @@ class SpanDTO(DisplayBase): nodes: Optional[Dict[str, Union["SpanDTO", List["SpanDTO"]]]] = None -class SpanCreateDTO(DisplayBase): - scope: ProjectScopeDTO - - root: RootDTO - tree: TreeDTO - node: NodeDTO - - parent: Optional[ParentDTO] = None - - time: TimeDTO - status: StatusDTO - - data: Optional[Data] = None - metrics: Optional[Metrics] = None - meta: Optional[Metadata] = None - tags: Optional[Tags] = None - refs: Optional[Refs] = None - - links: Optional[List[LinkDTO]] = None - - otel: Optional[OTelExtraDTO] = None - - class OTelSpanDTO(DisplayBase): context: OTelContextDTO @@ -321,20 +290,20 @@ class OTelSpanDTO(DisplayBase): def parse_span_dtos_to_span_idx( - span_dtos: List[SpanCreateDTO], -) -> Dict[str, SpanCreateDTO]: + span_dtos: List[SpanDTO], +) -> Dict[str, SpanDTO]: span_idx = {span_dto.node.id: span_dto for span_dto in span_dtos} return span_idx def parse_span_idx_to_span_id_tree( - span_idx: Dict[str, SpanCreateDTO], + span_idx: Dict[str, SpanDTO], ) -> OrderedDict: span_id_tree = OrderedDict() index = {} - def push(span_dto: SpanCreateDTO) -> None: + def push(span_dto: SpanDTO) -> None: if span_dto.parent is None: span_id_tree[span_dto.node.id] = OrderedDict() index[span_dto.node.id] = span_id_tree[span_dto.node.id] @@ -350,15 +319,16 @@ def push(span_dto: SpanCreateDTO) -> None: def cumulate_costs( spans_id_tree: OrderedDict, - spans_idx: Dict[str, SpanCreateDTO], + spans_idx: Dict[str, SpanDTO], ) -> None: - def _get_unit(span: SpanCreateDTO): + + def _get_unit(span: SpanDTO): if span.metrics is not None: return span.metrics.get("unit.costs.total", 0.0) return 0.0 - def _get_acc(span: SpanCreateDTO): + def _get_acc(span: SpanDTO): if span.metrics is not None: return span.metrics.get("acc.costs.total", 0.0) @@ -367,7 +337,7 @@ def _get_acc(span: SpanCreateDTO): def _acc(a: float, b: float): return a + b - def _set(span: SpanCreateDTO, cost: float): + def _set(span: SpanDTO, cost: float): if span.metrics is None: span.metrics = {} @@ -381,7 +351,8 @@ def cumulate_tokens( spans_id_tree: OrderedDict, spans_idx: Dict[str, dict], ) -> None: - def _get_unit(span: SpanCreateDTO): + + def _get_unit(span: SpanDTO): _tokens = { "prompt": 0.0, "completion": 0.0, @@ -397,7 +368,7 @@ def _get_unit(span: SpanCreateDTO): return _tokens - def _get_acc(span: SpanCreateDTO): + def _get_acc(span: SpanDTO): _tokens = { "prompt": 0.0, "completion": 0.0, @@ -420,7 +391,7 @@ def _acc(a: dict, b: dict): "total": a.get("total", 0.0) + b.get("total", 0.0), } - def _set(span: SpanCreateDTO, tokens: dict): + def _set(span: SpanDTO, tokens: dict): if span.metrics is None: span.metrics = {} @@ -442,7 +413,7 @@ def _set(span: SpanCreateDTO, tokens: dict): def _cumulate_tree_dfs( spans_id_tree: OrderedDict, - spans_idx: Dict[str, SpanCreateDTO], + spans_idx: Dict[str, SpanDTO], get_unit_metric, get_acc_metric, accumulate_metric, @@ -1032,7 +1003,7 @@ def parse_inline_trace( ############################################### ### services.observability.service.ingest() ### ### --------------------------------------- ### - calculate_cost(span_idx) + calculate_costs(span_idx) cumulate_costs(span_id_tree, span_idx) cumulate_tokens(span_id_tree, span_idx) ### --------------------------------------- ### @@ -1271,7 +1242,7 @@ def _parse_to_legacy_span(span: SpanDTO) -> CreateSpan: return legacy_span -PAYING_TYPES = [ +TYPES_WITH_COSTS = [ "embedding", "query", "completion", @@ -1280,9 +1251,14 @@ def _parse_to_legacy_span(span: SpanDTO) -> CreateSpan: ] -def calculate_cost(span_idx: Dict[str, SpanCreateDTO]): +def calculate_costs(span_idx: Dict[str, SpanDTO]): for span in span_idx.values(): - if span.node.type.name.lower() in PAYING_TYPES and span.meta and span.metrics: + if ( + span.node.type + and span.node.type.name.lower() in TYPES_WITH_COSTS + and span.meta + and span.metrics + ): try: costs = cost_calculator.cost_per_token( model=span.meta.get("response.model"), From 0f9c9ac3afcb8df950a743206715ab5ebe8808eb Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 24 Oct 2024 14:18:25 +0200 Subject: [PATCH 155/305] apply black --- .../observability/opentelemetry/otlp.py | 1 - .../core/observability/utils.py | 2 - agenta-cli/agenta/sdk/tracing/inline.py | 2 - .../langchain/simple_chain_openinference.py | 12 ++-- .../langchain/simple_chain_openllmetery.py | 12 ++-- .../integrations/openai/app.py | 4 +- .../integrations/openai/app_image.py | 70 ++++++++++--------- .../integrations/openai/app_no_workflow.py | 4 +- .../sanity_check/app_local.py | 40 +++++++++-- .../sanity_check/app_new_sdk_noinstrument.py | 29 +++++++- .../sanity_check/app_sync.py | 6 +- 11 files changed, 117 insertions(+), 65 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py index 683bf46cbe..303d56d252 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py @@ -56,7 +56,6 @@ def parse_otlp_stream(otlp_stream: bytes) -> List[OTelSpanDTO]: for resource_span in proto.resource_spans: for scope_span in resource_span.scope_spans: for span in scope_span.spans: - s_trace_id = "0x" + span.trace_id.hex() s_span_id = "0x" + span.span_id.hex() s_context = OTelContextDTO(trace_id=s_trace_id, span_id=s_span_id) diff --git a/agenta-backend/agenta_backend/core/observability/utils.py b/agenta-backend/agenta_backend/core/observability/utils.py index 68d2e9c5e3..d228e393c0 100644 --- a/agenta-backend/agenta_backend/core/observability/utils.py +++ b/agenta-backend/agenta_backend/core/observability/utils.py @@ -37,7 +37,6 @@ def cumulate_costs( spans_id_tree: OrderedDict, spans_idx: Dict[str, SpanDTO], ) -> None: - def _get_unit(span: SpanDTO): if span.metrics is not None: return span.metrics.get("unit.costs.total", 0.0) @@ -67,7 +66,6 @@ def cumulate_tokens( spans_id_tree: OrderedDict, spans_idx: Dict[str, dict], ) -> None: - def _get_unit(span: SpanDTO): _tokens = { "prompt": 0.0, diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py index 679074cab5..8171398de5 100644 --- a/agenta-cli/agenta/sdk/tracing/inline.py +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -321,7 +321,6 @@ def cumulate_costs( spans_id_tree: OrderedDict, spans_idx: Dict[str, SpanDTO], ) -> None: - def _get_unit(span: SpanDTO): if span.metrics is not None: return span.metrics.get("unit.costs.total", 0.0) @@ -351,7 +350,6 @@ def cumulate_tokens( spans_id_tree: OrderedDict, spans_idx: Dict[str, dict], ) -> None: - def _get_unit(span: SpanDTO): _tokens = { "prompt": 0.0, diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py b/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py index 1ff318551d..0b5631f114 100644 --- a/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py +++ b/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py @@ -6,10 +6,12 @@ import agenta as ag -ag.init(project_id="0192813f-60d5-7a65-8a75-6dda36b79267", - host="http://localhost/", - api_key="NYWy4We0.17ce0e85db4840a39ca9ee7b00e8817b22b60d7e152407a5a4cc98c5284f2e0e", - app_id="0192b552-ad65-7c61-a8dc-fedf3608b7a5") +ag.init( + project_id="0192813f-60d5-7a65-8a75-6dda36b79267", + host="http://localhost/", + api_key="NYWy4We0.17ce0e85db4840a39ca9ee7b00e8817b22b60d7e152407a5a4cc98c5284f2e0e", + app_id="0192b552-ad65-7c61-a8dc-fedf3608b7a5", +) LangChainInstrumentor().instrument() @@ -45,4 +47,4 @@ def langchain_app(): print(workflow({"subject": "OpenTelemetry"})) -langchain_app() \ No newline at end of file +langchain_app() diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openllmetery.py b/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openllmetery.py index a22155b168..e797a9be16 100644 --- a/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openllmetery.py +++ b/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openllmetery.py @@ -6,10 +6,12 @@ import agenta as ag -ag.init(project_id="0192813f-60d5-7a65-8a75-6dda36b79267", - host="https://cloud.beta.agenta.ai", - api_key="NYWy4We0.17ce0e85db4840a39ca9ee7b00e8817b22b60d7e152407a5a4cc98c5284f2e0e", - app_id="0192b552-ad65-7c61-a8dc-fedf3608b7a5") +ag.init( + project_id="0192813f-60d5-7a65-8a75-6dda36b79267", + host="https://cloud.beta.agenta.ai", + api_key="NYWy4We0.17ce0e85db4840a39ca9ee7b00e8817b22b60d7e152407a5a4cc98c5284f2e0e", + app_id="0192b552-ad65-7c61-a8dc-fedf3608b7a5", +) LangchainInstrumentor().instrument() @@ -45,4 +47,4 @@ def langchain_app(): print(workflow({"subject": "OpenTelemetry"})) -langchain_app() \ No newline at end of file +langchain_app() diff --git a/agenta-cli/tests/observability_sdk/integrations/openai/app.py b/agenta-cli/tests/observability_sdk/integrations/openai/app.py index db540f1af8..1cd3734d65 100644 --- a/agenta-cli/tests/observability_sdk/integrations/openai/app.py +++ b/agenta-cli/tests/observability_sdk/integrations/openai/app.py @@ -13,6 +13,7 @@ OpenAIInstrumentor().instrument() + class MyConfig(BaseModel): temperature: float = Field(default=0.2, le=1, ge=0) model: Annotated[str, ag.MultipleChoice(choices=supported_llm_models)] = Field( @@ -25,7 +26,6 @@ class MyConfig(BaseModel): ) - @ag.entrypoint @ag.instrument(spankind="WORKFLOW") async def rag(topic: str, genre: str): @@ -33,7 +33,7 @@ async def rag(topic: str, genre: str): model="gpt-3.5-turbo", messages=[ {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": f"Write a short {genre} story about {topic}."} + {"role": "user", "content": f"Write a short {genre} story about {topic}."}, ], ) diff --git a/agenta-cli/tests/observability_sdk/integrations/openai/app_image.py b/agenta-cli/tests/observability_sdk/integrations/openai/app_image.py index ea735574ce..962aba8019 100644 --- a/agenta-cli/tests/observability_sdk/integrations/openai/app_image.py +++ b/agenta-cli/tests/observability_sdk/integrations/openai/app_image.py @@ -13,6 +13,7 @@ OpenAIInstrumentor().instrument() + class MyConfig(BaseModel): temperature: float = Field(default=0.2, le=1, ge=0) model: Annotated[str, ag.MultipleChoice(choices=supported_llm_models)] = Field( @@ -24,45 +25,46 @@ class MyConfig(BaseModel): default="a" ) -base64="data:image/jpeg;base64,/9j/4QC8RXhpZgAASUkqAAgAAAAGABIBAwABAAAAAQAAABoBBQABAAAAVgAAABsBBQABAAAAXgAAACgBAwABAAAAAgAAABMCAwABAAAAAQAAAGmHBAABAAAAZgAAAAAAAABgAAAAAQAAAGAAAAABAAAABgAAkAcABAAAADAyMTABkQcABAAAAAECAwAAoAcABAAAADAxMDABoAMAAQAAAP//AAACoAQAAQAAALAEAAADoAQAAQAAALAEAAAAAAAA/+EOLmh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8APD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc+CjxyZGY6UkRGIHhtbG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyc+CgogPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9JycKICB4bWxuczpBdHRyaWI9J2h0dHA6Ly9ucy5hdHRyaWJ1dGlvbi5jb20vYWRzLzEuMC8nPgogIDxBdHRyaWI6QWRzPgogICA8cmRmOlNlcT4KICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0nUmVzb3VyY2UnPgogICAgIDxBdHRyaWI6Q3JlYXRlZD4yMDIyLTA5LTE0PC9BdHRyaWI6Q3JlYXRlZD4KICAgICA8QXR0cmliOkV4dElkPmU3ZTM3MmU5LTAwZjgtNGJiYi1iNDNiLTFjMzlkMTllMTllMDwvQXR0cmliOkV4dElkPgogICAgIDxBdHRyaWI6RmJJZD41MjUyNjU5MTQxNzk1ODA8L0F0dHJpYjpGYklkPgogICAgIDxBdHRyaWI6VG91Y2hUeXBlPjI8L0F0dHJpYjpUb3VjaFR5cGU+CiAgICA8L3JkZjpsaT4KICAgPC9yZGY6U2VxPgogIDwvQXR0cmliOkFkcz4KIDwvcmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PScnCiAgeG1sbnM6ZGM9J2h0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvJz4KICA8ZGM6dGl0bGU+CiAgIDxyZGY6QWx0PgogICAgPHJkZjpsaSB4bWw6bGFuZz0neC1kZWZhdWx0Jz5SRU1PVkUtQkctQiZhbXA7Vy1XLUlMTFVTVFJBVElPTi1hbnRsZXItdGVhbS1wcm9maWxlLWltYWdlcyAoMTIwMCDDlyAxMjAwcHgpPC9yZGY6bGk+CiAgIDwvcmRmOkFsdD4KICA8L2RjOnRpdGxlPgogPC9yZGY6RGVzY3JpcHRpb24+CgogPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9JycKICB4bWxuczpwZGY9J2h0dHA6Ly9ucy5hZG9iZS5jb20vcGRmLzEuMy8nPgogIDxwZGY6QXV0aG9yPkphY2sgTWNEb25hbGQ8L3BkZjpBdXRob3I+CiA8L3JkZjpEZXNjcmlwdGlvbj4KCiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0nJwogIHhtbG5zOnhtcD0naHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyc+CiAgPHhtcDpDcmVhdG9yVG9vbD5DYW52YTwveG1wOkNyZWF0b3JUb29sPgogPC9yZGY6RGVzY3JpcHRpb24+CjwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9J3cnPz7/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCASwBLADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwCvRRRXjH6QFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAPhiaaZY16scCvSNJ8B2Fxp8UlyHErD5sGuW8I6X9v1NXIOIiDXsaIEQKBgCuzD0k1dnzucY6dOSp03Y5L/hXmkf9NP8Avqj/AIV5pH/TT/vquvorp9lDseJ9fxP87OQ/4V5pH/TT/vqj/hXmkf8ATT/vquvoo9lDsH1/E/zs5D/hXmkf9NP++qP+FeaR/wBNP++q6+ij2UOwfX8T/OzkP+FeaR/00/76o/4V5pH/AE0/76rr6KPZQ7B9fxP87OQ/4V5pH/TT/vqj/hXmkf8ATT/vquvoo9lDsH1/E/zs5D/hXmkf9NP++qP+FeaR/wBNP++q6+ij2UOwfX8T/OzkP+FeaR/00/76rjfFXh1NGuMwA+Tjv617DXPeLdLXUNKcY+Zfm/Ks6tGLjojrwWY1o1l7SV0eM0U6RSkjKRjBxTa84+xTurhRRRQMKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAClVSzBR1NJWt4esGvtVhUDKqwLVUVd2M6tRU4OT6HovgnSvsmmpcMuGkXmusqG2gW3gWJBhQOlTV6sI8sbHwOIrOtUc31CiiiqMQooooAKKKKACiiigAooooAKKKKACmTIJImQ9xin0UAnY8X8V6YdP1V1C4Q81gV6r470n7Vp/nRLmQEEn2rysjBIPUV5leHLI+3yzEe2oJ9UJRRRWJ6IUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFek+ANK8qI3jrxIvFcFplqbu/hiCkgsAa9t0qySwsI4EGAorqw0LvmPCzrE8lP2a3ZeooorvPlAooooAKKKKACiiigAooooAKKKKACiiigAooooAgu4RPbSIRnKkV4jrlg2nanJCR717rXnnxA0nIF6i5YkA4rnxEOaNz2MmxHs63I9med0UUV5x9gFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFS20RnuI4h1ZsUITdldna+AtI82ZrqRcrjK16aOlY3hvThp2kwxEYYDmtmvUpQ5Y2PhcfiHWruXQKKKK1OIKKKKACiiigAooooAKKKKACiiigAooooAKKKKACs/WLJL2wkRhnCnH1rQpGG4EHpSaurFQk4SUkeBX1q9pdvE4wQTVau18eaT5F2bxVwrYFcVXlVI8srH3uErqtSU0FFFFQdIUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXUeC9J+36nvdfljwwJrmY0MkioOpOBXr/g7TBZaTE7LiRhzW9CHNI8zNcT7Gg0t2dKo2qBS0UV6R8UFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAYfijTl1HSnQjOwFhXi8sbRSMjDBB6V9BSIJI2Q9CMV494x0w2WrSOq4jOMVx4qH2j6LI8TZukzm6KKK4j6YKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiigAk4HWgDb8Mac2oasgxkIQxr2iGNYo1VRgAVxvgPSRBZreMuHcYNdtXpYeHLG58Zm+J9rW5Vsgooorc8oKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK5XxppIvtO3ovzqdxNdVUN1CJ7d4z/ECKmceZWNsPVdKoprofP7DaxB7Ula/iPTjpurSQgfL1BrIrypKzsffUqiqQUl1CiiipNAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAq9pFm95qMUaKWwwz9Ko16D8PtK+Zr1xlWGBmtKUOaVjjx1dUKLkd7YWqWlokSDAAqzR7UV6iVj4SUnJ3YUUUUxBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBwfj/SvNt1uYky+fmI9K80r3vU7VbuxljIySpArxDVLJrC/kt26rXBiYWd0fV5Jieen7N7op0UUVynuhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAE9nA1xdRxqM5YA17boWnrp2mxwqMd6878DaSbq/M0i/uwMg+9erKNqgeld+GhZcx8rneJ5pqkugtFFFdR4IUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFACEZFea+PtJ8qUXiL984Nel1keItPW/0uVWGSqkis6seaNjtwGI9hXUuh4fRUk8LW8zROMMOtR15T0Puk01dBRRRQMKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACnxIZJUQdWYCmV0HhLS/7S1ZVYHamG/KqjHmdjGvVVKm5voekeFNM/s/So1ZcP1rfpkaBI1UdhT69WKsrHwNao6k3N9QoooqjMKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAprqHQqRkGnUUAeR+NtKNnqT3IXEchwK5SvY/F+lDUNMZscxgtXjrKVOD1rzcRDlkfaZTifbUEnuhKKKKwPUCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigBVBZgo6k4r1fwNpX2XT1mkTEhzXneg6cdR1KOMfwkN+te2WsKwW6IoxgCuvDQ15j5/O8TaKpLqT0UUV3Hy4UUUUAFFFFABRRRQAUUUUAFFFFABWZreprpenS3HGVGQDWnXmfj/Vi862sbZXBDVnVnyRudmBw/t6yh0O+0vUE1G0WVDnI5q9Xnfw+1QgPau2SzcZr0SinPmjcnG4f2FZwCiiitDlCiiigAooooAKKKKACiiigAooooAjniE0Lxt0YYrxjxTpxsNYmAXEZPy17XXF+O9J+1WizovKZY1hiIc0bnq5TifZV+V7M8sooorzT7MKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKOpoqzY2z3V3HGgySwz9M00ruxM5KMW2d98P9J2xfbnHJyvNegVQ0iySxsI4kGOATV+vVpx5Y2Pg8ZXdas5hRRRVnKFFFFABRRRQAUUUUAFFFFABRRRQBV1C7Wys5J2PCjNeIatdtealPKTkFyRXonjzVvs9n9lU/61a8t6muHEzu+U+qyTDcsHVfU0tC1A6dqkU+eB1Fe22VwLm0jlB+8oNeAA4Oa9X8DasbywaOQ/Mh2gUYadnyk53huaKqrodhRRRXcfLhRRRQAUUUUAFFFFABRRRQAUUUUAFVr+2F3ZSwkfeGKs0UPUcZOLujwnWrE6fqk0GOFPFZ1eheP9JI2XUS8k5Y157XlVYcsrH3eBrqtRUgooorM7AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACu38A6WZLv7Wy5TBHNcZBGZp0QAnLAV7V4b0xdN0xIwOvNdGHhzSuePnGJ9lR5FuzYAwMUtFFeifHhRRRQAUUUUAFFFFABRRRQAUUUUAFRzSrDEzscACpK5vxhqQstJkQNiR1+WplLlVzWhSdWooLqeb+JtSa/1WUE5VGIWsSnSOZJGdupOTTa8qT5nc++o01TgooK6DwlqRsdXiDNiM9a5+nIxRwynBFEJcruFekqtNwfU+gonEkSuOhGafWH4X1NdR0xCDygC1uV60XdXPgKtN05uD6BRRRTMwooooAKKKKACiiigAooooAKKKKAM3XLNbzS54yMsV4rxG8tmtLp4WGCpxXv5AIwa8q8daT9lvRcqOJW5rlxMLrmPeyTE8s3SfU46iiiuA+qCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoopUXc6qO5xQDdjpfBmmG91VHdcxj+devooRAo6AYrmPBelfYdMBdcOxzXU16dCHLE+JzTEe2ru2yCiiitjzQooooAKKKKACiiigAooooAKKKKAEJwCTXk3jjVhe34gRj+6JBr0bX74WGlzS5wwXIrxO8nN1dyznq5zXJiZ2XKe/kmG5puq+hBRRRXCfUhRRRQB2fgTVvs94LNmwHJNepqQQCO9eA2Vy9pdpMhwwNe36PeJeafE6nJ2jNd+GndWPlM7w3JNVF1NCiiiuo8IKKKKACiiigAooooAKKKKACiiigArB8VaYl/pcjFcuikrW9TJEEkbK3Q0pK6saUajpzU10Pn2SNopCjDDDqKbXReL9Max1eWTGEkbK1zteTKPK7H31CqqtNTXUKKKKk2CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACtnw1pn9p6skRB2j5s1jAZOK9O8A6V5NqbmRcPnjPpWtGHNI4MxxHsaDfU7WCIQwIgGNqgVJRRXqHw7d3cKKKKBBRRRQAUUUUAFFFFABRRRQAUUVWvrlbW0kmY4CjNDdhxi5OyOC8f6vlktom45DivPq0NavDeapcSlsqXJFZ9eVVnzSufd4GgqFFRCiiiszsCiiigAr0f4f6tmJrSRvmJyK84rU0C/OnarHMDjtWtKfLK5xY/DqvQcep7nRUFpMJ7ZJAQcqDU9eoj4VqzswooooEFFFFABRRRQAUUUUAFFFFABRRRQByPjfSVvLA3AXLRAnivJiCDgjBr6Au4Bc2zxMOGGK8T1+xay1WZCuF3ccVw4mFnzH0+R4m8XSfQy6KKK5D6EKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAu6VZPf38cKDnOa9v062W1s441GMKM1wPgDSC8n25xjaSOa9JAxXoYaFo3Pks6xPtKvs1sgooorpPECiiigAooooAKKKKACiiigAooooAK43x1qgtbIW6tzKCDiuvmkWKJnY4AFeNeK9Sa+1WVM5RG+WsK8+WJ6mU4b2tdN7IwKKKK80+0CiiigAooooAKVTtYH0NJRQG56z4H1T7Zp2x2+dTgCuurxzwfqf2HVkV2xGa9hjcOisO4zXpUJ80T4rNcP7Gu2tmOooorc8wKKKKACiiigAooooAKKKKACiiigArgfH+lB4UuY1+7ksa76qGr2S3+nSwH+IYqKkeaNjqwdd0aymeD0Va1G1NlfzQEcI2BVWvKas7H3kJKUVJBRRRSKCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAqW3heedY0BJJ6Coq63wPpRutSW5YZjTINXCPNKxz4qsqNJzZ6NoVgthp0aKuMgE/WtSkVQqgDoKWvVSsrHwVSbnJyfUKKKKZAUUUUAFFFFABRRRQAUUUUAFFFITgE0Ac/4t1JbHSZFzh3XC143I7SuXY5Y9a6/x3qv2q9Fsp4iauOrzsRPmlY+yyjD+yocz3YUUUVznrBRRRQAUUUUAFFFFAD4nMcqsDjBr2nwzqY1LTEcHO35fyrxOu38A6o0N2LMthDlq6MPPllY8jOMN7WjzLdHqFFIDkZFLXonxwUUUUAFFFFABRRRQAUUUUAFFFFABQelFFAHl/j3SfIuUuI14fJYiuIr27xJp4v9KmULl9vFeLXEJt7h4m6ocGvOxEOWVz7DJ8T7WjyvdEVFFFc57AUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAORC7hQMkmvZPCelJp2mLhcF8NXnHhPTTqGrxhh+7HU17JDGIoVQdFGK7cLD7R83nmJ2pIkooorsPmwooooAKKKKACiiigAooooAKKKKACs7Wr0WWmTSbsMFyK0a88+IGrEeXbRNzkhgKzqS5Y3OvBUHWrKJwd9dNeXkk7nLOcmq9FFeW3dn3cYqKSQUUUUigooooAKKKKACiiigAq1p13JZ3kcsbYOQM1Vozg5pp2dyZxUouLPetKu1vLGORTn5RmrtcF8P8AVd9ubN2y+Sfwrva9WnLmjc+DxlB0azgFFFFWcoUUUUAFFFFABRRRQAUUUUAFFFFADWUMpB715D4z0r7DqbSqvEpLV7BXL+M9LF5pckwXLxr8orGvDmielleI9jXV9meQUUrqUcqwwR1FJXmH2qdwooooGFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUdTRWloennUtUit8cNTSu7EVJqEXJ9D0TwLpX2WxaaRPmYgqTXY1XsrcW1pHEP4VAqxXqwjyxSPgcVWdaq5sKKKKswCiiigAooooAKKKKACiiigAooooAgvJ1trWSUnhRmvE9fvjf6tNKGyhbivRfG+r/YrARKcmXKmvJic1w4md3yn1GR4a0XVfUKKKK5D6AKKKKACiiigAooooAKKKKACiiigDX8OaidO1RJM8H5a9rtpRNAjqc5ANfPyttdWHY5r1zwXqv27TFVz84OMV2YWf2T53PMNdKqjqqKKK7T5kKKKKACiiigAooooAKKKKACiiigAqKeJZoWjYZBqWigadndHiXiXTX0/VJcjCuxK1i16f4+0vzrT7Wq/6sc15gRivLrQ5ZH3GXYj29BPqFFFFZHeFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAV6L4A0nCNdyJhgflJrgrG1a9u0gTq1e36PZrZ6fFGowdozXVhoXlc8TOsTyUvZrdmhRRRXefJBRRRQAUUUUAFFFFABRRRQAUUUUAFMlcRxs7dAKfWH4o1FbDSpcnDOpApSdlc0pU3UmoLqebeL9Ta91aSMHKI3Fc7T5JGlkLucse5pleTOXM7n31CkqVNQXQKKKKk2CiiigAooooAKKKKACiiigAooooAK6fwXqn2HVVWRv3bDGPeuYqSCQxTxuDjawP61UJcsrmGIpKrScH1PoFGDoGHcZp1Y/hzUBqOlpKDntWxXrRd1c+BqQdObi+gUUUUyAooooAKKKKACiiigAooooAKKKKAKmo2i3tlJA4yGFeI6taNZ6jNGRgBjiveeorzbx/pJWRbqNMKASxFc2JheNz2slxPs6vs3szgaKKK88+uCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoop8cbzOEQZY9BQJuyuzsfAmkG5vBdsuRG1epAADA6VieGNNWw0uPAwXUE1uV6lGHLE+GzHEe3rt9EFFFFanCFFFFABRRRQAUUUUAFFFFABRRRQAhIAya8u8ear9ouvsiN/q25r0PV7xLPT5ZGbBCnFeI6hdte3rzt1auXEzsrHu5Jhueo6j6FWiiiuA+rCiiigAooooAKKKKACiiigAooooAKKKKACiiigDu/AGreXcfY3bCYyK9LByMivBdMu2s76ORTj5hn6Zr2/TLtLyyjlQ5GK9DDTvGx8lnWG5KntFsy5RRRXSeIFFFFABRRRQAUUUUAFFFFABRRRQAVl69p66jpc0GOWGM1qUhGQRSaurF05uElJdDwC7gNtdywn+BiKgrrfHGlG01ASxp8j5LGuSrypx5ZWPvcLWVakpoKKKKg6AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArpvBmmm81ZJSuY0PNc0Bk4Fet+CdJFjp3msOZcNW9CHNI83NMR7Gg7bs6mNAiBQOBTqKK9I+J3CiiigAooooAKKKKACiiigAooooAKKKhuphBbSSn+EZoGld2Rw/j/VdkSW0bfNn5hXm1aviG/OoavNMGypPArKry6s+aVz7nL8P7Ggo9QooorI7gooooAKKKKACiiigAooooAKKKKACiiigAooooAM4Oa9O+H+qCWz+xs2XXJ5rzGtnw1qTadqiMDgOQta0Z8sjgzHD+3oNdT26io4ZBJErA5yKkr1D4dqzsFFFFAgooooAKKKKACiiigAooooAKKKKAOc8X6Z9u0mUqMyAcV49KhilZD1U4r6BkQSRlWGQRXjXizTTp2qkYwJCWFceKh9o+kyPE70WYFFFFcR9IFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAa/hzT21DVoUK5TPzV7VawC3tkiXooxXF+AtJ8m3a4kXlsFTXdV6OHhyxufHZvifa1uVbIKKKK6DyAooooAKKKKACiiigAooooAKKKKACuV8a6r9g07Yp5fK11EjiNGY9AK8g8ZambzVpIlbManisa8+WJ6WV4f21dX2RzROSSaSiivMPtkFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAU5GKSK46g5ptFAmrqx7B4N1MXulortmQda6evIvBGp/Y9U2O2EYYAr1xTlQfWvToT5onxOZ4f2Nd22YtFFFbHnBRRRQAUUUUAFFFFABRRRQAUUUUAFcb470kXOntdKMyIMCuyqve26XVs8TjIIqJx5o2N8NWdGqpo8BIKkg9RSVo61YvY6lKjDALEj6VnV5TVnY++pzU4qS6hRRRSLCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAq5pdmb7UIrcDO84qnXd+AdJ82Z7qRfuMCtaU480kjlxtdUaLkegaZaCz0+GEAAqoFXKBwKK9RKyPg5Scm2wooopkhRRRQAUUUUAFFFFABRRRQAUUUhOBmgDH8S6gLHSpCTgsCBXiksjSyM7Eknua7fx9q/nT/AGJT9wg8VwtediJ80rH2GT4f2dHme7Ciiiuc9gKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAmtZjBdRygkbWBr2zw/qI1HTI5s8mvDa77wBq5EzWchwirkV04adpWPGznDe0pc63R6TRQORRXoHyAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB574/0jcv21V4QYOK86r3fWLFb/T5IWHUV4hfW7W13LGwxhjiuDEwtK59bkuJ56Xs3uivRRRXKe2FFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAE1rbtc3CQoMsxwK9q8PaethpcK4w5X5q868EaWbzUhOR8sRzzXraqFUAdBXdhoWXMfL53ieaSpLoLRRRXWfPhRRRQAUUUUAFFFFABRRRQAUUUUAFU9TultbGWQnBCnFXK4Xx9qxhtltomw+eR7VFSXLG504Sg61ZQR57qV499fSTv1PFVKDyaK8pu7ufewioxUUFFFFIoKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKvaTdvZ6hFIpx8wz9Ko0A4OR1pp2dyJwU4uLPfdPulu7RJUOQRVquJ8Bar59iLRmyyDPNdtXqwlzRufBYqi6NVwYUUUVZzhRRRQAUUUUAFFFFABRRRQAUUUUAIRkEeteW+PNK8i+FxGuI8cn3r1OsPxPpg1LSpIwPm61lWhzRO7LsQ6FdPozxSipJ4zFO8ZH3WIqOvLPuU7q6CiiigYUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABSqpdgqjJNJW54X01r7VYmxlEb5qqK5nYyrVFTg5Poej+D9LWy0uOXbhpFBNdJUcESwwqijAAqSvVjHlVj4GvVdWo5vqFFFFUZBRRRQAUUUUAFFFFABRRRQAUUUUARXEohgeQn7oJrxbxNqJ1LWJJQTtxjFekeMtUGn6WcH5nO3ivH2bc5b1Oa4sVP7J9LkeG0dViUUUVxn0YUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAG54X1M6dqicnEhC17RFIssasDkEV8/ROY5VcdVOa9h8IamL3SIlY5kA5rtws/snzeeYbaqjo6KKK7D5sKKKKACiiigAooooAKKKKACiiigAprrvQr6inUUAeO+MdKOn6oSo+V8tmuar1vxvpn2vTGkRcyLXkrDaxU9QcV5leHLI+2yvEe2oK+6EooorE9IKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAxmvVPAmki1svtDLzKAa870aya91OGIDILYNe3WFsLOzjhA4RcV14WF3zHgZ3ieWCpLqWaKKK7j5YKKKKACiiigAooooAKKKKACiiigAprsEQsegGadWT4g1BdP0ySRjjcCBSbsrl04Oc1FdTzjxrqn2zVHiRsxriuVqW4maed5GOSSairypy5pXPvsNSVKkoIKKKKg3CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK67wNqn2TUTE7fK+ABXI1PZTm2vIpgfuNmrpy5ZXOfFUVWpODPfwcjNLWZoV+NR0yKfPLCtOvVTurnwU4OEnF9AooopkBRRRQAUUUUAFFFFABRRRQAUUUUARXEQmgdCM5BrxPxDprabqbxsPvEsPzr3GuE8faSJbc3qrl1wOK58RDmjc9fJ8T7KtyvZnmdFHQ0V5x9iFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRViytzdXkUAH32xTSuTKSim2d14A0n79zKnXBU16LWbodkLHS4YcYKrg1pV6lKPLGx8Jja7rVnIKKKK0OQKKKKACiiigAooooAKKKKACiiigAPFebfEDVRI/wBhByAQ1d/qFwttZyOzAYU4zXh+q3rX1/JK5yckVzYmdo2PayXDe0q+0eyKVFFFeefXBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB6F8P8AVclrSRvlUDaK9FrwjRbxrPUoXBwNwzXt9jcrd2iTIchhXoYad42PkM5w3s6vOtmWKKKK6TxgooooAKKKKACiiigAooooAKKKKACqepWiXlnJE4yCKuUEZGKTV0VGTi00eB6lZvZXskT9dxI/Oqld14+0kx3H2xF+XAFcLXl1I8srH3mDrqtRUwooorM6gooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArsfAmki7vjPInEeGU1yMMTTTLGvVjgV7L4U0wWGlRZXEhX5jXRh4c0rnk5vifZUeVbs3gMCloor0T40KKKKACiiigAooooAKKKKACiiigAoopk0gjiZyegzQCV9Di/H2qiGz+zI2JCQT9K8vJySfWt3xVqP9o6u8inKgbfyrCry60+aR9xluH9jQS6sKKKKyPQCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigABwcivVfAeq/adPW1J5iWvKq6DwlqZ0/VEQHAlYA1tRnyyPOzPD+2oNLdHs9FMjcSIGU5Bp9emfEPQKKKKACiiigAooooAKKKKACiiigAooooAyPEOnLqWmPER0Ga8UuImhuJI2BBDEc19AOoZCp7ivJPG+lmz1QyouIyB+dcmJhdcx9BkmJ5ZOk+pytFFFcJ9QFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUoBYgAZJoA6HwjpbX+ppJjKxMCa9jjQIgVRgCuT8DaWLXS0uCMPKvIrrq9KhDlifFZrifbV2lsgooorc8wKKKKACiiigAooooAKKKKACiiigArnPF+qf2fpbFT8x4xXRM21ST2FeTeOdUa61Iwo2YwOnvWNafLE9DLMP7aur7I5R23yM3qSabRRXmH26VlYKKKKBhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAU+KRopVkU4ZTkUyigTV1Y9n8J6iL7SIQzZkC/NXQV5T4F1U2t8YHbiTAUV6sORXqUZ80T4fMsP7Gu10YUUUVqcAUUUUAFFFFABRRRQAUUUUAFFFFABXM+MtK/tDSztHzKd2fpXTVHPGJYXQ9wRUyjzKxrQqulUU10Pn51KOynscU2tzxRph07VnRV+Q85rDrypR5XY++o1FUpqa6hRRRUmoUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFamg2D32pxKqkqrAt9Ky69J8AaT5cRvHXIkXitaUOaVjizDEKhRcup3FrbrbW6RIMBRU1FFeofCttu7CiiigQUUUUAFFFFABRRRQAUUUUAFFFHSgDM12+Ww02SVjjjFeI3U73FzJI7ZJY4rvPH+q5P2FWyCM157Xn4md5WPrsmw3s6XO92FFFFcx7QUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBZ0+5NpfQzg/cbNe3aNfLf6bFNnJZcmvCK9G8AatvV7WQ4CgBc11YadnY8TOsNz0vaLdHoNFFFd58kFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAcV490rz7D7RGmZARnHpXlpGCQa9+vrdbi0ljYZypA/KvENYsW0/UZIG6g5rhxMLPmPqckxPNB0n0KFFFFch74UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAXNMtWvNQhiAyGYA17dpVithYRwKOFFee+AtKMt01zIvyYypNen9BXfhoWjc+TzrE89RU1sgooorqPDCiiigAooooAKKKKACiiigAooooAKr3twltavI5wADViuN8d6p9nsDbI2HbmonLljc6MLRdaqoI861m/fUNQkkfsxA+maz6UnJJPc0leU3d3PvacFCKigooopFhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABWloV81jqkDhsLv+as2lBIORwRTi7O5FSCnBxfU9/s7hbq1jmXowzU9cl4H1UXmmiAtlohg11tetCXNG58DiaTpVXB9AoooqjAKKKKACiiigAooooAKKKKACiiigAPIrznx/pAX/TUX5iQDXo1Zut2C3+nSIw6KSKzqw5o2OvA13QrKR4VRU95bvbXTxOMEGoK8tqx93GSkroKKKKRQUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUtvEZ7iOIdWbFRV1PgrSvt2ol3X5Uwwq4R5pWMMTWVGk5s9F8N6cNP0iKIj5gOTWzTVUKoA7U6vVirKx8DVm6k3J9QooopkBRRRQAUUUUAFFFFABRRRQAUUUUAMkcJGzZ6CvGvFmqf2jqrFW+VMrivSPFep/2fpMjK3z9MV43K5kldz1Yk1x4qf2T6PI8NvVYyiiiuI+lCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA6PwhqZsdUSPdhZGwa9ijdZEDKcg18+wytDKsi/eU5Fez+FtRW+0qIbsuq/NXbhZ/ZPmc8w1mqqN2iiiuw+dCiiigAooooAKKKKACiiigAooooAKRgGUg9DS0UAeU+O9KNtqBu1XEb4H41x1e0eKtLXUdLYEcoC1eMyIUcqeoNediIcsrn2WUYn2tHle6G0UUVznrBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAORDI6oOpOBXr3gzShZaVHI64kYc15x4a05tR1VFUZ2EMa9qgiEUKoowAK7MLD7R85nmJslSRJRRRXafNBRRRQAUUUUAFFFFABRRRQAUUUUAFITgE0tZ+s3y6fp0k7HgCk3ZXKhFzkorqec+OtV+06gIYnzGBz9a46p7yc3F3JITnLEioK8qpLmlc++wlFUaSggoooqDoCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK7XwFqot7trZ2/1hAXNcVVrTrs2V9HODjYc1pTlyyuc2MoKtRcGe+jkUVR0i6F5psM2cllyavV6id1c+CnFxk4voFFFFMkKKKKACiiigAooooAKKKKACiiigBksYkiZD0IxXjni7TDY6vIVXERxivZq5LxvpQvNO8xB8yHcSKwrw5onp5VifY10nszyWig8GivNPtQooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKAMnAoq/o9k97qMUaLnDAn6U0ruxFSahFyZ6D4C0lYbNb1h87jFdvVWwtUtLRIkGAB0q1XqwjyxsfBYqs61VzYUUUVZzhRRRQAUUUUAFFFFABRRRQAUUUUAFefeP9X2L9hVvvjNd1dzrb2zyMcACvEtdv3v9SkdjkKxA+lc+Inyxsexk+G9pW53sjMooorzj7AKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAPSfAGqmWKS3kb7pAUV31eHeHr42OrQuWITd81e12s63FskqnIYZr0cPPmjY+OzjDeyrc62ZNRRRXQeQFFFFABRRRQAUUUUAFFFFABRRRQAVBeQC5tZIiM7lIqeih6jTs7o8M1/TjpuqSwYwB0rLr0jx/pJaJbqJcuT830rzevLqw5ZWPusBiPb0FLqFFFFZHaFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFd/4A0nMn21hwRgZrh7OBri6jjUZ3MBXtmhaeunadHCBjjNdOGheVzxc5xPs6XIt2alFFFegfIhRRRQAUUUUAFFFFABRRRQAUUUUAFFFNdgqEntQByXjrUhb6W9urYd+RivKCSxJPU10PjDUzqGqkBjiPK4rna8yvPmkfbZXh/Y0FfdhRRRWJ6QUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAKCQcg4NeueCtVF7poiJ5iAWvIq6bwZqj2eqxwZxHI3NbUJ8sjzM0w3tqDtuj2Gimo4dAw6GnV6Z8UFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAUdWtFu7CWMjJKnFeIajZtY3rwOMFa98IyK8w8faV5N19sVf9YccVy4mF1c9zJcTyVHTezOIooorgPrAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiinRoZJFQdWIFAm7K51vgXSTd6gZpFzGBwfevV1G1QB2rn/COm/2fpCKy4YnNdDXqUYcsT4jMsR7au30QUUUVqeeFFFFABRRRQAUUUUAFFFFABRRRQAVheKdSGn6TK4bD44rdPAry3x5qvn3iwRt8gBDfWsq0+WJ3Zdh/bV0uiOOnlM07yHqxzUdFFeWfcpWVgooooGFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUkE728yyp95elR0UITSasz27w3qK32lxENllUbvrWzXmPgDVPJuWtGb/WHjNenA5FepSnzRufC5hh/Y13HoFFFFanEFFFFABRRRQAUUUUAFFFFABRRRQAVi+JNOW/0yTK5ZVJFbVNdQ6FT0NKSurGlKo6c1JdD5+mheCUxuMMOtR10/jTS2s9VknC4jc8VzFeTOPLKx97h6qq01NBRRRUm4UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFdB4S0salqqq6navzVgKpZgo6k4r1fwPpP2TT1nkXEh71tQhzSPOzPE+xoO27OsiQRxqoHQYp9FFemfEN3CiiigAooooAKKKKACiiigAooooAKKKKAKOrXosNPknJxtFeIahcNdXssrHO5iRXoHj/VfLh+xq3+sXnFea1wYmd3Y+syXDclP2j3YUUUVynuBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAFzTL02F/HcAn5T2r2/TLlbqwhlByWUE14JXpvgLVxNbtbyN8wOFrqw07PlPCzvDc9NVFujuqKKK7z5QKKKKACiiigAooooAKKKKACiiigAooooA5jxnpgvtLL7eYgWrx8gg4Iwa+grmFbi3eJhkMMV4v4m082GrTKFwhPFcWKh9pH0uR4nR0mYtFFFcZ9GFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAGlodg2oanHEozggn869utIFt7dEUYAArhfh/pICfbmXDcjmvQa9HDwtG58fnGJ9pW5FsgoooroPHCiiigAooooAKKKKACiiigAooooAKiuZ1t4Gkc4AFS1yfjfVBaaW8CnEki8VM5cqubYek6tRQXU868Q6g9/qkpY5VWIWsmlZizFieT1pK8mTu7n31KCpwUV0CiiikaBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABWz4a1A6fq8UhbCZ5rGoHFVF2dzOtTVSDg+p9BW0wnt0kB4YZqWuZ8G6oL/SwpPMYC101erGXMrnwNek6VRwfQKKKKoxCiiigAooooAKKKKACiiigAooooAK4bx5pPn2y3Ea8qSWNdzVTUbUXdjLCQPmXFRUjzRsdOErujVU0eB0Ve1eyNhqU1uR9w4qjXlNWdj7yE1OKkuoUUUUiwooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACrNhavd3kcaDJ3DP51Wrt/AOledd/a3GUGRV0480rHNjK6o0XM9B0iySxsI40GOATV+kAwMDtS16qVlY+CnJyk5MKKKKZIUUUUAFFFFABRRRQAUUUUAFFFFACMwVST0ryDxpqov8AU/LXOIiVr0fxJqAsNJmkDYcLxXi1zMbi4klPVzmuTFT05T6HI8NeTqsiooorhPpwooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAOq8E6obTVEt2bEbnJr1tGDqGHQ18/28zQTLIhwRXtnh6/S+0yNlOSqgGu7DTuuU+XzvDcslVRrUUUV1nz4UUUUAFFFFABRRRQAUUUUAFFFFABRRRQB5r4/0ny5UuokyXOWIrgq9y1+xF7pcyYy23ivE7q3a1uXhf7ynBrz8TC0rn1+TYn2lLke6IaKKK5j2QooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAJIYzLMiKMkkCvafDOmLpulogAy3zfnXnHg3TDe6qrOmYwOvvXr8ahEVR0AxXdhoacx8xnmJu1SQ6iiius+eCiiigAooooAKKKKACiiigAooooAKDxRVPU7tbKxlmY42jNJuyuVGLlJRR574+1Uy3CW8bcDIYVw1XdUujeajPMTkM5IqlXl1Jc0rn3mDoKjRUQooorM6gooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACu9+H+qeXK1mzffORXBVe0i+bT9RjuF6jitKUuWVzkxtBVqLie89RRVTTrkXVnHIDnKgmrdeondHwcouLaYUUUUxBRRRQAUUUUAFFFFABRRRQAUUUUAIw3KQeleT+OdKFpf/aFXiVua9ZrnfF2lrf6VI+MtGuVrGtDmiehluI9jXTezPGqKdIjRuUYYI602vMPt07q4UUUUDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAClVdzhfU4pK2PDenf2jq0cZXK9c1UVd2M61RU4OT6Ho/gvSvsGlguvzsd2frXUVFbxCGBEAxgAVLXqwjyqx8DXqurUc31CiiiqMQooooAKKKKACiiigAooooAKKKKACuG8far5Fstqhz5gIOK7S4lWCFpGOABXiviS/a+1abLZRW+WufET5Y2PWyjDe1rcz2Rj0UUV5x9kFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFHQ5oooA9R8BaqZ7JoJXy4OBn0rtq8T8MaibDWInZsJ3r2iGQSwo4/iGa9LDz5o2PjM3w/sq/MtmSUUUVueUFFFFABRRRQAUUUUAFFFFABRRRQAUyaNZYmRhkEU+igE7anivirTWsNWlbGEdvlrCr1bxzpP2ux+0KvMQJrykjBwa8ytDlkfcZbiPbUE+qCiiisT0AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAAycV6b4C0jy7Y3Ugw+ePpXn+lWT39/HCgySc17fptqtpZRxquPlGa6sNC75jws7xPJT9kt2W6KKK7z5QKKKKACiiigAooooAKKKKACiiigAoopGYKpJPAoA5nxnqYstKeIHDyAhTXkDMXYsxyT1NdT421Q3momANkRNiuVrza8+aR9plWH9jQTe7CiiisD1AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAVW2sCOxr2Twjqo1HTBuPzJ8uK8arrfA+qm11FbZjhGya3w8+WR5WbYb2tBtbo9aopFYMoYdCKWvSPjAooooAKKKKACiiigAooooAKKKKACiiigCve263dpJCw4YYrxLXLFrDVZ4tuFDYWvda898f6TuCXUa/dBLEVz4iF43PZybE+zq8j2Z51RRRXnH14UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRU1tA9zcLGgySelNK4pNRV2dv8P8ASvMlN8w+6dtelAYFZPh/T00/TY0VcFgCfrWtXqUocsbHwmPxHt67l0CiiitDjCiiigAooooAKKKKACiiigAooooAKyfEF8LLSpn3YbbxWtXmvj7V/MlS1ibG0kMBWdWfLG52YCg69dROIup2ubl5mOSxzUNFFeW3c+7iklZBRRRSGFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABU1rO1vcJIhIINQ0U07ClFSVme56Ffrf6dG6noADWpXnPw/wBWwTYs2SSSK9Gr1KUuaNz4THUPYVnEKKKK0OMKKKKACiiigAooooAKKKKACiiigArO1qxF/pk0OOWXANaNIeRSaurFQm4SUl0PAr+2NnfSwHqjYqtXaeO9J+zXS3EacOSWNcXXlVI8srH3uErqtSU0FFFFQdIUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFdR4Lso5dTS4lPyrkYNcvUsV1PB/qpWT6GqhJRd2YYinKrTcIu1z3hbu3VQocYFO+2wf3xXhf9p33/P1J+dH9p33/P1J+ddf1ryPB/sF/wAx7p9tg/vij7bB/wA9BXhf9p33/P1J+dH9p33/AD9SfnR9a8g/sF/zHun22D++KPtsH/PQV4X/AGnff8/Un50f2nff8/Un50fWvIP7Bf8AMe6fbYP74o+2wf8APQV4X/ad9/z9SfnR/ad9/wA/Un50fWvIP7Bf8x7p9tg/vij7bB/z0FeF/wBp33/P1J+dH9p33/P1J+dH1ryD+wX/ADHun22D++KPtsH/AD0FeF/2nff8/Un50f2nff8AP1J+dH1ryD+wX/Me6fbYP74o+2wf89BXhf8Aad9/z9SfnR/ad9/z9SfnR9a8g/sF/wAx7ZfanBb2csocfKua8W1e8N9qc8+ch2yKjbUbx0KtcSFT1BNVaxq1ufQ9HL8uWFbbd2wooorA9QKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA0NGvm0/UY5lOOcV7RaalbzW0biUHKjNeD9KspqN4i7VuJAPQGt6Vbk0PLx+WrFNSTsz3b7bB/z0FH22D/noK8L/tO+/wCfqT86P7Tvv+fqT862+teR539gv+Y90+2wf3xR9tg/56CvC/7Tvv8An6k/Oj+077/n6k/Oj615B/YL/mPdPtsH98UfbYP+egrwv+077/n6k/Oj+077/n6k/Oj615B/YL/mPdPtsH98UfbYP+egrwv+077/AJ+pPzo/tO+/5+pPzo+teQf2C/5j3T7bB/fFH22D/noK8L/tO+/5+pPzo/tO+/5+pPzo+teQf2C/5j3T7bB/fFH22D/noK8L/tO+/wCfqT86P7Tvv+fqT86PrXkH9gv+Y90+2wf3xR9tg/56CvC/7Tvv+fqT86P7Tvv+fqT86PrXkH9gv+Y9X8Ux29/pEqhgXA+WvIJYzFKyH+E4qdtSvWGDcyEfWqxJYkk5JrCrUU3c9XAYOWFi4t3EooorE9AKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP//Z" + +base64 = "data:image/jpeg;base64,/9j/4QC8RXhpZgAASUkqAAgAAAAGABIBAwABAAAAAQAAABoBBQABAAAAVgAAABsBBQABAAAAXgAAACgBAwABAAAAAgAAABMCAwABAAAAAQAAAGmHBAABAAAAZgAAAAAAAABgAAAAAQAAAGAAAAABAAAABgAAkAcABAAAADAyMTABkQcABAAAAAECAwAAoAcABAAAADAxMDABoAMAAQAAAP//AAACoAQAAQAAALAEAAADoAQAAQAAALAEAAAAAAAA/+EOLmh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8APD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc+CjxyZGY6UkRGIHhtbG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyc+CgogPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9JycKICB4bWxuczpBdHRyaWI9J2h0dHA6Ly9ucy5hdHRyaWJ1dGlvbi5jb20vYWRzLzEuMC8nPgogIDxBdHRyaWI6QWRzPgogICA8cmRmOlNlcT4KICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0nUmVzb3VyY2UnPgogICAgIDxBdHRyaWI6Q3JlYXRlZD4yMDIyLTA5LTE0PC9BdHRyaWI6Q3JlYXRlZD4KICAgICA8QXR0cmliOkV4dElkPmU3ZTM3MmU5LTAwZjgtNGJiYi1iNDNiLTFjMzlkMTllMTllMDwvQXR0cmliOkV4dElkPgogICAgIDxBdHRyaWI6RmJJZD41MjUyNjU5MTQxNzk1ODA8L0F0dHJpYjpGYklkPgogICAgIDxBdHRyaWI6VG91Y2hUeXBlPjI8L0F0dHJpYjpUb3VjaFR5cGU+CiAgICA8L3JkZjpsaT4KICAgPC9yZGY6U2VxPgogIDwvQXR0cmliOkFkcz4KIDwvcmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PScnCiAgeG1sbnM6ZGM9J2h0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvJz4KICA8ZGM6dGl0bGU+CiAgIDxyZGY6QWx0PgogICAgPHJkZjpsaSB4bWw6bGFuZz0neC1kZWZhdWx0Jz5SRU1PVkUtQkctQiZhbXA7Vy1XLUlMTFVTVFJBVElPTi1hbnRsZXItdGVhbS1wcm9maWxlLWltYWdlcyAoMTIwMCDDlyAxMjAwcHgpPC9yZGY6bGk+CiAgIDwvcmRmOkFsdD4KICA8L2RjOnRpdGxlPgogPC9yZGY6RGVzY3JpcHRpb24+CgogPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9JycKICB4bWxuczpwZGY9J2h0dHA6Ly9ucy5hZG9iZS5jb20vcGRmLzEuMy8nPgogIDxwZGY6QXV0aG9yPkphY2sgTWNEb25hbGQ8L3BkZjpBdXRob3I+CiA8L3JkZjpEZXNjcmlwdGlvbj4KCiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0nJwogIHhtbG5zOnhtcD0naHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyc+CiAgPHhtcDpDcmVhdG9yVG9vbD5DYW52YTwveG1wOkNyZWF0b3JUb29sPgogPC9yZGY6RGVzY3JpcHRpb24+CjwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9J3cnPz7/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCASwBLADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwCvRRRXjH6QFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAPhiaaZY16scCvSNJ8B2Fxp8UlyHErD5sGuW8I6X9v1NXIOIiDXsaIEQKBgCuzD0k1dnzucY6dOSp03Y5L/hXmkf9NP8Avqj/AIV5pH/TT/vquvorp9lDseJ9fxP87OQ/4V5pH/TT/vqj/hXmkf8ATT/vquvoo9lDsH1/E/zs5D/hXmkf9NP++qP+FeaR/wBNP++q6+ij2UOwfX8T/OzkP+FeaR/00/76o/4V5pH/AE0/76rr6KPZQ7B9fxP87OQ/4V5pH/TT/vqj/hXmkf8ATT/vquvoo9lDsH1/E/zs5D/hXmkf9NP++qP+FeaR/wBNP++q6+ij2UOwfX8T/OzkP+FeaR/00/76rjfFXh1NGuMwA+Tjv617DXPeLdLXUNKcY+Zfm/Ks6tGLjojrwWY1o1l7SV0eM0U6RSkjKRjBxTa84+xTurhRRRQMKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAClVSzBR1NJWt4esGvtVhUDKqwLVUVd2M6tRU4OT6HovgnSvsmmpcMuGkXmusqG2gW3gWJBhQOlTV6sI8sbHwOIrOtUc31CiiiqMQooooAKKKKACiiigAooooAKKKKACmTIJImQ9xin0UAnY8X8V6YdP1V1C4Q81gV6r470n7Vp/nRLmQEEn2rysjBIPUV5leHLI+3yzEe2oJ9UJRRRWJ6IUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFek+ANK8qI3jrxIvFcFplqbu/hiCkgsAa9t0qySwsI4EGAorqw0LvmPCzrE8lP2a3ZeooorvPlAooooAKKKKACiiigAooooAKKKKACiiigAooooAgu4RPbSIRnKkV4jrlg2nanJCR717rXnnxA0nIF6i5YkA4rnxEOaNz2MmxHs63I9med0UUV5x9gFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFS20RnuI4h1ZsUITdldna+AtI82ZrqRcrjK16aOlY3hvThp2kwxEYYDmtmvUpQ5Y2PhcfiHWruXQKKKK1OIKKKKACiiigAooooAKKKKACiiigAooooAKKKKACs/WLJL2wkRhnCnH1rQpGG4EHpSaurFQk4SUkeBX1q9pdvE4wQTVau18eaT5F2bxVwrYFcVXlVI8srH3uErqtSU0FFFFQdIUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABXUeC9J+36nvdfljwwJrmY0MkioOpOBXr/g7TBZaTE7LiRhzW9CHNI8zNcT7Gg0t2dKo2qBS0UV6R8UFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAYfijTl1HSnQjOwFhXi8sbRSMjDBB6V9BSIJI2Q9CMV494x0w2WrSOq4jOMVx4qH2j6LI8TZukzm6KKK4j6YKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiigAk4HWgDb8Mac2oasgxkIQxr2iGNYo1VRgAVxvgPSRBZreMuHcYNdtXpYeHLG58Zm+J9rW5Vsgooorc8oKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK5XxppIvtO3ovzqdxNdVUN1CJ7d4z/ECKmceZWNsPVdKoprofP7DaxB7Ula/iPTjpurSQgfL1BrIrypKzsffUqiqQUl1CiiipNAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAq9pFm95qMUaKWwwz9Ko16D8PtK+Zr1xlWGBmtKUOaVjjx1dUKLkd7YWqWlokSDAAqzR7UV6iVj4SUnJ3YUUUUxBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBwfj/SvNt1uYky+fmI9K80r3vU7VbuxljIySpArxDVLJrC/kt26rXBiYWd0fV5Jieen7N7op0UUVynuhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAE9nA1xdRxqM5YA17boWnrp2mxwqMd6878DaSbq/M0i/uwMg+9erKNqgeld+GhZcx8rneJ5pqkugtFFFdR4IUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFACEZFea+PtJ8qUXiL984Nel1keItPW/0uVWGSqkis6seaNjtwGI9hXUuh4fRUk8LW8zROMMOtR15T0Puk01dBRRRQMKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACnxIZJUQdWYCmV0HhLS/7S1ZVYHamG/KqjHmdjGvVVKm5voekeFNM/s/So1ZcP1rfpkaBI1UdhT69WKsrHwNao6k3N9QoooqjMKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAprqHQqRkGnUUAeR+NtKNnqT3IXEchwK5SvY/F+lDUNMZscxgtXjrKVOD1rzcRDlkfaZTifbUEnuhKKKKwPUCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigBVBZgo6k4r1fwNpX2XT1mkTEhzXneg6cdR1KOMfwkN+te2WsKwW6IoxgCuvDQ15j5/O8TaKpLqT0UUV3Hy4UUUUAFFFFABRRRQAUUUUAFFFFABWZreprpenS3HGVGQDWnXmfj/Vi862sbZXBDVnVnyRudmBw/t6yh0O+0vUE1G0WVDnI5q9Xnfw+1QgPau2SzcZr0SinPmjcnG4f2FZwCiiitDlCiiigAooooAKKKKACiiigAooooAjniE0Lxt0YYrxjxTpxsNYmAXEZPy17XXF+O9J+1WizovKZY1hiIc0bnq5TifZV+V7M8sooorzT7MKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKOpoqzY2z3V3HGgySwz9M00ruxM5KMW2d98P9J2xfbnHJyvNegVQ0iySxsI4kGOATV+vVpx5Y2Pg8ZXdas5hRRRVnKFFFFABRRRQAUUUUAFFFFABRRRQBV1C7Wys5J2PCjNeIatdtealPKTkFyRXonjzVvs9n9lU/61a8t6muHEzu+U+qyTDcsHVfU0tC1A6dqkU+eB1Fe22VwLm0jlB+8oNeAA4Oa9X8DasbywaOQ/Mh2gUYadnyk53huaKqrodhRRRXcfLhRRRQAUUUUAFFFFABRRRQAUUUUAFVr+2F3ZSwkfeGKs0UPUcZOLujwnWrE6fqk0GOFPFZ1eheP9JI2XUS8k5Y157XlVYcsrH3eBrqtRUgooorM7AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACu38A6WZLv7Wy5TBHNcZBGZp0QAnLAV7V4b0xdN0xIwOvNdGHhzSuePnGJ9lR5FuzYAwMUtFFeifHhRRRQAUUUUAFFFFABRRRQAUUUUAFRzSrDEzscACpK5vxhqQstJkQNiR1+WplLlVzWhSdWooLqeb+JtSa/1WUE5VGIWsSnSOZJGdupOTTa8qT5nc++o01TgooK6DwlqRsdXiDNiM9a5+nIxRwynBFEJcruFekqtNwfU+gonEkSuOhGafWH4X1NdR0xCDygC1uV60XdXPgKtN05uD6BRRRTMwooooAKKKKACiiigAooooAKKKKAM3XLNbzS54yMsV4rxG8tmtLp4WGCpxXv5AIwa8q8daT9lvRcqOJW5rlxMLrmPeyTE8s3SfU46iiiuA+qCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoopUXc6qO5xQDdjpfBmmG91VHdcxj+devooRAo6AYrmPBelfYdMBdcOxzXU16dCHLE+JzTEe2ru2yCiiitjzQooooAKKKKACiiigAooooAKKKKAEJwCTXk3jjVhe34gRj+6JBr0bX74WGlzS5wwXIrxO8nN1dyznq5zXJiZ2XKe/kmG5puq+hBRRRXCfUhRRRQB2fgTVvs94LNmwHJNepqQQCO9eA2Vy9pdpMhwwNe36PeJeafE6nJ2jNd+GndWPlM7w3JNVF1NCiiiuo8IKKKKACiiigAooooAKKKKACiiigArB8VaYl/pcjFcuikrW9TJEEkbK3Q0pK6saUajpzU10Pn2SNopCjDDDqKbXReL9Max1eWTGEkbK1zteTKPK7H31CqqtNTXUKKKKk2CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACtnw1pn9p6skRB2j5s1jAZOK9O8A6V5NqbmRcPnjPpWtGHNI4MxxHsaDfU7WCIQwIgGNqgVJRRXqHw7d3cKKKKBBRRRQAUUUUAFFFFABRRRQAUUVWvrlbW0kmY4CjNDdhxi5OyOC8f6vlktom45DivPq0NavDeapcSlsqXJFZ9eVVnzSufd4GgqFFRCiiiszsCiiigAr0f4f6tmJrSRvmJyK84rU0C/OnarHMDjtWtKfLK5xY/DqvQcep7nRUFpMJ7ZJAQcqDU9eoj4VqzswooooEFFFFABRRRQAUUUUAFFFFABRRRQByPjfSVvLA3AXLRAnivJiCDgjBr6Au4Bc2zxMOGGK8T1+xay1WZCuF3ccVw4mFnzH0+R4m8XSfQy6KKK5D6EKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAu6VZPf38cKDnOa9v062W1s441GMKM1wPgDSC8n25xjaSOa9JAxXoYaFo3Pks6xPtKvs1sgooorpPECiiigAooooAKKKKACiiigAooooAK43x1qgtbIW6tzKCDiuvmkWKJnY4AFeNeK9Sa+1WVM5RG+WsK8+WJ6mU4b2tdN7IwKKKK80+0CiiigAooooAKVTtYH0NJRQG56z4H1T7Zp2x2+dTgCuurxzwfqf2HVkV2xGa9hjcOisO4zXpUJ80T4rNcP7Gu2tmOooorc8wKKKKACiiigAooooAKKKKACiiigArgfH+lB4UuY1+7ksa76qGr2S3+nSwH+IYqKkeaNjqwdd0aymeD0Va1G1NlfzQEcI2BVWvKas7H3kJKUVJBRRRSKCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAqW3heedY0BJJ6Coq63wPpRutSW5YZjTINXCPNKxz4qsqNJzZ6NoVgthp0aKuMgE/WtSkVQqgDoKWvVSsrHwVSbnJyfUKKKKZAUUUUAFFFFABRRRQAUUUUAFFFITgE0Ac/4t1JbHSZFzh3XC143I7SuXY5Y9a6/x3qv2q9Fsp4iauOrzsRPmlY+yyjD+yocz3YUUUVznrBRRRQAUUUUAFFFFAD4nMcqsDjBr2nwzqY1LTEcHO35fyrxOu38A6o0N2LMthDlq6MPPllY8jOMN7WjzLdHqFFIDkZFLXonxwUUUUAFFFFABRRRQAUUUUAFFFFABQelFFAHl/j3SfIuUuI14fJYiuIr27xJp4v9KmULl9vFeLXEJt7h4m6ocGvOxEOWVz7DJ8T7WjyvdEVFFFc57AUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAORC7hQMkmvZPCelJp2mLhcF8NXnHhPTTqGrxhh+7HU17JDGIoVQdFGK7cLD7R83nmJ2pIkooorsPmwooooAKKKKACiiigAooooAKKKKACs7Wr0WWmTSbsMFyK0a88+IGrEeXbRNzkhgKzqS5Y3OvBUHWrKJwd9dNeXkk7nLOcmq9FFeW3dn3cYqKSQUUUUigooooAKKKKACiiigAq1p13JZ3kcsbYOQM1Vozg5pp2dyZxUouLPetKu1vLGORTn5RmrtcF8P8AVd9ubN2y+Sfwrva9WnLmjc+DxlB0azgFFFFWcoUUUUAFFFFABRRRQAUUUUAFFFFADWUMpB715D4z0r7DqbSqvEpLV7BXL+M9LF5pckwXLxr8orGvDmielleI9jXV9meQUUrqUcqwwR1FJXmH2qdwooooGFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUdTRWloennUtUit8cNTSu7EVJqEXJ9D0TwLpX2WxaaRPmYgqTXY1XsrcW1pHEP4VAqxXqwjyxSPgcVWdaq5sKKKKswCiiigAooooAKKKKACiiigAooooAgvJ1trWSUnhRmvE9fvjf6tNKGyhbivRfG+r/YrARKcmXKmvJic1w4md3yn1GR4a0XVfUKKKK5D6AKKKKACiiigAooooAKKKKACiiigDX8OaidO1RJM8H5a9rtpRNAjqc5ANfPyttdWHY5r1zwXqv27TFVz84OMV2YWf2T53PMNdKqjqqKKK7T5kKKKKACiiigAooooAKKKKACiiigAqKeJZoWjYZBqWigadndHiXiXTX0/VJcjCuxK1i16f4+0vzrT7Wq/6sc15gRivLrQ5ZH3GXYj29BPqFFFFZHeFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAV6L4A0nCNdyJhgflJrgrG1a9u0gTq1e36PZrZ6fFGowdozXVhoXlc8TOsTyUvZrdmhRRRXefJBRRRQAUUUUAFFFFABRRRQAUUUUAFMlcRxs7dAKfWH4o1FbDSpcnDOpApSdlc0pU3UmoLqebeL9Ta91aSMHKI3Fc7T5JGlkLucse5pleTOXM7n31CkqVNQXQKKKKk2CiiigAooooAKKKKACiiigAooooAK6fwXqn2HVVWRv3bDGPeuYqSCQxTxuDjawP61UJcsrmGIpKrScH1PoFGDoGHcZp1Y/hzUBqOlpKDntWxXrRd1c+BqQdObi+gUUUUyAooooAKKKKACiiigAooooAKKKKAKmo2i3tlJA4yGFeI6taNZ6jNGRgBjiveeorzbx/pJWRbqNMKASxFc2JheNz2slxPs6vs3szgaKKK88+uCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoop8cbzOEQZY9BQJuyuzsfAmkG5vBdsuRG1epAADA6VieGNNWw0uPAwXUE1uV6lGHLE+GzHEe3rt9EFFFFanCFFFFABRRRQAUUUUAFFFFABRRRQAhIAya8u8ear9ouvsiN/q25r0PV7xLPT5ZGbBCnFeI6hdte3rzt1auXEzsrHu5Jhueo6j6FWiiiuA+rCiiigAooooAKKKKACiiigAooooAKKKKACiiigDu/AGreXcfY3bCYyK9LByMivBdMu2s76ORTj5hn6Zr2/TLtLyyjlQ5GK9DDTvGx8lnWG5KntFsy5RRRXSeIFFFFABRRRQAUUUUAFFFFABRRRQAVl69p66jpc0GOWGM1qUhGQRSaurF05uElJdDwC7gNtdywn+BiKgrrfHGlG01ASxp8j5LGuSrypx5ZWPvcLWVakpoKKKKg6AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArpvBmmm81ZJSuY0PNc0Bk4Fet+CdJFjp3msOZcNW9CHNI83NMR7Gg7bs6mNAiBQOBTqKK9I+J3CiiigAooooAKKKKACiiigAooooAKKKhuphBbSSn+EZoGld2Rw/j/VdkSW0bfNn5hXm1aviG/OoavNMGypPArKry6s+aVz7nL8P7Ggo9QooorI7gooooAKKKKACiiigAooooAKKKKACiiigAooooAM4Oa9O+H+qCWz+xs2XXJ5rzGtnw1qTadqiMDgOQta0Z8sjgzHD+3oNdT26io4ZBJErA5yKkr1D4dqzsFFFFAgooooAKKKKACiiigAooooAKKKKAOc8X6Z9u0mUqMyAcV49KhilZD1U4r6BkQSRlWGQRXjXizTTp2qkYwJCWFceKh9o+kyPE70WYFFFFcR9IFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAa/hzT21DVoUK5TPzV7VawC3tkiXooxXF+AtJ8m3a4kXlsFTXdV6OHhyxufHZvifa1uVbIKKKK6DyAooooAKKKKACiiigAooooAKKKKACuV8a6r9g07Yp5fK11EjiNGY9AK8g8ZambzVpIlbManisa8+WJ6WV4f21dX2RzROSSaSiivMPtkFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAU5GKSK46g5ptFAmrqx7B4N1MXulortmQda6evIvBGp/Y9U2O2EYYAr1xTlQfWvToT5onxOZ4f2Nd22YtFFFbHnBRRRQAUUUUAFFFFABRRRQAUUUUAFcb470kXOntdKMyIMCuyqve26XVs8TjIIqJx5o2N8NWdGqpo8BIKkg9RSVo61YvY6lKjDALEj6VnV5TVnY++pzU4qS6hRRRSLCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAq5pdmb7UIrcDO84qnXd+AdJ82Z7qRfuMCtaU480kjlxtdUaLkegaZaCz0+GEAAqoFXKBwKK9RKyPg5Scm2wooopkhRRRQAUUUUAFFFFABRRRQAUUUhOBmgDH8S6gLHSpCTgsCBXiksjSyM7Eknua7fx9q/nT/AGJT9wg8VwtediJ80rH2GT4f2dHme7Ciiiuc9gKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAmtZjBdRygkbWBr2zw/qI1HTI5s8mvDa77wBq5EzWchwirkV04adpWPGznDe0pc63R6TRQORRXoHyAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB574/0jcv21V4QYOK86r3fWLFb/T5IWHUV4hfW7W13LGwxhjiuDEwtK59bkuJ56Xs3uivRRRXKe2FFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAE1rbtc3CQoMsxwK9q8PaethpcK4w5X5q868EaWbzUhOR8sRzzXraqFUAdBXdhoWXMfL53ieaSpLoLRRRXWfPhRRRQAUUUUAFFFFABRRRQAUUUUAFU9TultbGWQnBCnFXK4Xx9qxhtltomw+eR7VFSXLG504Sg61ZQR57qV499fSTv1PFVKDyaK8pu7ufewioxUUFFFFIoKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKvaTdvZ6hFIpx8wz9Ko0A4OR1pp2dyJwU4uLPfdPulu7RJUOQRVquJ8Bar59iLRmyyDPNdtXqwlzRufBYqi6NVwYUUUVZzhRRRQAUUUUAFFFFABRRRQAUUUUAIRkEeteW+PNK8i+FxGuI8cn3r1OsPxPpg1LSpIwPm61lWhzRO7LsQ6FdPozxSipJ4zFO8ZH3WIqOvLPuU7q6CiiigYUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABSqpdgqjJNJW54X01r7VYmxlEb5qqK5nYyrVFTg5Poej+D9LWy0uOXbhpFBNdJUcESwwqijAAqSvVjHlVj4GvVdWo5vqFFFFUZBRRRQAUUUUAFFFFABRRRQAUUUUARXEohgeQn7oJrxbxNqJ1LWJJQTtxjFekeMtUGn6WcH5nO3ivH2bc5b1Oa4sVP7J9LkeG0dViUUUVxn0YUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAG54X1M6dqicnEhC17RFIssasDkEV8/ROY5VcdVOa9h8IamL3SIlY5kA5rtws/snzeeYbaqjo6KKK7D5sKKKKACiiigAooooAKKKKACiiigAprrvQr6inUUAeO+MdKOn6oSo+V8tmuar1vxvpn2vTGkRcyLXkrDaxU9QcV5leHLI+2yvEe2oK+6EooorE9IKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAxmvVPAmki1svtDLzKAa870aya91OGIDILYNe3WFsLOzjhA4RcV14WF3zHgZ3ieWCpLqWaKKK7j5YKKKKACiiigAooooAKKKKACiiigAprsEQsegGadWT4g1BdP0ySRjjcCBSbsrl04Oc1FdTzjxrqn2zVHiRsxriuVqW4maed5GOSSairypy5pXPvsNSVKkoIKKKKg3CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK67wNqn2TUTE7fK+ABXI1PZTm2vIpgfuNmrpy5ZXOfFUVWpODPfwcjNLWZoV+NR0yKfPLCtOvVTurnwU4OEnF9AooopkBRRRQAUUUUAFFFFABRRRQAUUUUARXEQmgdCM5BrxPxDprabqbxsPvEsPzr3GuE8faSJbc3qrl1wOK58RDmjc9fJ8T7KtyvZnmdFHQ0V5x9iFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRViytzdXkUAH32xTSuTKSim2d14A0n79zKnXBU16LWbodkLHS4YcYKrg1pV6lKPLGx8Jja7rVnIKKKK0OQKKKKACiiigAooooAKKKKACiiigAPFebfEDVRI/wBhByAQ1d/qFwttZyOzAYU4zXh+q3rX1/JK5yckVzYmdo2PayXDe0q+0eyKVFFFeefXBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB6F8P8AVclrSRvlUDaK9FrwjRbxrPUoXBwNwzXt9jcrd2iTIchhXoYad42PkM5w3s6vOtmWKKKK6TxgooooAKKKKACiiigAooooAKKKKACqepWiXlnJE4yCKuUEZGKTV0VGTi00eB6lZvZXskT9dxI/Oqld14+0kx3H2xF+XAFcLXl1I8srH3mDrqtRUwooorM6gooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArsfAmki7vjPInEeGU1yMMTTTLGvVjgV7L4U0wWGlRZXEhX5jXRh4c0rnk5vifZUeVbs3gMCloor0T40KKKKACiiigAooooAKKKKACiiigAoopk0gjiZyegzQCV9Di/H2qiGz+zI2JCQT9K8vJySfWt3xVqP9o6u8inKgbfyrCry60+aR9xluH9jQS6sKKKKyPQCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigABwcivVfAeq/adPW1J5iWvKq6DwlqZ0/VEQHAlYA1tRnyyPOzPD+2oNLdHs9FMjcSIGU5Bp9emfEPQKKKKACiiigAooooAKKKKACiiigAooooAyPEOnLqWmPER0Ga8UuImhuJI2BBDEc19AOoZCp7ivJPG+lmz1QyouIyB+dcmJhdcx9BkmJ5ZOk+pytFFFcJ9QFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUoBYgAZJoA6HwjpbX+ppJjKxMCa9jjQIgVRgCuT8DaWLXS0uCMPKvIrrq9KhDlifFZrifbV2lsgooorc8wKKKKACiiigAooooAKKKKACiiigArnPF+qf2fpbFT8x4xXRM21ST2FeTeOdUa61Iwo2YwOnvWNafLE9DLMP7aur7I5R23yM3qSabRRXmH26VlYKKKKBhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAU+KRopVkU4ZTkUyigTV1Y9n8J6iL7SIQzZkC/NXQV5T4F1U2t8YHbiTAUV6sORXqUZ80T4fMsP7Gu10YUUUVqcAUUUUAFFFFABRRRQAUUUUAFFFFABXM+MtK/tDSztHzKd2fpXTVHPGJYXQ9wRUyjzKxrQqulUU10Pn51KOynscU2tzxRph07VnRV+Q85rDrypR5XY++o1FUpqa6hRRRUmoUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFamg2D32pxKqkqrAt9Ky69J8AaT5cRvHXIkXitaUOaVjizDEKhRcup3FrbrbW6RIMBRU1FFeofCttu7CiiigQUUUUAFFFFABRRRQAUUUUAFFFHSgDM12+Ww02SVjjjFeI3U73FzJI7ZJY4rvPH+q5P2FWyCM157Xn4md5WPrsmw3s6XO92FFFFcx7QUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBZ0+5NpfQzg/cbNe3aNfLf6bFNnJZcmvCK9G8AatvV7WQ4CgBc11YadnY8TOsNz0vaLdHoNFFFd58kFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAcV490rz7D7RGmZARnHpXlpGCQa9+vrdbi0ljYZypA/KvENYsW0/UZIG6g5rhxMLPmPqckxPNB0n0KFFFFch74UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAXNMtWvNQhiAyGYA17dpVithYRwKOFFee+AtKMt01zIvyYypNen9BXfhoWjc+TzrE89RU1sgooorqPDCiiigAooooAKKKKACiiigAooooAKr3twltavI5wADViuN8d6p9nsDbI2HbmonLljc6MLRdaqoI861m/fUNQkkfsxA+maz6UnJJPc0leU3d3PvacFCKigooopFhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABWloV81jqkDhsLv+as2lBIORwRTi7O5FSCnBxfU9/s7hbq1jmXowzU9cl4H1UXmmiAtlohg11tetCXNG58DiaTpVXB9AoooqjAKKKKACiiigAooooAKKKKACiiigAPIrznx/pAX/TUX5iQDXo1Zut2C3+nSIw6KSKzqw5o2OvA13QrKR4VRU95bvbXTxOMEGoK8tqx93GSkroKKKKRQUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUtvEZ7iOIdWbFRV1PgrSvt2ol3X5Uwwq4R5pWMMTWVGk5s9F8N6cNP0iKIj5gOTWzTVUKoA7U6vVirKx8DVm6k3J9QooopkBRRRQAUUUUAFFFFABRRRQAUUUUAMkcJGzZ6CvGvFmqf2jqrFW+VMrivSPFep/2fpMjK3z9MV43K5kldz1Yk1x4qf2T6PI8NvVYyiiiuI+lCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA6PwhqZsdUSPdhZGwa9ijdZEDKcg18+wytDKsi/eU5Fez+FtRW+0qIbsuq/NXbhZ/ZPmc8w1mqqN2iiiuw+dCiiigAooooAKKKKACiiigAooooAKRgGUg9DS0UAeU+O9KNtqBu1XEb4H41x1e0eKtLXUdLYEcoC1eMyIUcqeoNediIcsrn2WUYn2tHle6G0UUVznrBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAORDI6oOpOBXr3gzShZaVHI64kYc15x4a05tR1VFUZ2EMa9qgiEUKoowAK7MLD7R85nmJslSRJRRRXafNBRRRQAUUUUAFFFFABRRRQAUUUUAFITgE0tZ+s3y6fp0k7HgCk3ZXKhFzkorqec+OtV+06gIYnzGBz9a46p7yc3F3JITnLEioK8qpLmlc++wlFUaSggoooqDoCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK7XwFqot7trZ2/1hAXNcVVrTrs2V9HODjYc1pTlyyuc2MoKtRcGe+jkUVR0i6F5psM2cllyavV6id1c+CnFxk4voFFFFMkKKKKACiiigAooooAKKKKACiiigBksYkiZD0IxXjni7TDY6vIVXERxivZq5LxvpQvNO8xB8yHcSKwrw5onp5VifY10nszyWig8GivNPtQooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKAMnAoq/o9k97qMUaLnDAn6U0ruxFSahFyZ6D4C0lYbNb1h87jFdvVWwtUtLRIkGAB0q1XqwjyxsfBYqs61VzYUUUVZzhRRRQAUUUUAFFFFABRRRQAUUUUAFefeP9X2L9hVvvjNd1dzrb2zyMcACvEtdv3v9SkdjkKxA+lc+Inyxsexk+G9pW53sjMooorzj7AKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAPSfAGqmWKS3kb7pAUV31eHeHr42OrQuWITd81e12s63FskqnIYZr0cPPmjY+OzjDeyrc62ZNRRRXQeQFFFFABRRRQAUUUUAFFFFABRRRQAVBeQC5tZIiM7lIqeih6jTs7o8M1/TjpuqSwYwB0rLr0jx/pJaJbqJcuT830rzevLqw5ZWPusBiPb0FLqFFFFZHaFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFd/4A0nMn21hwRgZrh7OBri6jjUZ3MBXtmhaeunadHCBjjNdOGheVzxc5xPs6XIt2alFFFegfIhRRRQAUUUUAFFFFABRRRQAUUUUAFFFNdgqEntQByXjrUhb6W9urYd+RivKCSxJPU10PjDUzqGqkBjiPK4rna8yvPmkfbZXh/Y0FfdhRRRWJ6QUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAKCQcg4NeueCtVF7poiJ5iAWvIq6bwZqj2eqxwZxHI3NbUJ8sjzM0w3tqDtuj2Gimo4dAw6GnV6Z8UFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAUdWtFu7CWMjJKnFeIajZtY3rwOMFa98IyK8w8faV5N19sVf9YccVy4mF1c9zJcTyVHTezOIooorgPrAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiinRoZJFQdWIFAm7K51vgXSTd6gZpFzGBwfevV1G1QB2rn/COm/2fpCKy4YnNdDXqUYcsT4jMsR7au30QUUUVqeeFFFFABRRRQAUUUUAFFFFABRRRQAVheKdSGn6TK4bD44rdPAry3x5qvn3iwRt8gBDfWsq0+WJ3Zdh/bV0uiOOnlM07yHqxzUdFFeWfcpWVgooooGFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABUkE728yyp95elR0UITSasz27w3qK32lxENllUbvrWzXmPgDVPJuWtGb/WHjNenA5FepSnzRufC5hh/Y13HoFFFFanEFFFFABRRRQAUUUUAFFFFABRRRQAVi+JNOW/0yTK5ZVJFbVNdQ6FT0NKSurGlKo6c1JdD5+mheCUxuMMOtR10/jTS2s9VknC4jc8VzFeTOPLKx97h6qq01NBRRRUm4UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFdB4S0salqqq6navzVgKpZgo6k4r1fwPpP2TT1nkXEh71tQhzSPOzPE+xoO27OsiQRxqoHQYp9FFemfEN3CiiigAooooAKKKKACiiigAooooAKKKKAKOrXosNPknJxtFeIahcNdXssrHO5iRXoHj/VfLh+xq3+sXnFea1wYmd3Y+syXDclP2j3YUUUVynuBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAFzTL02F/HcAn5T2r2/TLlbqwhlByWUE14JXpvgLVxNbtbyN8wOFrqw07PlPCzvDc9NVFujuqKKK7z5QKKKKACiiigAooooAKKKKACiiigAooooA5jxnpgvtLL7eYgWrx8gg4Iwa+grmFbi3eJhkMMV4v4m082GrTKFwhPFcWKh9pH0uR4nR0mYtFFFcZ9GFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAGlodg2oanHEozggn869utIFt7dEUYAArhfh/pICfbmXDcjmvQa9HDwtG58fnGJ9pW5FsgoooroPHCiiigAooooAKKKKACiiigAooooAKiuZ1t4Gkc4AFS1yfjfVBaaW8CnEki8VM5cqubYek6tRQXU868Q6g9/qkpY5VWIWsmlZizFieT1pK8mTu7n31KCpwUV0CiiikaBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABWz4a1A6fq8UhbCZ5rGoHFVF2dzOtTVSDg+p9BW0wnt0kB4YZqWuZ8G6oL/SwpPMYC101erGXMrnwNek6VRwfQKKKKoxCiiigAooooAKKKKACiiigAooooAK4bx5pPn2y3Ea8qSWNdzVTUbUXdjLCQPmXFRUjzRsdOErujVU0eB0Ve1eyNhqU1uR9w4qjXlNWdj7yE1OKkuoUUUUiwooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACrNhavd3kcaDJ3DP51Wrt/AOledd/a3GUGRV0480rHNjK6o0XM9B0iySxsI40GOATV+kAwMDtS16qVlY+CnJyk5MKKKKZIUUUUAFFFFABRRRQAUUUUAFFFFACMwVST0ryDxpqov8AU/LXOIiVr0fxJqAsNJmkDYcLxXi1zMbi4klPVzmuTFT05T6HI8NeTqsiooorhPpwooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAOq8E6obTVEt2bEbnJr1tGDqGHQ18/28zQTLIhwRXtnh6/S+0yNlOSqgGu7DTuuU+XzvDcslVRrUUUV1nz4UUUUAFFFFABRRRQAUUUUAFFFFABRRRQB5r4/0ny5UuokyXOWIrgq9y1+xF7pcyYy23ivE7q3a1uXhf7ynBrz8TC0rn1+TYn2lLke6IaKKK5j2QooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAJIYzLMiKMkkCvafDOmLpulogAy3zfnXnHg3TDe6qrOmYwOvvXr8ahEVR0AxXdhoacx8xnmJu1SQ6iiius+eCiiigAooooAKKKKACiiigAooooAKDxRVPU7tbKxlmY42jNJuyuVGLlJRR574+1Uy3CW8bcDIYVw1XdUujeajPMTkM5IqlXl1Jc0rn3mDoKjRUQooorM6gooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACu9+H+qeXK1mzffORXBVe0i+bT9RjuF6jitKUuWVzkxtBVqLie89RRVTTrkXVnHIDnKgmrdeondHwcouLaYUUUUxBRRRQAUUUUAFFFFABRRRQAUUUUAIw3KQeleT+OdKFpf/aFXiVua9ZrnfF2lrf6VI+MtGuVrGtDmiehluI9jXTezPGqKdIjRuUYYI602vMPt07q4UUUUDCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAClVdzhfU4pK2PDenf2jq0cZXK9c1UVd2M61RU4OT6Ho/gvSvsGlguvzsd2frXUVFbxCGBEAxgAVLXqwjyqx8DXqurUc31CiiiqMQooooAKKKKACiiigAooooAKKKKACuG8far5Fstqhz5gIOK7S4lWCFpGOABXiviS/a+1abLZRW+WufET5Y2PWyjDe1rcz2Rj0UUV5x9kFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFHQ5oooA9R8BaqZ7JoJXy4OBn0rtq8T8MaibDWInZsJ3r2iGQSwo4/iGa9LDz5o2PjM3w/sq/MtmSUUUVueUFFFFABRRRQAUUUUAFFFFABRRRQAUyaNZYmRhkEU+igE7anivirTWsNWlbGEdvlrCr1bxzpP2ux+0KvMQJrykjBwa8ytDlkfcZbiPbUE+qCiiisT0AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAAycV6b4C0jy7Y3Ugw+ePpXn+lWT39/HCgySc17fptqtpZRxquPlGa6sNC75jws7xPJT9kt2W6KKK7z5QKKKKACiiigAooooAKKKKACiiigAoopGYKpJPAoA5nxnqYstKeIHDyAhTXkDMXYsxyT1NdT421Q3momANkRNiuVrza8+aR9plWH9jQTe7CiiisD1AooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAVW2sCOxr2Twjqo1HTBuPzJ8uK8arrfA+qm11FbZjhGya3w8+WR5WbYb2tBtbo9aopFYMoYdCKWvSPjAooooAKKKKACiiigAooooAKKKKACiiigCve263dpJCw4YYrxLXLFrDVZ4tuFDYWvda898f6TuCXUa/dBLEVz4iF43PZybE+zq8j2Z51RRRXnH14UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRU1tA9zcLGgySelNK4pNRV2dv8P8ASvMlN8w+6dtelAYFZPh/T00/TY0VcFgCfrWtXqUocsbHwmPxHt67l0CiiitDjCiiigAooooAKKKKACiiigAooooAKyfEF8LLSpn3YbbxWtXmvj7V/MlS1ibG0kMBWdWfLG52YCg69dROIup2ubl5mOSxzUNFFeW3c+7iklZBRRRSGFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABU1rO1vcJIhIINQ0U07ClFSVme56Ffrf6dG6noADWpXnPw/wBWwTYs2SSSK9Gr1KUuaNz4THUPYVnEKKKK0OMKKKKACiiigAooooAKKKKACiiigArO1qxF/pk0OOWXANaNIeRSaurFQm4SUl0PAr+2NnfSwHqjYqtXaeO9J+zXS3EacOSWNcXXlVI8srH3uErqtSU0FFFFQdIUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFdR4Lso5dTS4lPyrkYNcvUsV1PB/qpWT6GqhJRd2YYinKrTcIu1z3hbu3VQocYFO+2wf3xXhf9p33/P1J+dH9p33/P1J+ddf1ryPB/sF/wAx7p9tg/vij7bB/wA9BXhf9p33/P1J+dH9p33/AD9SfnR9a8g/sF/zHun22D++KPtsH/PQV4X/AGnff8/Un50f2nff8/Un50fWvIP7Bf8AMe6fbYP74o+2wf8APQV4X/ad9/z9SfnR/ad9/wA/Un50fWvIP7Bf8x7p9tg/vij7bB/z0FeF/wBp33/P1J+dH9p33/P1J+dH1ryD+wX/ADHun22D++KPtsH/AD0FeF/2nff8/Un50f2nff8AP1J+dH1ryD+wX/Me6fbYP74o+2wf89BXhf8Aad9/z9SfnR/ad9/z9SfnR9a8g/sF/wAx7ZfanBb2csocfKua8W1e8N9qc8+ch2yKjbUbx0KtcSFT1BNVaxq1ufQ9HL8uWFbbd2wooorA9QKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA0NGvm0/UY5lOOcV7RaalbzW0biUHKjNeD9KspqN4i7VuJAPQGt6Vbk0PLx+WrFNSTsz3b7bB/z0FH22D/noK8L/tO+/wCfqT86P7Tvv+fqT862+teR539gv+Y90+2wf3xR9tg/56CvC/7Tvv8An6k/Oj+077/n6k/Oj615B/YL/mPdPtsH98UfbYP+egrwv+077/n6k/Oj+077/n6k/Oj615B/YL/mPdPtsH98UfbYP+egrwv+077/AJ+pPzo/tO+/5+pPzo+teQf2C/5j3T7bB/fFH22D/noK8L/tO+/5+pPzo/tO+/5+pPzo+teQf2C/5j3T7bB/fFH22D/noK8L/tO+/wCfqT86P7Tvv+fqT86PrXkH9gv+Y90+2wf3xR9tg/56CvC/7Tvv+fqT86P7Tvv+fqT86PrXkH9gv+Y9X8Ux29/pEqhgXA+WvIJYzFKyH+E4qdtSvWGDcyEfWqxJYkk5JrCrUU3c9XAYOWFi4t3EooorE9AKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP//Z" + @ag.entrypoint -async def image(none:str): +async def image(none: str): response = openai.chat.completions.create( - model="gpt-4o-mini", - messages=[ - { - "role": "user", - "content": [ - {"type": "text", "text": "What’s in this image?"}, - { - "type": "image_url", - "image_url": { - "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg", - }, - }, - ], - } - ], + model="gpt-4o-mini", + messages=[ + { + "role": "user", + "content": [ + {"type": "text", "text": "What’s in this image?"}, + { + "type": "image_url", + "image_url": { + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg", + }, + }, + ], + } + ], ) - + response = openai.chat.completions.create( - model="gpt-4o-mini", - messages=[ - { - "role": "user", - "content": [ - {"type": "text", "text": "What’s in this image?"}, - { - "type": "image_url", - "image_url": { - "url": base64, - }, - }, - ], - } - ], + model="gpt-4o-mini", + messages=[ + { + "role": "user", + "content": [ + {"type": "text", "text": "What’s in this image?"}, + { + "type": "image_url", + "image_url": { + "url": base64, + }, + }, + ], + } + ], ) - return response.choices[0].message.content diff --git a/agenta-cli/tests/observability_sdk/integrations/openai/app_no_workflow.py b/agenta-cli/tests/observability_sdk/integrations/openai/app_no_workflow.py index 9b42d554bc..58a0e3bec6 100644 --- a/agenta-cli/tests/observability_sdk/integrations/openai/app_no_workflow.py +++ b/agenta-cli/tests/observability_sdk/integrations/openai/app_no_workflow.py @@ -13,6 +13,7 @@ OpenAIInstrumentor().instrument() + class MyConfig(BaseModel): temperature: float = Field(default=0.2, le=1, ge=0) model: Annotated[str, ag.MultipleChoice(choices=supported_llm_models)] = Field( @@ -25,14 +26,13 @@ class MyConfig(BaseModel): ) - @ag.entrypoint async def rag(topic: str, genre: str): response = openai.chat.completions.create( model="gpt-3.5-turbo", messages=[ {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": f"Write a short {genre} story about {topic}."} + {"role": "user", "content": f"Write a short {genre} story about {topic}."}, ], ) diff --git a/agenta-cli/tests/observability_sdk/sanity_check/app_local.py b/agenta-cli/tests/observability_sdk/sanity_check/app_local.py index d1ef912f18..8907013293 100644 --- a/agenta-cli/tests/observability_sdk/sanity_check/app_local.py +++ b/agenta-cli/tests/observability_sdk/sanity_check/app_local.py @@ -2,10 +2,12 @@ from pydantic import BaseModel from agenta.sdk.assets import supported_llm_models -ag.init(project_id="0192813f-60d5-7a65-8a75-6dda36b79267", - host="https://cloud.beta.agenta.ai", - api_key="NYWy4We0.17ce0e85db4840a39ca9ee7b00e8817b22b60d7e152407a5a4cc98c5284f2e0e", - app_id="0192b51a-bfc8-7a91-9276-470c3b35597f") +ag.init( + project_id="0192813f-60d5-7a65-8a75-6dda36b79267", + host="https://cloud.beta.agenta.ai", + api_key="NYWy4We0.17ce0e85db4840a39ca9ee7b00e8817b22b60d7e152407a5a4cc98c5284f2e0e", + app_id="0192b51a-bfc8-7a91-9276-470c3b35597f", +) ag.config.default( temperature=ag.FloatParam(0.2), @@ -16,15 +18,18 @@ prompt_system=ag.TextParam("MY_SYSTEM_PROMPT"), ) + # Pydantic models class InputData(BaseModel): text: str value: int + class OutputData(BaseModel): result: str count: int + # Function with ignored outputs @ag.instrument( spankind="EMBEDDING", @@ -38,6 +43,7 @@ def ignore_some_outputs_embedding(description: str): "cost": 15, } + # Function with all outputs ignored @ag.instrument(spankind="AGENT", ignore_outputs=True) def ignore_all_outputs_agent(query: str): @@ -47,36 +53,42 @@ def ignore_all_outputs_agent(query: str): "confidence": 0.9, } + # Function with ignored inputs @ag.instrument(spankind="CHAIN", ignore_inputs=["secret"]) def function_with_ignored_inputs(public_data: str, secret: str): print("function with ignored inputs") return f"Public: {public_data}, Secret: {secret}" + # Function with all inputs ignored @ag.instrument(spankind="CHAIN", ignore_inputs=True) def function_with_all_ignored_inputs(data: str): print("function with all ignored inputs") return f"Data: {data}" + # Function using dict inputs/outputs @ag.instrument(spankind="CHAIN") def dict_function(input_dict: dict) -> dict: print("dict function") return {"output_data": input_dict.get("key", None)} + # Function using Pydantic models @ag.instrument(spankind="CHAIN") def pydantic_function(input_data: InputData) -> OutputData: print("pydantic function") return OutputData(result=input_data.text.upper(), count=input_data.value + 1) + # Function with None output @ag.instrument(spankind="CHAIN") def none_output_function(): print("none output function") return None + # Nested function calls @ag.instrument(spankind="CHAIN") def nested_function(value: int): @@ -84,63 +96,75 @@ def nested_function(value: int): inner_result = inner_function(value) return f"Nested result: {inner_result}" + @ag.instrument(spankind="CHAIN") def inner_function(value: int): print("inner function") return value * 2 + # Function called multiple times @ag.instrument(spankind="CHAIN") def multiple_calls_function(counter: int): print(f"multiple calls function call {counter}") return f"Call number: {counter}" + # Existing functions @ag.instrument(spankind="CHAIN") def chain_function(input_data: str): print("chain") return f"Processed: {input_data}" + @ag.instrument(spankind="TASK") def task_function(task: str): print("task") return f"Completed task: {task}" + @ag.instrument(spankind="TOOL") def tool_function(tool_input: str): print("tool") return f"Tool output: {tool_input}" + @ag.instrument(spankind="QUERY") def query_function(query: str): print("query") return f"Query result: {query}" + @ag.instrument(spankind="COMPLETION") def completion_function(prompt: str): print("completion") return f"Completed: {prompt}" + @ag.instrument(spankind="CHAT") async def chat_function(message: str): print("chat") return f"Chat response: {message}" + @ag.instrument(spankind="RERANK") def rerank_function(documents: list): print("rerank") return sorted(documents, reverse=True) + # @ag.instrument(spankind="WRONGKIND") # def wrong_kind_function(input_data: str): # print("wrong kind") # return f"Processed with wrong kind: {input_data}" + @ag.instrument(spankind="COMPLETION", ignore_inputs=True) async def summarizer(topic: str, genre: str, report: dict) -> dict: print("summarizer") return {"report": "mya"} + @ag.instrument(spankind="CHAT") async def exception_func(): raise Exception("This is an exception") @@ -168,7 +192,9 @@ async def main2(topic: str, genre: str, count: int = 5): pydantic_result = pydantic_function(pydantic_input) none_output_result = none_output_function() nested_result = nested_function(5) - multiple_calls_results = [multiple_calls_function(i) for i in range(3)] # Called multiple times + multiple_calls_results = [ + multiple_calls_function(i) for i in range(3) + ] # Called multiple times return f"""Results: Embedding: {result} Agent: {agent_result} @@ -190,6 +216,8 @@ async def main2(topic: str, genre: str, count: int = 5): app_old_sdk""" return "x" + if __name__ == "__main__": import asyncio - asyncio.run(main2(topic="df", genre="d", count=1)) \ No newline at end of file + + asyncio.run(main2(topic="df", genre="d", count=1)) diff --git a/agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk_noinstrument.py b/agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk_noinstrument.py index 3fb4e610ab..6582e13a8e 100644 --- a/agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk_noinstrument.py +++ b/agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk_noinstrument.py @@ -5,6 +5,7 @@ ag.init() + class MyConfig(BaseModel): temperature: float = Field(default=0.2, le=1, ge=0) model: Annotated[str, ag.MultipleChoice(choices=supported_llm_models)] = Field( @@ -22,10 +23,12 @@ class InputData(BaseModel): text: str value: int + class OutputData(BaseModel): result: str count: int + # Function with ignored outputs def ignore_some_outputs_embedding(description: str): print("embed") @@ -35,6 +38,7 @@ def ignore_some_outputs_embedding(description: str): "cost": 15, } + # Function with all outputs ignored def ignore_all_outputs_agent(query: str): print("agent") @@ -43,87 +47,106 @@ def ignore_all_outputs_agent(query: str): "confidence": 0.9, } + # Function with ignored inputs def function_with_ignored_inputs(public_data: str, secret: str): print("function with ignored inputs") return f"Public: {public_data}, Secret: {secret}" + # Function with all inputs ignored def function_with_all_ignored_inputs(data: str): print("function with all ignored inputs") return f"Data: {data}" + # Function using dict inputs/outputs def dict_function(input_dict: dict) -> dict: print("dict function") return {"output_data": input_dict.get("key", None)} + # Function using Pydantic models def pydantic_function(input_data: InputData) -> OutputData: print("pydantic function") return OutputData(result=input_data.text.upper(), count=input_data.value + 1) + # Function with None output def none_output_function(): print("none output function") return None + # Nested function calls def nested_function(value: int): print("nested function") inner_result = inner_function(value) return f"Nested result: {inner_result}" + def inner_function(value: int): print("inner function") return value * 2 + # Function called multiple times def multiple_calls_function(counter: int): print(f"multiple calls function call {counter}") return f"Call number: {counter}" + # Existing functions def chain_function(input_data: str): print("chain") return f"Processed: {input_data}" + def task_function(task: str): print("task") return f"Completed task: {task}" + def tool_function(tool_input: str): print("tool") return f"Tool output: {tool_input}" + def query_function(query: str): print("query") return f"Query result: {query}" + def completion_function(prompt: str): print("completion") return f"Completed: {prompt}" + async def chat_function(message: str): print("chat") return f"Chat response: {message}" + def rerank_function(documents: list): print("rerank") return sorted(documents, reverse=True) + def wrong_kind_function(input_data: str): print("wrong kind") return f"Processed with wrong kind: {input_data}" + async def summarizer(topic: str, genre: str, report: dict) -> dict: print("summarizer") return {"report": "mya"} + async def exception_func(): raise Exception("This is an exception") return "dummy" + @ag.route("/", config_schema=MyConfig) async def newsdk(topic: str, genre: str, count: int = 5): result = ignore_some_outputs_embedding("something") @@ -146,7 +169,9 @@ async def newsdk(topic: str, genre: str, count: int = 5): pydantic_result = pydantic_function(pydantic_input) none_output_result = none_output_function() nested_result = nested_function(5) - multiple_calls_results = [multiple_calls_function(i) for i in range(3)] # Called multiple times + multiple_calls_results = [ + multiple_calls_function(i) for i in range(3) + ] # Called multiple times # exception_result = await exception_func() return f"""Results: Embedding: {result} @@ -167,4 +192,4 @@ async def newsdk(topic: str, genre: str, count: int = 5): None Output Function: {none_output_result} Nested Function: {nested_result} Multiple Calls Function: {multiple_calls_results} -app_old_sdk""" \ No newline at end of file +app_old_sdk""" diff --git a/agenta-cli/tests/observability_sdk/sanity_check/app_sync.py b/agenta-cli/tests/observability_sdk/sanity_check/app_sync.py index 12df510ef6..65f8b2a044 100644 --- a/agenta-cli/tests/observability_sdk/sanity_check/app_sync.py +++ b/agenta-cli/tests/observability_sdk/sanity_check/app_sync.py @@ -1,12 +1,13 @@ - import agenta as ag from pydantic import BaseModel, Field ag.init() + class MyConfig(BaseModel): prompt: str = Field(default="somevalue") + @ag.instrument( spankind="EMBEDDING", ignore_outputs=["ignored", "cost", "usage"], @@ -23,7 +24,6 @@ def embed(description: str): @ag.instrument(spankind="GENERATOR", ignore_inputs=True) async def summarizer(topic: str, genre: str, report: dict) -> dict: - return {"report": report} @@ -34,8 +34,6 @@ async def rag(topic: str, genre: str, count: int = 5): result = await embed("something") - result = await summarizer("topic", "genre", "report") - return result["report"] From 6041a61a568c0843d56b6cf7fbc7be58015289bb Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 24 Oct 2024 18:42:11 +0200 Subject: [PATCH 156/305] fix references to use dot instead of underscore, Add 'Reference' Enum/str, clean up conventions in SDK, add attribute regex checker, actaully use 'suppress' in 'Tracing()', Add 'store_refs()', 'store_meta()', and 'store_metrics()', bump pre-release version --- .../apis/fastapi/observability/utils.py | 2 +- agenta-cli/agenta/__init__.py | 2 +- agenta-cli/agenta/sdk/__init__.py | 2 +- agenta-cli/agenta/sdk/tracing/conventions.py | 44 +++++---- agenta-cli/agenta/sdk/tracing/inline.py | 2 +- agenta-cli/agenta/sdk/tracing/tracing.py | 98 ++++++++++++++----- agenta-cli/pyproject.toml | 2 +- 7 files changed, 103 insertions(+), 49 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py index 4b56a47775..a3bf663d77 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py @@ -469,7 +469,7 @@ def parse_from_otel_span_dto( exception = _parse_from_events(otel_span_dto) - root_id = refs.get("scenario_id", tree.id.hex) + root_id = refs.get("scenario.id", tree.id.hex) root = RootDTO(id=UUID(root_id)) diff --git a/agenta-cli/agenta/__init__.py b/agenta-cli/agenta/__init__.py index 82efa4d0fa..23b9500a52 100644 --- a/agenta-cli/agenta/__init__.py +++ b/agenta-cli/agenta/__init__.py @@ -15,7 +15,7 @@ from .sdk.utils.logging import log as logging from .sdk.tracing import Tracing -from .sdk.decorators.tracing import instrument +from .sdk.decorators.tracing import instrument, Reference from .sdk.decorators.routing import entrypoint, app, route from .sdk.agenta_init import Config, AgentaSingleton, init from .sdk.utils.costs import calculate_token_usage diff --git a/agenta-cli/agenta/sdk/__init__.py b/agenta-cli/agenta/sdk/__init__.py index ca691bcb3d..761a526ea6 100644 --- a/agenta-cli/agenta/sdk/__init__.py +++ b/agenta-cli/agenta/sdk/__init__.py @@ -16,7 +16,7 @@ ) from .tracing import Tracing, get_tracer -from .decorators.tracing import instrument +from .decorators.tracing import instrument, Reference from .decorators.routing import entrypoint, app, route from .agenta_init import Config, AgentaSingleton, init as _init from .utils.costs import calculate_token_usage diff --git a/agenta-cli/agenta/sdk/tracing/conventions.py b/agenta-cli/agenta/sdk/tracing/conventions.py index a3423782a3..c2780651db 100644 --- a/agenta-cli/agenta/sdk/tracing/conventions.py +++ b/agenta-cli/agenta/sdk/tracing/conventions.py @@ -1,25 +1,29 @@ -from typing import Literal - from opentelemetry.trace import SpanKind -Namespace = Literal[ - "data.inputs", - "data.internals", - "data.outputs", - "metrics.scores", - "metrics.unit.costs", - "metrics.unit.tokens", - "meta.configuration", - "meta.version", - "tags", - "refs", -] - -Code = Literal[ - "OK", - "UNSET", - "ERROR", -] +from enum import Enum + +from re import fullmatch + +_PATTERN = r"[A-Za-z0-9._-]+" + + +def is_valid_attribute_key(string): + return bool(fullmatch(_PATTERN, string)) + + +class Reference(Enum, str): + # + VARIANT_ID = "variant.id" + VARIANT_SLUG = "variant.slug" + VARIANT_VERSION = "variant.version" + # + ENVIRONMENT_ID = "environment.id" + ENVIRONMENT_SLUG = "environment.slug" + ENVIRONMENT_VERSION = "environment.version" + # + APPLICATION_ID = "application.id" + APPLICATION_SLUG = "application.slug" + # def parse_span_kind(type: str) -> SpanKind: diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py index 8171398de5..419bf2d749 100644 --- a/agenta-cli/agenta/sdk/tracing/inline.py +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -1223,7 +1223,7 @@ def _parse_to_legacy_span(span: SpanDTO) -> CreateSpan: ), # app_id=( - span.refs.get("application_id", "missing-app-id") + span.refs.get("application.id", "missing-app-id") if span.refs else "missing-app-id" ), diff --git a/agenta-cli/agenta/sdk/tracing/tracing.py b/agenta-cli/agenta/sdk/tracing/tracing.py index 5c91969a80..4647c920ff 100644 --- a/agenta-cli/agenta/sdk/tracing/tracing.py +++ b/agenta-cli/agenta/sdk/tracing/tracing.py @@ -13,15 +13,16 @@ from opentelemetry.sdk.resources import Resource from agenta.sdk.utils.singleton import Singleton -from agenta.sdk.utils.exceptions import suppress # USE IT ! +from agenta.sdk.utils.exceptions import suppress from agenta.sdk.utils.logging import log from agenta.sdk.tracing.processors import TraceProcessor from agenta.sdk.tracing.exporters import ConsoleExporter, InlineExporter, OTLPExporter from agenta.sdk.tracing.spans import CustomSpan -from agenta.sdk.tracing.context import tracing_context from agenta.sdk.tracing.inline import parse_inline_trace +from agenta.sdk.tracing.conventions import Reference, is_valid_attribute_key + class Tracing(metaclass=Singleton): VERSION = "0.1.0" @@ -74,7 +75,7 @@ def configure( if api_key: self.headers.update(**{"Authorization": self.api_key}) # REFERENCES - self.references = {"application_id": app_id} + self.references = {"application.id": app_id} # TRACER PROVIDER self.tracer_provider = TracerProvider( @@ -120,10 +121,13 @@ def configure( def get_current_span( self, ): - _span = get_current_span() + _span = None + + with suppress(): + _span = get_current_span() - if _span.is_recording(): - return CustomSpan(_span) + if _span.is_recording(): + return CustomSpan(_span) return _span @@ -132,39 +136,85 @@ def store_internals( attributes: Dict[str, Any], span: Optional[Span] = None, ): - if span is None: - span = self.get_current_span() + with suppress(): + if span is None: + span = self.get_current_span() + + span.set_attributes(attributes={"internals": attributes}, namespace="data") + + def store_refs( + self, + refs: Dict[str, str], + span: Optional[Span] = None, + ): + with suppress(): + if span is None: + span = self.get_current_span() + + for key in refs.keys(): + if key in Reference: + # ADD REFERENCE TO THIS SPAN + span.set_attribute(key, refs[key], namespace="refs") + # AND TO ALL SPANS CREATED AFTER THIS ONE + self.references[key] = refs[key] + # TODO: THIS SHOULD BE REPLACED BY A TRACE CONTEXT !!! + + def store_meta( + self, + meta: Dict[str, Any], + span: Optional[Span] = None, + ): + with suppress(): + if span is None: + span = self.get_current_span() + + for key in meta.keys(): + if is_valid_attribute_key(key): + span.set_attribute(key, meta[key], namespace="meta") - span.set_attributes(attributes={"internals": attributes}, namespace="data") + def store_metrics( + self, + metrics: Dict[str, Any], + span: Optional[Span] = None, + ): + with suppress(): + if span is None: + span = self.get_current_span() + + for key in metrics.keys(): + if is_valid_attribute_key(key): + span.set_attribute(key, metrics[key], namespace="metrics") def is_inline_trace_ready( self, - trace_id: int, + trace_id: Optional[int] = None, ) -> bool: - is_ready = self.inline.is_ready(trace_id) + is_ready = True + + with suppress(): + if trace_id is not None: + is_ready = self.inline.is_ready(trace_id) return is_ready def get_inline_trace( self, - trace_id: int, + trace_id: Optional[int] = None, ) -> Dict[str, Any]: - if trace_id is None: - return {} - - is_ready = self.inline.is_ready(trace_id) - - if is_ready is False: - return {} + _inline_trace = {} - otel_spans = self.inline.fetch(trace_id) + with suppress(): + is_ready = self.inline.is_ready(trace_id) - if not otel_spans: - return {} + if is_ready is True: + otel_spans = self.inline.fetch(trace_id) - inline_trace = parse_inline_trace(self.project_id or self.app_id, otel_spans) + if otel_spans: + _inline_trace = parse_inline_trace( + self.project_id or self.app_id, otel_spans + ) - return inline_trace + return _inline_trace def get_tracer(tracing: Tracing) -> Tracer: diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index a2856b1f3e..195cd2b06f 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.27.0a0" +version = "0.27.0a1" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] From c42616e35f2f451423da90aa5187c53679f59884 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 24 Oct 2024 18:58:15 +0200 Subject: [PATCH 157/305] Fix 'store_refs()' and 'Reference' --- agenta-cli/agenta/__init__.py | 3 ++- agenta-cli/agenta/sdk/__init__.py | 3 ++- agenta-cli/agenta/sdk/tracing/conventions.py | 2 +- agenta-cli/agenta/sdk/tracing/tracing.py | 7 ++++++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/agenta-cli/agenta/__init__.py b/agenta-cli/agenta/__init__.py index 23b9500a52..8386822a7a 100644 --- a/agenta-cli/agenta/__init__.py +++ b/agenta-cli/agenta/__init__.py @@ -15,7 +15,8 @@ from .sdk.utils.logging import log as logging from .sdk.tracing import Tracing -from .sdk.decorators.tracing import instrument, Reference +from .sdk.decorators.tracing import instrument +from .sdk.tracing.conventions import Reference from .sdk.decorators.routing import entrypoint, app, route from .sdk.agenta_init import Config, AgentaSingleton, init from .sdk.utils.costs import calculate_token_usage diff --git a/agenta-cli/agenta/sdk/__init__.py b/agenta-cli/agenta/sdk/__init__.py index 761a526ea6..c06e41e5ab 100644 --- a/agenta-cli/agenta/sdk/__init__.py +++ b/agenta-cli/agenta/sdk/__init__.py @@ -16,7 +16,8 @@ ) from .tracing import Tracing, get_tracer -from .decorators.tracing import instrument, Reference +from .decorators.tracing import instrument +from .tracing.conventions import Reference from .decorators.routing import entrypoint, app, route from .agenta_init import Config, AgentaSingleton, init as _init from .utils.costs import calculate_token_usage diff --git a/agenta-cli/agenta/sdk/tracing/conventions.py b/agenta-cli/agenta/sdk/tracing/conventions.py index c2780651db..379b94f4c2 100644 --- a/agenta-cli/agenta/sdk/tracing/conventions.py +++ b/agenta-cli/agenta/sdk/tracing/conventions.py @@ -11,7 +11,7 @@ def is_valid_attribute_key(string): return bool(fullmatch(_PATTERN, string)) -class Reference(Enum, str): +class Reference(str, Enum): # VARIANT_ID = "variant.id" VARIANT_SLUG = "variant.slug" diff --git a/agenta-cli/agenta/sdk/tracing/tracing.py b/agenta-cli/agenta/sdk/tracing/tracing.py index 4647c920ff..2a625ef786 100644 --- a/agenta-cli/agenta/sdk/tracing/tracing.py +++ b/agenta-cli/agenta/sdk/tracing/tracing.py @@ -1,4 +1,5 @@ from typing import Optional, Any, Dict +from enum import Enum from httpx import get as check @@ -154,7 +155,11 @@ def store_refs( for key in refs.keys(): if key in Reference: # ADD REFERENCE TO THIS SPAN - span.set_attribute(key, refs[key], namespace="refs") + span.set_attribute( + key.value if isinstance(key, Enum) else key, + refs[key], + namespace="refs", + ) # AND TO ALL SPANS CREATED AFTER THIS ONE self.references[key] = refs[key] # TODO: THIS SHOULD BE REPLACED BY A TRACE CONTEXT !!! From b0f6f3fb41fbdc6a0e2041b7b725a382b2571ba5 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 25 Oct 2024 08:18:10 +0200 Subject: [PATCH 158/305] remove dead code --- agenta-cli/agenta/sdk/tracing/attributes.py | 45 --------------------- 1 file changed, 45 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/attributes.py b/agenta-cli/agenta/sdk/tracing/attributes.py index 0c6894f1d2..6854f4d14a 100644 --- a/agenta-cli/agenta/sdk/tracing/attributes.py +++ b/agenta-cli/agenta/sdk/tracing/attributes.py @@ -134,48 +134,3 @@ def serialize( } return _attributes - - -""" -def _decode_key(namespace: Optional[str] = None, key: str = "") -> str: - if namespace is None: - return key - - return key.replace(f"ag.{namespace}.", "") -""" - -""" -def _decode_value(value: Attribute) -> Any: - if isinstance(value, (int, float, bool, bytes)): - return value - - if isinstance(value, str): - if value == "@ag.type=none:": - return None - - if value.startswith("@ag.type=json:"): - encoded = value[len("@ag.type=json:") :] - value = loads(encoded) - return value - - return value - - return value -""" - -""" -def deserialize( - *, - namespace: str, - attributes: Dict[str, Any], - max_depth: Optional[int] = None, -) -> Dict[str, Any]: - if not isinstance(attributes, dict): - return {} - - return { - _decode_key(namespace, key): _decode_value(value) - for key, value in attributes - if key != _decode_key(namespace, key) - } -""" From 09377ad7c2c3efb9d73708de8845368e61e30b74 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 25 Oct 2024 09:19:36 +0200 Subject: [PATCH 159/305] Fix Union[] --- agenta-cli/agenta/sdk/decorators/tracing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 268c287529..819fafc016 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -2,7 +2,7 @@ import traceback from functools import wraps from itertools import chain -from typing import Callable, Optional, Any, Dict, List +from typing import Callable, Optional, Any, Dict, List, Union import agenta as ag @@ -47,7 +47,7 @@ def parse(*args, **kwargs) -> Dict[str, Any]: return inputs def redact( - io: Dict[str, Any], ignore: List[str] | bool = False + io: Dict[str, Any], ignore: Union[List[str], bool] = False ) -> Dict[str, Any]: """ Redact user-defined sensitive information from inputs and outputs as defined by the ignore list or boolean flag. From 6cef238af4fe2711136dedbc142b5b2defd9fa6c Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Fri, 25 Oct 2024 09:30:02 +0200 Subject: [PATCH 160/305] Update pyproject.toml --- agenta-cli/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index 195cd2b06f..03d4a89f8d 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.27.0a1" +version = "0.27.0a2" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] From efdf841fef4ebfb5c73a2b584f63cfbd56555e90 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 25 Oct 2024 10:49:17 +0200 Subject: [PATCH 161/305] fix issue with references --- agenta-backend/agenta_backend/core/observability/dtos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-backend/agenta_backend/core/observability/dtos.py b/agenta-backend/agenta_backend/core/observability/dtos.py index 557703b33d..4385e7d773 100644 --- a/agenta-backend/agenta_backend/core/observability/dtos.py +++ b/agenta-backend/agenta_backend/core/observability/dtos.py @@ -90,7 +90,7 @@ class Config: Data = Dict[str, Any] Metrics = Dict[str, Any] Metadata = Dict[str, Any] -Refs = Dict[str, str] +Refs = Dict[str, Any] class LinkDTO(DisplayBase): From 346e0356ff45d3a8c844d96f9bfbf68ef272b243 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Fri, 25 Oct 2024 11:23:38 +0200 Subject: [PATCH 162/305] updated node type(frontend) --- agenta-web/src/services/observability/types/index.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/agenta-web/src/services/observability/types/index.ts b/agenta-web/src/services/observability/types/index.ts index 81853f0ccf..081d761e1a 100644 --- a/agenta-web/src/services/observability/types/index.ts +++ b/agenta-web/src/services/observability/types/index.ts @@ -31,8 +31,7 @@ interface TreeContextDTO { type?: NodeTreeType | null } export interface AgentaNodeDTO { - scope: ProjectScopeDTO - lifecycle: NodeLifecycleDTO + lifecycle?: NodeLifecycleDTO | null time: NodeTimeDTO status: NodeStatusDTO exception?: NodeExceptionDTO | null @@ -46,15 +45,13 @@ export interface AgentaNodeDTO { parent?: ParentContextDTO | null links?: NodeLinkDTO[] | null otel?: NodeOTelExtraDTO | null - tags?: NodeTags | null nodes?: Record | null } type NodeData = Record type NodeMetrics = Record type NodeMetadata = Record -type NodeTags = Record -type NodeRefs = Record +type NodeRefs = Record type NodeLinkDTO = { type: string id: string @@ -86,9 +83,6 @@ type NodeOTelLinkDTO = { } attributes?: Record | null } -interface ProjectScopeDTO { - project_id: string -} interface NodeLifecycleDTO { created_at: string @@ -128,7 +122,6 @@ interface ParentContextDTO { interface NodeTimeDTO { start: string end: string - span: number } export interface NodeStatusDTO { From 5fc6f29b2ef8837f33a623141923e425b9541791 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Fri, 25 Oct 2024 11:24:55 +0200 Subject: [PATCH 163/305] fix(frontend): updated latency path and made 'open in playground' button functional --- .../pages/observability/drawer/TraceContent.tsx | 10 +++++++--- .../pages/observability/drawer/TraceTree.tsx | 4 ++-- .../src/pages/apps/[app_id]/observability/index.tsx | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx index 6ed6fa813e..d5906eb88a 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx @@ -222,8 +222,11 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { - {!parent && ( - @@ -254,7 +257,8 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { - {formatLatency(activeTrace.time.span / 1000000)} + {" "} + {formatLatency(activeTrace?.metrics?.acc?.duration.total)} } /> diff --git a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx index e38910b25b..b6cd9ef269 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx @@ -67,7 +67,7 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ })) const TreeContent = ({value}: {value: _AgentaRootsResponse}) => { - const {node, time, metrics, status} = value + const {node, metrics, status} = value const classes = useStyles() return ( @@ -88,7 +88,7 @@ const TreeContent = ({value}: {value: _AgentaRootsResponse}) => {
- {formatLatency(time.span / 1000000)} + {formatLatency(metrics?.acc?.duration.total)}
{metrics?.acc?.costs?.total && ( diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index 2aca13c173..3111b090ce 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -103,7 +103,7 @@ const ObservabilityDashboard = ({}: Props) => { onHeaderCell: () => ({ style: {minWidth: 80}, }), - render: (_, record) =>
{formatLatency(record.time.span / 1000000)}
, + render: (_, record) =>
{formatLatency(record?.metrics?.acc?.duration.total)}
, }, { title: "Usage", From 112e46411cd8429d7549fbc07edcf782f71f35ad Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Fri, 25 Oct 2024 15:43:07 +0600 Subject: [PATCH 164/305] enhance(frontnd): made series of changes: - Implemented advance sorting - improved filtering functionalities - synchronized filter component with search and group buttons --- agenta-web/src/components/Filters/Filters.tsx | 107 +++++--- agenta-web/src/components/Filters/Sort.tsx | 245 +++++++++++++++--- agenta-web/src/lib/Types.ts | 29 ++- .../apps/[app_id]/observability/index.tsx | 210 +++++++++++++-- 4 files changed, 487 insertions(+), 104 deletions(-) diff --git a/agenta-web/src/components/Filters/Filters.tsx b/agenta-web/src/components/Filters/Filters.tsx index be795bedcf..7e30dcc48b 100644 --- a/agenta-web/src/components/Filters/Filters.tsx +++ b/agenta-web/src/components/Filters/Filters.tsx @@ -3,6 +3,7 @@ import {Filter, JSSTheme} from "@/lib/Types" import {ArrowCounterClockwise, CaretDown, Funnel, Plus, Trash, X} from "@phosphor-icons/react" import {Button, Divider, Input, Popover, Select, Space, Typography} from "antd" import {createUseStyles} from "react-jss" +import {useUpdateEffect} from "usehooks-ts" const useStyles = createUseStyles((theme: JSSTheme) => ({ popover: { @@ -28,32 +29,54 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ })) type Props = { - columns: {column: string; mapping: string}[] + filterData?: Filter[] + setFilterData?: React.Dispatch> + columns: {value: string; label: string}[] onApplyFilter: (filters: Filter[]) => void onClearFilter: (filters: Filter[]) => void } -const Filters: React.FC = ({columns, onApplyFilter, onClearFilter}) => { +const Filters: React.FC = ({ + filterData, + setFilterData, + columns, + onApplyFilter, + onClearFilter, +}) => { const classes = useStyles() - const emptyFilter = [{condition: "", column: "", keyword: ""}] as Filter[] + const emptyFilter = [{key: "", operator: "", value: ""}] as Filter[] const [filter, setFilter] = useState(emptyFilter) const [isFilterOpen, setIsFilterOpen] = useState(false) - const conditions = [ - "contains", - "does not contain", - "starts with", - "ends with", - "exists", - "does not exist", - "=", - ">", - "<", - ">=", - "<=", + useUpdateEffect(() => { + if (filterData && filterData.length > 0) { + setFilter(filterData) + } else { + setFilter(emptyFilter) + } + }, [filterData]) + + const operators = [ + {value: "contains", lable: "contains"}, + {value: "matches", lable: "matches"}, + {value: "like", lable: "like"}, + {value: "startswith", lable: "startswith"}, + {value: "endswith", lable: "endswith"}, + {value: "exists", lable: "exists"}, + {value: "not_exists", lable: "not exists"}, + {value: "eq", lable: "="}, + {value: "neq", lable: "!="}, + {value: "gt", lable: ">"}, + {value: "lt", lable: "<"}, + {value: "gte", lable: ">="}, + {value: "lte", lable: "<="}, ] + const filteredOptions = columns.filter( + (col) => !filter.some((item, i) => item.key === col.value), + ) + const onFilterChange = ({ columnName, value, @@ -73,7 +96,7 @@ const Filters: React.FC = ({columns, onApplyFilter, onClearFilter}) => { } const addNestedFilter = () => { - setFilter([...filter, {column: "", condition: "", keyword: ""}]) + setFilter([...filter, {key: "", operator: "", value: ""}]) } const clearFilter = () => { @@ -82,9 +105,7 @@ const Filters: React.FC = ({columns, onApplyFilter, onClearFilter}) => { } const applyFilter = () => { - const sanitizedFilters = filter.filter( - ({column, condition, keyword}) => column && condition && keyword, - ) + const sanitizedFilters = filter.filter(({key, operator}) => key && operator) onApplyFilter(sanitizedFilters) setIsFilterOpen(false) @@ -116,52 +137,58 @@ const Filters: React.FC = ({columns, onApplyFilter, onClearFilter}) => {
{filter.map((item, idx) => ( -

{idx == 0 ? "Where" : "And"}

+

{idx == 0 ? "Where" : "And"}

- !label.value ? "Condition" : label.value + !label.value ? "Condition" : label.label } style={{width: 95}} suffixIcon={} onChange={(value) => onFilterChange({ - columnName: "condition", + columnName: "operator", value, idx, }) } - popupMatchSelectWidth={250} - value={item.condition} - options={conditions.map((con) => ({ - value: con, - label: con, + popupMatchSelectWidth={100} + value={item.operator} + options={operators.map((operator) => ({ + value: operator.value, + label: operator.lable, }))} /> onFilterChange({ - columnName: "keyword", + columnName: "value", value: e.target.value, idx, }) @@ -191,7 +218,7 @@ const Filters: React.FC = ({columns, onApplyFilter, onClearFilter}) => {
-
@@ -203,7 +230,7 @@ const Filters: React.FC = ({columns, onApplyFilter, onClearFilter}) => { onClick={() => setIsFilterOpen(true)} className="flex items-center gap-2" > - Filters {filter[0]?.keyword && } + Filters {filter[0]?.value && } ) diff --git a/agenta-web/src/components/Filters/Sort.tsx b/agenta-web/src/components/Filters/Sort.tsx index 8b0c8a0faf..6e80c6abab 100644 --- a/agenta-web/src/components/Filters/Sort.tsx +++ b/agenta-web/src/components/Filters/Sort.tsx @@ -1,50 +1,233 @@ import React, {useState} from "react" -import {Hourglass} from "@phosphor-icons/react" -import {Select} from "antd" -import {SortTypes} from "@/lib/Types" +import {CaretRight, Clock, Hourglass} from "@phosphor-icons/react" +import {DatePicker, Button, Typography, Divider, Popover} from "antd" +import {JSSTheme, SortTypes} from "@/lib/Types" +import {Dayjs} from "dayjs" +import type {SelectProps} from "antd" +import {createUseStyles} from "react-jss" + +const useStyles = createUseStyles((theme: JSSTheme) => ({ + title: { + fontSize: theme.fontSizeLG, + fontWeight: theme.fontWeightMedium, + padding: theme.paddingXS, + }, + customDateContainer: { + flex: 1, + padding: theme.paddingXS, + gap: 16, + display: "flex", + flexDirection: "column", + }, + popover: { + "& .ant-popover-inner": { + transition: "width 0.3s ease", + padding: 4, + }, + }, + popupItems: { + display: "flex", + alignItems: "center", + justifyContent: "space-between", + padding: `5px ${theme.paddingContentHorizontal}px`, + gap: theme.marginXS, + borderRadius: theme.borderRadiusSM, + cursor: "pointer", + "&:hover": { + backgroundColor: theme.controlItemBgActive, + }, + }, + popupSelectedItem: { + backgroundColor: theme.controlItemBgActive, + }, +})) type Props = { - onSortApply: (sort: SortTypes) => void + onSortApply: ({ + sortData, + customSortData, + }: { + sortData: SortTypes + customSortData?: CustomTimeRange + }) => void defaultSortValue: SortTypes } +export type CustomTimeRange = { + startTime: Dayjs | null + endTime: Dayjs | null +} const Sort: React.FC = ({onSortApply, defaultSortValue}) => { + const classes = useStyles() + const [sort, setSort] = useState(defaultSortValue) + const [customTime, setCustomTime] = useState({startTime: null, endTime: null}) + const [dropdownVisible, setDropdownVisible] = useState(false) + const [customOptionSelected, setCustomOptionSelected] = useState( + customTime.startTime == null ? false : true, + ) + + const handleApplyCustomRange = () => { + if (customTime.startTime && customTime.endTime) { + onSortApply({sortData: sort, customSortData: customTime}) + setDropdownVisible(false) + } + } + + const options: SelectProps["options"] = [ + {value: "30 minutes", label: "30 mins"}, + {value: "1 hour", label: "1 hour"}, + {value: "6 hours", label: "6 hours"}, + {value: "24 hours", label: "24 hours"}, + {value: "3 days", label: "3 days"}, + {value: "7 days", label: "7 days"}, + {value: "14 days", label: "14 days"}, + {value: "1 month", label: "1 month"}, + {value: "3 months", label: "3 months"}, + {value: "all time", label: "All time"}, + ] return ( -
[]} - dataSource={traces} - bordered - style={{cursor: "pointer"}} - onRow={(record) => ({ - onClick: () => { - setSelectedTraceId(record.root.id) - }, - })} - components={{ - header: { - cell: ResizableTitle, - }, - }} - pagination={false} - scroll={{x: "max-content"}} - /> - ) : null} +
[]} + dataSource={traces} + bordered + style={{cursor: "pointer"}} + onRow={(record) => ({ + onClick: () => { + setSelectedTraceId(record.root.id) + }, + })} + components={{ + header: { + cell: ResizableTitle, + }, + }} + pagination={false} + scroll={{x: "max-content"}} + locale={{ + emptyText: ( +
+ + } + description="Monitor the performance and results of your LLM applications here." + primaryCta={{ + text: "Go to Playground", + onClick: () => router.push(`/apps/${appId}/playground`), + tooltip: + "Run your LLM app in the playground to generate and view insights.", + }} + secondaryCta={{ + text: "Learn More", + onClick: () => + router.push( + "https://docs.agenta.ai/observability/quickstart", + ), + tooltip: + "Explore more about tracking and analyzing your app's observability data.", + }} + /> +
+ ), + }} + /> {activeTrace && !!traces?.length && ( Date: Fri, 25 Oct 2024 14:27:10 +0200 Subject: [PATCH 172/305] fix trace fetch endpoint(frontend) --- agenta-web/src/services/observability/core/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-web/src/services/observability/core/index.ts b/agenta-web/src/services/observability/core/index.ts index de67a74fb4..ef456362da 100644 --- a/agenta-web/src/services/observability/core/index.ts +++ b/agenta-web/src/services/observability/core/index.ts @@ -13,7 +13,7 @@ const PROJECT_ID = "019233b0-2967-76c0-bde2-f5b78b3a9a04" export const fetchAllTraces = async () => { const response = await axios.get( - `${getAgentaApiUrl()}/api/observability/v1/traces/search?project_id=${PROJECT_ID}&focus=node`, + `${getAgentaApiUrl()}/api/observability/v1/traces/search?project_id=${PROJECT_ID}`, ) return response.data } From f14f85057792030f0606c8051c73cea0a02c0ea5 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Fri, 25 Oct 2024 19:02:33 +0600 Subject: [PATCH 173/305] temp(frontend): temporarily getting the project_id dynamically --- agenta-web/src/lib/hooks/useTraces.ts | 5 ++++- agenta-web/src/services/observability/core/index.ts | 5 ++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/agenta-web/src/lib/hooks/useTraces.ts b/agenta-web/src/lib/hooks/useTraces.ts index 955ae8052a..732fdfe653 100644 --- a/agenta-web/src/lib/hooks/useTraces.ts +++ b/agenta-web/src/lib/hooks/useTraces.ts @@ -7,15 +7,18 @@ import { } from "@/services/observability/types" import {useEffect, useState} from "react" import {buildNodeTree, observabilityTransformer} from "../helpers/observability_helpers" +import {useRouter} from "next/router" export const useTraces = () => { const [traces, setTraces] = useState<_AgentaRootsResponse[]>([]) const [isLoadingTraces, setIsLoadingTraces] = useState(true) + const router = useRouter() + const projectId = router.query.project_id const fetchTraces = async () => { try { setIsLoadingTraces(true) - const data = await fetchAllTraces() + const data = await fetchAllTraces(projectId as string) const transformedTraces: _AgentaRootsResponse[] = [] diff --git a/agenta-web/src/services/observability/core/index.ts b/agenta-web/src/services/observability/core/index.ts index ef456362da..e7995d072e 100644 --- a/agenta-web/src/services/observability/core/index.ts +++ b/agenta-web/src/services/observability/core/index.ts @@ -9,11 +9,10 @@ import axios from "@/lib/helpers/axiosConfig" // - update: PUT data to server // - delete: DELETE data from server -const PROJECT_ID = "019233b0-2967-76c0-bde2-f5b78b3a9a04" -export const fetchAllTraces = async () => { +export const fetchAllTraces = async (projectId: string) => { const response = await axios.get( - `${getAgentaApiUrl()}/api/observability/v1/traces/search?project_id=${PROJECT_ID}`, + `${getAgentaApiUrl()}/api/observability/v1/traces/search?project_id=${projectId}`, ) return response.data } From 7ca1f02aa7b1dece344cdd25c85bd13189137542 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 25 Oct 2024 15:58:16 +0200 Subject: [PATCH 174/305] Remove DisplayBase, Add /traces/query as POST, general clean-up, bump prerelease version of the SDK, fix error handling in SDK, clean up instrument in SDK with '_pre_instrument' and '_post_instrument --- .../apis/fastapi/observability/models.py | 21 ++-- .../observability/opentelemetry/otlp.py | 14 ++- .../apis/fastapi/observability/router.py | 92 ++++++++++------ .../apis/fastapi/observability/utils.py | 9 +- .../apis/fastapi/shared/models.py | 78 ------------- .../agenta_backend/core/observability/dtos.py | 49 ++++----- .../agenta_backend/core/shared/dtos.py | 84 +------------- .../dbs/postgres/observability/dao.py | 1 + .../dbs/postgres/observability/dbas.py | 20 ++-- .../dbs/postgres/observability/dbes.py | 8 +- .../dbs/postgres/observability/mappings.py | 5 +- .../dbs/postgres/shared/dbas.py | 78 +------------ agenta-cli/agenta/sdk/decorators/routing.py | 2 +- agenta-cli/agenta/sdk/decorators/tracing.py | 11 +- agenta-cli/agenta/sdk/tracing/inline.py | 103 +++--------------- agenta-cli/pyproject.toml | 2 +- 16 files changed, 155 insertions(+), 422 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/models.py b/agenta-backend/agenta_backend/apis/fastapi/observability/models.py index 5c42ed9704..73c6755049 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/models.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/models.py @@ -1,6 +1,7 @@ -from typing import List, Dict +from typing import List +from pydantic import BaseModel -from agenta_backend.apis.fastapi.shared.models import DisplayBase, VersionedModel +from agenta_backend.apis.fastapi.shared.models import VersionedModel from agenta_backend.core.observability.dtos import ( OTelSpanDTO, @@ -23,28 +24,28 @@ class AgentaNodeDTO(SpanDTO): pass -class AgentaNodesDTO(DisplayBase): +class AgentaNodesDTO(BaseModel): nodes: List[AgentaNodeDTO] -class AgentaTreeDTO(DisplayBase): +class AgentaTreeDTO(BaseModel): tree: TreeDTO nodes: List[AgentaNodeDTO] -class AgentaTreesDTO(DisplayBase): - trees: List[AgentaTreeDTO] # -> Dict with tree.name ? +class AgentaTreesDTO(BaseModel): + trees: List[AgentaTreeDTO] -class AgentaRootDTO(DisplayBase): +class AgentaRootDTO(BaseModel): root: RootDTO - trees: List[AgentaTreeDTO] # -> Dict with tree.name ? + trees: List[AgentaTreeDTO] -class AgentaRootsDTO(DisplayBase): - roots: List[AgentaRootDTO] # -> Dict with root.name ? root.id ? +class AgentaRootsDTO(BaseModel): + roots: List[AgentaRootDTO] class AgentaNodesResponse(VersionedModel, AgentaNodesDTO): diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py index 303d56d252..2f14150712 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/otlp.py @@ -13,10 +13,6 @@ ) -RESOURCE_ID_SIZE = 16 # 64-bit int -TRACE_ID_SIZE = 32 # 128-bit int -SPAN_ID_SIZE = 16 # 64-bit int - SPAN_KINDS = [ "SPAN_KIND_UNSPECIFIED", "SPAN_KIND_INTERNAL", @@ -56,10 +52,12 @@ def parse_otlp_stream(otlp_stream: bytes) -> List[OTelSpanDTO]: for resource_span in proto.resource_spans: for scope_span in resource_span.scope_spans: for span in scope_span.spans: + # SPAN CONTEXT s_trace_id = "0x" + span.trace_id.hex() s_span_id = "0x" + span.span_id.hex() s_context = OTelContextDTO(trace_id=s_trace_id, span_id=s_span_id) + # SPAN PARENT CONTEXT s_parent_id = span.parent_span_id.hex() s_parent_id = "0x" + s_parent_id if s_parent_id else None p_context = ( @@ -68,13 +66,17 @@ def parse_otlp_stream(otlp_stream: bytes) -> List[OTelSpanDTO]: else None ) + # SPAN NAME s_name = span.name + # SPAN KIND s_kind = SPAN_KINDS[span.kind] + # SPAN TIME s_start_time = _parse_timestamp(span.start_time_unix_nano) s_end_time = _parse_timestamp(span.end_time_unix_nano) + # SPAN STATUS s_status_code = SPAN_STATUS_CODES[ span.status.code if span.status.code else 0 ] @@ -82,6 +84,7 @@ def parse_otlp_stream(otlp_stream: bytes) -> List[OTelSpanDTO]: span.status.message if span.status.message != "" else None ) + # SPAN ATTRIBUTES s_attributes = { k: v for k, v in [ @@ -89,6 +92,7 @@ def parse_otlp_stream(otlp_stream: bytes) -> List[OTelSpanDTO]: ] } + # SPAN EVENTS s_events = [ OTelEventDTO( name=event.name, @@ -105,6 +109,7 @@ def parse_otlp_stream(otlp_stream: bytes) -> List[OTelSpanDTO]: ] s_events = s_events if len(s_events) > 0 else None + # SPAN LINKS s_links = [ OTelLinkDTO( context=OTelContextDTO( @@ -123,6 +128,7 @@ def parse_otlp_stream(otlp_stream: bytes) -> List[OTelSpanDTO]: ] s_links = s_links if len(s_links) > 0 else None + # PUTTING IT ALL TOGETHER otel_span_dto = OTelSpanDTO( context=s_context, name=s_name, diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py index a63e1468b1..1355ea920a 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py @@ -62,6 +62,22 @@ def __init__( "/traces/search", self.query_traces, methods=["GET"], + operation_id="query_traces_deprecated", + summary="Query traces, with optional grouping, filtering, (sorting,) and pagination.", + status_code=status.HTTP_200_OK, + response_model=Union[ + OTelSpansResponse, + AgentaNodesResponse, + AgentaTreesResponse, + AgentaRootsResponse, + ], + response_model_exclude_none=True, + ) + + self.router.add_api_route( + "/traces/query", + self.query_traces, + methods=["POST"], operation_id="query_traces", summary="Query traces, with optional grouping, filtering, (sorting,) and pagination.", status_code=status.HTTP_200_OK, @@ -126,6 +142,7 @@ async def query_traces( spans = [] + # format = opentelemetry -> focus = node if format == "opentelemetry": spans = [parse_to_otel_span_dto(span_dto) for span_dto in span_dtos] @@ -134,42 +151,45 @@ async def query_traces( spans=spans, ) + # format = agenta elif format == "agenta": spans = [parse_to_agenta_span_dto(span_dto) for span_dto in span_dtos] - if query_dto.grouping: - if query_dto.grouping.focus.value in ["tree", "root"]: - _nodes_by_tree: Dict[str, List[AgentaNodeDTO]] = dict() - _types_by_tree: Dict[str, str] = dict() + # focus = tree | root + if query_dto.grouping and query_dto.grouping.focus.value != "node": + _nodes_by_tree: Dict[str, List[AgentaNodeDTO]] = dict() + _types_by_tree: Dict[str, str] = dict() - for span in spans: - if span.tree.id not in _nodes_by_tree: - _nodes_by_tree[span.tree.id] = list() - _types_by_tree[span.tree.id] = None + for span in spans: + if span.tree.id not in _nodes_by_tree: + _nodes_by_tree[span.tree.id] = list() + _types_by_tree[span.tree.id] = None - _nodes_by_tree[span.tree.id].append( - AgentaNodeDTO(**span.model_dump()) - ) - _types_by_tree[span.tree.id] = span.tree.type - - if query_dto.grouping.focus.value == "tree": - return AgentaTreesResponse( - version=self.VERSION, - trees=[ - AgentaTreeDTO( - tree=TreeDTO( - id=tree_id, - type=_types_by_tree[tree_id], - ), - nodes=[ - AgentaNodeDTO(**span.model_dump()) - for span in nodes - ], - ) - for tree_id, nodes in _nodes_by_tree.items() - ], - ) + _nodes_by_tree[span.tree.id].append( + AgentaNodeDTO(**span.model_dump()) + ) + _types_by_tree[span.tree.id] = span.tree.type + # focus = tree + if query_dto.grouping.focus.value == "tree": + return AgentaTreesResponse( + version=self.VERSION, + trees=[ + AgentaTreeDTO( + tree=TreeDTO( + id=tree_id, + type=_types_by_tree[tree_id], + ), + nodes=[ + AgentaNodeDTO(**span.model_dump()) for span in nodes + ], + ) + for tree_id, nodes in _nodes_by_tree.items() + ], + ) + + # focus = root + else: _nodes_by_root: Dict[str, List[AgentaTreeDTO]] = dict() _types_by_root: Dict[str, str] = dict() @@ -202,6 +222,7 @@ async def query_traces( ], ) + # focus = node return AgentaNodesResponse( version=self.VERSION, nodes=[AgentaNodeDTO(**span.model_dump()) for span in spans], @@ -220,10 +241,19 @@ async def otlp_collect_traces( otlp_stream = await request.body() + # TODO: GET project_id FROM request.state project_id = request.headers.get("AG-PROJECT-ID") or request.state.project_id + + # TODO: DROP app_id ONCE LEGACY IS DROPPED app_id = request.headers.get("AG-APP-ID") ### LEGACY ### + # TODO: DROP LEGACY + project_id = project_id or app_id + ### LEGACY ### + + ### LEGACY ### + # TODO: DROP LEGACY if self.legacy_receiver: await self.legacy_receiver( project_id=project_id, @@ -238,7 +268,7 @@ async def otlp_collect_traces( ] await self.service.ingest( - project_id=UUID(project_id or app_id), + project_id=UUID(project_id), span_dtos=span_dtos, ) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py index d17a722c98..1ff17c3a26 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py @@ -33,11 +33,11 @@ QueryDTO, ) + # --- PARSE QUERY DTO --- def _parse_windowing( - *, earliest: Optional[str] = None, latest: Optional[str] = None, ) -> Optional[WindowingDTO]: @@ -50,7 +50,6 @@ def _parse_windowing( def _parse_filtering( - *, filtering: Optional[str] = None, ) -> Optional[FilteringDTO]: # Parse JSON filtering @@ -70,7 +69,6 @@ def _parse_filtering( def _parse_grouping( - *, focus: Optional[str] = None, ) -> Optional[GroupingDTO]: _grouping = None @@ -82,7 +80,6 @@ def _parse_grouping( def _parse_pagination( - *, page: Optional[int] = None, size: Optional[int] = None, ) -> Optional[PaginationDTO]: @@ -209,7 +206,9 @@ def _decode_key( return key.replace(f"ag.{namespace}.", "") -def _encode_value(value: Any) -> Optional[Any]: +def _encode_value( + value: Any, +) -> Optional[Any]: if value is None: return None diff --git a/agenta-backend/agenta_backend/apis/fastapi/shared/models.py b/agenta-backend/agenta_backend/apis/fastapi/shared/models.py index cb7a496db0..53cb9c3ab2 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/shared/models.py +++ b/agenta-backend/agenta_backend/apis/fastapi/shared/models.py @@ -1,82 +1,4 @@ -from collections import OrderedDict - from pydantic import BaseModel -from enum import Enum - - -## --- DISPLAY --- ## - - -NOF_CHARS = 8 - - -def _p_id(id): - return repr(str(id)[:NOF_CHARS]) - - -def _p_osa(o): - elements = [] - - for i in OrderedDict(sorted(o.items())).items(): - if not i[0].startswith("_"): - if i[1].__class__.__module__ != "builtins": - if repr(i[1]).startswith("<"): - elements.append(f"{i[0]}: {i[1].name}") - elif repr(i[1]).startswith("UUID("): - elements.append(f"{i[0]}: {_p_id(i[1])}") - else: - elements.append(f"{i[0]}: {i[1].__str__()}") - else: - if isinstance(i[1], list): - elements.append( - f"{i[0]}: [" + ", ".join([el.__str__() for el in i[1]]) + "]" - ) - elif isinstance(i[1], dict): - elements.append(f"{i[0]}: {{{_p_osa(i[1])}}}") - else: - if i[1] is not None: - if i[0] == "slug": - elements.append(f"{i[0]}: {repr(i[1][:8])}") - else: - elements.append(f"{i[0]}: {repr(i[1])}") - - return ", ".join(elements) - - -def _p_ora(o, open="{", close="}", sep=": ", foo=repr): - if o.__class__.__module__ != "builtins": - if o.__class__.__name__ == "UUID": - return repr(o) - if isinstance(o, Enum): - return o - return f"{o.__class__.__name__}({_p_ora(o.__dict__, open='', close='', sep='=', foo=lambda x : x)})" - elif isinstance(o, list): - return f"[{', '.join([repr(el) for el in o])}]" - elif isinstance(o, dict): - o = OrderedDict(sorted(o.items())) - return f"{open}{', '.join([f'{foo(elk)}{sep}{_p_ora(elv)}' for elk, elv in o.items()])}{close}" - else: - if o is not None: - return repr(o) - - -def _str(o): - return f"{{{_p_osa(o.__dict__)}}}" - - -def _repr(o): - return _p_ora(o) - - -class DisplayBase(BaseModel): - def __str__(self): - return _str(self) - - def __repr__(self): - return _repr(self) - - -## --- VERSIONED --- ## class VersionedModel(BaseModel): diff --git a/agenta-backend/agenta_backend/core/observability/dtos.py b/agenta-backend/agenta_backend/core/observability/dtos.py index 4385e7d773..57fe4b5892 100644 --- a/agenta-backend/agenta_backend/core/observability/dtos.py +++ b/agenta-backend/agenta_backend/core/observability/dtos.py @@ -1,17 +1,17 @@ -from typing import List, Dict, Any, Union, Optional, Sequence +from pydantic import BaseModel +from typing import List, Dict, Any, Union, Optional from enum import Enum -from datetime import datetime, time +from datetime import datetime from uuid import UUID -from agenta_backend.core.shared.dtos import DisplayBase from agenta_backend.core.shared.dtos import LifecycleDTO ## --- SUB-ENTITIES --- ## -class RootDTO(DisplayBase): +class RootDTO(BaseModel): id: UUID @@ -21,7 +21,7 @@ class TreeType(Enum): # --- VARIANTS --- # -class TreeDTO(DisplayBase): +class TreeDTO(BaseModel): id: UUID type: Optional[TreeType] = None @@ -44,17 +44,17 @@ class NodeType(Enum): # --- VARIANTS --- # -class NodeDTO(DisplayBase): +class NodeDTO(BaseModel): id: UUID name: str type: Optional[NodeType] = None -class ParentDTO(DisplayBase): +class ParentDTO(BaseModel): id: UUID -class TimeDTO(DisplayBase): +class TimeDTO(BaseModel): start: datetime end: datetime @@ -65,7 +65,7 @@ class StatusCode(Enum): ERROR = "ERROR" -class StatusDTO(DisplayBase): +class StatusDTO(BaseModel): code: StatusCode message: Optional[str] = None @@ -76,7 +76,7 @@ class Config: Attributes = Dict[str, Any] -class ExceptionDTO(DisplayBase): +class ExceptionDTO(BaseModel): timestamp: datetime type: str message: Optional[str] = None @@ -93,7 +93,7 @@ class Config: Refs = Dict[str, Any] -class LinkDTO(DisplayBase): +class LinkDTO(BaseModel): type: TreeType # Yes, this is correct id: UUID tree_id: Optional[UUID] = None @@ -120,25 +120,25 @@ class OTelStatusCode(Enum): STATUS_CODE_UNSET = "STATUS_CODE_UNSET" -class OTelContextDTO(DisplayBase): +class OTelContextDTO(BaseModel): trace_id: str span_id: str -class OTelEventDTO(DisplayBase): +class OTelEventDTO(BaseModel): name: str timestamp: str attributes: Optional[Attributes] = None -class OTelLinkDTO(DisplayBase): +class OTelLinkDTO(BaseModel): context: OTelContextDTO attributes: Optional[Attributes] = None -class OTelExtraDTO(DisplayBase): +class OTelExtraDTO(BaseModel): kind: Optional[str] = None attributes: Optional[Attributes] = None @@ -149,7 +149,7 @@ class OTelExtraDTO(DisplayBase): ## --- ENTITIES --- ## -class SpanDTO(DisplayBase): +class SpanDTO(BaseModel): lifecycle: Optional[LifecycleDTO] = None root: RootDTO @@ -175,7 +175,7 @@ class SpanDTO(DisplayBase): nodes: Optional[Dict[str, Union["SpanDTO", List["SpanDTO"]]]] = None -class OTelSpanDTO(DisplayBase): +class OTelSpanDTO(BaseModel): context: OTelContextDTO name: str @@ -197,7 +197,7 @@ class OTelSpanDTO(DisplayBase): ## --- QUERY --- ## -class WindowingDTO(DisplayBase): +class WindowingDTO(BaseModel): earliest: Optional[datetime] = None latest: Optional[datetime] = None @@ -240,12 +240,12 @@ class ExistenceOperator(Enum): NOT_EXISTS = "not_exists" -class TextOptionsDTO(DisplayBase): +class TextOptionsDTO(BaseModel): case_sensitive: Optional[bool] = False exact_match: Optional[bool] = False -class ConditionDTO(DisplayBase): +class ConditionDTO(BaseModel): # column/field in a[.b[.c]] format # where a is the column name, and # b[.c] is the optional, and optionally nested, field name @@ -266,7 +266,7 @@ class ConditionDTO(DisplayBase): options: Optional[TextOptionsDTO] = None -class FilteringDTO(DisplayBase): +class FilteringDTO(BaseModel): operator: Optional[LogicalOperator] = LogicalOperator.AND conditions: List[Union[ConditionDTO, "FilteringDTO"]] @@ -281,17 +281,16 @@ class Focus(Enum): NODE = "node" # SPAN -class GroupingDTO(DisplayBase): +class GroupingDTO(BaseModel): focus: Focus = "node" - # SET TO ROOT ? TO TREE ? TO NODE ? -class PaginationDTO(DisplayBase): +class PaginationDTO(BaseModel): page: int size: int -class QueryDTO(DisplayBase): +class QueryDTO(BaseModel): grouping: Optional[GroupingDTO] = None windowing: Optional[WindowingDTO] = None filtering: Optional[FilteringDTO] = None diff --git a/agenta-backend/agenta_backend/core/shared/dtos.py b/agenta-backend/agenta_backend/core/shared/dtos.py index 13582e39bf..2810c57cd6 100644 --- a/agenta-backend/agenta_backend/core/shared/dtos.py +++ b/agenta-backend/agenta_backend/core/shared/dtos.py @@ -1,90 +1,10 @@ from typing import Optional - from pydantic import BaseModel -from uuid import UUID from datetime import datetime -from enum import Enum -from collections import OrderedDict - - -## --- DISPLAY --- ## - - -NOF_CHARS = 8 - - -def _p_id(id): - return repr(str(id)[:NOF_CHARS]) - - -def _p_osa(o): - elements = [] - - for i in OrderedDict(sorted(o.items())).items(): - if not i[0].startswith("_"): - if i[1].__class__.__module__ != "builtins": - if repr(i[1]).startswith("<"): - elements.append(f"{i[0]}: {i[1].name}") - elif repr(i[1]).startswith("UUID("): - elements.append(f"{i[0]}: {_p_id(i[1])}") - else: - elements.append(f"{i[0]}: {i[1].__str__()}") - else: - if isinstance(i[1], list): - elements.append( - f"{i[0]}: [" + ", ".join([el.__str__() for el in i[1]]) + "]" - ) - elif isinstance(i[1], dict): - elements.append(f"{i[0]}: {{{_p_osa(i[1])}}}") - else: - if i[1] is not None: - if i[0] == "slug": - elements.append(f"{i[0]}: {repr(i[1][:8])}") - else: - elements.append(f"{i[0]}: {repr(i[1])}") - - return ", ".join(elements) - - -def _p_ora(o, open="{", close="}", sep=": ", foo=repr): - if o.__class__.__module__ != "builtins": - if o.__class__.__name__ == "UUID": - return repr(o) - if isinstance(o, Enum): - return o - if isinstance(o, datetime): - return o.isoformat() - return f"{o.__class__.__name__}({_p_ora(o.__dict__, open='', close='', sep='=', foo=lambda x : x)})" - elif isinstance(o, list): - return f"[{', '.join([repr(el) for el in o])}]" - elif isinstance(o, dict): - o = OrderedDict(sorted(o.items())) - return f"{open}{', '.join([f'{foo(elk)}{sep}{_p_ora(elv)}' for elk, elv in o.items()])}{close}" - else: - if o is not None: - return repr(o) - - -def _str(o): - return f"{{{_p_osa(o.__dict__)}}}" - - -def _repr(o): - return _p_ora(o) - - -class DisplayBase(BaseModel): - def __str__(self): - return _str(self) - - def __repr__(self): - return _repr(self) - - -## --- LIFECYCLE --- ## +from uuid import UUID -class LifecycleDTO(DisplayBase): +class LifecycleDTO(BaseModel): created_at: datetime updated_at: Optional[datetime] = None diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py index ff35e0404e..0eae714928 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py @@ -113,6 +113,7 @@ async def query( # -------- # DEBUGGING + # TODO: HIDE THIS BEFORE RELEASING print( str( query.compile( diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py index 2ea9772270..3393e809bf 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py @@ -3,24 +3,24 @@ from agenta_backend.core.observability.dtos import TreeType, NodeType -from agenta_backend.dbs.postgres.shared.dbas import DisplayBase +from agenta_backend.dbs.postgres.shared.base import Base from agenta_backend.dbs.postgres.shared.dbas import ProjectScopeDBA, LifecycleDBA -class RootDBA(DisplayBase): +class RootDBA: __abstract__ = True root_id = Column(UUID(as_uuid=True), nullable=False) -class TreeDBA(DisplayBase): +class TreeDBA: __abstract__ = True tree_id = Column(UUID(as_uuid=True), nullable=False) tree_type = Column(SQLEnum(TreeType), nullable=True) -class NodeDBA(DisplayBase): +class NodeDBA: __abstract__ = True node_id = Column(UUID(as_uuid=True), nullable=False) @@ -28,32 +28,32 @@ class NodeDBA(DisplayBase): node_type = Column(SQLEnum(NodeType), nullable=True) -class ParentDBA(DisplayBase): +class ParentDBA: __abstract__ = True parent_id = Column(UUID(as_uuid=True), nullable=True) -class TimeDBA(DisplayBase): +class TimeDBA: __abstract__ = True time_start = Column(TIMESTAMP, nullable=False) time_end = Column(TIMESTAMP, nullable=False) -class StatusDBA(DisplayBase): +class StatusDBA: __abstract__ = True status = Column(JSONB, nullable=True) -class ExceptionDBA(DisplayBase): +class ExceptionDBA: __abstract__ = True exception = Column(JSONB, nullable=True) -class AttributesDBA(DisplayBase): +class AttributesDBA: __abstract__ = True data = Column(String, nullable=True) # STRING for full-text search @@ -63,7 +63,7 @@ class AttributesDBA(DisplayBase): links = Column(HSTORE, nullable=True) # HSTORE for fast querying -class OTelDBA(DisplayBase): +class OTelDBA: __abstract__ = True otel = Column(JSONB, nullable=False) diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py index 1c6100f714..a119e70970 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py @@ -12,20 +12,20 @@ class InvocationSpanDBE(Base, SpanDBA): PrimaryKeyConstraint( "project_id", "node_id", - ), + ), # focus = node Index( "index_project_id_tree_id", "project_id", "tree_id", - ), + ), # focus = tree Index( "index_project_id_root_id", "project_id", "root_id", - ), + ), # focus = root Index( "index_project_id_node_id", "project_id", "created_at", - ), + ), # sorting and pagination ) diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py index 1ea79e35e0..8b703878ab 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py @@ -1,6 +1,5 @@ from uuid import UUID - -from agenta_backend.dbs.postgres.observability.dbes import InvocationSpanDBE +from json import dumps, loads from agenta_backend.core.shared.dtos import LifecycleDTO from agenta_backend.core.observability.dtos import ( @@ -16,7 +15,7 @@ SpanDTO, ) -from json import dumps, loads +from agenta_backend.dbs.postgres.observability.dbes import InvocationSpanDBE def map_span_dbe_to_dto(span: InvocationSpanDBE) -> SpanDTO: diff --git a/agenta-backend/agenta_backend/dbs/postgres/shared/dbas.py b/agenta-backend/agenta_backend/dbs/postgres/shared/dbas.py index 9810bf18f0..e400be3ec1 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/shared/dbas.py +++ b/agenta-backend/agenta_backend/dbs/postgres/shared/dbas.py @@ -1,78 +1,7 @@ -from collections import OrderedDict - from sqlalchemy import Column, UUID, TIMESTAMP, func -from agenta_backend.dbs.postgres.shared.base import Base - -## --- DISPLAY --- ## - - -NOF_CHARS = 8 - - -def _p_id(id): - return repr(str(id)[:NOF_CHARS]) - - -def _str(o): - return f"{{ {_p_osa(o)} }}" - - -def _repr(o): - return f"{o.__class__.__name__}({_p_ora(o)})" - - -def _p_osa(o): - return ", ".join( - [ - ( - f"{i[0]}: {_p_id(i[1])}" - if repr(i[1]).startswith("UUID(") - or (i[0] == "slug" and i[1] is not None) - else ( - f"{i[0]}: {i[1].name}" - if repr(i[1]).startswith("<") - else f"{i[0]}: {repr(i[1])}" - ) - ) - for i in OrderedDict( - sorted(o.__dict__.items(), key=lambda item: item[0]) - ).items() - if not i[0].startswith("_") - ] - ) - - -def _p_ora(o): - return ", ".join( - [ - ( - f"{i[0]}={str(i[1])}" - if repr(i[1]).startswith("<") - else f"{i[0]}={repr(i[1])}" - ) - for i in OrderedDict( - sorted(o.__dict__.items(), key=lambda item: item[0]) - ).items() - if not i[0].startswith("_") - ] - ) - -class DisplayBase: - __abstract__ = True - - def __str__(self): - return _str(self) - - def __repr__(self): - return _repr(self) - - -## --- SCOPE (DBA) --- ## - - -class ProjectScopeDBA(DisplayBase): +class ProjectScopeDBA: __abstract__ = True project_id = Column( @@ -81,10 +10,7 @@ class ProjectScopeDBA(DisplayBase): ) -## --- LIFECYCLE (DBA) --- ## - - -class LifecycleDBA(DisplayBase): +class LifecycleDBA: __abstract__ = True created_at = Column( diff --git a/agenta-cli/agenta/sdk/decorators/routing.py b/agenta-cli/agenta/sdk/decorators/routing.py index d59b45ea71..57bb5df2e2 100644 --- a/agenta-cli/agenta/sdk/decorators/routing.py +++ b/agenta-cli/agenta/sdk/decorators/routing.py @@ -362,7 +362,7 @@ def handle_failure(self, error: Exception): status_code = error.status_code if hasattr(error, "status_code") else 500 message = str(error) - stacktrace = format_exception(e, value=e, tb=e.__traceback__) # type: ignore + stacktrace = format_exception(error, value=error, tb=error.__traceback__) # type: ignore detail = {"message": message, "stacktrace": stacktrace} log.error(f"----------------------------------") diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 62ef189fcb..11db3fea6f 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -15,15 +15,16 @@ class instrument: def __init__( self, - kind: str = "task", + type: str = "task", config: Optional[Dict[str, Any]] = None, ignore_inputs: Optional[bool] = None, ignore_outputs: Optional[bool] = None, max_depth: Optional[int] = 2, # DEPRECATING + kind: str = "task", spankind: Optional[str] = "TASK", ) -> None: - self.type = spankind if spankind is not None else kind + self.type = spankind or kind or type self.kind = None self.config = config self.ignore_inputs = ignore_inputs @@ -43,7 +44,7 @@ async def _async_auto_instrumented(*args, **kwargs): result = await func(*args, **kwargs) - self._post_instrument(self, result) + self._post_instrument(result) return result @@ -59,7 +60,7 @@ def _sync_auto_instrumented(*args, **kwargs): result = func(*args, **kwargs) - self._post_instrument(self, result) + self._post_instrument(result) return result @@ -83,7 +84,7 @@ def _pre_instrument( with suppress(): span.set_attributes( - attributes={"node": self.kind}, + attributes={"node": self.type}, namespace="type", ) diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py index 419bf2d749..f219e40daf 100644 --- a/agenta-cli/agenta/sdk/tracing/inline.py +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -11,82 +11,11 @@ from collections import OrderedDict -NOF_CHARS = 8 - - -def _p_id(id): - return repr(str(id)[:NOF_CHARS]) - - -def _p_osa(o): - elements = [] - - for i in OrderedDict(sorted(o.items())).items(): - if not i[0].startswith("_"): - if i[1].__class__.__module__ != "builtins": - if repr(i[1]).startswith("<"): - elements.append(f"{i[0]}: {i[1].name}") - elif repr(i[1]).startswith("UUID("): - elements.append(f"{i[0]}: {_p_id(i[1])}") - else: - elements.append(f"{i[0]}: {i[1].__str__()}") - else: - if isinstance(i[1], list): - elements.append( - f"{i[0]}: [" + ", ".join([el.__str__() for el in i[1]]) + "]" - ) - elif isinstance(i[1], dict): - elements.append(f"{i[0]}: {{{_p_osa(i[1])}}}") - else: - if i[1] is not None: - if i[0] == "slug": - elements.append(f"{i[0]}: {repr(i[1][:8])}") - else: - elements.append(f"{i[0]}: {repr(i[1])}") - - return ", ".join(elements) - - -def _p_ora(o, open="{", close="}", sep=": ", foo=repr): - if o.__class__.__module__ != "builtins": - if o.__class__.__name__ == "UUID": - return repr(o) - if isinstance(o, Enum): - return o - if isinstance(o, datetime): - return o.isoformat() - return f"{o.__class__.__name__}({_p_ora(o.__dict__, open='', close='', sep='=', foo=lambda x : x)})" - elif isinstance(o, list): - return f"[{', '.join([repr(el) for el in o])}]" - elif isinstance(o, dict): - o = OrderedDict(sorted(o.items())) - return f"{open}{', '.join([f'{foo(elk)}{sep}{_p_ora(elv)}' for elk, elv in o.items()])}{close}" - else: - if o is not None: - return repr(o) - - -def _str(o): - return f"{{{_p_osa(o.__dict__)}}}" - - -def _repr(o): - return _p_ora(o) - - -class DisplayBase(BaseModel): - def __str__(self): - return _str(self) - - def __repr__(self): - return _repr(self) - - -class ProjectScopeDTO(DisplayBase): +class ProjectScopeDTO(BaseModel): project_id: UUID -class LifecycleDTO(DisplayBase): +class LifecycleDTO(BaseModel): created_at: datetime updated_at: Optional[datetime] = None @@ -102,14 +31,14 @@ class LifecycleDTO(DisplayBase): ### services.observability.dtos ### ### --------------------------- ### -from typing import List, Dict, Any, Union, Optional, Sequence +from typing import List, Dict, Any, Union, Optional from enum import Enum from datetime import datetime from uuid import UUID -class TimeDTO(DisplayBase): +class TimeDTO(BaseModel): start: datetime end: datetime span: int @@ -121,7 +50,7 @@ class StatusCode(Enum): ERROR = "ERROR" -class StatusDTO(DisplayBase): +class StatusDTO(BaseModel): code: StatusCode message: Optional[str] = None stacktrace: Optional[str] = None @@ -155,16 +84,16 @@ class NodeType(Enum): # --- VARIANTS --- # -class RootDTO(DisplayBase): +class RootDTO(BaseModel): id: UUID -class TreeDTO(DisplayBase): +class TreeDTO(BaseModel): id: UUID type: Optional[TreeType] = None -class NodeDTO(DisplayBase): +class NodeDTO(BaseModel): id: UUID type: Optional[NodeType] = None name: str @@ -177,13 +106,13 @@ class NodeDTO(DisplayBase): Refs = Dict[str, str] -class LinkDTO(DisplayBase): +class LinkDTO(BaseModel): type: str id: UUID tree_id: Optional[UUID] = None -class ParentDTO(DisplayBase): +class ParentDTO(BaseModel): id: UUID @@ -205,25 +134,25 @@ class OTelStatusCode(Enum): STATUS_CODE_UNSET = "STATUS_CODE_UNSET" -class OTelContextDTO(DisplayBase): +class OTelContextDTO(BaseModel): trace_id: str span_id: str -class OTelEventDTO(DisplayBase): +class OTelEventDTO(BaseModel): name: str timestamp: datetime attributes: Optional[Attributes] = None -class OTelLinkDTO(DisplayBase): +class OTelLinkDTO(BaseModel): context: OTelContextDTO attributes: Optional[Attributes] = None -class OTelExtraDTO(DisplayBase): +class OTelExtraDTO(BaseModel): kind: Optional[str] = None attributes: Optional[Attributes] = None @@ -231,7 +160,7 @@ class OTelExtraDTO(DisplayBase): links: Optional[List[OTelLinkDTO]] = None -class SpanDTO(DisplayBase): +class SpanDTO(BaseModel): scope: ProjectScopeDTO lifecycle: Optional[LifecycleDTO] = None @@ -258,7 +187,7 @@ class SpanDTO(DisplayBase): nodes: Optional[Dict[str, Union["SpanDTO", List["SpanDTO"]]]] = None -class OTelSpanDTO(DisplayBase): +class OTelSpanDTO(BaseModel): context: OTelContextDTO name: str diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index 18a5659a1f..d5e772a842 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.27.0a3" +version = "0.27.0a4" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] From 7685ca0f287c90d638aea32a59e594c36e47b1a6 Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Fri, 25 Oct 2024 17:25:48 +0200 Subject: [PATCH 175/305] test(app): clean up tests sanity --- agenta-cli/tests/observability_sdk/Readme.md | 93 +++++++++ .../sanity_check/01_app_sanity.py | 27 +++ .../{app_local.py => 02_app_local.py} | 7 +- .../{app_errors.py => 03_app_errors_local.py} | 45 ++-- ...pp_new_sdk.py => 04_app_old_sdk_agenta.py} | 0 ...pp_old_sdk.py => 05_app_new_sdk_agenta.py} | 26 ++- .../sanity_check/app_new_sdk_noinstrument.py | 195 ------------------ .../sanity_check/app_sync.py | 39 ---- 8 files changed, 164 insertions(+), 268 deletions(-) create mode 100644 agenta-cli/tests/observability_sdk/Readme.md create mode 100644 agenta-cli/tests/observability_sdk/sanity_check/01_app_sanity.py rename agenta-cli/tests/observability_sdk/sanity_check/{app_local.py => 02_app_local.py} (96%) rename agenta-cli/tests/observability_sdk/sanity_check/{app_errors.py => 03_app_errors_local.py} (88%) rename agenta-cli/tests/observability_sdk/sanity_check/{app_new_sdk.py => 04_app_old_sdk_agenta.py} (100%) rename agenta-cli/tests/observability_sdk/sanity_check/{app_old_sdk.py => 05_app_new_sdk_agenta.py} (90%) delete mode 100644 agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk_noinstrument.py delete mode 100644 agenta-cli/tests/observability_sdk/sanity_check/app_sync.py diff --git a/agenta-cli/tests/observability_sdk/Readme.md b/agenta-cli/tests/observability_sdk/Readme.md new file mode 100644 index 0000000000..92f949d7ad --- /dev/null +++ b/agenta-cli/tests/observability_sdk/Readme.md @@ -0,0 +1,93 @@ +# Setup + +Create multiple `.env` files containing the following variables: + +``` +AGENTA_PROJECT_ID=your_project_id +AGENTA_HOST=your_host +AGENTA_APP_ID=your_app_id +AGENTA_API_KEY=your_api_key +``` + +Create one `.env` file for each environment: `local`, `cloud`, and `oss`. For example: + +- `.env.local` +- `.env.cloud` +- `.env.oss` + +--- + +# Install the Requirements + +You need to install the requirements for each folder. It is recommended to do this in a virtual environment. + +1. **Create a virtual environment:** + + ```bash + python -m venv .venv + ``` + +2. **Activate the virtual environment:** + + ```bash + source .venv/bin/activate + ``` + +3. **Install the required packages:** + + ```bash + pip install -r requirements.txt + ``` + +--- + +# Running the Scripts + +Load the environment variables and run the script. All scripts run locally. The requirements for each folder are specified in `requirements.txt`. + +For example, to run the `app_sanity.py` script with the `oss` environment: + +```bash +source .env.oss && env $(cat .env.oss | xargs) python app_sanity.py +``` + +--- + +# The Tests + +## Sanity Check + +The tests in the `sanity_check` folder do not use an LLM but test the functionalities of the SDK: + +### Functionalities Tested + +- **Async Functionality** + - Works with asynchronous functions. +- **Sync Functionality** + - Works with synchronous functions. +- **Old SDK Compatibility** + - Checks if it is instrumenting the config correctly. +- **New SDK Functionality** + - Checks if it is instrumenting the config correctly. +- **Ignoring Inputs and Outputs** + - **Inputs:** + - Some inputs + - All inputs + - **Outputs:** + - Some outputs + - All outputs +- **Data Types Handling** + - Works with dictionaries as inputs/outputs. + - Works with Pydantic models as inputs/outputs. +- **Workflow Types** + - Works with all types of workflows. +- **Error Handling** + - Correctly handles errors. + +### Tests + +#### 01_app_sanity.py + +Minimum app to test the functionalities of the SDK. + +#### diff --git a/agenta-cli/tests/observability_sdk/sanity_check/01_app_sanity.py b/agenta-cli/tests/observability_sdk/sanity_check/01_app_sanity.py new file mode 100644 index 0000000000..441e091d72 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/sanity_check/01_app_sanity.py @@ -0,0 +1,27 @@ +import agenta as ag +from pydantic import BaseModel +from agenta.sdk.assets import supported_llm_models + + + + +ag.init( +) + +ag.config.default( + temperature=ag.FloatParam(0.2), + model=ag.GroupedMultipleChoiceParam( + default="gpt-3.5-turbo", choices=supported_llm_models + ), + max_tokens=ag.IntParam(-1, -1, 4000), + prompt_system=ag.TextParam("MY_SYSTEM_PROMPT"), +) + +@ag.instrument(spankind="WORKFLOW") +async def main2(topic: str, genre: str, count: int = 5): + return "test" + + +if __name__ == "__main__": + import asyncio + asyncio.run(main2(topic="df", genre="d", count=1)) diff --git a/agenta-cli/tests/observability_sdk/sanity_check/app_local.py b/agenta-cli/tests/observability_sdk/sanity_check/02_app_local.py similarity index 96% rename from agenta-cli/tests/observability_sdk/sanity_check/app_local.py rename to agenta-cli/tests/observability_sdk/sanity_check/02_app_local.py index 8907013293..b4774018a9 100644 --- a/agenta-cli/tests/observability_sdk/sanity_check/app_local.py +++ b/agenta-cli/tests/observability_sdk/sanity_check/02_app_local.py @@ -2,12 +2,7 @@ from pydantic import BaseModel from agenta.sdk.assets import supported_llm_models -ag.init( - project_id="0192813f-60d5-7a65-8a75-6dda36b79267", - host="https://cloud.beta.agenta.ai", - api_key="NYWy4We0.17ce0e85db4840a39ca9ee7b00e8817b22b60d7e152407a5a4cc98c5284f2e0e", - app_id="0192b51a-bfc8-7a91-9276-470c3b35597f", -) +ag.init() ag.config.default( temperature=ag.FloatParam(0.2), diff --git a/agenta-cli/tests/observability_sdk/sanity_check/app_errors.py b/agenta-cli/tests/observability_sdk/sanity_check/03_app_errors_local.py similarity index 88% rename from agenta-cli/tests/observability_sdk/sanity_check/app_errors.py rename to agenta-cli/tests/observability_sdk/sanity_check/03_app_errors_local.py index 54328d4b1b..4a42eb2f78 100644 --- a/agenta-cli/tests/observability_sdk/sanity_check/app_errors.py +++ b/agenta-cli/tests/observability_sdk/sanity_check/03_app_errors_local.py @@ -14,6 +14,7 @@ ) + # Pydantic models class InputData(BaseModel): text: str @@ -50,56 +51,56 @@ def ignore_all_outputs_agent(query: str): # Function with ignored inputs -@ag.instrument(spankind="IGNORE_INPUTS", ignore_inputs=["secret"]) +@ag.instrument(spankind="CHAIN", ignore_inputs=["secret"]) def function_with_ignored_inputs(public_data: str, secret: str): print("function with ignored inputs") return f"Public: {public_data}, Secret: {secret}" # Function with all inputs ignored -@ag.instrument(spankind="IGNORE_ALL_INPUTS", ignore_inputs=True) +@ag.instrument(spankind="CHAIN", ignore_inputs=True) def function_with_all_ignored_inputs(data: str): print("function with all ignored inputs") return f"Data: {data}" # Function using dict inputs/outputs -@ag.instrument(spankind="DICT_IO") +@ag.instrument(spankind="CHAIN") def dict_function(input_dict: dict) -> dict: print("dict function") return {"output_data": input_dict.get("key", None)} # Function using Pydantic models -@ag.instrument(spankind="PYDANTIC") +@ag.instrument(spankind="CHAIN") def pydantic_function(input_data: InputData) -> OutputData: print("pydantic function") return OutputData(result=input_data.text.upper(), count=input_data.value + 1) # Function with None output -@ag.instrument(spankind="NONE_OUTPUT") +@ag.instrument(spankind="CHAIN") def none_output_function(): print("none output function") return None # Nested function calls -@ag.instrument(spankind="NESTED") +@ag.instrument(spankind="CHAIN") def nested_function(value: int): print("nested function") inner_result = inner_function(value) return f"Nested result: {inner_result}" -@ag.instrument(spankind="INNER") +@ag.instrument(spankind="CHAIN") def inner_function(value: int): print("inner function") return value * 2 # Function called multiple times -@ag.instrument(spankind="MULTIPLE_CALLS") +@ag.instrument(spankind="CHAIN") def multiple_calls_function(counter: int): print(f"multiple calls function call {counter}") return f"Call number: {counter}" @@ -148,13 +149,13 @@ def rerank_function(documents: list): return sorted(documents, reverse=True) -@ag.instrument(spankind="WRONGKIND") -def wrong_kind_function(input_data: str): - print("wrong kind") - return f"Processed with wrong kind: {input_data}" +# @ag.instrument(spankind="WRONGKIND") +# def wrong_kind_function(input_data: str): +# print("wrong kind") +# return f"Processed with wrong kind: {input_data}" -@ag.instrument(spankind="GENERATOR", ignore_inputs=True) +@ag.instrument(spankind="COMPLETION", ignore_inputs=True) async def summarizer(topic: str, genre: str, report: dict) -> dict: print("summarizer") return {"report": "mya"} @@ -166,7 +167,8 @@ async def exception_func(): return "dummy" -@ag.entrypoint + + @ag.instrument(spankind="WORKFLOW") async def errors(topic: str, genre: str, count: int = 5): result = ignore_some_outputs_embedding("something") @@ -180,7 +182,6 @@ async def errors(topic: str, genre: str, count: int = 5): completion_result = completion_function("complete this") chat_result = await chat_function("Hello, AI!") rerank_result = rerank_function([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]) - wrong_kind_result = wrong_kind_function("test wrong kind") summarizer_result = await summarizer("topic", "genre", {"content": "report"}) ignored_input_result = function_with_ignored_inputs("public info", "secret info") all_ignored_input_result = function_with_all_ignored_inputs("some data") @@ -203,7 +204,6 @@ async def errors(topic: str, genre: str, count: int = 5): Completion: {completion_result} Chat: {chat_result} Rerank: {rerank_result} -Wrong Kind: {wrong_kind_result} Summarizer: {summarizer_result} Ignored Inputs: {ignored_input_result} All Ignored Inputs: {all_ignored_input_result} @@ -212,4 +212,15 @@ async def errors(topic: str, genre: str, count: int = 5): None Output Function: {none_output_result} Nested Function: {nested_result} Multiple Calls Function: {multiple_calls_results} -app_old_sdk""" +app_errors_local""" + + + + +if __name__ == "__main__": + import asyncio + + try: + asyncio.run(errors(topic="df", genre="d", count=1)) + except Exception as e: + print(e) diff --git a/agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk.py b/agenta-cli/tests/observability_sdk/sanity_check/04_app_old_sdk_agenta.py similarity index 100% rename from agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk.py rename to agenta-cli/tests/observability_sdk/sanity_check/04_app_old_sdk_agenta.py diff --git a/agenta-cli/tests/observability_sdk/sanity_check/app_old_sdk.py b/agenta-cli/tests/observability_sdk/sanity_check/05_app_new_sdk_agenta.py similarity index 90% rename from agenta-cli/tests/observability_sdk/sanity_check/app_old_sdk.py rename to agenta-cli/tests/observability_sdk/sanity_check/05_app_new_sdk_agenta.py index 3ae0ec4e3c..046f62e630 100644 --- a/agenta-cli/tests/observability_sdk/sanity_check/app_old_sdk.py +++ b/agenta-cli/tests/observability_sdk/sanity_check/05_app_new_sdk_agenta.py @@ -1,17 +1,21 @@ import agenta as ag -from pydantic import BaseModel +from pydantic import BaseModel, Field from agenta.sdk.assets import supported_llm_models +from typing import Annotated ag.init() -ag.config.default( - temperature=ag.FloatParam(0.2), - model=ag.GroupedMultipleChoiceParam( - default="gpt-3.5-turbo", choices=supported_llm_models - ), - max_tokens=ag.IntParam(-1, -1, 4000), - prompt_system=ag.TextParam("MY_SYSTEM_PROMPT"), -) + +class MyConfig(BaseModel): + temperature: float = Field(default=0.2, le=1, ge=0) + model: Annotated[str, ag.MultipleChoice(choices=supported_llm_models)] = Field( + default="gpt-3.5-turbo" + ) + max_tokens: int = Field(default=-1, ge=-1, le=4000) + prompt_system: str = Field(default="system prompt") + multiselect: Annotated[str, ag.MultipleChoice(choices=["a", "b", "c"])] = Field( + default="a" + ) # Pydantic models @@ -166,9 +170,9 @@ async def exception_func(): return "dummy" -@ag.entrypoint +@ag.route("/", config_schema=MyConfig) @ag.instrument(spankind="WORKFLOW") -async def rag(topic: str, genre: str, count: int = 5): +async def newsdk(topic: str, genre: str, count: int = 5): result = ignore_some_outputs_embedding("something") agent_result = ignore_all_outputs_agent("agent query") chain_result1 = chain_function("chain input 1") diff --git a/agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk_noinstrument.py b/agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk_noinstrument.py deleted file mode 100644 index 6582e13a8e..0000000000 --- a/agenta-cli/tests/observability_sdk/sanity_check/app_new_sdk_noinstrument.py +++ /dev/null @@ -1,195 +0,0 @@ -import agenta as ag -from pydantic import BaseModel, Field -from agenta.sdk.assets import supported_llm_models -from typing import Annotated - -ag.init() - - -class MyConfig(BaseModel): - temperature: float = Field(default=0.2, le=1, ge=0) - model: Annotated[str, ag.MultipleChoice(choices=supported_llm_models)] = Field( - default="gpt-3.5-turbo" - ) - max_tokens: int = Field(default=-1, ge=-1, le=4000) - prompt_system: str = Field(default="system prompt") - multiselect: Annotated[str, ag.MultipleChoice(choices=["a", "b", "c"])] = Field( - default="a" - ) - - -# Pydantic models -class InputData(BaseModel): - text: str - value: int - - -class OutputData(BaseModel): - result: str - count: int - - -# Function with ignored outputs -def ignore_some_outputs_embedding(description: str): - print("embed") - return { - "embedding": "somedata", - "ignored": "ignored", - "cost": 15, - } - - -# Function with all outputs ignored -def ignore_all_outputs_agent(query: str): - print("agent") - return { - "result": "agent result", - "confidence": 0.9, - } - - -# Function with ignored inputs -def function_with_ignored_inputs(public_data: str, secret: str): - print("function with ignored inputs") - return f"Public: {public_data}, Secret: {secret}" - - -# Function with all inputs ignored -def function_with_all_ignored_inputs(data: str): - print("function with all ignored inputs") - return f"Data: {data}" - - -# Function using dict inputs/outputs -def dict_function(input_dict: dict) -> dict: - print("dict function") - return {"output_data": input_dict.get("key", None)} - - -# Function using Pydantic models -def pydantic_function(input_data: InputData) -> OutputData: - print("pydantic function") - return OutputData(result=input_data.text.upper(), count=input_data.value + 1) - - -# Function with None output -def none_output_function(): - print("none output function") - return None - - -# Nested function calls -def nested_function(value: int): - print("nested function") - inner_result = inner_function(value) - return f"Nested result: {inner_result}" - - -def inner_function(value: int): - print("inner function") - return value * 2 - - -# Function called multiple times -def multiple_calls_function(counter: int): - print(f"multiple calls function call {counter}") - return f"Call number: {counter}" - - -# Existing functions -def chain_function(input_data: str): - print("chain") - return f"Processed: {input_data}" - - -def task_function(task: str): - print("task") - return f"Completed task: {task}" - - -def tool_function(tool_input: str): - print("tool") - return f"Tool output: {tool_input}" - - -def query_function(query: str): - print("query") - return f"Query result: {query}" - - -def completion_function(prompt: str): - print("completion") - return f"Completed: {prompt}" - - -async def chat_function(message: str): - print("chat") - return f"Chat response: {message}" - - -def rerank_function(documents: list): - print("rerank") - return sorted(documents, reverse=True) - - -def wrong_kind_function(input_data: str): - print("wrong kind") - return f"Processed with wrong kind: {input_data}" - - -async def summarizer(topic: str, genre: str, report: dict) -> dict: - print("summarizer") - return {"report": "mya"} - - -async def exception_func(): - raise Exception("This is an exception") - return "dummy" - - -@ag.route("/", config_schema=MyConfig) -async def newsdk(topic: str, genre: str, count: int = 5): - result = ignore_some_outputs_embedding("something") - agent_result = ignore_all_outputs_agent("agent query") - chain_result1 = chain_function("chain input 1") - chain_result2 = chain_function("chain input 2") - chain_result3 = chain_function("chain input 3") # Called multiple times - task_result = task_function("important task") - tool_result = tool_function("tool input") - query_result = query_function("search query") - completion_result = completion_function("complete this") - chat_result = await chat_function("Hello, AI!") - rerank_result = rerank_function([3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]) - wrong_kind_result = wrong_kind_function("test wrong kind") - summarizer_result = await summarizer("topic", "genre", {"content": "report"}) - ignored_input_result = function_with_ignored_inputs("public info", "secret info") - all_ignored_input_result = function_with_all_ignored_inputs("some data") - dict_result = dict_function({"key": "value"}) - pydantic_input = InputData(text="hello", value=42) - pydantic_result = pydantic_function(pydantic_input) - none_output_result = none_output_function() - nested_result = nested_function(5) - multiple_calls_results = [ - multiple_calls_function(i) for i in range(3) - ] # Called multiple times - # exception_result = await exception_func() - return f"""Results: -Embedding: {result} -Agent: {agent_result} -Chain Results: {chain_result1}, {chain_result2}, {chain_result3} -Task: {task_result} -Tool: {tool_result} -Query: {query_result} -Completion: {completion_result} -Chat: {chat_result} -Rerank: {rerank_result} -Wrong Kind: {wrong_kind_result} -Summarizer: {summarizer_result} -Ignored Inputs: {ignored_input_result} -All Ignored Inputs: {all_ignored_input_result} -Dict Function: {dict_result} -Pydantic Function: {pydantic_result} -None Output Function: {none_output_result} -Nested Function: {nested_result} -Multiple Calls Function: {multiple_calls_results} -app_old_sdk""" diff --git a/agenta-cli/tests/observability_sdk/sanity_check/app_sync.py b/agenta-cli/tests/observability_sdk/sanity_check/app_sync.py deleted file mode 100644 index 65f8b2a044..0000000000 --- a/agenta-cli/tests/observability_sdk/sanity_check/app_sync.py +++ /dev/null @@ -1,39 +0,0 @@ -import agenta as ag -from pydantic import BaseModel, Field - -ag.init() - - -class MyConfig(BaseModel): - prompt: str = Field(default="somevalue") - - -@ag.instrument( - spankind="EMBEDDING", - ignore_outputs=["ignored", "cost", "usage"], -) -def embed(description: str): - print("embed") - return { - "embedding": "somedata", - "ignored": "ignored", - "cost": 15, - "usage": 20, - } - - -@ag.instrument(spankind="GENERATOR", ignore_inputs=True) -async def summarizer(topic: str, genre: str, report: dict) -> dict: - return {"report": report} - - -@ag.entrypoint -@ag.instrument(spankind="WORKFLOW") -async def rag(topic: str, genre: str, count: int = 5): - count = int(count) - - result = await embed("something") - - result = await summarizer("topic", "genre", "report") - - return result["report"] From 6df4625d6dc6ba08ad1dc7329bb34bfaedd95be1 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 25 Oct 2024 17:46:43 +0200 Subject: [PATCH 176/305] Fix issue with exceptions --- .../agenta_backend/apis/fastapi/observability/utils.py | 2 +- agenta-backend/agenta_backend/core/observability/dtos.py | 4 ++++ .../agenta_backend/dbs/postgres/observability/mappings.py | 6 +----- agenta-cli/agenta/sdk/tracing/tracing.py | 2 +- agenta-cli/pyproject.toml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py index 1ff17c3a26..5bd20695ef 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py @@ -371,7 +371,7 @@ def _parse_from_attributes( for key in _refs.keys(): if key.endswith(".id"): try: - _refs[key] = UUID(_refs[key]).hex + _refs[key] = str(UUID(_refs[key])) except: _refs[key] = None diff --git a/agenta-backend/agenta_backend/core/observability/dtos.py b/agenta-backend/agenta_backend/core/observability/dtos.py index 57fe4b5892..957608ba39 100644 --- a/agenta-backend/agenta_backend/core/observability/dtos.py +++ b/agenta-backend/agenta_backend/core/observability/dtos.py @@ -1,4 +1,5 @@ from pydantic import BaseModel +from json import loads from typing import List, Dict, Any, Union, Optional from enum import Enum @@ -86,6 +87,9 @@ class ExceptionDTO(BaseModel): class Config: json_encoders = {datetime: lambda dt: dt.isoformat()} + def to_json(self) -> dict: + return loads(self.model_dump_json(exclude_none=True)) + Data = Dict[str, Any] Metrics = Dict[str, Any] diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py index 8b703878ab..0b10808d00 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py @@ -115,11 +115,7 @@ def map_span_dto_to_dbe( span_dto.status.model_dump(exclude_none=True) if span_dto.status else None ), # EXCEPTION - exception=( - span_dto.exception.model_dump(exclude_none=True) - if span_dto.exception - else None - ), + exception=(span_dto.exception.to_json() if span_dto.exception else None), # ATTRIBUTES data=dumps(span_dto.data), metrics=span_dto.metrics, diff --git a/agenta-cli/agenta/sdk/tracing/tracing.py b/agenta-cli/agenta/sdk/tracing/tracing.py index 59d2d1366e..a72220c1ca 100644 --- a/agenta-cli/agenta/sdk/tracing/tracing.py +++ b/agenta-cli/agenta/sdk/tracing/tracing.py @@ -167,7 +167,7 @@ def store_refs( # TYPE AND FORMAT CHECKING if key.endswith(".id"): try: - refs[key] = UUID(refs[key]).hex + refs[key] = str(UUID(refs[key])) except: refs[key] = None diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index d5e772a842..1996a26322 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.27.0a4" +version = "0.27.0a5" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] From ba73ebb52c05479dfad37f0faa3385eaddca227e Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 25 Oct 2024 17:56:10 +0200 Subject: [PATCH 177/305] fix status.code=UNSET not working, may adde regression issues, use with caution --- agenta-backend/agenta_backend/dbs/postgres/observability/dao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py index 0eae714928..9be2b2e8cd 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py @@ -326,7 +326,7 @@ def _filters(filtering: FilteringDTO) -> list: attribute = cast(attribute, UUID) elif isinstance(value, str): attribute = cast(attribute, String) - value = f'"{value}"' + # value = f'"{value}"' # WILL ADD THIS BACK AS SOON AS I FIGURE OUT WHY THE QUOTES WERE ADDED elif isinstance(value, int): attribute = cast(attribute, Float) # Yes, Float elif isinstance(value, float): From dacceb233076655c8741b635c66963e180b9784a Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Fri, 25 Oct 2024 20:32:35 +0200 Subject: [PATCH 178/305] fix(sdk): remove self from instrumentation --- agenta-cli/agenta/sdk/decorators/tracing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 11db3fea6f..27d8badcb7 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -40,7 +40,7 @@ async def _async_auto_instrumented(*args, **kwargs): self._parse_type_and_kind() with ag.tracer.start_as_current_span(func.__name__, kind=self.kind): - self._pre_instrument(self, func, *args, **kwargs) + self._pre_instrument(func, *args, **kwargs) result = await func(*args, **kwargs) @@ -56,7 +56,7 @@ def _sync_auto_instrumented(*args, **kwargs): self._parse_type_and_kind() with ag.tracer.start_as_current_span(func.__name__, kind=self.kind): - self._pre_instrument(self, func, *args, **kwargs) + self._pre_instrument(func, *args, **kwargs) result = func(*args, **kwargs) From 918d856789198244cefdda668bb26fa7b0b4405e Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Fri, 25 Oct 2024 20:46:14 +0200 Subject: [PATCH 179/305] test(app): updated tests (openai, readme) --- agenta-cli/tests/observability_sdk/Readme.md | 6 +- .../instructor/01_simple_local.py | 29 +++++++++ .../integrations/instructor/requirements.txt | 4 ++ ...1_simple_chain_openllmetery_noworkflow.py} | 4 -- .../02_simple_chain_openllmetery_workflow.py | 46 ++++++++++++++ .../integrations/langchain/requirements.txt | 5 +- .../langchain/simple_chain_openinference.py | 4 -- .../openai/01_with_workflow_local_async.py | 42 +++++++++++++ ...with_workflow_local_async_function_call.py | 63 +++++++++++++++++++ .../sanity_check/requirements.txt | 2 +- 10 files changed, 193 insertions(+), 12 deletions(-) create mode 100644 agenta-cli/tests/observability_sdk/integrations/instructor/01_simple_local.py create mode 100644 agenta-cli/tests/observability_sdk/integrations/instructor/requirements.txt rename agenta-cli/tests/observability_sdk/integrations/langchain/{simple_chain_openllmetery.py => 01_simple_chain_openllmetery_noworkflow.py} (86%) create mode 100644 agenta-cli/tests/observability_sdk/integrations/langchain/02_simple_chain_openllmetery_workflow.py create mode 100644 agenta-cli/tests/observability_sdk/integrations/openai/01_with_workflow_local_async.py create mode 100644 agenta-cli/tests/observability_sdk/integrations/openai/02_with_workflow_local_async_function_call.py diff --git a/agenta-cli/tests/observability_sdk/Readme.md b/agenta-cli/tests/observability_sdk/Readme.md index 92f949d7ad..21edb6f747 100644 --- a/agenta-cli/tests/observability_sdk/Readme.md +++ b/agenta-cli/tests/observability_sdk/Readme.md @@ -90,4 +90,8 @@ The tests in the `sanity_check` folder do not use an LLM but test the functional Minimum app to test the functionalities of the SDK. -#### +#### 02_app_local_old_sdk.py + +App testing most of the functionalities above but using the old SDK. + +#### 03_app_local_new_sdk.py diff --git a/agenta-cli/tests/observability_sdk/integrations/instructor/01_simple_local.py b/agenta-cli/tests/observability_sdk/integrations/instructor/01_simple_local.py new file mode 100644 index 0000000000..76a3135886 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/instructor/01_simple_local.py @@ -0,0 +1,29 @@ +import agenta as ag +import openai +import instructor +from pydantic import BaseModel + +from opentelemetry.instrumentation.openai import OpenAIInstrumentor +ag.init() +OpenAIInstrumentor().instrument() + + +# Define your desired output structure +class UserInfo(BaseModel): + name: str + age: int + +@ag.instrument(spankind="WORKFLOW") +def instructor_workflow(): + # Patch the OpenAI client + client = instructor.from_openai(openai.OpenAI()) + + # Extract structured data from natural language + user_info = client.chat.completions.create( + model="gpt-3.5-turbo", + response_model=UserInfo, + messages=[{"role": "user", "content": "John Doe is 30 years old."}], + ) + return user_info + +user_info = instructor_workflow() diff --git a/agenta-cli/tests/observability_sdk/integrations/instructor/requirements.txt b/agenta-cli/tests/observability_sdk/integrations/instructor/requirements.txt new file mode 100644 index 0000000000..f464969d1e --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/instructor/requirements.txt @@ -0,0 +1,4 @@ +agenta==0.27.0a0 +opentelemetry.instrumentation.openai==0.31.2 +openai +instructor \ No newline at end of file diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openllmetery.py b/agenta-cli/tests/observability_sdk/integrations/langchain/01_simple_chain_openllmetery_noworkflow.py similarity index 86% rename from agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openllmetery.py rename to agenta-cli/tests/observability_sdk/integrations/langchain/01_simple_chain_openllmetery_noworkflow.py index e797a9be16..ed7fcfabae 100644 --- a/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openllmetery.py +++ b/agenta-cli/tests/observability_sdk/integrations/langchain/01_simple_chain_openllmetery_noworkflow.py @@ -7,10 +7,6 @@ import agenta as ag ag.init( - project_id="0192813f-60d5-7a65-8a75-6dda36b79267", - host="https://cloud.beta.agenta.ai", - api_key="NYWy4We0.17ce0e85db4840a39ca9ee7b00e8817b22b60d7e152407a5a4cc98c5284f2e0e", - app_id="0192b552-ad65-7c61-a8dc-fedf3608b7a5", ) LangchainInstrumentor().instrument() diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/02_simple_chain_openllmetery_workflow.py b/agenta-cli/tests/observability_sdk/integrations/langchain/02_simple_chain_openllmetery_workflow.py new file mode 100644 index 0000000000..89f0ced268 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/langchain/02_simple_chain_openllmetery_workflow.py @@ -0,0 +1,46 @@ +from langchain.schema import SystemMessage, HumanMessage +from langchain.prompts import ChatPromptTemplate, HumanMessagePromptTemplate +from langchain_community.chat_models import ChatOpenAI +from langchain.chains import LLMChain, SequentialChain, TransformChain +from opentelemetry.instrumentation.langchain import LangchainInstrumentor + +import agenta as ag + +ag.init( +) + +LangchainInstrumentor().instrument() + +@ag.instrument(spankind="WORKFLOW") +def langchain_app(): + chat = ChatOpenAI(temperature=0) + + transform = TransformChain( + input_variables=["subject"], + output_variables=["prompt"], + transform=lambda subject: {"prompt": f"Tell me a joke about {subject}."}, + ) + + first_prompt_messages = [ + SystemMessage(content="You are a funny sarcastic nerd."), + HumanMessage(content="{prompt}"), + ] + first_prompt_template = ChatPromptTemplate.from_messages(first_prompt_messages) + first_chain = LLMChain(llm=chat, prompt=first_prompt_template, output_key="joke") + + second_prompt_messages = [ + SystemMessage(content="You are an Elf."), + HumanMessagePromptTemplate.from_template( + "Translate the joke below into Sindarin language:\n {joke}" + ), + ] + second_prompt_template = ChatPromptTemplate.from_messages(second_prompt_messages) + second_chain = LLMChain(llm=chat, prompt=second_prompt_template) + + workflow = SequentialChain( + chains=[transform, first_chain, second_chain], input_variables=["subject"] + ) + print(workflow({"subject": "OpenTelemetry"})) + + +langchain_app() diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt b/agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt index ab803b39b9..6395726368 100644 --- a/agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt +++ b/agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt @@ -1,7 +1,8 @@ -agenta==0.27.0a0 +agenta==0.27.0a5 # opentelemetry.instrumentation.openai==0.31.2 openai opentelemetry-instrumentation-langchain langchain langchain_community -langchain_openai \ No newline at end of file +langchain_openai +openinference-instrumentation-langchain \ No newline at end of file diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py b/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py index 0b5631f114..fe8c78aee5 100644 --- a/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py +++ b/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py @@ -7,10 +7,6 @@ import agenta as ag ag.init( - project_id="0192813f-60d5-7a65-8a75-6dda36b79267", - host="http://localhost/", - api_key="NYWy4We0.17ce0e85db4840a39ca9ee7b00e8817b22b60d7e152407a5a4cc98c5284f2e0e", - app_id="0192b552-ad65-7c61-a8dc-fedf3608b7a5", ) LangChainInstrumentor().instrument() diff --git a/agenta-cli/tests/observability_sdk/integrations/openai/01_with_workflow_local_async.py b/agenta-cli/tests/observability_sdk/integrations/openai/01_with_workflow_local_async.py new file mode 100644 index 0000000000..4a3faf0821 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/openai/01_with_workflow_local_async.py @@ -0,0 +1,42 @@ +import agenta as ag +from pydantic import BaseModel, Field +from agenta.sdk.assets import supported_llm_models +from typing import Annotated +from opentelemetry.instrumentation.openai import OpenAIInstrumentor +from openai import OpenAI +import asyncio +ag.init() + + +openai = OpenAI() + + +OpenAIInstrumentor().instrument() + + +class MyConfig(BaseModel): + temperature: float = Field(default=0.2, le=1, ge=0) + model: Annotated[str, ag.MultipleChoice(choices=supported_llm_models)] = Field( + default="gpt-3.5-turbo" + ) + max_tokens: int = Field(default=-1, ge=-1, le=4000) + prompt_system: str = Field(default="system prompt") + multiselect: Annotated[str, ag.MultipleChoice(choices=["a", "b", "c"])] = Field( + default="a" + ) + + +@ag.instrument(spankind="WORKFLOW") +async def openai_workflow(topic: str, genre: str): + response = openai.chat.completions.create( + model="gpt-3.5-turbo", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": f"Write a short {genre} story about {topic}."}, + ], + ) + + return response.choices[0].message.content + +if __name__ == "__main__": + asyncio.run(openai_workflow(topic="df", genre="d")) diff --git a/agenta-cli/tests/observability_sdk/integrations/openai/02_with_workflow_local_async_function_call.py b/agenta-cli/tests/observability_sdk/integrations/openai/02_with_workflow_local_async_function_call.py new file mode 100644 index 0000000000..f160d525f3 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/openai/02_with_workflow_local_async_function_call.py @@ -0,0 +1,63 @@ +import agenta as ag +from pydantic import BaseModel, Field +from agenta.sdk.assets import supported_llm_models +from typing import Annotated +from opentelemetry.instrumentation.openai import OpenAIInstrumentor +from openai import OpenAI +import asyncio +ag.init() + + +openai = OpenAI() + + +OpenAIInstrumentor().instrument() + + +class MyConfig(BaseModel): + temperature: float = Field(default=0.2, le=1, ge=0) + model: Annotated[str, ag.MultipleChoice(choices=supported_llm_models)] = Field( + default="gpt-3.5-turbo" + ) + max_tokens: int = Field(default=-1, ge=-1, le=4000) + prompt_system: str = Field(default="system prompt") + multiselect: Annotated[str, ag.MultipleChoice(choices=["a", "b", "c"])] = Field( + default="a" + ) + +@ag.instrument(spankind="WORKFLOW") +async def openai_workflow(topic: str, genre: str): + functions = [ + { + "name": "generate_story", + "description": "Generate a story based on a topic and genre", + "parameters": { + "type": "object", + "properties": { + "topic": { + "type": "string", + "description": "The topic of the story" + }, + "genre": { + "type": "string", + "description": "The genre of the story" + } + }, + "required": ["topic", "genre"] + } + } + ] + + response = openai.chat.completions.create( + model="gpt-3.5-turbo", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": f"Write a short {genre} story about {topic}."}, + ], + functions=functions + ) + + return response.choices[0].message.content + +if __name__ == "__main__": + asyncio.run(openai_workflow(topic="df", genre="d")) \ No newline at end of file diff --git a/agenta-cli/tests/observability_sdk/sanity_check/requirements.txt b/agenta-cli/tests/observability_sdk/sanity_check/requirements.txt index b90626aa67..2fe0ce82c7 100644 --- a/agenta-cli/tests/observability_sdk/sanity_check/requirements.txt +++ b/agenta-cli/tests/observability_sdk/sanity_check/requirements.txt @@ -1 +1 @@ -agenta==0.27.0a0 \ No newline at end of file +agenta==0.27.0a5 \ No newline at end of file From ddd722e079f66e0089cc0f56885ee089dc3effde Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Sat, 26 Oct 2024 10:21:17 +0200 Subject: [PATCH 180/305] chore(app): formatting --- agenta-cli/agenta/sdk/decorators/tracing.py | 4 +++- .../integrations/instructor/01_simple_local.py | 3 +++ .../01_simple_chain_openllmetery_noworkflow.py | 3 +-- .../02_simple_chain_openllmetery_workflow.py | 4 ++-- .../langchain/simple_chain_openinference.py | 3 +-- .../openai/01_with_workflow_local_async.py | 2 ++ ...2_with_workflow_local_async_function_call.py | 17 ++++++++++------- .../sanity_check/01_app_sanity.py | 7 +++---- .../sanity_check/03_app_errors_local.py | 5 ----- 9 files changed, 25 insertions(+), 23 deletions(-) diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 27d8badcb7..29e3bbe9f9 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -210,7 +210,9 @@ def _redact( not in ( ignore if isinstance(ignore, list) - else io.keys() if ignore is True else [] + else io.keys() + if ignore is True + else [] ) } diff --git a/agenta-cli/tests/observability_sdk/integrations/instructor/01_simple_local.py b/agenta-cli/tests/observability_sdk/integrations/instructor/01_simple_local.py index 76a3135886..4310f10200 100644 --- a/agenta-cli/tests/observability_sdk/integrations/instructor/01_simple_local.py +++ b/agenta-cli/tests/observability_sdk/integrations/instructor/01_simple_local.py @@ -4,6 +4,7 @@ from pydantic import BaseModel from opentelemetry.instrumentation.openai import OpenAIInstrumentor + ag.init() OpenAIInstrumentor().instrument() @@ -13,6 +14,7 @@ class UserInfo(BaseModel): name: str age: int + @ag.instrument(spankind="WORKFLOW") def instructor_workflow(): # Patch the OpenAI client @@ -26,4 +28,5 @@ def instructor_workflow(): ) return user_info + user_info = instructor_workflow() diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/01_simple_chain_openllmetery_noworkflow.py b/agenta-cli/tests/observability_sdk/integrations/langchain/01_simple_chain_openllmetery_noworkflow.py index ed7fcfabae..05ab8285fc 100644 --- a/agenta-cli/tests/observability_sdk/integrations/langchain/01_simple_chain_openllmetery_noworkflow.py +++ b/agenta-cli/tests/observability_sdk/integrations/langchain/01_simple_chain_openllmetery_noworkflow.py @@ -6,8 +6,7 @@ import agenta as ag -ag.init( -) +ag.init() LangchainInstrumentor().instrument() diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/02_simple_chain_openllmetery_workflow.py b/agenta-cli/tests/observability_sdk/integrations/langchain/02_simple_chain_openllmetery_workflow.py index 89f0ced268..00db96ab6d 100644 --- a/agenta-cli/tests/observability_sdk/integrations/langchain/02_simple_chain_openllmetery_workflow.py +++ b/agenta-cli/tests/observability_sdk/integrations/langchain/02_simple_chain_openllmetery_workflow.py @@ -6,11 +6,11 @@ import agenta as ag -ag.init( -) +ag.init() LangchainInstrumentor().instrument() + @ag.instrument(spankind="WORKFLOW") def langchain_app(): chat = ChatOpenAI(temperature=0) diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py b/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py index fe8c78aee5..b256fb3f6a 100644 --- a/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py +++ b/agenta-cli/tests/observability_sdk/integrations/langchain/simple_chain_openinference.py @@ -6,8 +6,7 @@ import agenta as ag -ag.init( -) +ag.init() LangChainInstrumentor().instrument() diff --git a/agenta-cli/tests/observability_sdk/integrations/openai/01_with_workflow_local_async.py b/agenta-cli/tests/observability_sdk/integrations/openai/01_with_workflow_local_async.py index 4a3faf0821..689f003714 100644 --- a/agenta-cli/tests/observability_sdk/integrations/openai/01_with_workflow_local_async.py +++ b/agenta-cli/tests/observability_sdk/integrations/openai/01_with_workflow_local_async.py @@ -5,6 +5,7 @@ from opentelemetry.instrumentation.openai import OpenAIInstrumentor from openai import OpenAI import asyncio + ag.init() @@ -38,5 +39,6 @@ async def openai_workflow(topic: str, genre: str): return response.choices[0].message.content + if __name__ == "__main__": asyncio.run(openai_workflow(topic="df", genre="d")) diff --git a/agenta-cli/tests/observability_sdk/integrations/openai/02_with_workflow_local_async_function_call.py b/agenta-cli/tests/observability_sdk/integrations/openai/02_with_workflow_local_async_function_call.py index f160d525f3..de145d2646 100644 --- a/agenta-cli/tests/observability_sdk/integrations/openai/02_with_workflow_local_async_function_call.py +++ b/agenta-cli/tests/observability_sdk/integrations/openai/02_with_workflow_local_async_function_call.py @@ -5,6 +5,7 @@ from opentelemetry.instrumentation.openai import OpenAIInstrumentor from openai import OpenAI import asyncio + ag.init() @@ -25,6 +26,7 @@ class MyConfig(BaseModel): default="a" ) + @ag.instrument(spankind="WORKFLOW") async def openai_workflow(topic: str, genre: str): functions = [ @@ -36,15 +38,15 @@ async def openai_workflow(topic: str, genre: str): "properties": { "topic": { "type": "string", - "description": "The topic of the story" + "description": "The topic of the story", }, "genre": { "type": "string", - "description": "The genre of the story" - } + "description": "The genre of the story", + }, }, - "required": ["topic", "genre"] - } + "required": ["topic", "genre"], + }, } ] @@ -54,10 +56,11 @@ async def openai_workflow(topic: str, genre: str): {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": f"Write a short {genre} story about {topic}."}, ], - functions=functions + functions=functions, ) return response.choices[0].message.content + if __name__ == "__main__": - asyncio.run(openai_workflow(topic="df", genre="d")) \ No newline at end of file + asyncio.run(openai_workflow(topic="df", genre="d")) diff --git a/agenta-cli/tests/observability_sdk/sanity_check/01_app_sanity.py b/agenta-cli/tests/observability_sdk/sanity_check/01_app_sanity.py index 441e091d72..0061b76110 100644 --- a/agenta-cli/tests/observability_sdk/sanity_check/01_app_sanity.py +++ b/agenta-cli/tests/observability_sdk/sanity_check/01_app_sanity.py @@ -3,10 +3,7 @@ from agenta.sdk.assets import supported_llm_models - - -ag.init( -) +ag.init() ag.config.default( temperature=ag.FloatParam(0.2), @@ -17,6 +14,7 @@ prompt_system=ag.TextParam("MY_SYSTEM_PROMPT"), ) + @ag.instrument(spankind="WORKFLOW") async def main2(topic: str, genre: str, count: int = 5): return "test" @@ -24,4 +22,5 @@ async def main2(topic: str, genre: str, count: int = 5): if __name__ == "__main__": import asyncio + asyncio.run(main2(topic="df", genre="d", count=1)) diff --git a/agenta-cli/tests/observability_sdk/sanity_check/03_app_errors_local.py b/agenta-cli/tests/observability_sdk/sanity_check/03_app_errors_local.py index 4a42eb2f78..69a52cb19f 100644 --- a/agenta-cli/tests/observability_sdk/sanity_check/03_app_errors_local.py +++ b/agenta-cli/tests/observability_sdk/sanity_check/03_app_errors_local.py @@ -14,7 +14,6 @@ ) - # Pydantic models class InputData(BaseModel): text: str @@ -167,8 +166,6 @@ async def exception_func(): return "dummy" - - @ag.instrument(spankind="WORKFLOW") async def errors(topic: str, genre: str, count: int = 5): result = ignore_some_outputs_embedding("something") @@ -215,8 +212,6 @@ async def errors(topic: str, genre: str, count: int = 5): app_errors_local""" - - if __name__ == "__main__": import asyncio From 191bd658108571014d59b5402105d287353c8c3a Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Sat, 26 Oct 2024 11:33:34 +0200 Subject: [PATCH 181/305] test(app): llama index example --- .../integrations/llama_index/01_simple_RAG.py | 43 +++ .../data/paul_graham/paul_graham_essay.txt | 353 ++++++++++++++++++ .../integrations/llama_index/requirements.txt | 6 + 3 files changed, 402 insertions(+) create mode 100644 agenta-cli/tests/observability_sdk/integrations/llama_index/01_simple_RAG.py create mode 100644 agenta-cli/tests/observability_sdk/integrations/llama_index/data/paul_graham/paul_graham_essay.txt create mode 100644 agenta-cli/tests/observability_sdk/integrations/llama_index/requirements.txt diff --git a/agenta-cli/tests/observability_sdk/integrations/llama_index/01_simple_RAG.py b/agenta-cli/tests/observability_sdk/integrations/llama_index/01_simple_RAG.py new file mode 100644 index 0000000000..1615b86aea --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/llama_index/01_simple_RAG.py @@ -0,0 +1,43 @@ +import chromadb + +from llama_index.core import ( + VectorStoreIndex, + SimpleDirectoryReader, + StorageContext, +) +from llama_index.vector_stores.chroma import ChromaVectorStore +from llama_index.embeddings.openai import OpenAIEmbedding +import agenta as ag +from opentelemetry.instrumentation.llamaindex import LlamaIndexInstrumentor + +LlamaIndexInstrumentor().instrument() + +ag.init() + + +@ag.instrument(spankind="WORKFLOW") +def rag_with_chroma(): + # Create persistent client to save embeddings + chroma_client = chromadb.PersistentClient(path="./chroma_db") + chroma_collection = chroma_client.get_or_create_collection("quickstart") + + # Set up vector store and storage context + vector_store = ChromaVectorStore(chroma_collection=chroma_collection) + storage_context = StorageContext.from_defaults(vector_store=vector_store) + + # Configure embedding model + embed_model = OpenAIEmbedding(model="text-embedding-3-large") + + # Load documents and create index + documents = SimpleDirectoryReader("./data/paul_graham/").load_data() + index = VectorStoreIndex.from_documents( + documents, storage_context=storage_context, embed_model=embed_model + ) + + # Query the index + query_engine = index.as_query_engine() + return query_engine.query("What did the author do growing up?") + + +if __name__ == "__main__": + rag_with_chroma() diff --git a/agenta-cli/tests/observability_sdk/integrations/llama_index/data/paul_graham/paul_graham_essay.txt b/agenta-cli/tests/observability_sdk/integrations/llama_index/data/paul_graham/paul_graham_essay.txt new file mode 100644 index 0000000000..7f0350da39 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/llama_index/data/paul_graham/paul_graham_essay.txt @@ -0,0 +1,353 @@ + + +What I Worked On + +February 2021 + +Before college the two main things I worked on, outside of school, were writing and programming. I didn't write essays. I wrote what beginning writers were supposed to write then, and probably still are: short stories. My stories were awful. They had hardly any plot, just characters with strong feelings, which I imagined made them deep. + +The first programs I tried writing were on the IBM 1401 that our school district used for what was then called "data processing." This was in 9th grade, so I was 13 or 14. The school district's 1401 happened to be in the basement of our junior high school, and my friend Rich Draves and I got permission to use it. It was like a mini Bond villain's lair down there, with all these alien-looking machines — CPU, disk drives, printer, card reader — sitting up on a raised floor under bright fluorescent lights. + +The language we used was an early version of Fortran. You had to type programs on punch cards, then stack them in the card reader and press a button to load the program into memory and run it. The result would ordinarily be to print something on the spectacularly loud printer. + +I was puzzled by the 1401. I couldn't figure out what to do with it. And in retrospect there's not much I could have done with it. The only form of input to programs was data stored on punched cards, and I didn't have any data stored on punched cards. The only other option was to do things that didn't rely on any input, like calculate approximations of pi, but I didn't know enough math to do anything interesting of that type. So I'm not surprised I can't remember any programs I wrote, because they can't have done much. My clearest memory is of the moment I learned it was possible for programs not to terminate, when one of mine didn't. On a machine without time-sharing, this was a social as well as a technical error, as the data center manager's expression made clear. + +With microcomputers, everything changed. Now you could have a computer sitting right in front of you, on a desk, that could respond to your keystrokes as it was running instead of just churning through a stack of punch cards and then stopping. [1] + +The first of my friends to get a microcomputer built it himself. It was sold as a kit by Heathkit. I remember vividly how impressed and envious I felt watching him sitting in front of it, typing programs right into the computer. + +Computers were expensive in those days and it took me years of nagging before I convinced my father to buy one, a TRS-80, in about 1980. The gold standard then was the Apple II, but a TRS-80 was good enough. This was when I really started programming. I wrote simple games, a program to predict how high my model rockets would fly, and a word processor that my father used to write at least one book. There was only room in memory for about 2 pages of text, so he'd write 2 pages at a time and then print them out, but it was a lot better than a typewriter. + +Though I liked programming, I didn't plan to study it in college. In college I was going to study philosophy, which sounded much more powerful. It seemed, to my naive high school self, to be the study of the ultimate truths, compared to which the things studied in other fields would be mere domain knowledge. What I discovered when I got to college was that the other fields took up so much of the space of ideas that there wasn't much left for these supposed ultimate truths. All that seemed left for philosophy were edge cases that people in other fields felt could safely be ignored. + +I couldn't have put this into words when I was 18. All I knew at the time was that I kept taking philosophy courses and they kept being boring. So I decided to switch to AI. + +AI was in the air in the mid 1980s, but there were two things especially that made me want to work on it: a novel by Heinlein called The Moon is a Harsh Mistress, which featured an intelligent computer called Mike, and a PBS documentary that showed Terry Winograd using SHRDLU. I haven't tried rereading The Moon is a Harsh Mistress, so I don't know how well it has aged, but when I read it I was drawn entirely into its world. It seemed only a matter of time before we'd have Mike, and when I saw Winograd using SHRDLU, it seemed like that time would be a few years at most. All you had to do was teach SHRDLU more words. + +There weren't any classes in AI at Cornell then, not even graduate classes, so I started trying to teach myself. Which meant learning Lisp, since in those days Lisp was regarded as the language of AI. The commonly used programming languages then were pretty primitive, and programmers' ideas correspondingly so. The default language at Cornell was a Pascal-like language called PL/I, and the situation was similar elsewhere. Learning Lisp expanded my concept of a program so fast that it was years before I started to have a sense of where the new limits were. This was more like it; this was what I had expected college to do. It wasn't happening in a class, like it was supposed to, but that was ok. For the next couple years I was on a roll. I knew what I was going to do. + +For my undergraduate thesis, I reverse-engineered SHRDLU. My God did I love working on that program. It was a pleasing bit of code, but what made it even more exciting was my belief — hard to imagine now, but not unique in 1985 — that it was already climbing the lower slopes of intelligence. + +I had gotten into a program at Cornell that didn't make you choose a major. You could take whatever classes you liked, and choose whatever you liked to put on your degree. I of course chose "Artificial Intelligence." When I got the actual physical diploma, I was dismayed to find that the quotes had been included, which made them read as scare-quotes. At the time this bothered me, but now it seems amusingly accurate, for reasons I was about to discover. + +I applied to 3 grad schools: MIT and Yale, which were renowned for AI at the time, and Harvard, which I'd visited because Rich Draves went there, and was also home to Bill Woods, who'd invented the type of parser I used in my SHRDLU clone. Only Harvard accepted me, so that was where I went. + +I don't remember the moment it happened, or if there even was a specific moment, but during the first year of grad school I realized that AI, as practiced at the time, was a hoax. By which I mean the sort of AI in which a program that's told "the dog is sitting on the chair" translates this into some formal representation and adds it to the list of things it knows. + +What these programs really showed was that there's a subset of natural language that's a formal language. But a very proper subset. It was clear that there was an unbridgeable gap between what they could do and actually understanding natural language. It was not, in fact, simply a matter of teaching SHRDLU more words. That whole way of doing AI, with explicit data structures representing concepts, was not going to work. Its brokenness did, as so often happens, generate a lot of opportunities to write papers about various band-aids that could be applied to it, but it was never going to get us Mike. + +So I looked around to see what I could salvage from the wreckage of my plans, and there was Lisp. I knew from experience that Lisp was interesting for its own sake and not just for its association with AI, even though that was the main reason people cared about it at the time. So I decided to focus on Lisp. In fact, I decided to write a book about Lisp hacking. It's scary to think how little I knew about Lisp hacking when I started writing that book. But there's nothing like writing a book about something to help you learn it. The book, On Lisp, wasn't published till 1993, but I wrote much of it in grad school. + +Computer Science is an uneasy alliance between two halves, theory and systems. The theory people prove things, and the systems people build things. I wanted to build things. I had plenty of respect for theory — indeed, a sneaking suspicion that it was the more admirable of the two halves — but building things seemed so much more exciting. + +The problem with systems work, though, was that it didn't last. Any program you wrote today, no matter how good, would be obsolete in a couple decades at best. People might mention your software in footnotes, but no one would actually use it. And indeed, it would seem very feeble work. Only people with a sense of the history of the field would even realize that, in its time, it had been good. + +There were some surplus Xerox Dandelions floating around the computer lab at one point. Anyone who wanted one to play around with could have one. I was briefly tempted, but they were so slow by present standards; what was the point? No one else wanted one either, so off they went. That was what happened to systems work. + +I wanted not just to build things, but to build things that would last. + +In this dissatisfied state I went in 1988 to visit Rich Draves at CMU, where he was in grad school. One day I went to visit the Carnegie Institute, where I'd spent a lot of time as a kid. While looking at a painting there I realized something that might seem obvious, but was a big surprise to me. There, right on the wall, was something you could make that would last. Paintings didn't become obsolete. Some of the best ones were hundreds of years old. + +And moreover this was something you could make a living doing. Not as easily as you could by writing software, of course, but I thought if you were really industrious and lived really cheaply, it had to be possible to make enough to survive. And as an artist you could be truly independent. You wouldn't have a boss, or even need to get research funding. + +I had always liked looking at paintings. Could I make them? I had no idea. I'd never imagined it was even possible. I knew intellectually that people made art — that it didn't just appear spontaneously — but it was as if the people who made it were a different species. They either lived long ago or were mysterious geniuses doing strange things in profiles in Life magazine. The idea of actually being able to make art, to put that verb before that noun, seemed almost miraculous. + +That fall I started taking art classes at Harvard. Grad students could take classes in any department, and my advisor, Tom Cheatham, was very easy going. If he even knew about the strange classes I was taking, he never said anything. + +So now I was in a PhD program in computer science, yet planning to be an artist, yet also genuinely in love with Lisp hacking and working away at On Lisp. In other words, like many a grad student, I was working energetically on multiple projects that were not my thesis. + +I didn't see a way out of this situation. I didn't want to drop out of grad school, but how else was I going to get out? I remember when my friend Robert Morris got kicked out of Cornell for writing the internet worm of 1988, I was envious that he'd found such a spectacular way to get out of grad school. + +Then one day in April 1990 a crack appeared in the wall. I ran into professor Cheatham and he asked if I was far enough along to graduate that June. I didn't have a word of my dissertation written, but in what must have been the quickest bit of thinking in my life, I decided to take a shot at writing one in the 5 weeks or so that remained before the deadline, reusing parts of On Lisp where I could, and I was able to respond, with no perceptible delay "Yes, I think so. I'll give you something to read in a few days." + +I picked applications of continuations as the topic. In retrospect I should have written about macros and embedded languages. There's a whole world there that's barely been explored. But all I wanted was to get out of grad school, and my rapidly written dissertation sufficed, just barely. + +Meanwhile I was applying to art schools. I applied to two: RISD in the US, and the Accademia di Belli Arti in Florence, which, because it was the oldest art school, I imagined would be good. RISD accepted me, and I never heard back from the Accademia, so off to Providence I went. + +I'd applied for the BFA program at RISD, which meant in effect that I had to go to college again. This was not as strange as it sounds, because I was only 25, and art schools are full of people of different ages. RISD counted me as a transfer sophomore and said I had to do the foundation that summer. The foundation means the classes that everyone has to take in fundamental subjects like drawing, color, and design. + +Toward the end of the summer I got a big surprise: a letter from the Accademia, which had been delayed because they'd sent it to Cambridge England instead of Cambridge Massachusetts, inviting me to take the entrance exam in Florence that fall. This was now only weeks away. My nice landlady let me leave my stuff in her attic. I had some money saved from consulting work I'd done in grad school; there was probably enough to last a year if I lived cheaply. Now all I had to do was learn Italian. + +Only stranieri (foreigners) had to take this entrance exam. In retrospect it may well have been a way of excluding them, because there were so many stranieri attracted by the idea of studying art in Florence that the Italian students would otherwise have been outnumbered. I was in decent shape at painting and drawing from the RISD foundation that summer, but I still don't know how I managed to pass the written exam. I remember that I answered the essay question by writing about Cezanne, and that I cranked up the intellectual level as high as I could to make the most of my limited vocabulary. [2] + +I'm only up to age 25 and already there are such conspicuous patterns. Here I was, yet again about to attend some august institution in the hopes of learning about some prestigious subject, and yet again about to be disappointed. The students and faculty in the painting department at the Accademia were the nicest people you could imagine, but they had long since arrived at an arrangement whereby the students wouldn't require the faculty to teach anything, and in return the faculty wouldn't require the students to learn anything. And at the same time all involved would adhere outwardly to the conventions of a 19th century atelier. We actually had one of those little stoves, fed with kindling, that you see in 19th century studio paintings, and a nude model sitting as close to it as possible without getting burned. Except hardly anyone else painted her besides me. The rest of the students spent their time chatting or occasionally trying to imitate things they'd seen in American art magazines. + +Our model turned out to live just down the street from me. She made a living from a combination of modelling and making fakes for a local antique dealer. She'd copy an obscure old painting out of a book, and then he'd take the copy and maltreat it to make it look old. [3] + +While I was a student at the Accademia I started painting still lives in my bedroom at night. These paintings were tiny, because the room was, and because I painted them on leftover scraps of canvas, which was all I could afford at the time. Painting still lives is different from painting people, because the subject, as its name suggests, can't move. People can't sit for more than about 15 minutes at a time, and when they do they don't sit very still. So the traditional m.o. for painting people is to know how to paint a generic person, which you then modify to match the specific person you're painting. Whereas a still life you can, if you want, copy pixel by pixel from what you're seeing. You don't want to stop there, of course, or you get merely photographic accuracy, and what makes a still life interesting is that it's been through a head. You want to emphasize the visual cues that tell you, for example, that the reason the color changes suddenly at a certain point is that it's the edge of an object. By subtly emphasizing such things you can make paintings that are more realistic than photographs not just in some metaphorical sense, but in the strict information-theoretic sense. [4] + +I liked painting still lives because I was curious about what I was seeing. In everyday life, we aren't consciously aware of much we're seeing. Most visual perception is handled by low-level processes that merely tell your brain "that's a water droplet" without telling you details like where the lightest and darkest points are, or "that's a bush" without telling you the shape and position of every leaf. This is a feature of brains, not a bug. In everyday life it would be distracting to notice every leaf on every bush. But when you have to paint something, you have to look more closely, and when you do there's a lot to see. You can still be noticing new things after days of trying to paint something people usually take for granted, just as you can after days of trying to write an essay about something people usually take for granted. + +This is not the only way to paint. I'm not 100% sure it's even a good way to paint. But it seemed a good enough bet to be worth trying. + +Our teacher, professor Ulivi, was a nice guy. He could see I worked hard, and gave me a good grade, which he wrote down in a sort of passport each student had. But the Accademia wasn't teaching me anything except Italian, and my money was running out, so at the end of the first year I went back to the US. + +I wanted to go back to RISD, but I was now broke and RISD was very expensive, so I decided to get a job for a year and then return to RISD the next fall. I got one at a company called Interleaf, which made software for creating documents. You mean like Microsoft Word? Exactly. That was how I learned that low end software tends to eat high end software. But Interleaf still had a few years to live yet. [5] + +Interleaf had done something pretty bold. Inspired by Emacs, they'd added a scripting language, and even made the scripting language a dialect of Lisp. Now they wanted a Lisp hacker to write things in it. This was the closest thing I've had to a normal job, and I hereby apologize to my boss and coworkers, because I was a bad employee. Their Lisp was the thinnest icing on a giant C cake, and since I didn't know C and didn't want to learn it, I never understood most of the software. Plus I was terribly irresponsible. This was back when a programming job meant showing up every day during certain working hours. That seemed unnatural to me, and on this point the rest of the world is coming around to my way of thinking, but at the time it caused a lot of friction. Toward the end of the year I spent much of my time surreptitiously working on On Lisp, which I had by this time gotten a contract to publish. + +The good part was that I got paid huge amounts of money, especially by art student standards. In Florence, after paying my part of the rent, my budget for everything else had been $7 a day. Now I was getting paid more than 4 times that every hour, even when I was just sitting in a meeting. By living cheaply I not only managed to save enough to go back to RISD, but also paid off my college loans. + +I learned some useful things at Interleaf, though they were mostly about what not to do. I learned that it's better for technology companies to be run by product people than sales people (though sales is a real skill and people who are good at it are really good at it), that it leads to bugs when code is edited by too many people, that cheap office space is no bargain if it's depressing, that planned meetings are inferior to corridor conversations, that big, bureaucratic customers are a dangerous source of money, and that there's not much overlap between conventional office hours and the optimal time for hacking, or conventional offices and the optimal place for it. + +But the most important thing I learned, and which I used in both Viaweb and Y Combinator, is that the low end eats the high end: that it's good to be the "entry level" option, even though that will be less prestigious, because if you're not, someone else will be, and will squash you against the ceiling. Which in turn means that prestige is a danger sign. + +When I left to go back to RISD the next fall, I arranged to do freelance work for the group that did projects for customers, and this was how I survived for the next several years. When I came back to visit for a project later on, someone told me about a new thing called HTML, which was, as he described it, a derivative of SGML. Markup language enthusiasts were an occupational hazard at Interleaf and I ignored him, but this HTML thing later became a big part of my life. + +In the fall of 1992 I moved back to Providence to continue at RISD. The foundation had merely been intro stuff, and the Accademia had been a (very civilized) joke. Now I was going to see what real art school was like. But alas it was more like the Accademia than not. Better organized, certainly, and a lot more expensive, but it was now becoming clear that art school did not bear the same relationship to art that medical school bore to medicine. At least not the painting department. The textile department, which my next door neighbor belonged to, seemed to be pretty rigorous. No doubt illustration and architecture were too. But painting was post-rigorous. Painting students were supposed to express themselves, which to the more worldly ones meant to try to cook up some sort of distinctive signature style. + +A signature style is the visual equivalent of what in show business is known as a "schtick": something that immediately identifies the work as yours and no one else's. For example, when you see a painting that looks like a certain kind of cartoon, you know it's by Roy Lichtenstein. So if you see a big painting of this type hanging in the apartment of a hedge fund manager, you know he paid millions of dollars for it. That's not always why artists have a signature style, but it's usually why buyers pay a lot for such work. [6] + +There were plenty of earnest students too: kids who "could draw" in high school, and now had come to what was supposed to be the best art school in the country, to learn to draw even better. They tended to be confused and demoralized by what they found at RISD, but they kept going, because painting was what they did. I was not one of the kids who could draw in high school, but at RISD I was definitely closer to their tribe than the tribe of signature style seekers. + +I learned a lot in the color class I took at RISD, but otherwise I was basically teaching myself to paint, and I could do that for free. So in 1993 I dropped out. I hung around Providence for a bit, and then my college friend Nancy Parmet did me a big favor. A rent-controlled apartment in a building her mother owned in New York was becoming vacant. Did I want it? It wasn't much more than my current place, and New York was supposed to be where the artists were. So yes, I wanted it! [7] + +Asterix comics begin by zooming in on a tiny corner of Roman Gaul that turns out not to be controlled by the Romans. You can do something similar on a map of New York City: if you zoom in on the Upper East Side, there's a tiny corner that's not rich, or at least wasn't in 1993. It's called Yorkville, and that was my new home. Now I was a New York artist — in the strictly technical sense of making paintings and living in New York. + +I was nervous about money, because I could sense that Interleaf was on the way down. Freelance Lisp hacking work was very rare, and I didn't want to have to program in another language, which in those days would have meant C++ if I was lucky. So with my unerring nose for financial opportunity, I decided to write another book on Lisp. This would be a popular book, the sort of book that could be used as a textbook. I imagined myself living frugally off the royalties and spending all my time painting. (The painting on the cover of this book, ANSI Common Lisp, is one that I painted around this time.) + +The best thing about New York for me was the presence of Idelle and Julian Weber. Idelle Weber was a painter, one of the early photorealists, and I'd taken her painting class at Harvard. I've never known a teacher more beloved by her students. Large numbers of former students kept in touch with her, including me. After I moved to New York I became her de facto studio assistant. + +She liked to paint on big, square canvases, 4 to 5 feet on a side. One day in late 1994 as I was stretching one of these monsters there was something on the radio about a famous fund manager. He wasn't that much older than me, and was super rich. The thought suddenly occurred to me: why don't I become rich? Then I'll be able to work on whatever I want. + +Meanwhile I'd been hearing more and more about this new thing called the World Wide Web. Robert Morris showed it to me when I visited him in Cambridge, where he was now in grad school at Harvard. It seemed to me that the web would be a big deal. I'd seen what graphical user interfaces had done for the popularity of microcomputers. It seemed like the web would do the same for the internet. + +If I wanted to get rich, here was the next train leaving the station. I was right about that part. What I got wrong was the idea. I decided we should start a company to put art galleries online. I can't honestly say, after reading so many Y Combinator applications, that this was the worst startup idea ever, but it was up there. Art galleries didn't want to be online, and still don't, not the fancy ones. That's not how they sell. I wrote some software to generate web sites for galleries, and Robert wrote some to resize images and set up an http server to serve the pages. Then we tried to sign up galleries. To call this a difficult sale would be an understatement. It was difficult to give away. A few galleries let us make sites for them for free, but none paid us. + +Then some online stores started to appear, and I realized that except for the order buttons they were identical to the sites we'd been generating for galleries. This impressive-sounding thing called an "internet storefront" was something we already knew how to build. + +So in the summer of 1995, after I submitted the camera-ready copy of ANSI Common Lisp to the publishers, we started trying to write software to build online stores. At first this was going to be normal desktop software, which in those days meant Windows software. That was an alarming prospect, because neither of us knew how to write Windows software or wanted to learn. We lived in the Unix world. But we decided we'd at least try writing a prototype store builder on Unix. Robert wrote a shopping cart, and I wrote a new site generator for stores — in Lisp, of course. + +We were working out of Robert's apartment in Cambridge. His roommate was away for big chunks of time, during which I got to sleep in his room. For some reason there was no bed frame or sheets, just a mattress on the floor. One morning as I was lying on this mattress I had an idea that made me sit up like a capital L. What if we ran the software on the server, and let users control it by clicking on links? Then we'd never have to write anything to run on users' computers. We could generate the sites on the same server we'd serve them from. Users wouldn't need anything more than a browser. + +This kind of software, known as a web app, is common now, but at the time it wasn't clear that it was even possible. To find out, we decided to try making a version of our store builder that you could control through the browser. A couple days later, on August 12, we had one that worked. The UI was horrible, but it proved you could build a whole store through the browser, without any client software or typing anything into the command line on the server. + +Now we felt like we were really onto something. I had visions of a whole new generation of software working this way. You wouldn't need versions, or ports, or any of that crap. At Interleaf there had been a whole group called Release Engineering that seemed to be at least as big as the group that actually wrote the software. Now you could just update the software right on the server. + +We started a new company we called Viaweb, after the fact that our software worked via the web, and we got $10,000 in seed funding from Idelle's husband Julian. In return for that and doing the initial legal work and giving us business advice, we gave him 10% of the company. Ten years later this deal became the model for Y Combinator's. We knew founders needed something like this, because we'd needed it ourselves. + +At this stage I had a negative net worth, because the thousand dollars or so I had in the bank was more than counterbalanced by what I owed the government in taxes. (Had I diligently set aside the proper proportion of the money I'd made consulting for Interleaf? No, I had not.) So although Robert had his graduate student stipend, I needed that seed funding to live on. + +We originally hoped to launch in September, but we got more ambitious about the software as we worked on it. Eventually we managed to build a WYSIWYG site builder, in the sense that as you were creating pages, they looked exactly like the static ones that would be generated later, except that instead of leading to static pages, the links all referred to closures stored in a hash table on the server. + +It helped to have studied art, because the main goal of an online store builder is to make users look legit, and the key to looking legit is high production values. If you get page layouts and fonts and colors right, you can make a guy running a store out of his bedroom look more legit than a big company. + +(If you're curious why my site looks so old-fashioned, it's because it's still made with this software. It may look clunky today, but in 1996 it was the last word in slick.) + +In September, Robert rebelled. "We've been working on this for a month," he said, "and it's still not done." This is funny in retrospect, because he would still be working on it almost 3 years later. But I decided it might be prudent to recruit more programmers, and I asked Robert who else in grad school with him was really good. He recommended Trevor Blackwell, which surprised me at first, because at that point I knew Trevor mainly for his plan to reduce everything in his life to a stack of notecards, which he carried around with him. But Rtm was right, as usual. Trevor turned out to be a frighteningly effective hacker. + +It was a lot of fun working with Robert and Trevor. They're the two most independent-minded people I know, and in completely different ways. If you could see inside Rtm's brain it would look like a colonial New England church, and if you could see inside Trevor's it would look like the worst excesses of Austrian Rococo. + +We opened for business, with 6 stores, in January 1996. It was just as well we waited a few months, because although we worried we were late, we were actually almost fatally early. There was a lot of talk in the press then about ecommerce, but not many people actually wanted online stores. [8] + +There were three main parts to the software: the editor, which people used to build sites and which I wrote, the shopping cart, which Robert wrote, and the manager, which kept track of orders and statistics, and which Trevor wrote. In its time, the editor was one of the best general-purpose site builders. I kept the code tight and didn't have to integrate with any other software except Robert's and Trevor's, so it was quite fun to work on. If all I'd had to do was work on this software, the next 3 years would have been the easiest of my life. Unfortunately I had to do a lot more, all of it stuff I was worse at than programming, and the next 3 years were instead the most stressful. + +There were a lot of startups making ecommerce software in the second half of the 90s. We were determined to be the Microsoft Word, not the Interleaf. Which meant being easy to use and inexpensive. It was lucky for us that we were poor, because that caused us to make Viaweb even more inexpensive than we realized. We charged $100 a month for a small store and $300 a month for a big one. This low price was a big attraction, and a constant thorn in the sides of competitors, but it wasn't because of some clever insight that we set the price low. We had no idea what businesses paid for things. $300 a month seemed like a lot of money to us. + +We did a lot of things right by accident like that. For example, we did what's now called "doing things that don't scale," although at the time we would have described it as "being so lame that we're driven to the most desperate measures to get users." The most common of which was building stores for them. This seemed particularly humiliating, since the whole raison d'etre of our software was that people could use it to make their own stores. But anything to get users. + +We learned a lot more about retail than we wanted to know. For example, that if you could only have a small image of a man's shirt (and all images were small then by present standards), it was better to have a closeup of the collar than a picture of the whole shirt. The reason I remember learning this was that it meant I had to rescan about 30 images of men's shirts. My first set of scans were so beautiful too. + +Though this felt wrong, it was exactly the right thing to be doing. Building stores for users taught us about retail, and about how it felt to use our software. I was initially both mystified and repelled by "business" and thought we needed a "business person" to be in charge of it, but once we started to get users, I was converted, in much the same way I was converted to fatherhood once I had kids. Whatever users wanted, I was all theirs. Maybe one day we'd have so many users that I couldn't scan their images for them, but in the meantime there was nothing more important to do. + +Another thing I didn't get at the time is that growth rate is the ultimate test of a startup. Our growth rate was fine. We had about 70 stores at the end of 1996 and about 500 at the end of 1997. I mistakenly thought the thing that mattered was the absolute number of users. And that is the thing that matters in the sense that that's how much money you're making, and if you're not making enough, you might go out of business. But in the long term the growth rate takes care of the absolute number. If we'd been a startup I was advising at Y Combinator, I would have said: Stop being so stressed out, because you're doing fine. You're growing 7x a year. Just don't hire too many more people and you'll soon be profitable, and then you'll control your own destiny. + +Alas I hired lots more people, partly because our investors wanted me to, and partly because that's what startups did during the Internet Bubble. A company with just a handful of employees would have seemed amateurish. So we didn't reach breakeven until about when Yahoo bought us in the summer of 1998. Which in turn meant we were at the mercy of investors for the entire life of the company. And since both we and our investors were noobs at startups, the result was a mess even by startup standards. + +It was a huge relief when Yahoo bought us. In principle our Viaweb stock was valuable. It was a share in a business that was profitable and growing rapidly. But it didn't feel very valuable to me; I had no idea how to value a business, but I was all too keenly aware of the near-death experiences we seemed to have every few months. Nor had I changed my grad student lifestyle significantly since we started. So when Yahoo bought us it felt like going from rags to riches. Since we were going to California, I bought a car, a yellow 1998 VW GTI. I remember thinking that its leather seats alone were by far the most luxurious thing I owned. + +The next year, from the summer of 1998 to the summer of 1999, must have been the least productive of my life. I didn't realize it at the time, but I was worn out from the effort and stress of running Viaweb. For a while after I got to California I tried to continue my usual m.o. of programming till 3 in the morning, but fatigue combined with Yahoo's prematurely aged culture and grim cube farm in Santa Clara gradually dragged me down. After a few months it felt disconcertingly like working at Interleaf. + +Yahoo had given us a lot of options when they bought us. At the time I thought Yahoo was so overvalued that they'd never be worth anything, but to my astonishment the stock went up 5x in the next year. I hung on till the first chunk of options vested, then in the summer of 1999 I left. It had been so long since I'd painted anything that I'd half forgotten why I was doing this. My brain had been entirely full of software and men's shirts for 4 years. But I had done this to get rich so I could paint, I reminded myself, and now I was rich, so I should go paint. + +When I said I was leaving, my boss at Yahoo had a long conversation with me about my plans. I told him all about the kinds of pictures I wanted to paint. At the time I was touched that he took such an interest in me. Now I realize it was because he thought I was lying. My options at that point were worth about $2 million a month. If I was leaving that kind of money on the table, it could only be to go and start some new startup, and if I did, I might take people with me. This was the height of the Internet Bubble, and Yahoo was ground zero of it. My boss was at that moment a billionaire. Leaving then to start a new startup must have seemed to him an insanely, and yet also plausibly, ambitious plan. + +But I really was quitting to paint, and I started immediately. There was no time to lose. I'd already burned 4 years getting rich. Now when I talk to founders who are leaving after selling their companies, my advice is always the same: take a vacation. That's what I should have done, just gone off somewhere and done nothing for a month or two, but the idea never occurred to me. + +So I tried to paint, but I just didn't seem to have any energy or ambition. Part of the problem was that I didn't know many people in California. I'd compounded this problem by buying a house up in the Santa Cruz Mountains, with a beautiful view but miles from anywhere. I stuck it out for a few more months, then in desperation I went back to New York, where unless you understand about rent control you'll be surprised to hear I still had my apartment, sealed up like a tomb of my old life. Idelle was in New York at least, and there were other people trying to paint there, even though I didn't know any of them. + +When I got back to New York I resumed my old life, except now I was rich. It was as weird as it sounds. I resumed all my old patterns, except now there were doors where there hadn't been. Now when I was tired of walking, all I had to do was raise my hand, and (unless it was raining) a taxi would stop to pick me up. Now when I walked past charming little restaurants I could go in and order lunch. It was exciting for a while. Painting started to go better. I experimented with a new kind of still life where I'd paint one painting in the old way, then photograph it and print it, blown up, on canvas, and then use that as the underpainting for a second still life, painted from the same objects (which hopefully hadn't rotted yet). + +Meanwhile I looked for an apartment to buy. Now I could actually choose what neighborhood to live in. Where, I asked myself and various real estate agents, is the Cambridge of New York? Aided by occasional visits to actual Cambridge, I gradually realized there wasn't one. Huh. + +Around this time, in the spring of 2000, I had an idea. It was clear from our experience with Viaweb that web apps were the future. Why not build a web app for making web apps? Why not let people edit code on our server through the browser, and then host the resulting applications for them? [9] You could run all sorts of services on the servers that these applications could use just by making an API call: making and receiving phone calls, manipulating images, taking credit card payments, etc. + +I got so excited about this idea that I couldn't think about anything else. It seemed obvious that this was the future. I didn't particularly want to start another company, but it was clear that this idea would have to be embodied as one, so I decided to move to Cambridge and start it. I hoped to lure Robert into working on it with me, but there I ran into a hitch. Robert was now a postdoc at MIT, and though he'd made a lot of money the last time I'd lured him into working on one of my schemes, it had also been a huge time sink. So while he agreed that it sounded like a plausible idea, he firmly refused to work on it. + +Hmph. Well, I'd do it myself then. I recruited Dan Giffin, who had worked for Viaweb, and two undergrads who wanted summer jobs, and we got to work trying to build what it's now clear is about twenty companies and several open source projects worth of software. The language for defining applications would of course be a dialect of Lisp. But I wasn't so naive as to assume I could spring an overt Lisp on a general audience; we'd hide the parentheses, like Dylan did. + +By then there was a name for the kind of company Viaweb was, an "application service provider," or ASP. This name didn't last long before it was replaced by "software as a service," but it was current for long enough that I named this new company after it: it was going to be called Aspra. + +I started working on the application builder, Dan worked on network infrastructure, and the two undergrads worked on the first two services (images and phone calls). But about halfway through the summer I realized I really didn't want to run a company — especially not a big one, which it was looking like this would have to be. I'd only started Viaweb because I needed the money. Now that I didn't need money anymore, why was I doing this? If this vision had to be realized as a company, then screw the vision. I'd build a subset that could be done as an open source project. + +Much to my surprise, the time I spent working on this stuff was not wasted after all. After we started Y Combinator, I would often encounter startups working on parts of this new architecture, and it was very useful to have spent so much time thinking about it and even trying to write some of it. + +The subset I would build as an open source project was the new Lisp, whose parentheses I now wouldn't even have to hide. A lot of Lisp hackers dream of building a new Lisp, partly because one of the distinctive features of the language is that it has dialects, and partly, I think, because we have in our minds a Platonic form of Lisp that all existing dialects fall short of. I certainly did. So at the end of the summer Dan and I switched to working on this new dialect of Lisp, which I called Arc, in a house I bought in Cambridge. + +The following spring, lightning struck. I was invited to give a talk at a Lisp conference, so I gave one about how we'd used Lisp at Viaweb. Afterward I put a postscript file of this talk online, on paulgraham.com, which I'd created years before using Viaweb but had never used for anything. In one day it got 30,000 page views. What on earth had happened? The referring urls showed that someone had posted it on Slashdot. [10] + +Wow, I thought, there's an audience. If I write something and put it on the web, anyone can read it. That may seem obvious now, but it was surprising then. In the print era there was a narrow channel to readers, guarded by fierce monsters known as editors. The only way to get an audience for anything you wrote was to get it published as a book, or in a newspaper or magazine. Now anyone could publish anything. + +This had been possible in principle since 1993, but not many people had realized it yet. I had been intimately involved with building the infrastructure of the web for most of that time, and a writer as well, and it had taken me 8 years to realize it. Even then it took me several years to understand the implications. It meant there would be a whole new generation of essays. [11] + +In the print era, the channel for publishing essays had been vanishingly small. Except for a few officially anointed thinkers who went to the right parties in New York, the only people allowed to publish essays were specialists writing about their specialties. There were so many essays that had never been written, because there had been no way to publish them. Now they could be, and I was going to write them. [12] + +I've worked on several different things, but to the extent there was a turning point where I figured out what to work on, it was when I started publishing essays online. From then on I knew that whatever else I did, I'd always write essays too. + +I knew that online essays would be a marginal medium at first. Socially they'd seem more like rants posted by nutjobs on their GeoCities sites than the genteel and beautifully typeset compositions published in The New Yorker. But by this point I knew enough to find that encouraging instead of discouraging. + +One of the most conspicuous patterns I've noticed in my life is how well it has worked, for me at least, to work on things that weren't prestigious. Still life has always been the least prestigious form of painting. Viaweb and Y Combinator both seemed lame when we started them. I still get the glassy eye from strangers when they ask what I'm writing, and I explain that it's an essay I'm going to publish on my web site. Even Lisp, though prestigious intellectually in something like the way Latin is, also seems about as hip. + +It's not that unprestigious types of work are good per se. But when you find yourself drawn to some kind of work despite its current lack of prestige, it's a sign both that there's something real to be discovered there, and that you have the right kind of motives. Impure motives are a big danger for the ambitious. If anything is going to lead you astray, it will be the desire to impress people. So while working on things that aren't prestigious doesn't guarantee you're on the right track, it at least guarantees you're not on the most common type of wrong one. + +Over the next several years I wrote lots of essays about all kinds of different topics. O'Reilly reprinted a collection of them as a book, called Hackers & Painters after one of the essays in it. I also worked on spam filters, and did some more painting. I used to have dinners for a group of friends every thursday night, which taught me how to cook for groups. And I bought another building in Cambridge, a former candy factory (and later, twas said, porn studio), to use as an office. + +One night in October 2003 there was a big party at my house. It was a clever idea of my friend Maria Daniels, who was one of the thursday diners. Three separate hosts would all invite their friends to one party. So for every guest, two thirds of the other guests would be people they didn't know but would probably like. One of the guests was someone I didn't know but would turn out to like a lot: a woman called Jessica Livingston. A couple days later I asked her out. + +Jessica was in charge of marketing at a Boston investment bank. This bank thought it understood startups, but over the next year, as she met friends of mine from the startup world, she was surprised how different reality was. And how colorful their stories were. So she decided to compile a book of interviews with startup founders. + +When the bank had financial problems and she had to fire half her staff, she started looking for a new job. In early 2005 she interviewed for a marketing job at a Boston VC firm. It took them weeks to make up their minds, and during this time I started telling her about all the things that needed to be fixed about venture capital. They should make a larger number of smaller investments instead of a handful of giant ones, they should be funding younger, more technical founders instead of MBAs, they should let the founders remain as CEO, and so on. + +One of my tricks for writing essays had always been to give talks. The prospect of having to stand up in front of a group of people and tell them something that won't waste their time is a great spur to the imagination. When the Harvard Computer Society, the undergrad computer club, asked me to give a talk, I decided I would tell them how to start a startup. Maybe they'd be able to avoid the worst of the mistakes we'd made. + +So I gave this talk, in the course of which I told them that the best sources of seed funding were successful startup founders, because then they'd be sources of advice too. Whereupon it seemed they were all looking expectantly at me. Horrified at the prospect of having my inbox flooded by business plans (if I'd only known), I blurted out "But not me!" and went on with the talk. But afterward it occurred to me that I should really stop procrastinating about angel investing. I'd been meaning to since Yahoo bought us, and now it was 7 years later and I still hadn't done one angel investment. + +Meanwhile I had been scheming with Robert and Trevor about projects we could work on together. I missed working with them, and it seemed like there had to be something we could collaborate on. + +As Jessica and I were walking home from dinner on March 11, at the corner of Garden and Walker streets, these three threads converged. Screw the VCs who were taking so long to make up their minds. We'd start our own investment firm and actually implement the ideas we'd been talking about. I'd fund it, and Jessica could quit her job and work for it, and we'd get Robert and Trevor as partners too. [13] + +Once again, ignorance worked in our favor. We had no idea how to be angel investors, and in Boston in 2005 there were no Ron Conways to learn from. So we just made what seemed like the obvious choices, and some of the things we did turned out to be novel. + +There are multiple components to Y Combinator, and we didn't figure them all out at once. The part we got first was to be an angel firm. In those days, those two words didn't go together. There were VC firms, which were organized companies with people whose job it was to make investments, but they only did big, million dollar investments. And there were angels, who did smaller investments, but these were individuals who were usually focused on other things and made investments on the side. And neither of them helped founders enough in the beginning. We knew how helpless founders were in some respects, because we remembered how helpless we'd been. For example, one thing Julian had done for us that seemed to us like magic was to get us set up as a company. We were fine writing fairly difficult software, but actually getting incorporated, with bylaws and stock and all that stuff, how on earth did you do that? Our plan was not only to make seed investments, but to do for startups everything Julian had done for us. + +YC was not organized as a fund. It was cheap enough to run that we funded it with our own money. That went right by 99% of readers, but professional investors are thinking "Wow, that means they got all the returns." But once again, this was not due to any particular insight on our part. We didn't know how VC firms were organized. It never occurred to us to try to raise a fund, and if it had, we wouldn't have known where to start. [14] + +The most distinctive thing about YC is the batch model: to fund a bunch of startups all at once, twice a year, and then to spend three months focusing intensively on trying to help them. That part we discovered by accident, not merely implicitly but explicitly due to our ignorance about investing. We needed to get experience as investors. What better way, we thought, than to fund a whole bunch of startups at once? We knew undergrads got temporary jobs at tech companies during the summer. Why not organize a summer program where they'd start startups instead? We wouldn't feel guilty for being in a sense fake investors, because they would in a similar sense be fake founders. So while we probably wouldn't make much money out of it, we'd at least get to practice being investors on them, and they for their part would probably have a more interesting summer than they would working at Microsoft. + +We'd use the building I owned in Cambridge as our headquarters. We'd all have dinner there once a week — on tuesdays, since I was already cooking for the thursday diners on thursdays — and after dinner we'd bring in experts on startups to give talks. + +We knew undergrads were deciding then about summer jobs, so in a matter of days we cooked up something we called the Summer Founders Program, and I posted an announcement on my site, inviting undergrads to apply. I had never imagined that writing essays would be a way to get "deal flow," as investors call it, but it turned out to be the perfect source. [15] We got 225 applications for the Summer Founders Program, and we were surprised to find that a lot of them were from people who'd already graduated, or were about to that spring. Already this SFP thing was starting to feel more serious than we'd intended. + +We invited about 20 of the 225 groups to interview in person, and from those we picked 8 to fund. They were an impressive group. That first batch included reddit, Justin Kan and Emmett Shear, who went on to found Twitch, Aaron Swartz, who had already helped write the RSS spec and would a few years later become a martyr for open access, and Sam Altman, who would later become the second president of YC. I don't think it was entirely luck that the first batch was so good. You had to be pretty bold to sign up for a weird thing like the Summer Founders Program instead of a summer job at a legit place like Microsoft or Goldman Sachs. + +The deal for startups was based on a combination of the deal we did with Julian ($10k for 10%) and what Robert said MIT grad students got for the summer ($6k). We invested $6k per founder, which in the typical two-founder case was $12k, in return for 6%. That had to be fair, because it was twice as good as the deal we ourselves had taken. Plus that first summer, which was really hot, Jessica brought the founders free air conditioners. [16] + +Fairly quickly I realized that we had stumbled upon the way to scale startup funding. Funding startups in batches was more convenient for us, because it meant we could do things for a lot of startups at once, but being part of a batch was better for the startups too. It solved one of the biggest problems faced by founders: the isolation. Now you not only had colleagues, but colleagues who understood the problems you were facing and could tell you how they were solving them. + +As YC grew, we started to notice other advantages of scale. The alumni became a tight community, dedicated to helping one another, and especially the current batch, whose shoes they remembered being in. We also noticed that the startups were becoming one another's customers. We used to refer jokingly to the "YC GDP," but as YC grows this becomes less and less of a joke. Now lots of startups get their initial set of customers almost entirely from among their batchmates. + +I had not originally intended YC to be a full-time job. I was going to do three things: hack, write essays, and work on YC. As YC grew, and I grew more excited about it, it started to take up a lot more than a third of my attention. But for the first few years I was still able to work on other things. + +In the summer of 2006, Robert and I started working on a new version of Arc. This one was reasonably fast, because it was compiled into Scheme. To test this new Arc, I wrote Hacker News in it. It was originally meant to be a news aggregator for startup founders and was called Startup News, but after a few months I got tired of reading about nothing but startups. Plus it wasn't startup founders we wanted to reach. It was future startup founders. So I changed the name to Hacker News and the topic to whatever engaged one's intellectual curiosity. + +HN was no doubt good for YC, but it was also by far the biggest source of stress for me. If all I'd had to do was select and help founders, life would have been so easy. And that implies that HN was a mistake. Surely the biggest source of stress in one's work should at least be something close to the core of the work. Whereas I was like someone who was in pain while running a marathon not from the exertion of running, but because I had a blister from an ill-fitting shoe. When I was dealing with some urgent problem during YC, there was about a 60% chance it had to do with HN, and a 40% chance it had do with everything else combined. [17] + +As well as HN, I wrote all of YC's internal software in Arc. But while I continued to work a good deal in Arc, I gradually stopped working on Arc, partly because I didn't have time to, and partly because it was a lot less attractive to mess around with the language now that we had all this infrastructure depending on it. So now my three projects were reduced to two: writing essays and working on YC. + +YC was different from other kinds of work I've done. Instead of deciding for myself what to work on, the problems came to me. Every 6 months there was a new batch of startups, and their problems, whatever they were, became our problems. It was very engaging work, because their problems were quite varied, and the good founders were very effective. If you were trying to learn the most you could about startups in the shortest possible time, you couldn't have picked a better way to do it. + +There were parts of the job I didn't like. Disputes between cofounders, figuring out when people were lying to us, fighting with people who maltreated the startups, and so on. But I worked hard even at the parts I didn't like. I was haunted by something Kevin Hale once said about companies: "No one works harder than the boss." He meant it both descriptively and prescriptively, and it was the second part that scared me. I wanted YC to be good, so if how hard I worked set the upper bound on how hard everyone else worked, I'd better work very hard. + +One day in 2010, when he was visiting California for interviews, Robert Morris did something astonishing: he offered me unsolicited advice. I can only remember him doing that once before. One day at Viaweb, when I was bent over double from a kidney stone, he suggested that it would be a good idea for him to take me to the hospital. That was what it took for Rtm to offer unsolicited advice. So I remember his exact words very clearly. "You know," he said, "you should make sure Y Combinator isn't the last cool thing you do." + +At the time I didn't understand what he meant, but gradually it dawned on me that he was saying I should quit. This seemed strange advice, because YC was doing great. But if there was one thing rarer than Rtm offering advice, it was Rtm being wrong. So this set me thinking. It was true that on my current trajectory, YC would be the last thing I did, because it was only taking up more of my attention. It had already eaten Arc, and was in the process of eating essays too. Either YC was my life's work or I'd have to leave eventually. And it wasn't, so I would. + +In the summer of 2012 my mother had a stroke, and the cause turned out to be a blood clot caused by colon cancer. The stroke destroyed her balance, and she was put in a nursing home, but she really wanted to get out of it and back to her house, and my sister and I were determined to help her do it. I used to fly up to Oregon to visit her regularly, and I had a lot of time to think on those flights. On one of them I realized I was ready to hand YC over to someone else. + +I asked Jessica if she wanted to be president, but she didn't, so we decided we'd try to recruit Sam Altman. We talked to Robert and Trevor and we agreed to make it a complete changing of the guard. Up till that point YC had been controlled by the original LLC we four had started. But we wanted YC to last for a long time, and to do that it couldn't be controlled by the founders. So if Sam said yes, we'd let him reorganize YC. Robert and I would retire, and Jessica and Trevor would become ordinary partners. + +When we asked Sam if he wanted to be president of YC, initially he said no. He wanted to start a startup to make nuclear reactors. But I kept at it, and in October 2013 he finally agreed. We decided he'd take over starting with the winter 2014 batch. For the rest of 2013 I left running YC more and more to Sam, partly so he could learn the job, and partly because I was focused on my mother, whose cancer had returned. + +She died on January 15, 2014. We knew this was coming, but it was still hard when it did. + +I kept working on YC till March, to help get that batch of startups through Demo Day, then I checked out pretty completely. (I still talk to alumni and to new startups working on things I'm interested in, but that only takes a few hours a week.) + +What should I do next? Rtm's advice hadn't included anything about that. I wanted to do something completely different, so I decided I'd paint. I wanted to see how good I could get if I really focused on it. So the day after I stopped working on YC, I started painting. I was rusty and it took a while to get back into shape, but it was at least completely engaging. [18] + +I spent most of the rest of 2014 painting. I'd never been able to work so uninterruptedly before, and I got to be better than I had been. Not good enough, but better. Then in November, right in the middle of a painting, I ran out of steam. Up till that point I'd always been curious to see how the painting I was working on would turn out, but suddenly finishing this one seemed like a chore. So I stopped working on it and cleaned my brushes and haven't painted since. So far anyway. + +I realize that sounds rather wimpy. But attention is a zero sum game. If you can choose what to work on, and you choose a project that's not the best one (or at least a good one) for you, then it's getting in the way of another project that is. And at 50 there was some opportunity cost to screwing around. + +I started writing essays again, and wrote a bunch of new ones over the next few months. I even wrote a couple that weren't about startups. Then in March 2015 I started working on Lisp again. + +The distinctive thing about Lisp is that its core is a language defined by writing an interpreter in itself. It wasn't originally intended as a programming language in the ordinary sense. It was meant to be a formal model of computation, an alternative to the Turing machine. If you want to write an interpreter for a language in itself, what's the minimum set of predefined operators you need? The Lisp that John McCarthy invented, or more accurately discovered, is an answer to that question. [19] + +McCarthy didn't realize this Lisp could even be used to program computers till his grad student Steve Russell suggested it. Russell translated McCarthy's interpreter into IBM 704 machine language, and from that point Lisp started also to be a programming language in the ordinary sense. But its origins as a model of computation gave it a power and elegance that other languages couldn't match. It was this that attracted me in college, though I didn't understand why at the time. + +McCarthy's 1960 Lisp did nothing more than interpret Lisp expressions. It was missing a lot of things you'd want in a programming language. So these had to be added, and when they were, they weren't defined using McCarthy's original axiomatic approach. That wouldn't have been feasible at the time. McCarthy tested his interpreter by hand-simulating the execution of programs. But it was already getting close to the limit of interpreters you could test that way — indeed, there was a bug in it that McCarthy had overlooked. To test a more complicated interpreter, you'd have had to run it, and computers then weren't powerful enough. + +Now they are, though. Now you could continue using McCarthy's axiomatic approach till you'd defined a complete programming language. And as long as every change you made to McCarthy's Lisp was a discoveredness-preserving transformation, you could, in principle, end up with a complete language that had this quality. Harder to do than to talk about, of course, but if it was possible in principle, why not try? So I decided to take a shot at it. It took 4 years, from March 26, 2015 to October 12, 2019. It was fortunate that I had a precisely defined goal, or it would have been hard to keep at it for so long. + +I wrote this new Lisp, called Bel, in itself in Arc. That may sound like a contradiction, but it's an indication of the sort of trickery I had to engage in to make this work. By means of an egregious collection of hacks I managed to make something close enough to an interpreter written in itself that could actually run. Not fast, but fast enough to test. + +I had to ban myself from writing essays during most of this time, or I'd never have finished. In late 2015 I spent 3 months writing essays, and when I went back to working on Bel I could barely understand the code. Not so much because it was badly written as because the problem is so convoluted. When you're working on an interpreter written in itself, it's hard to keep track of what's happening at what level, and errors can be practically encrypted by the time you get them. + +So I said no more essays till Bel was done. But I told few people about Bel while I was working on it. So for years it must have seemed that I was doing nothing, when in fact I was working harder than I'd ever worked on anything. Occasionally after wrestling for hours with some gruesome bug I'd check Twitter or HN and see someone asking "Does Paul Graham still code?" + +Working on Bel was hard but satisfying. I worked on it so intensively that at any given time I had a decent chunk of the code in my head and could write more there. I remember taking the boys to the coast on a sunny day in 2015 and figuring out how to deal with some problem involving continuations while I watched them play in the tide pools. It felt like I was doing life right. I remember that because I was slightly dismayed at how novel it felt. The good news is that I had more moments like this over the next few years. + +In the summer of 2016 we moved to England. We wanted our kids to see what it was like living in another country, and since I was a British citizen by birth, that seemed the obvious choice. We only meant to stay for a year, but we liked it so much that we still live there. So most of Bel was written in England. + +In the fall of 2019, Bel was finally finished. Like McCarthy's original Lisp, it's a spec rather than an implementation, although like McCarthy's Lisp it's a spec expressed as code. + +Now that I could write essays again, I wrote a bunch about topics I'd had stacked up. I kept writing essays through 2020, but I also started to think about other things I could work on. How should I choose what to do? Well, how had I chosen what to work on in the past? I wrote an essay for myself to answer that question, and I was surprised how long and messy the answer turned out to be. If this surprised me, who'd lived it, then I thought perhaps it would be interesting to other people, and encouraging to those with similarly messy lives. So I wrote a more detailed version for others to read, and this is the last sentence of it. + + + + + + + + + +Notes + +[1] My experience skipped a step in the evolution of computers: time-sharing machines with interactive OSes. I went straight from batch processing to microcomputers, which made microcomputers seem all the more exciting. + +[2] Italian words for abstract concepts can nearly always be predicted from their English cognates (except for occasional traps like polluzione). It's the everyday words that differ. So if you string together a lot of abstract concepts with a few simple verbs, you can make a little Italian go a long way. + +[3] I lived at Piazza San Felice 4, so my walk to the Accademia went straight down the spine of old Florence: past the Pitti, across the bridge, past Orsanmichele, between the Duomo and the Baptistery, and then up Via Ricasoli to Piazza San Marco. I saw Florence at street level in every possible condition, from empty dark winter evenings to sweltering summer days when the streets were packed with tourists. + +[4] You can of course paint people like still lives if you want to, and they're willing. That sort of portrait is arguably the apex of still life painting, though the long sitting does tend to produce pained expressions in the sitters. + +[5] Interleaf was one of many companies that had smart people and built impressive technology, and yet got crushed by Moore's Law. In the 1990s the exponential growth in the power of commodity (i.e. Intel) processors rolled up high-end, special-purpose hardware and software companies like a bulldozer. + +[6] The signature style seekers at RISD weren't specifically mercenary. In the art world, money and coolness are tightly coupled. Anything expensive comes to be seen as cool, and anything seen as cool will soon become equally expensive. + +[7] Technically the apartment wasn't rent-controlled but rent-stabilized, but this is a refinement only New Yorkers would know or care about. The point is that it was really cheap, less than half market price. + +[8] Most software you can launch as soon as it's done. But when the software is an online store builder and you're hosting the stores, if you don't have any users yet, that fact will be painfully obvious. So before we could launch publicly we had to launch privately, in the sense of recruiting an initial set of users and making sure they had decent-looking stores. + +[9] We'd had a code editor in Viaweb for users to define their own page styles. They didn't know it, but they were editing Lisp expressions underneath. But this wasn't an app editor, because the code ran when the merchants' sites were generated, not when shoppers visited them. + +[10] This was the first instance of what is now a familiar experience, and so was what happened next, when I read the comments and found they were full of angry people. How could I claim that Lisp was better than other languages? Weren't they all Turing complete? People who see the responses to essays I write sometimes tell me how sorry they feel for me, but I'm not exaggerating when I reply that it has always been like this, since the very beginning. It comes with the territory. An essay must tell readers things they don't already know, and some people dislike being told such things. + +[11] People put plenty of stuff on the internet in the 90s of course, but putting something online is not the same as publishing it online. Publishing online means you treat the online version as the (or at least a) primary version. + +[12] There is a general lesson here that our experience with Y Combinator also teaches: Customs continue to constrain you long after the restrictions that caused them have disappeared. Customary VC practice had once, like the customs about publishing essays, been based on real constraints. Startups had once been much more expensive to start, and proportionally rare. Now they could be cheap and common, but the VCs' customs still reflected the old world, just as customs about writing essays still reflected the constraints of the print era. + +Which in turn implies that people who are independent-minded (i.e. less influenced by custom) will have an advantage in fields affected by rapid change (where customs are more likely to be obsolete). + +Here's an interesting point, though: you can't always predict which fields will be affected by rapid change. Obviously software and venture capital will be, but who would have predicted that essay writing would be? + +[13] Y Combinator was not the original name. At first we were called Cambridge Seed. But we didn't want a regional name, in case someone copied us in Silicon Valley, so we renamed ourselves after one of the coolest tricks in the lambda calculus, the Y combinator. + +I picked orange as our color partly because it's the warmest, and partly because no VC used it. In 2005 all the VCs used staid colors like maroon, navy blue, and forest green, because they were trying to appeal to LPs, not founders. The YC logo itself is an inside joke: the Viaweb logo had been a white V on a red circle, so I made the YC logo a white Y on an orange square. + +[14] YC did become a fund for a couple years starting in 2009, because it was getting so big I could no longer afford to fund it personally. But after Heroku got bought we had enough money to go back to being self-funded. + +[15] I've never liked the term "deal flow," because it implies that the number of new startups at any given time is fixed. This is not only false, but it's the purpose of YC to falsify it, by causing startups to be founded that would not otherwise have existed. + +[16] She reports that they were all different shapes and sizes, because there was a run on air conditioners and she had to get whatever she could, but that they were all heavier than she could carry now. + +[17] Another problem with HN was a bizarre edge case that occurs when you both write essays and run a forum. When you run a forum, you're assumed to see if not every conversation, at least every conversation involving you. And when you write essays, people post highly imaginative misinterpretations of them on forums. Individually these two phenomena are tedious but bearable, but the combination is disastrous. You actually have to respond to the misinterpretations, because the assumption that you're present in the conversation means that not responding to any sufficiently upvoted misinterpretation reads as a tacit admission that it's correct. But that in turn encourages more; anyone who wants to pick a fight with you senses that now is their chance. + +[18] The worst thing about leaving YC was not working with Jessica anymore. We'd been working on YC almost the whole time we'd known each other, and we'd neither tried nor wanted to separate it from our personal lives, so leaving was like pulling up a deeply rooted tree. + +[19] One way to get more precise about the concept of invented vs discovered is to talk about space aliens. Any sufficiently advanced alien civilization would certainly know about the Pythagorean theorem, for example. I believe, though with less certainty, that they would also know about the Lisp in McCarthy's 1960 paper. + +But if so there's no reason to suppose that this is the limit of the language that might be known to them. Presumably aliens need numbers and errors and I/O too. So it seems likely there exists at least one path out of McCarthy's Lisp along which discoveredness is preserved. + + + +Thanks to Trevor Blackwell, John Collison, Patrick Collison, Daniel Gackle, Ralph Hazell, Jessica Livingston, Robert Morris, and Harj Taggar for reading drafts of this. \ No newline at end of file diff --git a/agenta-cli/tests/observability_sdk/integrations/llama_index/requirements.txt b/agenta-cli/tests/observability_sdk/integrations/llama_index/requirements.txt new file mode 100644 index 0000000000..b09ab54fa3 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/llama_index/requirements.txt @@ -0,0 +1,6 @@ +opentelemetry-instrumentation-llamaindex +llama-index +chromadb +opentelemetry.instrumentation.openai==0.31.2 +agenta==0.27.0a5 +llama-index-vector-stores-chroma \ No newline at end of file From 63c6434007b0e752ee34b256f57e2b5f5a400b98 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sun, 27 Oct 2024 19:32:31 +0100 Subject: [PATCH 182/305] fix(frontend): added inputs and outputs values in observability table --- .../apps/[app_id]/observability/index.tsx | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index fab602e7f0..a775aa1e62 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -16,7 +16,7 @@ import {useTraces} from "@/lib/hooks/useTraces" import {JSSTheme} from "@/lib/Types" import {_AgentaRootsResponse} from "@/services/observability/types" import {SwapOutlined} from "@ant-design/icons" -import {Space, Table, TableColumnType, Typography} from "antd" +import {Space, Table, TableColumnType, Tag, Typography} from "antd" import {ColumnsType} from "antd/es/table" import dayjs from "dayjs" import {useRouter} from "next/router" @@ -92,27 +92,31 @@ const ObservabilityDashboard = ({}: Props) => { title: "Inputs", key: "inputs", width: 350, - onHeaderCell: () => ({ - style: {minWidth: 350}, - }), - // render: (_, record) => { - // return - // }, + render: (_, record) => { + return ( + + {getStringOrJson(record?.data?.inputs)} + + ) + }, }, { title: "Outputs", key: "outputs", width: 350, - onHeaderCell: () => ({ - style: {minWidth: 350}, - }), - // render: (_, record) => { - // return ( - //
- // - //
- // ) - // }, + render: (_, record) => { + return ( + + {getStringOrJson(record?.data?.outputs)} + + ) + }, }, { title: "Status", From 0679b4e2d8f1d8f0cb428b09d0bbdd01785b5fd4 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sun, 27 Oct 2024 20:02:54 +0100 Subject: [PATCH 183/305] fix(frontend): customized editor scrollbar --- .../pages/observability/components/AccordionTreePanel.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx b/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx index 3d66b51f96..ca2fca4b58 100644 --- a/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx +++ b/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx @@ -57,6 +57,12 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ "& .monaco-editor .margin": { backgroundColor: bgColor, }, + "& .monaco-editor .scrollbar.vertical .slider": { + borderRadius: 6, + }, + "& .monaco-editor .scrollbar.vertical": { + backgroundColor: theme.colorBgContainerDisabled, + }, }), })) From 28e9b3b53a75eaf0231dbb670889662a714d7ddb Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Mon, 28 Oct 2024 03:24:57 +0100 Subject: [PATCH 184/305] fix(frontend): reduced monaco editor scrollbar size --- .../pages/observability/components/AccordionTreePanel.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx b/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx index ca2fca4b58..f4670e3409 100644 --- a/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx +++ b/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx @@ -118,6 +118,10 @@ const AccordionTreePanel = ({ readOnly: true, lineNumbers: "off", lineDecorationsWidth: 0, + scrollbar: { + verticalScrollbarSize: 8, + horizontalScrollbarSize: 8, + }, }} /> ), From 656d5b05e52fd68cbbf460501dac6aecde48455d Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Mon, 28 Oct 2024 03:30:21 +0100 Subject: [PATCH 185/305] feat(frontend): set selected state to clicked span --- .../observability/drawer/TraceHeader.tsx | 18 +++-------- .../apps/[app_id]/observability/index.tsx | 31 ++++++++++++++++--- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx b/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx index 5dd8cd0340..9821459ade 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx @@ -3,7 +3,7 @@ import {_AgentaRootsResponse} from "@/services/observability/types" import {DeleteOutlined} from "@ant-design/icons" import {CaretDown, CaretUp} from "@phosphor-icons/react" import {Button, Space, Tag, Typography} from "antd" -import React, {useCallback, useState} from "react" +import React, {useState} from "react" import {createUseStyles} from "react-jss" import DeleteTraceModal from "../components/DeleteTraceModal" @@ -12,6 +12,8 @@ interface TraceHeaderProps { traces: _AgentaRootsResponse[] setSelectedTraceId: (val: string) => void activeTraceIndex: number + handleNextTrace: () => void + handlePrevTrace: () => void } const useStyles = createUseStyles((theme: JSSTheme) => ({ @@ -27,22 +29,12 @@ const TraceHeader = ({ traces, setSelectedTraceId, activeTraceIndex, + handleNextTrace, + handlePrevTrace, }: TraceHeaderProps) => { const classes = useStyles() const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false) - const handleNextTrace = useCallback(() => { - if (activeTraceIndex !== undefined && activeTraceIndex < traces.length - 1) { - setSelectedTraceId(traces[activeTraceIndex + 1].root.id) - } - }, [activeTraceIndex, traces, setSelectedTraceId]) - - const handlePrevTrace = useCallback(() => { - if (activeTraceIndex !== undefined && activeTraceIndex > 0) { - setSelectedTraceId(traces[activeTraceIndex - 1].root.id) - } - }, [activeTraceIndex, traces, setSelectedTraceId]) - return ( <>
diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index a775aa1e62..a506e36ff3 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -20,7 +20,7 @@ import {Space, Table, TableColumnType, Tag, Typography} from "antd" import {ColumnsType} from "antd/es/table" import dayjs from "dayjs" import {useRouter} from "next/router" -import React, {useEffect, useMemo, useState} from "react" +import React, {useCallback, useEffect, useMemo, useState} from "react" import {createUseStyles} from "react-jss" const useStyles = createUseStyles((theme: JSSTheme) => ({ @@ -169,16 +169,34 @@ const ObservabilityDashboard = ({}: Props) => { const activeTrace = useMemo(() => traces[activeTraceIndex] ?? null, [activeTraceIndex, traces]) - const [selected, setSelected] = useState(activeTrace?.key) + const [selected, setSelected] = useState("") + + useEffect(() => { + if (!selected) { + setSelected(activeTrace.node.id) + } + }, [activeTrace, selected]) const selectedItem = useMemo( () => (traces?.length ? getNodeById(traces, selected) : null), [selected, traces], ) - useEffect(() => { - setSelected(activeTrace?.key) - }, [activeTrace]) + const handleNextTrace = useCallback(() => { + if (activeTraceIndex !== undefined && activeTraceIndex < traces.length - 1) { + const nextTrace = traces[activeTraceIndex + 1] + setSelectedTraceId(nextTrace.root.id) + setSelected(nextTrace.node.id) + } + }, [activeTraceIndex, traces, setSelectedTraceId]) + + const handlePrevTrace = useCallback(() => { + if (activeTraceIndex !== undefined && activeTraceIndex > 0) { + const prevTrace = traces[activeTraceIndex - 1] + setSelectedTraceId(prevTrace.root.id) + setSelected(prevTrace.node.id) + } + }, [activeTraceIndex, traces, setSelectedTraceId]) const handleResize = (key: string) => @@ -214,6 +232,7 @@ const ObservabilityDashboard = ({}: Props) => { style={{cursor: "pointer"}} onRow={(record) => ({ onClick: () => { + setSelected(record.node.id) setSelectedTraceId(record.root.id) }, })} @@ -267,6 +286,8 @@ const ObservabilityDashboard = ({}: Props) => { traces={traces} setSelectedTraceId={setSelectedTraceId} activeTraceIndex={activeTraceIndex} + handleNextTrace={handleNextTrace} + handlePrevTrace={handlePrevTrace} /> } mainContent={selectedItem ? : null} From 53b5541324507be85ec64fe40154b0211288e042 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Mon, 28 Oct 2024 03:47:15 +0100 Subject: [PATCH 186/305] fix function calls not being shown correctly(frontend) --- .../src/components/pages/observability/drawer/TraceContent.tsx | 2 +- agenta-web/src/pages/apps/[app_id]/observability/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx index d5906eb88a..daafc29f26 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx @@ -162,7 +162,7 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { ), diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index a506e36ff3..4fc78c940a 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -173,7 +173,7 @@ const ObservabilityDashboard = ({}: Props) => { useEffect(() => { if (!selected) { - setSelected(activeTrace.node.id) + setSelected(activeTrace?.node.id) } }, [activeTrace, selected]) From fd5cab1c966a9631a2add6101bed036ed6b5d7e2 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Mon, 28 Oct 2024 04:04:53 +0100 Subject: [PATCH 187/305] fix(frontend): aligned segmented button styles with figma design --- .../observability/components/AccordionTreePanel.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx b/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx index f4670e3409..57a30b258b 100644 --- a/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx +++ b/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx @@ -64,6 +64,16 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ backgroundColor: theme.colorBgContainerDisabled, }, }), + segmentedBtn: { + border: `1px solid ${theme.colorBorder}`, + padding: 0, + "& .ant-segmented-item": { + padding: 2, + }, + "& .ant-segmented-item-selected": { + outline: `1px solid ${theme.colorPrimary}`, + }, + }, })) const AccordionTreePanel = ({ @@ -135,6 +145,7 @@ const AccordionTreePanel = ({ setSegmentedValue(optValue) }} onClick={(e) => e.stopPropagation()} + className={classes.segmentedBtn} /> )} Date: Mon, 28 Oct 2024 14:28:50 +0600 Subject: [PATCH 188/305] enhance(frontend): connect filters with backend apis --- agenta-web/src/components/Filters/Filters.tsx | 30 +- agenta-web/src/components/Filters/Sort.tsx | 4 +- agenta-web/src/lib/hooks/useTraces.ts | 2 +- .../apps/[app_id]/observability/index.tsx | 364 ++++++++++-------- 4 files changed, 217 insertions(+), 183 deletions(-) diff --git a/agenta-web/src/components/Filters/Filters.tsx b/agenta-web/src/components/Filters/Filters.tsx index 7e30dcc48b..cdaf075228 100644 --- a/agenta-web/src/components/Filters/Filters.tsx +++ b/agenta-web/src/components/Filters/Filters.tsx @@ -124,10 +124,6 @@ const Filters: React.FC = ({
Filter - -
@@ -143,11 +139,16 @@ const Filters: React.FC = ({ showSearch labelRender={(label) => (!label.value ? "Column" : label.label)} style={{width: 100}} - popupMatchSelectWidth={150} + popupMatchSelectWidth={220} suffixIcon={} onChange={(value) => onFilterChange({columnName: "key", value, idx}) } + filterSort={(a, b) => + (a?.label ?? "") + .toLowerCase() + .localeCompare((b?.label ?? "").toLowerCase()) + } filterOption={(input, option) => (option?.label ?? "") .toLowerCase() @@ -217,11 +218,22 @@ const Filters: React.FC = ({
-
- + + -
+
} > @@ -230,7 +242,7 @@ const Filters: React.FC = ({ onClick={() => setIsFilterOpen(true)} className="flex items-center gap-2" > - Filters {filter[0]?.value && } + Filters {filter[0]?.operator && } ) diff --git a/agenta-web/src/components/Filters/Sort.tsx b/agenta-web/src/components/Filters/Sort.tsx index 14e3d684a0..a8ee1e2fa5 100644 --- a/agenta-web/src/components/Filters/Sort.tsx +++ b/agenta-web/src/components/Filters/Sort.tsx @@ -1,5 +1,5 @@ import React, {useState} from "react" -import {CaretRight, Clock, Hourglass} from "@phosphor-icons/react" +import {CaretRight, Clock, Calendar} from "@phosphor-icons/react" import {DatePicker, Button, Typography, Divider, Popover} from "antd" import {JSSTheme, SortTypes} from "@/lib/Types" import {Dayjs} from "dayjs" @@ -210,7 +210,7 @@ const Sort: React.FC = ({onSortApply, defaultSortValue}) => { } > diff --git a/agenta-web/src/services/observability/core/index.ts b/agenta-web/src/services/observability/core/index.ts index e7995d072e..ca92701ace 100644 --- a/agenta-web/src/services/observability/core/index.ts +++ b/agenta-web/src/services/observability/core/index.ts @@ -9,7 +9,6 @@ import axios from "@/lib/helpers/axiosConfig" // - update: PUT data to server // - delete: DELETE data from server - export const fetchAllTraces = async (projectId: string) => { const response = await axios.get( `${getAgentaApiUrl()}/api/observability/v1/traces/search?project_id=${projectId}`, From 7969b6caf7233e0607f54f83d16a39df2cf7d083 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Mon, 28 Oct 2024 11:31:41 +0100 Subject: [PATCH 190/305] add functions to semconv --- .../apis/fastapi/observability/opentelemetry/semconv.py | 1 + 1 file changed, 1 insertion(+) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py index 9567a08724..e9d4602f0b 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py @@ -39,6 +39,7 @@ # OPENLLMETRY ("gen_ai.prompt", "ag.data.inputs.prompt"), ("gen_ai.completion", "ag.data.outputs.completion"), + ("llm.request.functions", "ag.data.inputs.functions"), # OPENINFERENCE ("llm.token_count", "ag.metrics.unit.tokens"), ("llm.input_messages", "ag.data.inputs.prompt"), From 3dad16d047271a136d00326ed2d4bedd6be9c027 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Mon, 28 Oct 2024 12:06:42 +0100 Subject: [PATCH 191/305] Add tools, similar to functions --- .../apis/fastapi/observability/opentelemetry/semconv.py | 1 + 1 file changed, 1 insertion(+) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py index e9d4602f0b..ad5139f94d 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py @@ -40,6 +40,7 @@ ("gen_ai.prompt", "ag.data.inputs.prompt"), ("gen_ai.completion", "ag.data.outputs.completion"), ("llm.request.functions", "ag.data.inputs.functions"), + ("llm.request.tools", "ag.data.inputs.tools"), # OPENINFERENCE ("llm.token_count", "ag.metrics.unit.tokens"), ("llm.input_messages", "ag.data.inputs.prompt"), From 03ce448c7a68b7b5612b527eccecd281708c3df8 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Mon, 28 Oct 2024 12:21:13 +0100 Subject: [PATCH 192/305] Get 'project_id' from auth helper instead of query param --- .../apis/fastapi/observability/router.py | 48 +++++-------------- 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py index 1355ea920a..2081dca999 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/router.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/router.py @@ -60,26 +60,11 @@ def __init__( self.router.add_api_route( "/traces/search", + # "/traces/query", self.query_traces, - methods=["GET"], - operation_id="query_traces_deprecated", - summary="Query traces, with optional grouping, filtering, (sorting,) and pagination.", - status_code=status.HTTP_200_OK, - response_model=Union[ - OTelSpansResponse, - AgentaNodesResponse, - AgentaTreesResponse, - AgentaRootsResponse, - ], - response_model_exclude_none=True, - ) - - self.router.add_api_route( - "/traces/query", - self.query_traces, - methods=["POST"], + methods=["GET", "POST"], operation_id="query_traces", - summary="Query traces, with optional grouping, filtering, (sorting,) and pagination.", + summary="Query traces, with optional grouping, windowing, filtering, and pagination.", status_code=status.HTTP_200_OK, response_model=Union[ OTelSpansResponse, @@ -117,12 +102,15 @@ async def otlp_status(self): @handle_exceptions() async def query_traces( self, - project_id: str, + request: Request, query_dto: QueryDTO = Depends(parse_query_dto), - format: Literal["opentelemetry", "agenta"] = Query("agenta"), + format: Literal[ # pylint: disable=W0622 + "opentelemetry", + "agenta", + ] = Query("agenta"), ): """ - Query traces, with optional grouping, filtering, (sorting,) and pagination. + Query traces, with optional grouping, windowing, filtering, and pagination. """ if ( @@ -136,7 +124,7 @@ async def query_traces( ) span_dtos = await self.service.query( - project_id=UUID(project_id), + project_id=UUID(request.state.project_id), query_dto=query_dto, ) @@ -241,22 +229,10 @@ async def otlp_collect_traces( otlp_stream = await request.body() - # TODO: GET project_id FROM request.state - project_id = request.headers.get("AG-PROJECT-ID") or request.state.project_id - - # TODO: DROP app_id ONCE LEGACY IS DROPPED - app_id = request.headers.get("AG-APP-ID") - - ### LEGACY ### - # TODO: DROP LEGACY - project_id = project_id or app_id - ### LEGACY ### - ### LEGACY ### - # TODO: DROP LEGACY if self.legacy_receiver: await self.legacy_receiver( - project_id=project_id, + project_id=request.state.project_id, otlp_stream=otlp_stream, ) ### LEGACY ### @@ -268,7 +244,7 @@ async def otlp_collect_traces( ] await self.service.ingest( - project_id=UUID(project_id), + project_id=UUID(request.state.project_id), span_dtos=span_dtos, ) From 01be3ac75fa800256dc27da2b330f8b08800e821 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Mon, 28 Oct 2024 17:56:45 +0600 Subject: [PATCH 193/305] feat(frontend): added pagination UI --- .../apps/[app_id]/observability/index.tsx | 122 +++++++++++------- 1 file changed, 72 insertions(+), 50 deletions(-) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index 88900a00bf..ba65519cd8 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -25,6 +25,7 @@ import {SwapOutlined} from "@ant-design/icons" import { Button, Input, + Pagination, Radio, RadioChangeEvent, Space, @@ -51,6 +52,12 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ fontWeight: theme.fontWeightMedium, lineHeight: theme.lineHeightHeading4, }, + pagination: { + "& > .ant-pagination-options": { + order: -1, + marginRight: 8, + }, + }, })) interface Props {} @@ -68,6 +75,7 @@ const ObservabilityDashboard = ({}: Props) => { const [editColumns, setEditColumns] = useState(["span_type"]) const [filters, setFilters] = useState([]) const [isFilterColsDropdownOpen, setIsFilterColsDropdownOpen] = useState(false) + const [pagination, setPagination] = useState({current: 1, page: 10}) const [columns, setColumns] = useState>([ { title: "ID", @@ -520,6 +528,10 @@ const ObservabilityDashboard = ({}: Props) => { } } + const onPaginationChange = (current: number, pageSize: number) => { + setPagination({current, page: pageSize}) + } + return (
Observability @@ -572,56 +584,66 @@ const ObservabilityDashboard = ({}: Props) => {
-
[]} - dataSource={traces} - bordered - style={{cursor: "pointer"}} - onRow={(record) => ({ - onClick: () => { - setSelected(record.node.id) - setSelectedTraceId(record.root.id) - }, - })} - components={{ - header: { - cell: ResizableTitle, - }, - }} - pagination={false} - scroll={{x: "max-content"}} - locale={{ - emptyText: ( -
- - } - description="Monitor the performance and results of your LLM applications here." - primaryCta={{ - text: "Go to Playground", - onClick: () => router.push(`/apps/${appId}/playground`), - tooltip: - "Run your LLM app in the playground to generate and view insights.", - }} - secondaryCta={{ - text: "Learn More", - onClick: () => - router.push( - "https://docs.agenta.ai/observability/quickstart", - ), - tooltip: - "Explore more about tracking and analyzing your app's observability data.", - }} - /> -
- ), - }} - /> +
+
[]} + dataSource={traces} + bordered + style={{cursor: "pointer"}} + onRow={(record) => ({ + onClick: () => { + setSelected(record.node.id) + setSelectedTraceId(record.root.id) + }, + })} + components={{ + header: { + cell: ResizableTitle, + }, + }} + pagination={false} + scroll={{x: "max-content"}} + locale={{ + emptyText: ( +
+ + } + description="Monitor the performance and results of your LLM applications here." + primaryCta={{ + text: "Go to Playground", + onClick: () => router.push(`/apps/${appId}/playground`), + tooltip: + "Run your LLM app in the playground to generate and view insights.", + }} + secondaryCta={{ + text: "Learn More", + onClick: () => + router.push( + "https://docs.agenta.ai/observability/quickstart", + ), + tooltip: + "Explore more about tracking and analyzing your app's observability data.", + }} + /> +
+ ), + }} + /> + + {activeTrace && !!traces?.length && ( Date: Mon, 28 Oct 2024 18:09:55 +0600 Subject: [PATCH 194/305] enhane(frontend): refactor endpoint api url --- agenta-web/src/lib/hooks/useTraces.ts | 5 +---- agenta-web/src/pages/apps/[app_id]/observability/index.tsx | 6 +++--- agenta-web/src/services/observability/core/index.ts | 6 ++---- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/agenta-web/src/lib/hooks/useTraces.ts b/agenta-web/src/lib/hooks/useTraces.ts index 9f928d8fd1..f5c5bb72ee 100644 --- a/agenta-web/src/lib/hooks/useTraces.ts +++ b/agenta-web/src/lib/hooks/useTraces.ts @@ -7,18 +7,15 @@ import { } from "@/services/observability/types" import {useEffect, useState} from "react" import {buildNodeTree, observabilityTransformer} from "../helpers/observability_helpers" -import {useRouter} from "next/router" export const useTraces = () => { const [traces, setTraces] = useState<_AgentaRootsResponse[]>([]) const [isLoadingTraces, setIsLoadingTraces] = useState(true) - const router = useRouter() - const projectId = router.query.project_id const fetchTraces = async () => { try { setIsLoadingTraces(true) - const data = await fetchAllTraces(projectId as string) + const data = await fetchAllTraces() const transformedTraces: _AgentaRootsResponse[] = [] diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index ba65519cd8..fc59c7c895 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -370,7 +370,7 @@ const ObservabilityDashboard = ({}: Props) => { try { const fetchAllTraces = async () => { const response = await axios.get( - `${getAgentaApiUrl()}/api/observability/v1/traces/search?project_id=0192c229-3760-759d-a637-959921135050&${query}`, + `${getAgentaApiUrl()}/api/observability/v1/traces/search?${query}`, ) return response.data } @@ -471,13 +471,13 @@ const ObservabilityDashboard = ({}: Props) => { const fetchAllFilteredTraces = async () => { const response = await axios.get( - `${getAgentaApiUrl()}/api/observability/v1/traces/search?project_id=0192c229-3760-759d-a637-959921135050&${focusPoint}&filtering={"conditions":${JSON.stringify(filters)}}`, + `${getAgentaApiUrl()}/api/observability/v1/traces/search?${focusPoint}&filtering={"conditions":${JSON.stringify(filters)}}`, ) return response.data } const fetchAllTraces = async () => { const response = await axios.get( - `${getAgentaApiUrl()}/api/observability/v1/traces/search?project_id=0192c229-3760-759d-a637-959921135050&${focusPoint}`, + `${getAgentaApiUrl()}/api/observability/v1/traces/search?${focusPoint}`, ) return response.data } diff --git a/agenta-web/src/services/observability/core/index.ts b/agenta-web/src/services/observability/core/index.ts index ca92701ace..4234fd8cce 100644 --- a/agenta-web/src/services/observability/core/index.ts +++ b/agenta-web/src/services/observability/core/index.ts @@ -9,9 +9,7 @@ import axios from "@/lib/helpers/axiosConfig" // - update: PUT data to server // - delete: DELETE data from server -export const fetchAllTraces = async (projectId: string) => { - const response = await axios.get( - `${getAgentaApiUrl()}/api/observability/v1/traces/search?project_id=${projectId}`, - ) +export const fetchAllTraces = async () => { + const response = await axios.get(`${getAgentaApiUrl()}/api/observability/v1/traces/search`) return response.data } From 147bf8647b34fc7a8fe57f2676b6903002d6dbee Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Mon, 28 Oct 2024 21:35:57 +0600 Subject: [PATCH 195/305] enhance(frontend): sort data functionalities --- agenta-web/src/components/Filters/Sort.tsx | 78 +++++++++++++++---- .../observability/drawer/TraceContent.tsx | 4 +- .../apps/[app_id]/observability/index.tsx | 36 +++------ 3 files changed, 77 insertions(+), 41 deletions(-) diff --git a/agenta-web/src/components/Filters/Sort.tsx b/agenta-web/src/components/Filters/Sort.tsx index a8ee1e2fa5..09b63b0c93 100644 --- a/agenta-web/src/components/Filters/Sort.tsx +++ b/agenta-web/src/components/Filters/Sort.tsx @@ -2,7 +2,7 @@ import React, {useState} from "react" import {CaretRight, Clock, Calendar} from "@phosphor-icons/react" import {DatePicker, Button, Typography, Divider, Popover} from "antd" import {JSSTheme, SortTypes} from "@/lib/Types" -import {Dayjs} from "dayjs" +import dayjs, {Dayjs} from "dayjs" import type {SelectProps} from "antd" import {createUseStyles} from "react-jss" @@ -42,13 +42,21 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ }, })) +export type SortResult = { + type: "custom" | "standerd" + sorted: string + customRange?: {startTime: string; endTime: string} +} + type Props = { onSortApply: ({ - sortData, - customSortData, + type, + sorted, + customRange, }: { - sortData: SortTypes - customSortData?: CustomTimeRange + type: "custom" | "standerd" + sorted: string + customRange?: {startTime: string; endTime: string} }) => void defaultSortValue: SortTypes } @@ -67,13 +75,62 @@ const Sort: React.FC = ({onSortApply, defaultSortValue}) => { customTime.startTime == null ? false : true, ) + const apply = ({ + sortData, + customRange, + }: { + sortData: SortTypes + customRange?: CustomTimeRange + }) => { + let sortedTime + let customRangeTime + + if (sortData && sortData !== "custom" && sortData !== "all time") { + const now = dayjs().utc() + + // Split the value into number and unit (e.g., "30 minutes" becomes ["30", "minutes"]) + const [amount, unit] = (sortData as SortTypes).split(" ") + sortedTime = now + .subtract(parseInt(amount), unit as dayjs.ManipulateType) + .toISOString() + .split(".")[0] + } else if (customRange?.startTime && sortData == "custom") { + customRangeTime = { + startTime: customRange.startTime.toISOString().split(".")[0], + endTime: customRange.endTime?.toISOString().split(".")[0] as string, + } + } else if (sortData === "all time") { + sortedTime = "1970-01-01T00:00:00" + } + + onSortApply({ + type: sortData == "custom" ? "custom" : "standerd", + sorted: sortedTime as string, + customRange: customRangeTime, + }) + } + const handleApplyCustomRange = () => { if (customTime.startTime && customTime.endTime) { - onSortApply({sortData: sort, customSortData: customTime}) + apply({sortData: "custom", customRange: customTime}) setDropdownVisible(false) } } + const onSelectItem = (item: any) => { + setTimeout(() => { + setCustomOptionSelected(false) + }, 500) + + if (customTime.startTime) { + setCustomTime({startTime: null, endTime: null}) + } + + setDropdownVisible(false) + setSort(item.value as SortTypes) + apply({sortData: item.value as SortTypes}) + } + const options: SelectProps["options"] = [ {value: "30 minutes", label: "30 mins"}, {value: "1 hour", label: "1 hour"}, @@ -110,14 +167,7 @@ const Sort: React.FC = ({onSortApply, defaultSortValue}) => { {options.map((item) => (
{ - setTimeout(() => { - setCustomOptionSelected(false) - }, 500) - setDropdownVisible(false) - setSort(item.value as SortTypes) - onSortApply({sortData: item.value as SortTypes}) - }} + onClick={() => onSelectItem(item)} className={`${classes.popupItems} ${sort === item.value && classes.popupSelectedItem}`} > {item.label} diff --git a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx index daafc29f26..5e383af457 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx @@ -222,10 +222,10 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { - {!activeTrace.parent && activeTrace.refs?.application.id && ( + {!activeTrace.parent && activeTrace.refs?.application_id && (
[]} dataSource={traces} bordered @@ -645,4 +647,8 @@ const ObservabilityDashboard = ({}: Props) => { ) } -export default ObservabilityDashboard +export default () => ( + + + +) From 93abdbd54f4773edd117c081f5d3b07d5372b883 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Wed, 30 Oct 2024 10:48:49 +0600 Subject: [PATCH 204/305] fix(frontend): table header functionalities - renderd filter operators conditionally - removed reapeated code - fix export node data issue --- agenta-web/src/components/Filters/Filters.tsx | 190 ++++++------- agenta-web/src/components/Filters/Sort.tsx | 34 ++- agenta-web/src/lib/Types.ts | 14 - agenta-web/src/lib/hooks/useTraces.ts | 10 +- .../apps/[app_id]/observability/index.tsx | 265 ++++++++---------- .../src/services/observability/core/index.ts | 6 +- 6 files changed, 236 insertions(+), 283 deletions(-) diff --git a/agenta-web/src/components/Filters/Filters.tsx b/agenta-web/src/components/Filters/Filters.tsx index f48313cc5d..391bdf2090 100644 --- a/agenta-web/src/components/Filters/Filters.tsx +++ b/agenta-web/src/components/Filters/Filters.tsx @@ -1,4 +1,4 @@ -import React, {useState} from "react" +import React, {useMemo, useState} from "react" import {Filter, JSSTheme} from "@/lib/Types" import {ArrowCounterClockwise, CaretDown, Funnel, Plus, Trash, X} from "@phosphor-icons/react" import {Button, Divider, Input, Popover, Select, Space, Typography} from "antd" @@ -8,7 +8,7 @@ import {useUpdateEffect} from "usehooks-ts" const useStyles = createUseStyles((theme: JSSTheme) => ({ popover: { "& .ant-popover-inner": { - width: "600px !important", + width: "650px !important", padding: `0px ${theme.paddingXS}px ${theme.paddingXS}px ${theme.padding}px`, }, }, @@ -30,7 +30,7 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ type Props = { filterData?: Filter[] - columns: {value: string; label: string}[] + columns: {value: string; label: string; type?: string}[] onApplyFilter: (filters: Filter[]) => void onClearFilter: (filters: Filter[]) => void } @@ -51,23 +51,28 @@ const Filters: React.FC = ({filterData, columns, onApplyFilter, onClearFi }, [filterData]) const operators = [ - {value: "contains", lable: "contains"}, - {value: "matches", lable: "matches"}, - {value: "like", lable: "like"}, - {value: "startswith", lable: "startswith"}, - {value: "endswith", lable: "endswith"}, - {value: "exists", lable: "exists"}, - {value: "not_exists", lable: "not exists"}, - {value: "eq", lable: "="}, - {value: "neq", lable: "!="}, - {value: "gt", lable: ">"}, - {value: "lt", lable: "<"}, - {value: "gte", lable: ">="}, - {value: "lte", lable: "<="}, + {type: "string", value: "contains", label: "contains"}, + {type: "string", value: "matches", label: "matches"}, + {type: "string", value: "like", label: "like"}, + {type: "string", value: "startswith", label: "startswith"}, + {type: "string", value: "endswith", label: "endswith"}, + {type: "exists", value: "exists", label: "exists"}, + {type: "exists", value: "not_exists", label: "not exists"}, + {type: "exists", value: "in", label: "in"}, + {type: "exists", value: "is", label: "is"}, + {type: "exists", value: "is_not", label: "is not"}, + {type: "number", value: "eq", label: "="}, + {type: "number", value: "neq", label: "!="}, + {type: "number", value: "gt", label: ">"}, + {type: "number", value: "lt", label: "<"}, + {type: "number", value: "gte", label: ">="}, + {type: "number", value: "lte", label: "<="}, + {type: "number", value: "btwn", label: "between"}, ] - const filteredOptions = columns.filter( - (col) => !filter.some((item, i) => item.key === col.value), + const filteredColumns = useMemo( + () => columns.filter((col) => !filter.some((item) => item.key === col.value)), + [columns, filter], ) const onFilterChange = ({ @@ -80,7 +85,7 @@ const Filters: React.FC = ({filterData, columns, onApplyFilter, onClearFi idx: number }) => { const newFilters = [...filter] - newFilters[idx][columnName as keyof Filter] = value + newFilters[idx][columnName] = value setFilter(newFilters) } @@ -99,7 +104,6 @@ const Filters: React.FC = ({filterData, columns, onApplyFilter, onClearFi const applyFilter = () => { const sanitizedFilters = filter.filter(({key, operator}) => key && operator) - onApplyFilter(sanitizedFilters) setIsFilterOpen(false) } @@ -124,81 +128,77 @@ const Filters: React.FC = ({filterData, columns, onApplyFilter, onClearFi
- {filter.map((item, idx) => ( - -

{idx == 0 ? "Where" : "And"}

- - - !label.value ? "Condition" : label.label - } - style={{width: 95}} - suffixIcon={} - onChange={(value) => - onFilterChange({ - columnName: "operator", - value, - idx, - }) - } - popupMatchSelectWidth={100} - value={item.operator} - options={operators.map((operator) => ({ - value: operator.value, - label: operator.lable, - }))} - /> + {filter.map((item, idx) => { + const selectedColumn = columns.find((col) => col.value === item.key) + const filteredOperators = operators.filter( + (operator) => operator.type === selectedColumn?.type, + ) + + return ( + +

+ {idx === 0 ? "Where" : "And"} +

+ + - onFilterChange({ - columnName: "value", - value: e.target.value, - idx, - }) - } + {item.key && ( + <> + + onFilterChange({ + columnName: "value", + value: e.target.value, + idx, + }) + } + /> + + )} + {filter.length > 1 && ( +
- + + + + + + +
+
[]} + dataSource={traces} + bordered + style={{cursor: "pointer"}} + onRow={(record) => ({ + onClick: () => { + setSelected(record.node.id) + if (traceTabs === "node") { + setSelectedTraceId(record.node.id) + } else { + setSelectedTraceId(record.root.id) + } + }, + })} + components={{ + header: { + cell: ResizableTitle, + }, + }} + pagination={false} + scroll={{x: "max-content"}} + locale={{ + emptyText: ( +
+ + } + description="Monitor the performance and results of your LLM applications here." + primaryCta={{ + text: appId ? "Go to Playground" : "Create an Application", + onClick: () => + router.push( + appId ? `/apps/${appId}/playground` : "/apps", + ), + tooltip: + "Run your LLM app in the playground to generate and view insights.", + }} + secondaryCta={{ + text: "Learn More", + onClick: () => + router.push( + "https://docs.agenta.ai/observability/quickstart", + ), + tooltip: + "Explore more about tracking and analyzing your app's observability data.", + }} + /> +
+ ), + }} + /> + + + + {activeTrace && !!traces?.length && ( + setSelectedTraceId("")} + expandable + headerExtra={ + + } + mainContent={selectedItem ? : null} + sideContent={ + + } + /> + )} + + ) +} + +export default () => ( + + + +) diff --git a/agenta-web/src/contexts/observability.context.tsx b/agenta-web/src/contexts/observability.context.tsx index 4e34b4527a..ac923a2a24 100644 --- a/agenta-web/src/contexts/observability.context.tsx +++ b/agenta-web/src/contexts/observability.context.tsx @@ -78,7 +78,7 @@ const ObservabilityContextProvider: React.FC = ({children}) = } useEffect(() => { - fetchTraces("?focus=tree&size=10&page=1") + fetchTraces("focus=tree&size=10&page=1") }, [appId]) observabilityContextValues.traces = traces diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index 921a76f523..a5b2574727 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -1,636 +1,7 @@ -import EmptyComponent from "@/components/EmptyComponent" -import GenericDrawer from "@/components/GenericDrawer" -import {nodeTypeStyles} from "@/components/pages/observability/components/AvatarTreeContent" -import StatusRenderer from "@/components/pages/observability/components/StatusRenderer" -import TraceContent from "@/components/pages/observability/drawer/TraceContent" -import TraceHeader from "@/components/pages/observability/drawer/TraceHeader" -import TraceTree from "@/components/pages/observability/drawer/TraceTree" -import Filters from "@/components/Filters/Filters" -import Sort, {SortResult} from "@/components/Filters/Sort" -import EditColumns from "@/components/Filters/EditColumns" -import ResultTag from "@/components/ResultTag/ResultTag" -import {ResizableTitle} from "@/components/ServerTable/components" -import {useAppId} from "@/hooks/useAppId" -import {useQueryParam} from "@/hooks/useQuery" -import {formatCurrency, formatLatency, formatTokenUsage} from "@/lib/helpers/formatters" -import {getNodeById} from "@/lib/helpers/observability_helpers" -import {Filter, FilterConditions, JSSTheme} from "@/lib/Types" -import {_AgentaRootsResponse} from "@/services/observability/types" -import {SwapOutlined} from "@ant-design/icons" -import { - Button, - Input, - Pagination, - Radio, - RadioChangeEvent, - Space, - Table, - TableColumnType, - Tag, - Typography, -} from "antd" -import {ColumnsType} from "antd/es/table" -import dayjs from "dayjs" -import {useRouter} from "next/router" -import React, {useCallback, useEffect, useMemo, useState} from "react" -import {createUseStyles} from "react-jss" -import {Export} from "@phosphor-icons/react" -import {getAppValues} from "@/contexts/app.context" -import {convertToCsv, downloadCsv} from "@/lib/helpers/fileManipulations" -import {useUpdateEffect} from "usehooks-ts" -import {getStringOrJson} from "@/lib/helpers/utils" -import ObservabilityContextProvider, {useObservabilityData} from "@/contexts/observability.context" +import ObservabilityDashboard from "@/components/pages/observability/ObservabilityDashboard" -const useStyles = createUseStyles((theme: JSSTheme) => ({ - title: { - fontSize: theme.fontSizeHeading4, - fontWeight: theme.fontWeightMedium, - lineHeight: theme.lineHeightHeading4, - }, - pagination: { - "& > .ant-pagination-options": { - order: -1, - marginRight: 8, - }, - }, -})) - -interface Props {} - -type TraceTabTypes = "tree" | "node" | "chat" - -const ObservabilityDashboard = ({}: Props) => { - const {traces, isLoading, count, fetchTraces} = useObservabilityData() - const appId = useAppId() - const router = useRouter() - const classes = useStyles() - const [selectedTraceId, setSelectedTraceId] = useQueryParam("trace", "") - const [searchQuery, setSearchQuery] = useState("") - const [traceTabs, setTraceTabs] = useState("tree") - const [editColumns, setEditColumns] = useState(["span_type"]) - const [filters, setFilters] = useState([]) - const [sort, setSort] = useState({} as SortResult) - const [isFilterColsDropdownOpen, setIsFilterColsDropdownOpen] = useState(false) - const [pagination, setPagination] = useState({current: 1, page: 10}) - const [columns, setColumns] = useState>([ - { - title: "ID", - dataIndex: ["key"], - key: "key", - width: 200, - onHeaderCell: () => ({ - style: {minWidth: 200}, - }), - fixed: "left", - render: (_, record) => { - const {icon: Icon} = nodeTypeStyles[record.node.type ?? "default"] - - return !record.parent ? ( - - ) : ( - -
- -
- {record.node.name} -
- ) - }, - }, - { - title: "Span type", - key: "span_type", - dataIndex: ["node", "type"], - width: 200, - onHeaderCell: () => ({ - style: {minWidth: 200}, - }), - render: (_, record) => { - return
{record.node.type}
- }, - }, - { - title: "Timestamp", - key: "timestamp", - dataIndex: ["time", "start"], - width: 200, - onHeaderCell: () => ({ - style: {minWidth: 200}, - }), - render: (_, record) => { - return
{dayjs(record.time.start).format("HH:mm:ss DD MMM YYYY")}
- }, - }, - { - title: "Inputs", - key: "inputs", - width: 350, - render: (_, record) => { - return ( - - {getStringOrJson(record?.data?.inputs)} - - ) - }, - }, - { - title: "Outputs", - key: "outputs", - width: 350, - render: (_, record) => { - return ( - - {getStringOrJson(record?.data?.outputs)} - - ) - }, - }, - { - title: "Status", - key: "status", - dataIndex: ["status", "code"], - width: 160, - onHeaderCell: () => ({ - style: {minWidth: 160}, - }), - render: (_, record) => StatusRenderer({status: record.status, showMore: true}), - }, - { - title: "Latency", - key: "latency", - dataIndex: ["time", "span"], - width: 80, - onHeaderCell: () => ({ - style: {minWidth: 80}, - }), - render: (_, record) =>
{formatLatency(record?.metrics?.acc?.duration.total)}
, - }, - { - title: "Usage", - key: "usage", - dataIndex: ["metrics", "acc", "tokens", "total"], - width: 80, - onHeaderCell: () => ({ - style: {minWidth: 80}, - }), - render: (_, record) => ( -
{formatTokenUsage(record.metrics?.acc?.tokens?.total)}
- ), - }, - { - title: "Total cost", - key: "total_cost", - dataIndex: ["metrics", "acc", "costs", "total"], - width: 80, - onHeaderCell: () => ({ - style: {minWidth: 80}, - }), - render: (_, record) =>
{formatCurrency(record.metrics?.acc?.costs?.total)}
, - }, - ]) - - const activeTraceIndex = useMemo( - () => - traces?.findIndex((item) => - traceTabs === "node" - ? item.node.id === selectedTraceId - : item.root.id === selectedTraceId, - ), - [selectedTraceId, traces, traceTabs], - ) - - const activeTrace = useMemo(() => traces[activeTraceIndex] ?? null, [activeTraceIndex, traces]) - - const [selected, setSelected] = useState("") - - useEffect(() => { - if (!selected) { - setSelected(activeTrace?.node.id) - } - }, [activeTrace, selected]) - - const selectedItem = useMemo( - () => (traces?.length ? getNodeById(traces, selected) : null), - [selected, traces], - ) - - const handleNextTrace = useCallback(() => { - if (activeTraceIndex !== undefined && activeTraceIndex < traces.length - 1) { - const nextTrace = traces[activeTraceIndex + 1] - if (traceTabs === "node") { - setSelectedTraceId(nextTrace.node.id) - } else { - setSelectedTraceId(nextTrace.root.id) - } - setSelected(nextTrace.node.id) - } - }, [activeTraceIndex, traces, traceTabs, setSelectedTraceId]) - - const handlePrevTrace = useCallback(() => { - if (activeTraceIndex !== undefined && activeTraceIndex > 0) { - const prevTrace = traces[activeTraceIndex - 1] - if (traceTabs === "node") { - setSelectedTraceId(prevTrace.node.id) - } else { - setSelectedTraceId(prevTrace.root.id) - } - setSelected(prevTrace.node.id) - } - }, [activeTraceIndex, traces, traceTabs, setSelectedTraceId]) - - const handleResize = - (key: string) => - (_: any, {size}: {size: {width: number}}) => { - setColumns((cols) => { - return cols.map((col) => ({ - ...col, - width: col.key === key ? size.width : col.width, - })) - }) - } - - const mergedColumns = useMemo(() => { - return columns.map((col) => ({ - ...col, - width: col.width || 200, - hidden: editColumns.includes(col.key as string), - onHeaderCell: (column: TableColumnType<_AgentaRootsResponse>) => ({ - width: column.width, - onResize: handleResize(column.key?.toString()!), - }), - })) - }, [columns, editColumns]) - - const filterColumns = [ - {type: "exists", value: "root.id", label: "root.id"}, - {type: "exists", value: "tree.id", label: "tree.id"}, - {type: "exists", value: "tree.type", label: "tree.type"}, - {type: "exists", value: "node.id", label: "node.id"}, - {type: "exists", value: "node.type", label: "node.type"}, - {type: "exists", value: "node.name", label: "node.name"}, - {type: "exists", value: "parent.id", label: "parent.id"}, - {type: "exists", value: "status.code", label: "status.code"}, - {type: "exists", value: "status.message", label: "status.message"}, - {type: "exists", value: "exception.type", label: "exception.type"}, - {type: "exists", value: "exception.message", label: "exception.message"}, - {type: "exists", value: "exception.stacktrace", label: "exception.stacktrace"}, - {type: "string", value: "data", label: "data"}, - {type: "number", value: "metrics.acc.duration.total", label: "metrics.acc.duration.total"}, - {type: "number", value: "metrics.acc.costs.total", label: "metrics.acc.costs.total"}, - {type: "number", value: "metrics.unit.costs.total", label: "metrics.unit.costs.total"}, - {type: "number", value: "metrics.acc.tokens.prompt", label: "metrics.acc.tokens.prompt"}, - { - type: "number", - value: "metrics.acc.tokens.completion", - label: "metrics.acc.tokens.completion", - }, - {type: "number", value: "metrics.acc.tokens.total", label: "metrics.acc.tokens.total"}, - {type: "number", value: "metrics.unit.tokens.prompt", label: "metrics.unit.tokens.prompt"}, - { - type: "number", - value: "metrics.unit.tokens.completion", - label: "metrics.unit.tokens.completion", - }, - {type: "number", value: "metrics.unit.tokens.total", label: "metrics.unit.tokens.total"}, - {type: "exists", value: "refs.variant.id", label: "refs.variant.id"}, - {type: "exists", value: "refs.variant.slug", label: "refs.variant.slug"}, - {type: "exists", value: "refs.variant.version", label: "refs.variant.version"}, - {type: "exists", value: "refs.environment.id", label: "refs.environment.id"}, - {type: "exists", value: "refs.environment.slug", label: "refs.environment.slug"}, - {type: "exists", value: "refs.environment.version", label: "refs.environment.version"}, - {type: "exists", value: "refs.application.id", label: "refs.application.id"}, - {type: "exists", value: "refs.application.slug", label: "refs.application.slug"}, - {type: "exists", value: "link.type", label: "link.type"}, - {type: "exists", value: "link.node.id", label: "link.node.id"}, - {type: "exists", value: "otel.kind", label: "otel.kind"}, - ] - - const onExport = async () => { - try { - if (traces.length) { - const {currentApp} = getAppValues() - const filename = `${currentApp?.app_name}_observability.csv` - - const convertToStringOrJson = (value: any) => { - return typeof value === "string" ? value : JSON.stringify(value) - } - - // Helper function to create a trace object - const createTraceObject = (trace: any) => ({ - "Trace ID": trace.key, - Timestamp: dayjs(trace.time.start).format("HH:mm:ss DD MMM YYYY"), - Inputs: trace?.data?.inputs?.topic || "N/A", - Outputs: convertToStringOrJson(trace?.data?.outputs) || "N/A", - Status: trace.status.code, - Latency: formatLatency(trace.metrics?.acc?.duration.total), - Usage: formatTokenUsage(trace.metrics?.acc?.tokens?.total || 0), - "Total Cost": formatCurrency(trace.metrics?.acc?.costs?.total || 0), - "Span Type": trace.node.type || "N/A", - "Span ID": trace.node.id, - }) - - const csvData = convertToCsv( - traces.flatMap((trace) => { - const parentTrace = createTraceObject(trace) - return trace.children && Array.isArray(trace.children) - ? [parentTrace, ...trace.children.map(createTraceObject)] - : [parentTrace] - }), - [ - ...columns.map((col) => - col.title === "ID" ? "Trace ID" : (col.title as string), - ), - "Span ID", - "Span Type", - ], - ) - - downloadCsv(csvData, filename) - } - } catch (error) { - console.error("Export error:", error) - } - } - - const handleToggleColumnVisibility = useCallback((key: string) => { - setEditColumns((prev) => - prev.includes(key) ? prev.filter((item) => item !== key) : [...prev, key], - ) - }, []) - - const updateFilter = ({ - key, - operator, - value, - }: { - key: string - operator: FilterConditions - value: string - }) => { - setFilters((prevFilters) => { - const otherFilters = prevFilters.filter((f) => f.key !== key) - return value ? [...otherFilters, {key, operator, value}] : otherFilters - }) - } - - const onPaginationChange = (current: number, pageSize: number) => { - setPagination({current, page: pageSize}) - } - - const onSearchChange = (e: React.ChangeEvent) => { - const query = e.target.value - setSearchQuery(query) - - if (!query) { - setFilters((prevFilters) => prevFilters.filter((f) => f.key !== "data")) - } - } - - const onSearchQueryApply = () => { - if (searchQuery) { - updateFilter({key: "data", operator: "contains", value: searchQuery}) - } - } - - const onSearchClear = () => { - const isSearchFilterExist = filters.some((item) => item.key === "data") - - if (isSearchFilterExist) { - setFilters((prevFilters) => prevFilters.filter((f) => f.key !== "data")) - } - } - // Sync searchQuery with filters state - useUpdateEffect(() => { - const dataFilter = filters.find((f) => f.key === "data") - setSearchQuery(dataFilter ? dataFilter.value : "") - }, [filters]) - - const onApplyFilter = useCallback((newFilters: Filter[]) => { - setFilters(newFilters) - }, []) - - const onClearFilter = useCallback(() => { - setFilters([]) - setSearchQuery("") - if (traceTabs === "chat") { - setTraceTabs("tree") - } - }, []) - - const onTraceTabChange = async (e: RadioChangeEvent) => { - const selectedTab = e.target.value - setTraceTabs(selectedTab) - - if (selectedTab === "chat") { - updateFilter({key: "node.type", operator: "is", value: selectedTab}) - } else { - const isNodeTypeFilterExist = filters.some( - (item) => item.key === "node.type" && item.value === "chat", - ) - - if (isNodeTypeFilterExist) { - setFilters((prevFilters) => prevFilters.filter((f) => f.key !== "node.type")) - } - } - if (pagination.current > 1) { - setPagination({...pagination, current: 1}) - } - } - // Sync traceTabs with filters state - useUpdateEffect(() => { - const nodeTypeFilter = filters.find((f) => f.key === "node.type")?.value - setTraceTabs((prev) => - nodeTypeFilter === "chat" ? "chat" : prev == "chat" ? "tree" : prev, - ) - }, [filters]) - - const onSortApply = useCallback(({type, sorted, customRange}: SortResult) => { - setSort({type, sorted, customRange}) - }, []) - - const fetchFilterdTrace = async () => { - const focusPoint = traceTabs == "tree" || traceTabs == "node" ? `focus=${traceTabs}` : "" - const filterQuery = filters[0]?.operator - ? `&filtering={"conditions":${JSON.stringify(filters)}}` - : "" - const paginationQuery = `&size=${pagination.page}&page=${pagination.current}` - - let sortQuery = "" - if (sort) { - sortQuery = - sort.type === "standard" - ? `&oldest=${sort.sorted}` - : sort.type === "custom" && sort.customRange?.startTime - ? `&oldest=${sort.customRange.startTime}&newest=${sort.customRange.endTime}` - : "" - } - - const data = await fetchTraces(`?${focusPoint}${paginationQuery}${sortQuery}${filterQuery}`) - - return data - } - - useUpdateEffect(() => { - fetchFilterdTrace() - }, [filters, traceTabs, sort, pagination]) - - return ( -
- Observability - -
- - - - - -
- - - Root - LLM - All - - - - - - - -
-
- -
-
[]} - dataSource={traces} - bordered - style={{cursor: "pointer"}} - onRow={(record) => ({ - onClick: () => { - setSelected(record.node.id) - if (traceTabs === "node") { - setSelectedTraceId(record.node.id) - } else { - setSelectedTraceId(record.root.id) - } - }, - })} - components={{ - header: { - cell: ResizableTitle, - }, - }} - pagination={false} - scroll={{x: "max-content"}} - locale={{ - emptyText: ( -
- - } - description="Monitor the performance and results of your LLM applications here." - primaryCta={{ - text: appId ? "Go to Playground" : "Create an Application", - onClick: () => - router.push( - appId ? `/apps/${appId}/playground` : "/apps", - ), - tooltip: - "Run your LLM app in the playground to generate and view insights.", - }} - secondaryCta={{ - text: "Learn More", - onClick: () => - router.push( - "https://docs.agenta.ai/observability/quickstart", - ), - tooltip: - "Explore more about tracking and analyzing your app's observability data.", - }} - /> -
- ), - }} - /> - - - - {activeTrace && !!traces?.length && ( - setSelectedTraceId("")} - expandable - headerExtra={ - - } - mainContent={selectedItem ? : null} - sideContent={ - - } - /> - )} - - ) +const Observability = () => { + return } -export default () => ( - - - -) +export default Observability diff --git a/agenta-web/src/pages/apps/observability/index.tsx b/agenta-web/src/pages/apps/observability/index.tsx deleted file mode 100644 index 570d76d051..0000000000 --- a/agenta-web/src/pages/apps/observability/index.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import React from "react" -import ObservabilityDashboard from "../[app_id]/observability" - -const index = () => { - return -} - -export default index diff --git a/agenta-web/src/pages/observability/index.tsx b/agenta-web/src/pages/observability/index.tsx new file mode 100644 index 0000000000..977a415277 --- /dev/null +++ b/agenta-web/src/pages/observability/index.tsx @@ -0,0 +1,12 @@ +import ProtectedRoute from "@/components/ProtectedRoute/ProtectedRoute" +import ObservabilityDashboard from "@/components/pages/observability/ObservabilityDashboard" + +const GlobalObservability = () => { + return +} + +export default () => ( + + + +) diff --git a/agenta-web/src/services/observability/core/index.ts b/agenta-web/src/services/observability/core/index.ts index abc5b46ee2..41d3146e8f 100644 --- a/agenta-web/src/services/observability/core/index.ts +++ b/agenta-web/src/services/observability/core/index.ts @@ -11,7 +11,7 @@ import axios from "@/lib/helpers/axiosConfig" export const fetchAllTraces = async ({appId, queries}: {appId: string; queries?: string}) => { const filterByAppId = appId - ? `filtering={"conditions":[{"key":"refs.application_id","operator":"is","value":"${appId}"}]}&` + ? `filtering={"conditions":[{"key":"refs.application.id","operator":"is","value":"${appId}"}]}&` : "" const response = await axios.get( From 1c7c242968245997a02f5d4cbba0fe885c069b20 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Thu, 31 Oct 2024 16:21:54 +0600 Subject: [PATCH 224/305] fix(frontend): observability status render issue --- .../observability/components/StatusRenderer.tsx | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/agenta-web/src/components/pages/observability/components/StatusRenderer.tsx b/agenta-web/src/components/pages/observability/components/StatusRenderer.tsx index c4b0038a85..6303e03e6a 100644 --- a/agenta-web/src/components/pages/observability/components/StatusRenderer.tsx +++ b/agenta-web/src/components/pages/observability/components/StatusRenderer.tsx @@ -1,21 +1,10 @@ import {_AgentaRootsResponse, NodeStatusCode, NodeStatusDTO} from "@/services/observability/types" -import { - CheckCircleOutlined, - ClockCircleOutlined, - CloseCircleOutlined, - InfoCircleOutlined, -} from "@ant-design/icons" +import {CheckCircleOutlined, CloseCircleOutlined, InfoCircleOutlined} from "@ant-design/icons" import {Space, Tag, Tooltip} from "antd" import React from "react" export const statusMapper = (status: NodeStatusCode) => { switch (status) { - case NodeStatusCode.UNSET: - return { - label: "initiated", - color: "blue", - icon: , - } case NodeStatusCode.ERROR: return { label: "failed", From c5b3a17604ef25e248f696d7d8fcd7d17275494f Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Thu, 31 Oct 2024 12:21:36 +0100 Subject: [PATCH 225/305] fix(frontend): enabled delete trace feature --- .../components/DeleteTraceModal.tsx | 29 +++++++++++++++++-- .../observability/drawer/TraceHeader.tsx | 2 +- .../src/services/observability/core/index.ts | 4 +++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/agenta-web/src/components/pages/observability/components/DeleteTraceModal.tsx b/agenta-web/src/components/pages/observability/components/DeleteTraceModal.tsx index e3ed5dfddc..284bc1301a 100644 --- a/agenta-web/src/components/pages/observability/components/DeleteTraceModal.tsx +++ b/agenta-web/src/components/pages/observability/components/DeleteTraceModal.tsx @@ -1,20 +1,43 @@ +import {useObservabilityData} from "@/contexts/observability.context" +import {deleteTrace} from "@/services/observability/core" import {DeleteOutlined} from "@ant-design/icons" import {Modal} from "antd" -import React from "react" +import React, {useState} from "react" type DeleteTraceModalProps = { setSelectedTraceId: (val: string) => void + activeTraceNodeId: string } & React.ComponentProps -const DeleteTraceModal = ({setSelectedTraceId, ...props}: DeleteTraceModalProps) => { +const DeleteTraceModal = ({ + setSelectedTraceId, + activeTraceNodeId, + ...props +}: DeleteTraceModalProps) => { + const {fetchTraces} = useObservabilityData() + const [isLoading, setIsLoading] = useState(false) + + const handleDelete = async () => { + try { + setIsLoading(true) + await deleteTrace(activeTraceNodeId) + fetchTraces() + setSelectedTraceId("") + } catch (error) { + console.error(error) + } finally { + setIsLoading(false) + } + } return ( , danger: true}} + okButtonProps={{icon: , danger: true, loading: isLoading}} okText={"Delete"} + onOk={handleDelete} {...props} > This action is not reversible. diff --git a/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx b/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx index 9821459ade..95dd7be690 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceHeader.tsx @@ -64,7 +64,7 @@ const TraceHeader = ({ setIsDeleteModalOpen(false)} - onOk={() => setSelectedTraceId("")} + activeTraceNodeId={activeTrace.node.id} setSelectedTraceId={setSelectedTraceId} /> diff --git a/agenta-web/src/services/observability/core/index.ts b/agenta-web/src/services/observability/core/index.ts index 06c4e340ed..a867c9af2a 100644 --- a/agenta-web/src/services/observability/core/index.ts +++ b/agenta-web/src/services/observability/core/index.ts @@ -17,3 +17,7 @@ export const fetchAllTraces = async ({appId, queries}: {appId: string; queries?: ) return response.data } + +export const deleteTrace = async (nodeId: string) => { + return axios.delete(`${getAgentaApiUrl()}/api/observability/v1/traces?node_id=${nodeId}`) +} From d53e893b2b812a233dddeb999bd69574410bad02 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Thu, 31 Oct 2024 17:30:58 +0600 Subject: [PATCH 226/305] fix(frotnend): observability LLM traceTab issue --- agenta-web/src/pages/apps/[app_id]/observability/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index 8259fbd9e8..aa3d13cd15 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -455,7 +455,7 @@ const ObservabilityDashboard = ({}: Props) => { }, []) const fetchFilterdTrace = async () => { - const focusPoint = traceTabs == "tree" || traceTabs == "node" ? `focus=${traceTabs}` : "" + const focusPoint = traceTabs == "chat" ? "focus=node" : `focus=${traceTabs}` const filterQuery = filters[0]?.operator ? `&filtering={"conditions":${JSON.stringify(filters)}}` : "" From 35ba2e00fce56946a867bef30902a634dfdfa5ad Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Thu, 31 Oct 2024 18:04:28 +0600 Subject: [PATCH 227/305] fix(frontend): rearranged observability table columns --- .../apps/[app_id]/observability/index.tsx | 80 ++++++++++++------- 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index 8259fbd9e8..05e4fe678e 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -67,7 +67,7 @@ const ObservabilityDashboard = ({}: Props) => { const [selectedTraceId, setSelectedTraceId] = useQueryParam("trace", "") const [searchQuery, setSearchQuery] = useState("") const [traceTabs, setTraceTabs] = useState("tree") - const [editColumns, setEditColumns] = useState(["span_type"]) + const [editColumns, setEditColumns] = useState(["span_type", "key", "usage"]) const [filters, setFilters] = useState([]) const [sort, setSort] = useState({} as SortResult) const [isFilterColsDropdownOpen, setIsFilterColsDropdownOpen] = useState(false) @@ -98,27 +98,39 @@ const ObservabilityDashboard = ({}: Props) => { }, }, { - title: "Span type", - key: "span_type", - dataIndex: ["node", "type"], - width: 200, + title: "Name", + dataIndex: ["node", "name"], + key: "name", + width: 180, onHeaderCell: () => ({ - style: {minWidth: 200}, + style: {minWidth: 180}, }), + fixed: "left", render: (_, record) => { - return
{record.node.type}
+ const {icon: Icon} = nodeTypeStyles[record.node.type ?? "default"] + + return !record.parent ? ( + + ) : ( + +
+ +
+ {record.node.name} +
+ ) }, }, { - title: "Timestamp", - key: "timestamp", - dataIndex: ["time", "start"], + title: "Span type", + key: "span_type", + dataIndex: ["node", "type"], width: 200, onHeaderCell: () => ({ style: {minWidth: 200}, }), render: (_, record) => { - return
{dayjs(record.time.start).format("HH:mm:ss DD MMM YYYY")}
+ return
{record.node.type}
}, }, { @@ -151,16 +163,6 @@ const ObservabilityDashboard = ({}: Props) => { ) }, }, - { - title: "Status", - key: "status", - dataIndex: ["status", "code"], - width: 160, - onHeaderCell: () => ({ - style: {minWidth: 160}, - }), - render: (_, record) => StatusRenderer({status: record.status, showMore: true}), - }, { title: "Latency", key: "latency", @@ -171,6 +173,16 @@ const ObservabilityDashboard = ({}: Props) => { }), render: (_, record) =>
{formatLatency(record?.metrics?.acc?.duration.total)}
, }, + { + title: "Cost", + key: "cost", + dataIndex: ["metrics", "acc", "costs", "total"], + width: 80, + onHeaderCell: () => ({ + style: {minWidth: 80}, + }), + render: (_, record) =>
{formatCurrency(record.metrics?.acc?.costs?.total)}
, + }, { title: "Usage", key: "usage", @@ -184,17 +196,28 @@ const ObservabilityDashboard = ({}: Props) => { ), }, { - title: "Total cost", - key: "total_cost", - dataIndex: ["metrics", "acc", "costs", "total"], - width: 80, + title: "Start time", + key: "start_time", + dataIndex: ["time", "start"], + width: 200, onHeaderCell: () => ({ - style: {minWidth: 80}, + style: {minWidth: 200}, }), - render: (_, record) =>
{formatCurrency(record.metrics?.acc?.costs?.total)}
, + render: (_, record) => { + return
{dayjs(record.time.start).format("HH:mm:ss DD MMM YYYY")}
+ }, + }, + { + title: "Status", + key: "status", + dataIndex: ["status", "code"], + width: 160, + onHeaderCell: () => ({ + style: {minWidth: 160}, + }), + render: (_, record) => StatusRenderer({status: record.status, showMore: true}), }, ]) - const activeTraceIndex = useMemo( () => traces?.findIndex((item) => @@ -324,6 +347,7 @@ const ObservabilityDashboard = ({}: Props) => { // Helper function to create a trace object const createTraceObject = (trace: any) => ({ "Trace ID": trace.key, + Name: trace.node.name, Timestamp: dayjs(trace.time.start).format("HH:mm:ss DD MMM YYYY"), Inputs: trace?.data?.inputs?.topic || "N/A", Outputs: convertToStringOrJson(trace?.data?.outputs) || "N/A", From a70953a4824a0b1ef582308c09de04915c7968da Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Thu, 31 Oct 2024 14:44:42 +0100 Subject: [PATCH 228/305] fix(frontend): added tooltip to display input and output values on hover --- .../apps/[app_id]/observability/index.tsx | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index 8259fbd9e8..e8c9689e93 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -27,6 +27,7 @@ import { Table, TableColumnType, Tag, + Tooltip, Typography, } from "antd" import {ColumnsType} from "antd/es/table" @@ -124,30 +125,34 @@ const ObservabilityDashboard = ({}: Props) => { { title: "Inputs", key: "inputs", - width: 350, + width: 400, render: (_, record) => { return ( - - {getStringOrJson(record?.data?.inputs)} - + {getStringOrJson(record?.data?.inputs)} + ) }, }, { title: "Outputs", key: "outputs", - width: 350, + width: 400, render: (_, record) => { return ( - - {getStringOrJson(record?.data?.outputs)} - + {getStringOrJson(record?.data?.outputs)} + ) }, }, From d6d3da977bc0ff671614696297db10da2e051117 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Thu, 31 Oct 2024 15:48:53 +0100 Subject: [PATCH 229/305] fix(frontend): properly displayed traces with single span in traceTree --- .../src/components/pages/observability/drawer/TraceTree.tsx | 6 ++++-- agenta-web/src/pages/apps/[app_id]/observability/index.tsx | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx index b6cd9ef269..2793589b65 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx @@ -6,11 +6,13 @@ import {Space, Tree, Typography} from "antd" import React, {useEffect, useState} from "react" import {createUseStyles} from "react-jss" import AvatarTreeContent from "../components/AvatarTreeContent" +import {TraceTabTypes} from "@/pages/apps/[app_id]/observability" interface TraceTreeProps { activeTrace: _AgentaRootsResponse selected: string setSelected: React.Dispatch> + traceTabs: TraceTabTypes } interface NodeTreeChildren { @@ -118,7 +120,7 @@ const buildTreeData = (spans: _AgentaRootsResponse[]): NodeTreeChildren[] => { })) } -const TraceTree = ({activeTrace, selected, setSelected}: TraceTreeProps) => { +const TraceTree = ({activeTrace, selected, setSelected, traceTabs}: TraceTreeProps) => { const classes = useStyles() const [expandedKeys, setExpandedKeys] = useState([]) @@ -138,7 +140,7 @@ const TraceTree = ({activeTrace, selected, setSelected}: TraceTreeProps) => { return ( ({ interface Props {} -type TraceTabTypes = "tree" | "node" | "chat" +export type TraceTabTypes = "tree" | "node" | "chat" const ObservabilityDashboard = ({}: Props) => { const {traces, isLoading, count, fetchTraces} = useObservabilityData() @@ -618,6 +618,7 @@ const ObservabilityDashboard = ({}: Props) => { activeTrace={activeTrace} selected={selected} setSelected={setSelected} + traceTabs={traceTabs} /> } /> From 18dda4849fe6e057e84c395e3515521a9c5d2af7 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Thu, 31 Oct 2024 16:17:09 +0100 Subject: [PATCH 230/305] bug fix(frontend) --- .../src/components/pages/observability/drawer/TraceTree.tsx | 6 ++---- agenta-web/src/pages/apps/[app_id]/observability/index.tsx | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx index 2793589b65..3bba8ab428 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx @@ -6,13 +6,11 @@ import {Space, Tree, Typography} from "antd" import React, {useEffect, useState} from "react" import {createUseStyles} from "react-jss" import AvatarTreeContent from "../components/AvatarTreeContent" -import {TraceTabTypes} from "@/pages/apps/[app_id]/observability" interface TraceTreeProps { activeTrace: _AgentaRootsResponse selected: string setSelected: React.Dispatch> - traceTabs: TraceTabTypes } interface NodeTreeChildren { @@ -120,7 +118,7 @@ const buildTreeData = (spans: _AgentaRootsResponse[]): NodeTreeChildren[] => { })) } -const TraceTree = ({activeTrace, selected, setSelected, traceTabs}: TraceTreeProps) => { +const TraceTree = ({activeTrace, selected, setSelected}: TraceTreeProps) => { const classes = useStyles() const [expandedKeys, setExpandedKeys] = useState([]) @@ -140,7 +138,7 @@ const TraceTree = ({activeTrace, selected, setSelected, traceTabs}: TraceTreePro return ( { activeTrace={activeTrace} selected={selected} setSelected={setSelected} - traceTabs={traceTabs} /> } /> From 57f59827e0e66dd88da4ba4ff4aafb5a771ba82b Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Thu, 31 Oct 2024 22:02:25 +0600 Subject: [PATCH 231/305] fix(frontend): resolved the changes request --- .../apps/[app_id]/observability/index.tsx | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index 05e4fe678e..76a20d61b7 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -75,7 +75,7 @@ const ObservabilityDashboard = ({}: Props) => { const [columns, setColumns] = useState>([ { title: "ID", - dataIndex: ["key"], + dataIndex: ["node", "id"], key: "key", width: 200, onHeaderCell: () => ({ @@ -83,18 +83,7 @@ const ObservabilityDashboard = ({}: Props) => { }), fixed: "left", render: (_, record) => { - const {icon: Icon} = nodeTypeStyles[record.node.type ?? "default"] - - return !record.parent ? ( - - ) : ( - -
- -
- {record.node.name} -
- ) + return }, }, { @@ -109,9 +98,7 @@ const ObservabilityDashboard = ({}: Props) => { render: (_, record) => { const {icon: Icon} = nodeTypeStyles[record.node.type ?? "default"] - return !record.parent ? ( - - ) : ( + return (
From 6483c000e83226f6f564e333b407d0c702b6097f Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Thu, 31 Oct 2024 22:29:03 +0600 Subject: [PATCH 232/305] fix(frontend): namee column align issue --- agenta-web/src/pages/apps/[app_id]/observability/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index 76a20d61b7..a80507bd45 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -90,9 +90,9 @@ const ObservabilityDashboard = ({}: Props) => { title: "Name", dataIndex: ["node", "name"], key: "name", - width: 180, + width: 200, onHeaderCell: () => ({ - style: {minWidth: 180}, + style: {minWidth: 200}, }), fixed: "left", render: (_, record) => { From 98a7dc32147282bf726e340d12d26d3690cf1651 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 31 Oct 2024 20:33:05 +0100 Subject: [PATCH 233/305] remove latency on export --- agenta-cli/agenta/sdk/tracing/processors.py | 30 +++++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/agenta-cli/agenta/sdk/tracing/processors.py b/agenta-cli/agenta/sdk/tracing/processors.py index 7040209d84..513359fe90 100644 --- a/agenta-cli/agenta/sdk/tracing/processors.py +++ b/agenta-cli/agenta/sdk/tracing/processors.py @@ -1,4 +1,4 @@ -from typing import Optional, Dict +from typing import Optional, Dict, List from opentelemetry.context import Context from opentelemetry.sdk.trace import Span @@ -7,6 +7,7 @@ ReadableSpan, BatchSpanProcessor, _DEFAULT_MAX_QUEUE_SIZE, + _DEFAULT_MAX_EXPORT_BATCH_SIZE, ) from agenta.sdk.utils.logging import log @@ -27,14 +28,15 @@ def __init__( super().__init__( span_exporter, _DEFAULT_MAX_QUEUE_SIZE, - 60 * 60 * 1000, # 1 hour - _DEFAULT_MAX_QUEUE_SIZE, + 12 * 60 * 60 * 1000, # 12 hours + _DEFAULT_MAX_EXPORT_BATCH_SIZE, 500, # < 1 second (0.5 seconds) ) self._registry = dict() self._exporter = span_exporter self.references = references or dict() + self.spans: Dict[int, List[ReadableSpan]] = dict() def on_start( self, @@ -48,6 +50,7 @@ def on_start( if span.context.trace_id not in self._registry: self._registry[span.context.trace_id] = dict() + self.spans[span.context.trace_id] = list() self._registry[span.context.trace_id][span.context.span_id] = True @@ -57,10 +60,27 @@ def on_end( ): super().on_end(span) + self.spans[span.context.trace_id].append(span) del self._registry[span.context.trace_id][span.context.span_id] if self.is_ready(span.get_span_context().trace_id): - self.force_flush() + self.export(span.context.trace_id) + + # self.force_flush() + + def export( + self, + trace_id: int, + ): + spans = self.spans[trace_id] + + for span in spans: + self.queue.appendleft(span) + + with self.condition: + self.condition.notify() + + del self.spans[trace_id] def force_flush( self, @@ -77,7 +97,7 @@ def is_ready( self, trace_id: Optional[int] = None, ) -> bool: - is_ready = not len(self._registry.get(trace_id, {})) + is_ready = len(self._registry.get(trace_id, {})) != 0 return is_ready From 755826a084b782e5e24d590cef59a5d11ce8edc0 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Fri, 1 Nov 2024 14:51:27 +0600 Subject: [PATCH 234/305] fix(frontend): renamed app observability into traces --- agenta-web/src/components/Sidebar/config.tsx | 14 +++++------ .../observability/ObservabilityDashboard.tsx | 25 +++++++++++-------- .../{observability => traces}/index.tsx | 4 +-- 3 files changed, 24 insertions(+), 19 deletions(-) rename agenta-web/src/pages/apps/[app_id]/{observability => traces}/index.tsx (69%) diff --git a/agenta-web/src/components/Sidebar/config.tsx b/agenta-web/src/components/Sidebar/config.tsx index de4794a42d..eaea606695 100644 --- a/agenta-web/src/components/Sidebar/config.tsx +++ b/agenta-web/src/components/Sidebar/config.tsx @@ -16,6 +16,7 @@ import { SlackLogo, Gear, Dot, + TreeView, } from "@phosphor-icons/react" import {useAppsData} from "@/contexts/app.context" @@ -70,8 +71,8 @@ export const useSidebarConfig = () => { isHidden: apps.length === 0, }, { - key: "global-observability-link", - title: "Global Observability", + key: "app-observability-link", + title: "Observability", link: `/observability`, icon: , divider: true, @@ -107,12 +108,11 @@ export const useSidebarConfig = () => { icon: , }, { - key: "app-observability-link", - title: "Observability", - icon: , + key: "app-traces-link", + title: "Traces", + icon: , isHidden: !appId && !recentlyVisitedAppId, - link: `/apps/${appId || recentlyVisitedAppId}/observability`, - cloudFeatureTooltip: "Observability available in Cloud/Enterprise editions only", + link: `/apps/${appId || recentlyVisitedAppId}/traces`, }, { key: "invite-teammate-link", diff --git a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx index 297a5f9424..8ce64f8155 100644 --- a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx +++ b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx @@ -27,6 +27,7 @@ import { Table, TableColumnType, Tag, + Tooltip, Typography, } from "antd" import {ColumnsType} from "antd/es/table" @@ -122,30 +123,34 @@ const ObservabilityDashboard = () => { { title: "Inputs", key: "inputs", - width: 350, + width: 400, render: (_, record) => { return ( - - {getStringOrJson(record?.data?.inputs)} - + {getStringOrJson(record?.data?.inputs)} + ) }, }, { title: "Outputs", key: "outputs", - width: 350, + width: 400, render: (_, record) => { return ( - - {getStringOrJson(record?.data?.outputs)} - + {getStringOrJson(record?.data?.outputs)} + ) }, }, @@ -453,7 +458,7 @@ const ObservabilityDashboard = () => { }, []) const fetchFilterdTrace = async () => { - const focusPoint = traceTabs == "tree" || traceTabs == "node" ? `focus=${traceTabs}` : "" + const focusPoint = traceTabs == "chat" ? "focus=node" : `focus=${traceTabs}` const filterQuery = filters[0]?.operator ? `&filtering={"conditions":${JSON.stringify(filters)}}` : "" @@ -469,7 +474,7 @@ const ObservabilityDashboard = () => { : "" } - const data = await fetchTraces(`${focusPoint}${paginationQuery}${sortQuery}${filterQuery}`) + const data = await fetchTraces(`&${focusPoint}${paginationQuery}${sortQuery}${filterQuery}`) return data } diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/traces/index.tsx similarity index 69% rename from agenta-web/src/pages/apps/[app_id]/observability/index.tsx rename to agenta-web/src/pages/apps/[app_id]/traces/index.tsx index a5b2574727..9865379fcf 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/traces/index.tsx @@ -1,7 +1,7 @@ import ObservabilityDashboard from "@/components/pages/observability/ObservabilityDashboard" -const Observability = () => { +const Traces = () => { return } -export default Observability +export default Traces From f574ea3b94a7262ad4ec06f41e963584f0d59611 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Fri, 1 Nov 2024 15:57:22 +0600 Subject: [PATCH 235/305] fix(frontend): endpoint query --- .../components/pages/observability/ObservabilityDashboard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx index 8ce64f8155..d8582890f9 100644 --- a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx +++ b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx @@ -474,7 +474,7 @@ const ObservabilityDashboard = () => { : "" } - const data = await fetchTraces(`&${focusPoint}${paginationQuery}${sortQuery}${filterQuery}`) + const data = await fetchTraces(`${focusPoint}${paginationQuery}${sortQuery}${filterQuery}`) return data } From 8fca4274ceaa6a4f6d55e2d1070cada043fa6401 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Fri, 1 Nov 2024 17:57:44 +0600 Subject: [PATCH 236/305] fix(frontend): moved all the query states in the context-api --- .../src/contexts/observability.context.tsx | 98 +++++++++++++++++-- .../apps/[app_id]/observability/index.tsx | 64 +++++------- .../src/services/observability/core/index.ts | 8 +- 3 files changed, 118 insertions(+), 52 deletions(-) diff --git a/agenta-web/src/contexts/observability.context.tsx b/agenta-web/src/contexts/observability.context.tsx index a01a5ff8b3..3f89e2465f 100644 --- a/agenta-web/src/contexts/observability.context.tsx +++ b/agenta-web/src/contexts/observability.context.tsx @@ -8,19 +8,45 @@ import { } from "@/services/observability/types" import React, {createContext, PropsWithChildren, useContext, useEffect, useState} from "react" import {useRouter} from "next/router" +import {SortResult} from "@/components/Filters/Sort" +import {Filter} from "@/lib/Types" type ObservabilityContextType = { traces: _AgentaRootsResponse[] count: number isLoading: boolean - fetchTraces: (queries?: string) => void + fetchTraces: () => void + clearQueryStates: () => void + searchQuery: string + setSearchQuery: React.Dispatch> + traceTabs: TraceTabTypes + setTraceTabs: React.Dispatch> + filters: Filter[] + setFilters: React.Dispatch> + sort: SortResult + setSort: React.Dispatch> + pagination: {page: number; size: number} + setPagination: React.Dispatch> } +type TraceTabTypes = "tree" | "node" | "chat" + const initialValues: ObservabilityContextType = { traces: [], count: 0, isLoading: false, fetchTraces: () => {}, + clearQueryStates: () => {}, + searchQuery: "", + setSearchQuery: () => {}, + traceTabs: "tree", + setTraceTabs: () => {}, + filters: [], + setFilters: () => {}, + sort: {type: "standard", sorted: ""}, + setSort: () => {}, + pagination: {page: 1, size: 10}, + setPagination: () => {}, } export const ObservabilityContext = createContext(initialValues) @@ -37,11 +63,20 @@ const ObservabilityContextProvider: React.FC = ({children}) = const [traces, setTraces] = useState<_AgentaRootsResponse[]>([]) const [traceCount, setTraceCount] = useState(0) const [isLoading, setIsLoading] = useState(true) - - const fetchTraces = async (queries?: string) => { + // query states + const [searchQuery, setSearchQuery] = useState("") + const [traceTabs, setTraceTabs] = useState("tree") + const [filters, setFilters] = useState([]) + const [sort, setSort] = useState({} as SortResult) + const [pagination, setPagination] = useState({page: 1, size: 10}) + + const fetchTraces = async () => { try { setIsLoading(true) - const data = await fetchAllTraces({appId, queries: queries || ""}) + + const queries = generateTraceQueryString() + + const data = await fetchAllTraces(queries || "") const transformedTraces: _AgentaRootsResponse[] = [] @@ -77,11 +112,51 @@ const ObservabilityContextProvider: React.FC = ({children}) = } } + const generateTraceQueryString = () => { + const queryParts: string[] = [] + + if (appId) { + queryParts.push( + `filtering={"conditions":[{"key":"refs.application.id","operator":"is","value":"${appId}"}]}`, + ) + } + + const focusPoint = traceTabs === "chat" ? "focus=node" : `focus=${traceTabs}` + queryParts.push(focusPoint) + + queryParts.push(`size=${pagination.size}`, `page=${pagination.page}`) + + if (filters[0]?.operator) { + queryParts.push(`filtering={"conditions":${JSON.stringify(filters)}}`) + } + + if (sort) { + if (sort.type === "standard") { + queryParts.push(`oldest=${sort.sorted}`) + } else if (sort.type === "custom" && sort.customRange?.startTime) { + queryParts.push( + `oldest=${sort.customRange.startTime}`, + `newest=${sort.customRange.endTime}`, + ) + } + } + + return `?${queryParts.join("&")}` + } + + const clearQueryStates = () => { + setSearchQuery("") + setTraceTabs("tree") + setFilters([]) + setSort({} as SortResult) + setPagination({page: 1, size: 10}) + } + useEffect(() => { if (appId) { - fetchTraces("&focus=tree&size=10&page=1") + fetchTraces() } - }, [appId]) + }, [appId, filters, traceTabs, sort, pagination]) observabilityContextValues.traces = traces observabilityContextValues.isLoading = isLoading @@ -95,6 +170,17 @@ const ObservabilityContextProvider: React.FC = ({children}) = isLoading, fetchTraces, count: traceCount || 0, + clearQueryStates, + searchQuery, + setSearchQuery, + traceTabs, + setTraceTabs, + filters, + setFilters, + sort, + setSort, + pagination, + setPagination, }} > {children} diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index a01c954b4a..c430276e1c 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -58,21 +58,28 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ interface Props {} -type TraceTabTypes = "tree" | "node" | "chat" - const ObservabilityDashboard = ({}: Props) => { - const {traces, isLoading, count, fetchTraces} = useObservabilityData() + const { + traces, + isLoading, + count, + searchQuery, + setSearchQuery, + traceTabs, + setTraceTabs, + filters, + setFilters, + sort, + setSort, + pagination, + setPagination, + } = useObservabilityData() const appId = useAppId() const router = useRouter() const classes = useStyles() const [selectedTraceId, setSelectedTraceId] = useQueryParam("trace", "") - const [searchQuery, setSearchQuery] = useState("") - const [traceTabs, setTraceTabs] = useState("tree") const [editColumns, setEditColumns] = useState(["span_type"]) - const [filters, setFilters] = useState([]) - const [sort, setSort] = useState({} as SortResult) const [isFilterColsDropdownOpen, setIsFilterColsDropdownOpen] = useState(false) - const [pagination, setPagination] = useState({current: 1, page: 10}) const [columns, setColumns] = useState>([ { title: "ID", @@ -385,8 +392,14 @@ const ObservabilityDashboard = ({}: Props) => { } const onPaginationChange = (current: number, pageSize: number) => { - setPagination({current, page: pageSize}) + setPagination({size: pageSize, page: current}) } + // reset pagination to page 1 whenever quearies get updated + useUpdateEffect(() => { + if (pagination.page > 1) { + setPagination({...pagination, page: 1}) + } + }, [filters, sort, traceTabs]) const onSearchChange = (e: React.ChangeEvent) => { const query = e.target.value @@ -443,9 +456,6 @@ const ObservabilityDashboard = ({}: Props) => { setFilters((prevFilters) => prevFilters.filter((f) => f.key !== "node.type")) } } - if (pagination.current > 1) { - setPagination({...pagination, current: 1}) - } } // Sync traceTabs with filters state useUpdateEffect(() => { @@ -459,32 +469,6 @@ const ObservabilityDashboard = ({}: Props) => { setSort({type, sorted, customRange}) }, []) - const fetchFilterdTrace = async () => { - const focusPoint = traceTabs == "chat" ? "focus=node" : `focus=${traceTabs}` - const filterQuery = filters[0]?.operator - ? `&filtering={"conditions":${JSON.stringify(filters)}}` - : "" - const paginationQuery = `&size=${pagination.page}&page=${pagination.current}` - - let sortQuery = "" - if (sort) { - sortQuery = - sort.type === "standard" - ? `&oldest=${sort.sorted}` - : sort.type === "custom" && sort.customRange?.startTime - ? `&oldest=${sort.customRange.startTime}&newest=${sort.customRange.endTime}` - : "" - } - - const data = await fetchTraces(`&${focusPoint}${paginationQuery}${sortQuery}${filterQuery}`) - - return data - } - - useUpdateEffect(() => { - fetchFilterdTrace() - }, [filters, traceTabs, sort, pagination]) - return (
Observability @@ -596,8 +580,8 @@ const ObservabilityDashboard = ({}: Props) => { total={count} align="end" className={classes.pagination} - current={pagination.current} - pageSize={pagination.page} + current={pagination.page} + pageSize={pagination.size} onChange={onPaginationChange} />
diff --git a/agenta-web/src/services/observability/core/index.ts b/agenta-web/src/services/observability/core/index.ts index a867c9af2a..5ba3472479 100644 --- a/agenta-web/src/services/observability/core/index.ts +++ b/agenta-web/src/services/observability/core/index.ts @@ -9,12 +9,8 @@ import axios from "@/lib/helpers/axiosConfig" // - update: PUT data to server // - delete: DELETE data from server -export const fetchAllTraces = async ({appId, queries}: {appId: string; queries?: string}) => { - const filterByAppId = `filtering={"conditions":[{"key":"refs.application.id","operator":"is","value":"${appId}"}]}` - - const response = await axios.get( - `${getAgentaApiUrl()}/api/observability/v1/traces?${filterByAppId}${queries}`, - ) +export const fetchAllTraces = async (queries?: string) => { + const response = await axios.get(`${getAgentaApiUrl()}/api/observability/v1/traces${queries}`) return response.data } From 8684e4044598f26a1bae99cd089e2fe986a50af6 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 1 Nov 2024 13:53:36 +0100 Subject: [PATCH 237/305] fix the blocking delay fix --- agenta-cli/agenta/sdk/tracing/processors.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/agenta-cli/agenta/sdk/tracing/processors.py b/agenta-cli/agenta/sdk/tracing/processors.py index 513359fe90..d6adda540f 100644 --- a/agenta-cli/agenta/sdk/tracing/processors.py +++ b/agenta-cli/agenta/sdk/tracing/processors.py @@ -50,7 +50,6 @@ def on_start( if span.context.trace_id not in self._registry: self._registry[span.context.trace_id] = dict() - self.spans[span.context.trace_id] = list() self._registry[span.context.trace_id][span.context.span_id] = True @@ -60,7 +59,11 @@ def on_end( ): super().on_end(span) + if span.context.trace_id not in self.spans: + self.spans[span.context.trace_id] = list() + self.spans[span.context.trace_id].append(span) + del self._registry[span.context.trace_id][span.context.span_id] if self.is_ready(span.get_span_context().trace_id): From be7125dc993cfb81dc9aea7d700831cb4c0a895f Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Fri, 1 Nov 2024 18:54:24 +0600 Subject: [PATCH 238/305] fix(frontend): max limit column name character --- .../src/pages/apps/[app_id]/observability/index.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index 8401e023a6..e0d46b9a3c 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -104,7 +104,15 @@ const ObservabilityDashboard = ({}: Props) => {
- {record.node.name} + + {record.node.name.length >= 12 ? ( + + {record.node.name.slice(1, 12)}... + + ) : ( + record.node.name + )} + ) }, From 399ab0f95cf6de93aff6dec6569f80b688d5e4a4 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Fri, 1 Nov 2024 19:06:27 +0600 Subject: [PATCH 239/305] fix(frontend): max name charecter limit issue --- agenta-web/src/pages/apps/[app_id]/observability/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index e0d46b9a3c..bf1d9622b2 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -107,7 +107,7 @@ const ObservabilityDashboard = ({}: Props) => { {record.node.name.length >= 12 ? ( - {record.node.name.slice(1, 12)}... + {record.node.name.slice(0, 12)}... ) : ( record.node.name From 0b13958a94fc4a27e05a42feb45c77c55628c9bd Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Fri, 1 Nov 2024 19:34:21 +0600 Subject: [PATCH 240/305] fix(frontend): added human frindly filter column names --- .../apps/[app_id]/observability/index.tsx | 62 ++++++++----------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index a01c954b4a..aacca8e587 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -273,47 +273,37 @@ const ObservabilityDashboard = ({}: Props) => { }, [columns, editColumns]) const filterColumns = [ - {type: "exists", value: "root.id", label: "root.id"}, - {type: "exists", value: "tree.id", label: "tree.id"}, - {type: "exists", value: "tree.type", label: "tree.type"}, - {type: "exists", value: "node.id", label: "node.id"}, - {type: "exists", value: "node.type", label: "node.type"}, - {type: "exists", value: "node.name", label: "node.name"}, - {type: "exists", value: "parent.id", label: "parent.id"}, - {type: "exists", value: "status.code", label: "status.code"}, - {type: "exists", value: "status.message", label: "status.message"}, - {type: "exists", value: "exception.type", label: "exception.type"}, - {type: "exists", value: "exception.message", label: "exception.message"}, - {type: "exists", value: "exception.stacktrace", label: "exception.stacktrace"}, + {type: "exists", value: "tree.id", label: "tree ID"}, + {type: "exists", value: "node.id", label: "node ID"}, + {type: "exists", value: "node.type", label: "node type"}, + {type: "exists", value: "node.name", label: "node name"}, + {type: "exists", value: "status.code", label: "status code"}, + {type: "exists", value: "status.message", label: "status message"}, + {type: "exists", value: "exception.type", label: "exception type"}, + {type: "exists", value: "exception.message", label: "exception message"}, + {type: "exists", value: "exception.stacktrace", label: "exception stacktrace"}, {type: "string", value: "data", label: "data"}, - {type: "number", value: "metrics.acc.duration.total", label: "metrics.acc.duration.total"}, - {type: "number", value: "metrics.acc.costs.total", label: "metrics.acc.costs.total"}, - {type: "number", value: "metrics.unit.costs.total", label: "metrics.unit.costs.total"}, - {type: "number", value: "metrics.acc.tokens.prompt", label: "metrics.acc.tokens.prompt"}, + {type: "number", value: "metrics.acc.duration.total", label: "duration"}, + {type: "number", value: "metrics.acc.costs.total", label: "total cost (accumulated)"}, + {type: "number", value: "metrics.unit.costs.total", label: "total cost"}, + {type: "number", value: "metrics.acc.tokens.prompt", label: "prompt tokens (accumulated)"}, { type: "number", value: "metrics.acc.tokens.completion", - label: "metrics.acc.tokens.completion", + label: "completion tokens (accumulated)", }, - {type: "number", value: "metrics.acc.tokens.total", label: "metrics.acc.tokens.total"}, - {type: "number", value: "metrics.unit.tokens.prompt", label: "metrics.unit.tokens.prompt"}, - { - type: "number", - value: "metrics.unit.tokens.completion", - label: "metrics.unit.tokens.completion", - }, - {type: "number", value: "metrics.unit.tokens.total", label: "metrics.unit.tokens.total"}, - {type: "exists", value: "refs.variant.id", label: "refs.variant.id"}, - {type: "exists", value: "refs.variant.slug", label: "refs.variant.slug"}, - {type: "exists", value: "refs.variant.version", label: "refs.variant.version"}, - {type: "exists", value: "refs.environment.id", label: "refs.environment.id"}, - {type: "exists", value: "refs.environment.slug", label: "refs.environment.slug"}, - {type: "exists", value: "refs.environment.version", label: "refs.environment.version"}, - {type: "exists", value: "refs.application.id", label: "refs.application.id"}, - {type: "exists", value: "refs.application.slug", label: "refs.application.slug"}, - {type: "exists", value: "link.type", label: "link.type"}, - {type: "exists", value: "link.node.id", label: "link.node.id"}, - {type: "exists", value: "otel.kind", label: "otel.kind"}, + {type: "number", value: "metrics.acc.tokens.total", label: "total tokens (accumulated)"}, + {type: "number", value: "metrics.unit.tokens.prompt", label: "prompt tokens"}, + {type: "number", value: "metrics.unit.tokens.completion", label: "completion tokens"}, + {type: "number", value: "metrics.unit.tokens.total", label: "total tokens"}, + {type: "exists", value: "refs.variant.id", label: "variant ID"}, + {type: "exists", value: "refs.variant.slug", label: "variant slug"}, + {type: "exists", value: "refs.variant.version", label: "variant version"}, + {type: "exists", value: "refs.environment.id", label: "environment ID"}, + {type: "exists", value: "refs.environment.slug", label: "environment slug"}, + {type: "exists", value: "refs.environment.version", label: "environment version"}, + {type: "exists", value: "refs.application.id", label: "application ID"}, + {type: "exists", value: "refs.application.slug", label: "application slug"}, ] const onExport = async () => { From 3c43fcc2f1462504df007e8a6058b1111e8e0a84 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Fri, 1 Nov 2024 14:51:29 +0100 Subject: [PATCH 241/305] fix(frontend): replaced antd segmented component with radio group for json/yaml switch feature --- .../components/AccordionTreePanel.tsx | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx b/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx index 57a30b258b..b2eb45a73b 100644 --- a/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx +++ b/agenta-web/src/components/pages/observability/components/AccordionTreePanel.tsx @@ -1,7 +1,7 @@ import CopyButton from "@/components/CopyButton/CopyButton" import {getStringOrJson} from "@/lib/helpers/utils" import {JSSTheme} from "@/lib/Types" -import {Collapse, Segmented, Space} from "antd" +import {Collapse, Radio, Space} from "antd" import React, {useState, useMemo} from "react" import {createUseStyles} from "react-jss" import yaml from "js-yaml" @@ -64,16 +64,6 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ backgroundColor: theme.colorBgContainerDisabled, }, }), - segmentedBtn: { - border: `1px solid ${theme.colorBorder}`, - padding: 0, - "& .ant-segmented-item": { - padding: 2, - }, - "& .ant-segmented-item-selected": { - outline: `1px solid ${theme.colorPrimary}`, - }, - }, })) const AccordionTreePanel = ({ @@ -138,15 +128,13 @@ const AccordionTreePanel = ({ extra: ( {enableFormatSwitcher && typeof value !== "string" && ( - { - setSegmentedValue(optValue) - }} - onClick={(e) => e.stopPropagation()} - className={classes.segmentedBtn} - /> + onChange={(e) => setSegmentedValue(e.target.value)} + > + JSON + YAML + )} Date: Fri, 1 Nov 2024 20:00:30 +0600 Subject: [PATCH 242/305] fix(frontend): enabled ellipsis --- agenta-web/src/pages/apps/[app_id]/observability/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index bf1d9622b2..2d80d151b9 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -91,6 +91,7 @@ const ObservabilityDashboard = ({}: Props) => { title: "Name", dataIndex: ["node", "name"], key: "name", + ellipsis: true, width: 200, onHeaderCell: () => ({ style: {minWidth: 200}, @@ -105,9 +106,9 @@ const ObservabilityDashboard = ({}: Props) => {
- {record.node.name.length >= 12 ? ( + {record.node.name.length >= 15 ? ( - {record.node.name.slice(0, 12)}... + {record.node.name.slice(0, 15)}... ) : ( record.node.name From 50eaa35fede3e1dcb9805c730cea58a87f16d1c5 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sat, 2 Nov 2024 22:46:34 +0100 Subject: [PATCH 243/305] fix latency(frontend) --- .../src/components/pages/observability/drawer/TraceContent.tsx | 2 +- .../src/components/pages/observability/drawer/TraceTree.tsx | 2 +- agenta-web/src/pages/apps/[app_id]/observability/index.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx index b845b6680a..d0ea680f68 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx @@ -291,7 +291,7 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { value1={ <> {" "} - {formatLatency(activeTrace?.metrics?.acc?.duration.total / 1000000)} + {formatLatency(activeTrace?.metrics?.acc?.duration.total / 1000)} } /> diff --git a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx index 6a26acf6a4..307b7caf05 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx @@ -88,7 +88,7 @@ const TreeContent = ({value}: {value: _AgentaRootsResponse}) => {
- {formatLatency(metrics?.acc?.duration.total / 1000000)} + {formatLatency(metrics?.acc?.duration.total / 1000)}
{metrics?.acc?.costs?.total && ( diff --git a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx index b3aac55286..5a63601216 100644 --- a/agenta-web/src/pages/apps/[app_id]/observability/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/observability/index.tsx @@ -175,7 +175,7 @@ const ObservabilityDashboard = ({}: Props) => { style: {minWidth: 80}, }), render: (_, record) => ( -
{formatLatency(record?.metrics?.acc?.duration.total / 1000000)}
+
{formatLatency(record?.metrics?.acc?.duration.total / 1000)}
), }, { From 726a89941d6088f3c3dc848e219dda07a9918988 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sun, 3 Nov 2024 14:44:51 +0100 Subject: [PATCH 244/305] refactor(frontend): Refactor ObservabilityContextProvider to streamline query param handling and data transformation logic --- .../src/contexts/observability.context.tsx | 33 +++++++++---------- .../src/services/observability/core/index.ts | 4 +-- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/agenta-web/src/contexts/observability.context.tsx b/agenta-web/src/contexts/observability.context.tsx index 3f89e2465f..901a3f8573 100644 --- a/agenta-web/src/contexts/observability.context.tsx +++ b/agenta-web/src/contexts/observability.context.tsx @@ -76,7 +76,7 @@ const ObservabilityContextProvider: React.FC = ({children}) = const queries = generateTraceQueryString() - const data = await fetchAllTraces(queries || "") + const data = await fetchAllTraces(queries) const transformedTraces: _AgentaRootsResponse[] = [] @@ -113,35 +113,32 @@ const ObservabilityContextProvider: React.FC = ({children}) = } const generateTraceQueryString = () => { - const queryParts: string[] = [] + const params: Record = { + size: pagination.size, + page: pagination.page, + focus: traceTabs === "chat" ? "node" : traceTabs, + } if (appId) { - queryParts.push( - `filtering={"conditions":[{"key":"refs.application.id","operator":"is","value":"${appId}"}]}`, - ) + params.filtering = JSON.stringify({ + conditions: [{key: "refs.application.id", operator: "is", value: appId}], + }) } - const focusPoint = traceTabs === "chat" ? "focus=node" : `focus=${traceTabs}` - queryParts.push(focusPoint) - - queryParts.push(`size=${pagination.size}`, `page=${pagination.page}`) - - if (filters[0]?.operator) { - queryParts.push(`filtering={"conditions":${JSON.stringify(filters)}}`) + if (filters.length > 0) { + params.filtering = JSON.stringify({conditions: filters}) } if (sort) { if (sort.type === "standard") { - queryParts.push(`oldest=${sort.sorted}`) + params.oldest = sort.sorted } else if (sort.type === "custom" && sort.customRange?.startTime) { - queryParts.push( - `oldest=${sort.customRange.startTime}`, - `newest=${sort.customRange.endTime}`, - ) + params.oldest = sort.customRange.startTime + params.newest = sort.customRange.endTime } } - return `?${queryParts.join("&")}` + return params } const clearQueryStates = () => { diff --git a/agenta-web/src/services/observability/core/index.ts b/agenta-web/src/services/observability/core/index.ts index 5ba3472479..0687c8ff9b 100644 --- a/agenta-web/src/services/observability/core/index.ts +++ b/agenta-web/src/services/observability/core/index.ts @@ -9,8 +9,8 @@ import axios from "@/lib/helpers/axiosConfig" // - update: PUT data to server // - delete: DELETE data from server -export const fetchAllTraces = async (queries?: string) => { - const response = await axios.get(`${getAgentaApiUrl()}/api/observability/v1/traces${queries}`) +export const fetchAllTraces = async (params = {}) => { + const response = await axios.get(`${getAgentaApiUrl()}/api/observability/v1/traces`, {params}) return response.data } From df64d61a5d0e6248cdedc61f0589cc390b6cac44 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Mon, 4 Nov 2024 16:53:36 +0600 Subject: [PATCH 245/305] fix(frontend): application default filter visible to users --- agenta-web/src/components/Filters/Filters.tsx | 25 +++++++++++++------ .../observability/ObservabilityDashboard.tsx | 8 +++--- .../src/contexts/observability.context.tsx | 18 ++++++------- agenta-web/src/lib/Types.ts | 1 + 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/agenta-web/src/components/Filters/Filters.tsx b/agenta-web/src/components/Filters/Filters.tsx index 391bdf2090..faea7c2379 100644 --- a/agenta-web/src/components/Filters/Filters.tsx +++ b/agenta-web/src/components/Filters/Filters.tsx @@ -37,9 +37,9 @@ type Props = { const Filters: React.FC = ({filterData, columns, onApplyFilter, onClearFilter}) => { const classes = useStyles() - const emptyFilter = [{key: "", operator: "", value: ""}] as Filter[] + const emptyFilter = [{key: "", operator: "", value: "", isPermanent: false}] as Filter[] - const [filter, setFilter] = useState(emptyFilter) + const [filter, setFilter] = useState(filterData || emptyFilter) const [isFilterOpen, setIsFilterOpen] = useState(false) useUpdateEffect(() => { @@ -80,7 +80,7 @@ const Filters: React.FC = ({filterData, columns, onApplyFilter, onClearFi value, idx, }: { - columnName: keyof Filter + columnName: Exclude value: any idx: number }) => { @@ -94,17 +94,24 @@ const Filters: React.FC = ({filterData, columns, onApplyFilter, onClearFi } const addNestedFilter = () => { - setFilter([...filter, {key: "", operator: "", value: ""}]) + setFilter([...filter, {key: "", operator: "", value: "", isPermanent: false}]) } const clearFilter = () => { - setFilter(emptyFilter) - onClearFilter(emptyFilter) + const clearedFilters = filter.filter((f) => f.isPermanent) + + if (JSON.stringify(clearedFilters) !== JSON.stringify(filter)) { + setFilter(clearedFilters) + onClearFilter(clearedFilters) + } } const applyFilter = () => { const sanitizedFilters = filter.filter(({key, operator}) => key && operator) - onApplyFilter(sanitizedFilters) + + if (JSON.stringify(sanitizedFilters) !== JSON.stringify(filterData)) { + onApplyFilter(sanitizedFilters) + } setIsFilterOpen(false) } @@ -153,6 +160,7 @@ const Filters: React.FC = ({filterData, columns, onApplyFilter, onClearFi } value={item.key} options={filteredColumns} + disabled={item.isPermanent} /> {item.key && ( @@ -173,12 +181,14 @@ const Filters: React.FC = ({filterData, columns, onApplyFilter, onClearFi popupMatchSelectWidth={100} value={item.operator} options={filteredOperators} + disabled={item.isPermanent} /> onFilterChange({ columnName: "value", @@ -193,6 +203,7 @@ const Filters: React.FC = ({filterData, columns, onApplyFilter, onClearFi -
+
*/} From 018f2a67227d361d7bd0136eb7767262fb3d9d59 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 5 Nov 2024 15:26:08 +0100 Subject: [PATCH 255/305] Fix traceloop string to json --- .../observability/opentelemetry/semconv.py | 17 ++++++- .../apis/fastapi/observability/utils.py | 12 +++++ agenta-cli/agenta/sdk/tracing/inline.py | 45 +++++++++++++++++++ agenta-cli/pyproject.toml | 2 +- 4 files changed, 73 insertions(+), 3 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py index 9dbb87c66c..24bd9f6134 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/opentelemetry/semconv.py @@ -1,3 +1,5 @@ +from json import loads + VERSION = "0.4.1" V_0_4_1_ATTRIBUTES_EXACT = [ @@ -27,8 +29,6 @@ ("pinecone.query.top_k", "ag.meta.request.top_k"), ("traceloop.span.kind", "ag.type.node"), ("traceloop.entity.name", "ag.node.name"), - ("traceloop.entity.input", "ag.data.inputs"), - ("traceloop.entity.output", "ag.data.outputs"), # OPENINFERENCE ("output.value", "ag.data.outputs"), ("input.value", "ag.data.inputs"), @@ -50,6 +50,13 @@ ("llm.output_messages", "ag.data.outputs.completion"), ] +V_0_4_1_ATTRIBUTES_DYNAMIC = [ + # OPENLLMETRY + ("traceloop.entity.input", lambda x: ("ag.data.inputs", loads(x).get("inputs"))), + ("traceloop.entity.output", lambda x: ("ag.data.outputs", loads(x).get("outputs"))), +] + + V_0_4_1_MAPS = { "attributes": { "exact": { @@ -60,6 +67,9 @@ "from": {otel: agenta for otel, agenta in V_0_4_1_ATTRIBUTES_PREFIX[::-1]}, "to": {agenta: otel for otel, agenta in V_0_4_1_ATTRIBUTES_PREFIX[::-1]}, }, + "dynamic": { + "from": {otel: agenta for otel, agenta in V_0_4_1_ATTRIBUTES_DYNAMIC[::-1]} + }, }, } V_0_4_1_KEYS = { @@ -72,6 +82,9 @@ "from": list(V_0_4_1_MAPS["attributes"]["prefix"]["from"].keys()), "to": list(V_0_4_1_MAPS["attributes"]["prefix"]["to"].keys()), }, + "dynamic": { + "from": list(V_0_4_1_MAPS["attributes"]["dynamic"]["from"].keys()), + }, }, } diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py index d839971eea..2dc19be382 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py @@ -321,6 +321,18 @@ def _parse_from_semconv( del attributes[old_key] + for dynamic_key in CODEX["keys"]["attributes"]["dynamic"]["from"]: + if old_key == dynamic_key: + try: + new_key, new_value = CODEX["maps"]["attributes"]["dynamic"][ + "from" + ][dynamic_key](value) + + attributes[new_key] = new_value + + except: # pylint: disable=bare-except + pass + def _parse_from_links( otel_span_dto: OTelSpanDTO, diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py index 2d03389d76..da4240bc30 100644 --- a/agenta-cli/agenta/sdk/tracing/inline.py +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -412,9 +412,12 @@ def _connect_tree_dfs( ### apis.fastapi.observability.opentelemetry.semconv ### ### ------------------------------------------------ ### +from json import loads + VERSION = "0.4.1" V_0_4_1_ATTRIBUTES_EXACT = [ + # OPENLLMETRY ("gen_ai.system", "ag.meta.system"), ("gen_ai.request.base_url", "ag.meta.request.base_url"), ("gen_ai.request.endpoint", "ag.meta.request.endpoint"), @@ -439,12 +442,35 @@ def _connect_tree_dfs( ("db.vector.query.top_k", "ag.meta.request.top_k"), ("pinecone.query.top_k", "ag.meta.request.top_k"), ("traceloop.span.kind", "ag.type.node"), + ("traceloop.entity.name", "ag.node.name"), + # OPENINFERENCE + ("output.value", "ag.data.outputs"), + ("input.value", "ag.data.inputs"), + ("embedding.model_name", "ag.meta.request.model"), + ("llm.invocation_parameters", "ag.meta.request"), + ("llm.model_name", "ag.meta.request.model"), + ("llm.provider", "ag.meta.provider"), + ("llm.system", "ag.meta.system"), ] V_0_4_1_ATTRIBUTES_PREFIX = [ + # OPENLLMETRY ("gen_ai.prompt", "ag.data.inputs.prompt"), ("gen_ai.completion", "ag.data.outputs.completion"), + ("llm.request.functions", "ag.data.inputs.functions"), + ("llm.request.tools", "ag.data.inputs.tools"), + # OPENINFERENCE + ("llm.token_count", "ag.metrics.unit.tokens"), + ("llm.input_messages", "ag.data.inputs.prompt"), + ("llm.output_messages", "ag.data.outputs.completion"), +] + +V_0_4_1_ATTRIBUTES_DYNAMIC = [ + # OPENLLMETRY + ("traceloop.entity.input", lambda x: ("ag.data.inputs", loads(x).get("inputs"))), + ("traceloop.entity.output", lambda x: ("ag.data.outputs", loads(x).get("outputs"))), ] + V_0_4_1_MAPS = { "attributes": { "exact": { @@ -455,6 +481,9 @@ def _connect_tree_dfs( "from": {otel: agenta for otel, agenta in V_0_4_1_ATTRIBUTES_PREFIX[::-1]}, "to": {agenta: otel for otel, agenta in V_0_4_1_ATTRIBUTES_PREFIX[::-1]}, }, + "dynamic": { + "from": {otel: agenta for otel, agenta in V_0_4_1_ATTRIBUTES_DYNAMIC[::-1]} + }, }, } V_0_4_1_KEYS = { @@ -467,6 +496,9 @@ def _connect_tree_dfs( "from": list(V_0_4_1_MAPS["attributes"]["prefix"]["from"].keys()), "to": list(V_0_4_1_MAPS["attributes"]["prefix"]["to"].keys()), }, + "dynamic": { + "from": list(V_0_4_1_MAPS["attributes"]["dynamic"]["from"].keys()), + }, }, } @@ -480,6 +512,7 @@ def _connect_tree_dfs( CODEX = {"maps": MAPS[VERSION], "keys": KEYS[VERSION]} + ### ------------------------------------------------ ### ### apis.fastapi.observability.opentelemetry.semconv ### ######################################################## @@ -653,6 +686,18 @@ def _parse_from_semconv( del attributes[old_key] + for dynamic_key in CODEX["keys"]["attributes"]["dynamic"]["from"]: + if old_key == dynamic_key: + try: + new_key, new_value = CODEX["maps"]["attributes"]["dynamic"][ + "from" + ][dynamic_key](value) + + attributes[new_key] = new_value + + except: # pylint: disable=bare-except + pass + def _parse_from_links( otel_span_dto: OTelSpanDTO, diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index 94754c6412..09f14e9927 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.27.0a9" +version = "0.27.0a10" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] From 7a9c981ed18c84bbea307267abc443983694dbe1 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 5 Nov 2024 15:27:39 +0100 Subject: [PATCH 256/305] fix dependency in langchain tests --- .../observability_sdk/integrations/langchain/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt b/agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt index 6395726368..84be7f8bc6 100644 --- a/agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt +++ b/agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt @@ -1,4 +1,4 @@ -agenta==0.27.0a5 +agenta==0.27.0a10 # opentelemetry.instrumentation.openai==0.31.2 openai opentelemetry-instrumentation-langchain From 5329e82dc3c5e318424a8c6a9b3501977ec3e29f Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Tue, 5 Nov 2024 17:38:28 +0100 Subject: [PATCH 257/305] fix refs --- .../agenta_backend/apis/fastapi/observability/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py index d839971eea..593f9caba7 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py @@ -497,7 +497,7 @@ def parse_from_otel_span_dto( exception = _parse_from_events(otel_span_dto) - root_id = refs.get("scenario.id", str(tree.id)) + root_id = refs.get("scenario.id", str(tree.id)) if refs else str(tree.id) root = RootDTO(id=UUID(root_id)) From a64ab6bcdb1e90321b5d7ae86ae31f875b0596e5 Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Tue, 5 Nov 2024 17:39:14 +0100 Subject: [PATCH 258/305] updates to tests --- .../integrations/langchain/requirements.txt | 2 +- .../langchain/stateoftheunion.txt | 723 ++++++++++++++++++ .../integrations/litellm/01_sanity_check.py | 24 + .../integrations/litellm/requirements.txt | 5 + .../integrations/openai/requirements.txt | 2 +- .../sanity_check/requirements.txt | 2 +- 6 files changed, 755 insertions(+), 3 deletions(-) create mode 100644 agenta-cli/tests/observability_sdk/integrations/langchain/stateoftheunion.txt create mode 100644 agenta-cli/tests/observability_sdk/integrations/litellm/01_sanity_check.py create mode 100644 agenta-cli/tests/observability_sdk/integrations/litellm/requirements.txt diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt b/agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt index 6395726368..75d25b8180 100644 --- a/agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt +++ b/agenta-cli/tests/observability_sdk/integrations/langchain/requirements.txt @@ -1,4 +1,4 @@ -agenta==0.27.0a5 +agenta==0.27.0a9 # opentelemetry.instrumentation.openai==0.31.2 openai opentelemetry-instrumentation-langchain diff --git a/agenta-cli/tests/observability_sdk/integrations/langchain/stateoftheunion.txt b/agenta-cli/tests/observability_sdk/integrations/langchain/stateoftheunion.txt new file mode 100644 index 0000000000..d50175de40 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/langchain/stateoftheunion.txt @@ -0,0 +1,723 @@ +Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans. + +Last year COVID-19 kept us apart. This year we are finally together again. + +Tonight, we meet as Democrats Republicans and Independents. But most importantly as Americans. + +With a duty to one another to the American people to the Constitution. + +And with an unwavering resolve that freedom will always triumph over tyranny. + +Six days ago, Russia’s Vladimir Putin sought to shake the foundations of the free world thinking he could make it bend to his menacing ways. But he badly miscalculated. + +He thought he could roll into Ukraine and the world would roll over. Instead he met a wall of strength he never imagined. + +He met the Ukrainian people. + +From President Zelenskyy to every Ukrainian, their fearlessness, their courage, their determination, inspires the world. + +Groups of citizens blocking tanks with their bodies. Everyone from students to retirees teachers turned soldiers defending their homeland. + +In this struggle as President Zelenskyy said in his speech to the European Parliament “Light will win over darkness.” The Ukrainian Ambassador to the United States is here tonight. + +Let each of us here tonight in this Chamber send an unmistakable signal to Ukraine and to the world. + +Please rise if you are able and show that, Yes, we the United States of America stand with the Ukrainian people. + +Throughout our history we’ve learned this lesson when dictators do not pay a price for their aggression they cause more chaos. + +They keep moving. + +And the costs and the threats to America and the world keep rising. + +That’s why the NATO Alliance was created to secure peace and stability in Europe after World War 2. + +The United States is a member along with 29 other nations. + +It matters. American diplomacy matters. American resolve matters. + +Putin’s latest attack on Ukraine was premeditated and unprovoked. + +He rejected repeated efforts at diplomacy. + +He thought the West and NATO wouldn’t respond. And he thought he could divide us at home. Putin was wrong. We were ready. Here is what we did. + +We prepared extensively and carefully. + +We spent months building a coalition of other freedom-loving nations from Europe and the Americas to Asia and Africa to confront Putin. + +I spent countless hours unifying our European allies. We shared with the world in advance what we knew Putin was planning and precisely how he would try to falsely justify his aggression. + +We countered Russia’s lies with truth. + +And now that he has acted the free world is holding him accountable. + +Along with twenty-seven members of the European Union including France, Germany, Italy, as well as countries like the United Kingdom, Canada, Japan, Korea, Australia, New Zealand, and many others, even Switzerland. + +We are inflicting pain on Russia and supporting the people of Ukraine. Putin is now isolated from the world more than ever. + +Together with our allies –we are right now enforcing powerful economic sanctions. + +We are cutting off Russia’s largest banks from the international financial system. + +Preventing Russia’s central bank from defending the Russian Ruble making Putin’s $630 Billion “war fund” worthless. + +We are choking off Russia’s access to technology that will sap its economic strength and weaken its military for years to come. + +Tonight I say to the Russian oligarchs and corrupt leaders who have bilked billions of dollars off this violent regime no more. + +The U.S. Department of Justice is assembling a dedicated task force to go after the crimes of Russian oligarchs. + +We are joining with our European allies to find and seize your yachts your luxury apartments your private jets. We are coming for your ill-begotten gains. + +And tonight I am announcing that we will join our allies in closing off American air space to all Russian flights – further isolating Russia – and adding an additional squeeze –on their economy. The Ruble has lost 30% of its value. + +The Russian stock market has lost 40% of its value and trading remains suspended. Russia’s economy is reeling and Putin alone is to blame. + +Together with our allies we are providing support to the Ukrainians in their fight for freedom. Military assistance. Economic assistance. Humanitarian assistance. + +We are giving more than $1 Billion in direct assistance to Ukraine. + +And we will continue to aid the Ukrainian people as they defend their country and to help ease their suffering. + +Let me be clear, our forces are not engaged and will not engage in conflict with Russian forces in Ukraine. + +Our forces are not going to Europe to fight in Ukraine, but to defend our NATO Allies – in the event that Putin decides to keep moving west. + +For that purpose we’ve mobilized American ground forces, air squadrons, and ship deployments to protect NATO countries including Poland, Romania, Latvia, Lithuania, and Estonia. + +As I have made crystal clear the United States and our Allies will defend every inch of territory of NATO countries with the full force of our collective power. + +And we remain clear-eyed. The Ukrainians are fighting back with pure courage. But the next few days weeks, months, will be hard on them. + +Putin has unleashed violence and chaos. But while he may make gains on the battlefield – he will pay a continuing high price over the long run. + +And a proud Ukrainian people, who have known 30 years of independence, have repeatedly shown that they will not tolerate anyone who tries to take their country backwards. + +To all Americans, I will be honest with you, as I’ve always promised. A Russian dictator, invading a foreign country, has costs around the world. + +And I’m taking robust action to make sure the pain of our sanctions is targeted at Russia’s economy. And I will use every tool at our disposal to protect American businesses and consumers. + +Tonight, I can announce that the United States has worked with 30 other countries to release 60 Million barrels of oil from reserves around the world. + +America will lead that effort, releasing 30 Million barrels from our own Strategic Petroleum Reserve. And we stand ready to do more if necessary, unified with our allies. + +These steps will help blunt gas prices here at home. And I know the news about what’s happening can seem alarming. + +But I want you to know that we are going to be okay. + +When the history of this era is written Putin’s war on Ukraine will have left Russia weaker and the rest of the world stronger. + +While it shouldn’t have taken something so terrible for people around the world to see what’s at stake now everyone sees it clearly. + +We see the unity among leaders of nations and a more unified Europe a more unified West. And we see unity among the people who are gathering in cities in large crowds around the world even in Russia to demonstrate their support for Ukraine. + +In the battle between democracy and autocracy, democracies are rising to the moment, and the world is clearly choosing the side of peace and security. + +This is a real test. It’s going to take time. So let us continue to draw inspiration from the iron will of the Ukrainian people. + +To our fellow Ukrainian Americans who forge a deep bond that connects our two nations we stand with you. + +Putin may circle Kyiv with tanks, but he will never gain the hearts and souls of the Ukrainian people. + +He will never extinguish their love of freedom. He will never weaken the resolve of the free world. + +We meet tonight in an America that has lived through two of the hardest years this nation has ever faced. + +The pandemic has been punishing. + +And so many families are living paycheck to paycheck, struggling to keep up with the rising cost of food, gas, housing, and so much more. + +I understand. + +I remember when my Dad had to leave our home in Scranton, Pennsylvania to find work. I grew up in a family where if the price of food went up, you felt it. + +That’s why one of the first things I did as President was fight to pass the American Rescue Plan. + +Because people were hurting. We needed to act, and we did. + +Few pieces of legislation have done more in a critical moment in our history to lift us out of crisis. + +It fueled our efforts to vaccinate the nation and combat COVID-19. It delivered immediate economic relief for tens of millions of Americans. + +Helped put food on their table, keep a roof over their heads, and cut the cost of health insurance. + +And as my Dad used to say, it gave people a little breathing room. + +And unlike the $2 Trillion tax cut passed in the previous administration that benefitted the top 1% of Americans, the American Rescue Plan helped working people—and left no one behind. + +And it worked. It created jobs. Lots of jobs. + +In fact—our economy created over 6.5 Million new jobs just last year, more jobs created in one year +than ever before in the history of America. + +Our economy grew at a rate of 5.7% last year, the strongest growth in nearly 40 years, the first step in bringing fundamental change to an economy that hasn’t worked for the working people of this nation for too long. + +For the past 40 years we were told that if we gave tax breaks to those at the very top, the benefits would trickle down to everyone else. + +But that trickle-down theory led to weaker economic growth, lower wages, bigger deficits, and the widest gap between those at the top and everyone else in nearly a century. + +Vice President Harris and I ran for office with a new economic vision for America. + +Invest in America. Educate Americans. Grow the workforce. Build the economy from the bottom up +and the middle out, not from the top down. + +Because we know that when the middle class grows, the poor have a ladder up and the wealthy do very well. + +America used to have the best roads, bridges, and airports on Earth. + +Now our infrastructure is ranked 13th in the world. + +We won’t be able to compete for the jobs of the 21st Century if we don’t fix that. + +That’s why it was so important to pass the Bipartisan Infrastructure Law—the most sweeping investment to rebuild America in history. + +This was a bipartisan effort, and I want to thank the members of both parties who worked to make it happen. + +We’re done talking about infrastructure weeks. + +We’re going to have an infrastructure decade. + +It is going to transform America and put us on a path to win the economic competition of the 21st Century that we face with the rest of the world—particularly with China. + +As I’ve told Xi Jinping, it is never a good bet to bet against the American people. + +We’ll create good jobs for millions of Americans, modernizing roads, airports, ports, and waterways all across America. + +And we’ll do it all to withstand the devastating effects of the climate crisis and promote environmental justice. + +We’ll build a national network of 500,000 electric vehicle charging stations, begin to replace poisonous lead pipes—so every child—and every American—has clean water to drink at home and at school, provide affordable high-speed internet for every American—urban, suburban, rural, and tribal communities. + +4,000 projects have already been announced. + +And tonight, I’m announcing that this year we will start fixing over 65,000 miles of highway and 1,500 bridges in disrepair. + +When we use taxpayer dollars to rebuild America – we are going to Buy American: buy American products to support American jobs. + +The federal government spends about $600 Billion a year to keep the country safe and secure. + +There’s been a law on the books for almost a century +to make sure taxpayers’ dollars support American jobs and businesses. + +Every Administration says they’ll do it, but we are actually doing it. + +We will buy American to make sure everything from the deck of an aircraft carrier to the steel on highway guardrails are made in America. + +But to compete for the best jobs of the future, we also need to level the playing field with China and other competitors. + +That’s why it is so important to pass the Bipartisan Innovation Act sitting in Congress that will make record investments in emerging technologies and American manufacturing. + +Let me give you one example of why it’s so important to pass it. + +If you travel 20 miles east of Columbus, Ohio, you’ll find 1,000 empty acres of land. + +It won’t look like much, but if you stop and look closely, you’ll see a “Field of dreams,” the ground on which America’s future will be built. + +This is where Intel, the American company that helped build Silicon Valley, is going to build its $20 billion semiconductor “mega site”. + +Up to eight state-of-the-art factories in one place. 10,000 new good-paying jobs. + +Some of the most sophisticated manufacturing in the world to make computer chips the size of a fingertip that power the world and our everyday lives. + +Smartphones. The Internet. Technology we have yet to invent. + +But that’s just the beginning. + +Intel’s CEO, Pat Gelsinger, who is here tonight, told me they are ready to increase their investment from +$20 billion to $100 billion. + +That would be one of the biggest investments in manufacturing in American history. + +And all they’re waiting for is for you to pass this bill. + +So let’s not wait any longer. Send it to my desk. I’ll sign it. + +And we will really take off. + +And Intel is not alone. + +There’s something happening in America. + +Just look around and you’ll see an amazing story. + +The rebirth of the pride that comes from stamping products “Made In America.” The revitalization of American manufacturing. + +Companies are choosing to build new factories here, when just a few years ago, they would have built them overseas. + +That’s what is happening. Ford is investing $11 billion to build electric vehicles, creating 11,000 jobs across the country. + +GM is making the largest investment in its history—$7 billion to build electric vehicles, creating 4,000 jobs in Michigan. + +All told, we created 369,000 new manufacturing jobs in America just last year. + +Powered by people I’ve met like JoJo Burgess, from generations of union steelworkers from Pittsburgh, who’s here with us tonight. + +As Ohio Senator Sherrod Brown says, “It’s time to bury the label “Rust Belt.” + +It’s time. + +But with all the bright spots in our economy, record job growth and higher wages, too many families are struggling to keep up with the bills. + +Inflation is robbing them of the gains they might otherwise feel. + +I get it. That’s why my top priority is getting prices under control. + +Look, our economy roared back faster than most predicted, but the pandemic meant that businesses had a hard time hiring enough workers to keep up production in their factories. + +The pandemic also disrupted global supply chains. + +When factories close, it takes longer to make goods and get them from the warehouse to the store, and prices go up. + +Look at cars. + +Last year, there weren’t enough semiconductors to make all the cars that people wanted to buy. + +And guess what, prices of automobiles went up. + +So—we have a choice. + +One way to fight inflation is to drive down wages and make Americans poorer. + +I have a better plan to fight inflation. + +Lower your costs, not your wages. + +Make more cars and semiconductors in America. + +More infrastructure and innovation in America. + +More goods moving faster and cheaper in America. + +More jobs where you can earn a good living in America. + +And instead of relying on foreign supply chains, let’s make it in America. + +Economists call it “increasing the productive capacity of our economy.” + +I call it building a better America. + +My plan to fight inflation will lower your costs and lower the deficit. + +17 Nobel laureates in economics say my plan will ease long-term inflationary pressures. Top business leaders and most Americans support my plan. And here’s the plan: + +First – cut the cost of prescription drugs. Just look at insulin. One in ten Americans has diabetes. In Virginia, I met a 13-year-old boy named Joshua Davis. + +He and his Dad both have Type 1 diabetes, which means they need insulin every day. Insulin costs about $10 a vial to make. + +But drug companies charge families like Joshua and his Dad up to 30 times more. I spoke with Joshua’s mom. + +Imagine what it’s like to look at your child who needs insulin and have no idea how you’re going to pay for it. + +What it does to your dignity, your ability to look your child in the eye, to be the parent you expect to be. + +Joshua is here with us tonight. Yesterday was his birthday. Happy birthday, buddy. + +For Joshua, and for the 200,000 other young people with Type 1 diabetes, let’s cap the cost of insulin at $35 a month so everyone can afford it. + +Drug companies will still do very well. And while we’re at it let Medicare negotiate lower prices for prescription drugs, like the VA already does. + +Look, the American Rescue Plan is helping millions of families on Affordable Care Act plans save $2,400 a year on their health care premiums. Let’s close the coverage gap and make those savings permanent. + +Second – cut energy costs for families an average of $500 a year by combatting climate change. + +Let’s provide investments and tax credits to weatherize your homes and businesses to be energy efficient and you get a tax credit; double America’s clean energy production in solar, wind, and so much more; lower the price of electric vehicles, saving you another $80 a month because you’ll never have to pay at the gas pump again. + +Third – cut the cost of child care. Many families pay up to $14,000 a year for child care per child. + +Middle-class and working families shouldn’t have to pay more than 7% of their income for care of young children. + +My plan will cut the cost in half for most families and help parents, including millions of women, who left the workforce during the pandemic because they couldn’t afford child care, to be able to get back to work. + +My plan doesn’t stop there. It also includes home and long-term care. More affordable housing. And Pre-K for every 3- and 4-year-old. + +All of these will lower costs. + +And under my plan, nobody earning less than $400,000 a year will pay an additional penny in new taxes. Nobody. + +The one thing all Americans agree on is that the tax system is not fair. We have to fix it. + +I’m not looking to punish anyone. But let’s make sure corporations and the wealthiest Americans start paying their fair share. + +Just last year, 55 Fortune 500 corporations earned $40 billion in profits and paid zero dollars in federal income tax. + +That’s simply not fair. That’s why I’ve proposed a 15% minimum tax rate for corporations. + +We got more than 130 countries to agree on a global minimum tax rate so companies can’t get out of paying their taxes at home by shipping jobs and factories overseas. + +That’s why I’ve proposed closing loopholes so the very wealthy don’t pay a lower tax rate than a teacher or a firefighter. + +So that’s my plan. It will grow the economy and lower costs for families. + +So what are we waiting for? Let’s get this done. And while you’re at it, confirm my nominees to the Federal Reserve, which plays a critical role in fighting inflation. + +My plan will not only lower costs to give families a fair shot, it will lower the deficit. + +The previous Administration not only ballooned the deficit with tax cuts for the very wealthy and corporations, it undermined the watchdogs whose job was to keep pandemic relief funds from being wasted. + +But in my administration, the watchdogs have been welcomed back. + +We’re going after the criminals who stole billions in relief money meant for small businesses and millions of Americans. + +And tonight, I’m announcing that the Justice Department will name a chief prosecutor for pandemic fraud. + +By the end of this year, the deficit will be down to less than half what it was before I took office. + +The only president ever to cut the deficit by more than one trillion dollars in a single year. + +Lowering your costs also means demanding more competition. + +I’m a capitalist, but capitalism without competition isn’t capitalism. + +It’s exploitation—and it drives up prices. + +When corporations don’t have to compete, their profits go up, your prices go up, and small businesses and family farmers and ranchers go under. + +We see it happening with ocean carriers moving goods in and out of America. + +During the pandemic, these foreign-owned companies raised prices by as much as 1,000% and made record profits. + +Tonight, I’m announcing a crackdown on these companies overcharging American businesses and consumers. + +And as Wall Street firms take over more nursing homes, quality in those homes has gone down and costs have gone up. + +That ends on my watch. + +Medicare is going to set higher standards for nursing homes and make sure your loved ones get the care they deserve and expect. + +We’ll also cut costs and keep the economy going strong by giving workers a fair shot, provide more training and apprenticeships, hire them based on their skills not degrees. + +Let’s pass the Paycheck Fairness Act and paid leave. + +Raise the minimum wage to $15 an hour and extend the Child Tax Credit, so no one has to raise a family in poverty. + +Let’s increase Pell Grants and increase our historic support of HBCUs, and invest in what Jill—our First Lady who teaches full-time—calls America’s best-kept secret: community colleges. + +And let’s pass the PRO Act when a majority of workers want to form a union—they shouldn’t be stopped. + +When we invest in our workers, when we build the economy from the bottom up and the middle out together, we can do something we haven’t done in a long time: build a better America. + +For more than two years, COVID-19 has impacted every decision in our lives and the life of the nation. + +And I know you’re tired, frustrated, and exhausted. + +But I also know this. + +Because of the progress we’ve made, because of your resilience and the tools we have, tonight I can say +we are moving forward safely, back to more normal routines. + +We’ve reached a new moment in the fight against COVID-19, with severe cases down to a level not seen since last July. + +Just a few days ago, the Centers for Disease Control and Prevention—the CDC—issued new mask guidelines. + +Under these new guidelines, most Americans in most of the country can now be mask free. + +And based on the projections, more of the country will reach that point across the next couple of weeks. + +Thanks to the progress we have made this past year, COVID-19 need no longer control our lives. + +I know some are talking about “living with COVID-19”. Tonight – I say that we will never just accept living with COVID-19. + +We will continue to combat the virus as we do other diseases. And because this is a virus that mutates and spreads, we will stay on guard. + +Here are four common sense steps as we move forward safely. + +First, stay protected with vaccines and treatments. We know how incredibly effective vaccines are. If you’re vaccinated and boosted you have the highest degree of protection. + +We will never give up on vaccinating more Americans. Now, I know parents with kids under 5 are eager to see a vaccine authorized for their children. + +The scientists are working hard to get that done and we’ll be ready with plenty of vaccines when they do. + +We’re also ready with anti-viral treatments. If you get COVID-19, the Pfizer pill reduces your chances of ending up in the hospital by 90%. + +We’ve ordered more of these pills than anyone in the world. And Pfizer is working overtime to get us 1 Million pills this month and more than double that next month. + +And we’re launching the “Test to Treat” initiative so people can get tested at a pharmacy, and if they’re positive, receive antiviral pills on the spot at no cost. + +If you’re immunocompromised or have some other vulnerability, we have treatments and free high-quality masks. + +We’re leaving no one behind or ignoring anyone’s needs as we move forward. + +And on testing, we have made hundreds of millions of tests available for you to order for free. + +Even if you already ordered free tests tonight, I am announcing that you can order more from covidtests.gov starting next week. + +Second – we must prepare for new variants. Over the past year, we’ve gotten much better at detecting new variants. + +If necessary, we’ll be able to deploy new vaccines within 100 days instead of many more months or years. + +And, if Congress provides the funds we need, we’ll have new stockpiles of tests, masks, and pills ready if needed. + +I cannot promise a new variant won’t come. But I can promise you we’ll do everything within our power to be ready if it does. + +Third – we can end the shutdown of schools and businesses. We have the tools we need. + +It’s time for Americans to get back to work and fill our great downtowns again. People working from home can feel safe to begin to return to the office. + +We’re doing that here in the federal government. The vast majority of federal workers will once again work in person. + +Our schools are open. Let’s keep it that way. Our kids need to be in school. + +And with 75% of adult Americans fully vaccinated and hospitalizations down by 77%, most Americans can remove their masks, return to work, stay in the classroom, and move forward safely. + +We achieved this because we provided free vaccines, treatments, tests, and masks. + +Of course, continuing this costs money. + +I will soon send Congress a request. + +The vast majority of Americans have used these tools and may want to again, so I expect Congress to pass it quickly. + +Fourth, we will continue vaccinating the world. + +We’ve sent 475 Million vaccine doses to 112 countries, more than any other nation. + +And we won’t stop. + +We have lost so much to COVID-19. Time with one another. And worst of all, so much loss of life. + +Let’s use this moment to reset. Let’s stop looking at COVID-19 as a partisan dividing line and see it for what it is: A God-awful disease. + +Let’s stop seeing each other as enemies, and start seeing each other for who we really are: Fellow Americans. + +We can’t change how divided we’ve been. But we can change how we move forward—on COVID-19 and other issues we must face together. + +I recently visited the New York City Police Department days after the funerals of Officer Wilbert Mora and his partner, Officer Jason Rivera. + +They were responding to a 9-1-1 call when a man shot and killed them with a stolen gun. + +Officer Mora was 27 years old. + +Officer Rivera was 22. + +Both Dominican Americans who’d grown up on the same streets they later chose to patrol as police officers. + +I spoke with their families and told them that we are forever in debt for their sacrifice, and we will carry on their mission to restore the trust and safety every community deserves. + +I’ve worked on these issues a long time. + +I know what works: Investing in crime preventionand community police officers who’ll walk the beat, who’ll know the neighborhood, and who can restore trust and safety. + +So let’s not abandon our streets. Or choose between safety and equal justice. + +Let’s come together to protect our communities, restore trust, and hold law enforcement accountable. + +That’s why the Justice Department required body cameras, banned chokeholds, and restricted no-knock warrants for its officers. + +That’s why the American Rescue Plan provided $350 Billion that cities, states, and counties can use to hire more police and invest in proven strategies like community violence interruption—trusted messengers breaking the cycle of violence and trauma and giving young people hope. + +We should all agree: The answer is not to Defund the police. The answer is to FUND the police with the resources and training they need to protect our communities. + +I ask Democrats and Republicans alike: Pass my budget and keep our neighborhoods safe. + +And I will keep doing everything in my power to crack down on gun trafficking and ghost guns you can buy online and make at home—they have no serial numbers and can’t be traced. + +And I ask Congress to pass proven measures to reduce gun violence. Pass universal background checks. Why should anyone on a terrorist list be able to purchase a weapon? + +Ban assault weapons and high-capacity magazines. + +Repeal the liability shield that makes gun manufacturers the only industry in America that can’t be sued. + +These laws don’t infringe on the Second Amendment. They save lives. + +The most fundamental right in America is the right to vote – and to have it counted. And it’s under assault. + +In state after state, new laws have been passed, not only to suppress the vote, but to subvert entire elections. + +We cannot let this happen. + +Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections. + +Tonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service. + +One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court. + +And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence. + +A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since she’s been nominated, she’s received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans. + +And if we are to advance liberty and justice, we need to secure the Border and fix the immigration system. + +We can do both. At our border, we’ve installed new technology like cutting-edge scanners to better detect drug smuggling. + +We’ve set up joint patrols with Mexico and Guatemala to catch more human traffickers. + +We’re putting in place dedicated immigration judges so families fleeing persecution and violence can have their cases heard faster. + +We’re securing commitments and supporting partners in South and Central America to host more refugees and secure their own borders. + +We can do all this while keeping lit the torch of liberty that has led generations of immigrants to this land—my forefathers and so many of yours. + +Provide a pathway to citizenship for Dreamers, those on temporary status, farm workers, and essential workers. + +Revise our laws so businesses have the workers they need and families don’t wait decades to reunite. + +It’s not only the right thing to do—it’s the economically smart thing to do. + +That’s why immigration reform is supported by everyone from labor unions to religious leaders to the U.S. Chamber of Commerce. + +Let’s get it done once and for all. + +Advancing liberty and justice also requires protecting the rights of women. + +The constitutional right affirmed in Roe v. Wade—standing precedent for half a century—is under attack as never before. + +If we want to go forward—not backward—we must protect access to health care. Preserve a woman’s right to choose. And let’s continue to advance maternal health care in America. + +And for our LGBTQ+ Americans, let’s finally get the bipartisan Equality Act to my desk. The onslaught of state laws targeting transgender Americans and their families is wrong. + +As I said last year, especially to our younger transgender Americans, I will always have your back as your President, so you can be yourself and reach your God-given potential. + +While it often appears that we never agree, that isn’t true. I signed 80 bipartisan bills into law last year. From preventing government shutdowns to protecting Asian-Americans from still-too-common hate crimes to reforming military justice. + +And soon, we’ll strengthen the Violence Against Women Act that I first wrote three decades ago. It is important for us to show the nation that we can come together and do big things. + +So tonight I’m offering a Unity Agenda for the Nation. Four big things we can do together. + +First, beat the opioid epidemic. + +There is so much we can do. Increase funding for prevention, treatment, harm reduction, and recovery. + +Get rid of outdated rules that stop doctors from prescribing treatments. And stop the flow of illicit drugs by working with state and local law enforcement to go after traffickers. + +If you’re suffering from addiction, know you are not alone. I believe in recovery, and I celebrate the 23 million Americans in recovery. + +Second, let’s take on mental health. Especially among our children, whose lives and education have been turned upside down. + +The American Rescue Plan gave schools money to hire teachers and help students make up for lost learning. + +I urge every parent to make sure your school does just that. And we can all play a part—sign up to be a tutor or a mentor. + +Children were also struggling before the pandemic. Bullying, violence, trauma, and the harms of social media. + +As Frances Haugen, who is here with us tonight, has shown, we must hold social media platforms accountable for the national experiment they’re conducting on our children for profit. + +It’s time to strengthen privacy protections, ban targeted advertising to children, demand tech companies stop collecting personal data on our children. + +And let’s get all Americans the mental health services they need. More people they can turn to for help, and full parity between physical and mental health care. + +Third, support our veterans. + +Veterans are the best of us. + +I’ve always believed that we have a sacred obligation to equip all those we send to war and care for them and their families when they come home. + +My administration is providing assistance with job training and housing, and now helping lower-income veterans get VA care debt-free. + +Our troops in Iraq and Afghanistan faced many dangers. + +One was stationed at bases and breathing in toxic smoke from “burn pits” that incinerated wastes of war—medical and hazard material, jet fuel, and more. + +When they came home, many of the world’s fittest and best trained warriors were never the same. + +Headaches. Numbness. Dizziness. + +A cancer that would put them in a flag-draped coffin. + +I know. + +One of those soldiers was my son Major Beau Biden. + +We don’t know for sure if a burn pit was the cause of his brain cancer, or the diseases of so many of our troops. + +But I’m committed to finding out everything we can. + +Committed to military families like Danielle Robinson from Ohio. + +The widow of Sergeant First Class Heath Robinson. + +He was born a soldier. Army National Guard. Combat medic in Kosovo and Iraq. + +Stationed near Baghdad, just yards from burn pits the size of football fields. + +Heath’s widow Danielle is here with us tonight. They loved going to Ohio State football games. He loved building Legos with their daughter. + +But cancer from prolonged exposure to burn pits ravaged Heath’s lungs and body. + +Danielle says Heath was a fighter to the very end. + +He didn’t know how to stop fighting, and neither did she. + +Through her pain she found purpose to demand we do better. + +Tonight, Danielle—we are. + +The VA is pioneering new ways of linking toxic exposures to diseases, already helping more veterans get benefits. + +And tonight, I’m announcing we’re expanding eligibility to veterans suffering from nine respiratory cancers. + +I’m also calling on Congress: pass a law to make sure veterans devastated by toxic exposures in Iraq and Afghanistan finally get the benefits and comprehensive health care they deserve. + +And fourth, let’s end cancer as we know it. + +This is personal to me and Jill, to Kamala, and to so many of you. + +Cancer is the #2 cause of death in America–second only to heart disease. + +Last month, I announced our plan to supercharge +the Cancer Moonshot that President Obama asked me to lead six years ago. + +Our goal is to cut the cancer death rate by at least 50% over the next 25 years, turn more cancers from death sentences into treatable diseases. + +More support for patients and families. + +To get there, I call on Congress to fund ARPA-H, the Advanced Research Projects Agency for Health. + +It’s based on DARPA—the Defense Department project that led to the Internet, GPS, and so much more. + +ARPA-H will have a singular purpose—to drive breakthroughs in cancer, Alzheimer’s, diabetes, and more. + +A unity agenda for the nation. + +We can do this. + +My fellow Americans—tonight , we have gathered in a sacred space—the citadel of our democracy. + +In this Capitol, generation after generation, Americans have debated great questions amid great strife, and have done great things. + +We have fought for freedom, expanded liberty, defeated totalitarianism and terror. + +And built the strongest, freest, and most prosperous nation the world has ever known. + +Now is the hour. + +Our moment of responsibility. + +Our test of resolve and conscience, of history itself. + +It is in this moment that our character is formed. Our purpose is found. Our future is forged. + +Well I know this nation. + +We will meet the test. + +To protect freedom and liberty, to expand fairness and opportunity. + +We will save democracy. + +As hard as these times have been, I am more optimistic about America today than I have been my whole life. + +Because I see the future that is within our grasp. + +Because I know there is simply nothing beyond our capacity. + +We are the only nation on Earth that has always turned every crisis we have faced into an opportunity. + +The only nation that can be defined by a single word: possibilities. + +So on this night, in our 245th year as a nation, I have come to report on the State of the Union. + +And my report is this: the State of the Union is strong—because you, the American people, are strong. + +We are stronger today than we were a year ago. + +And we will be stronger a year from now than we are today. + +Now is our moment to meet and overcome the challenges of our time. + +And we will, as one people. + +One America. + +The United States of America. + +May God bless you all. May God protect our troops. \ No newline at end of file diff --git a/agenta-cli/tests/observability_sdk/integrations/litellm/01_sanity_check.py b/agenta-cli/tests/observability_sdk/integrations/litellm/01_sanity_check.py new file mode 100644 index 0000000000..bf93909ed7 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/litellm/01_sanity_check.py @@ -0,0 +1,24 @@ +import litellm +import os +os.environ["OTEL_EXPORTER"]="otlp_http" +os.environ["OTEL_ENDPOINT"]="http://localhost/api/observability/v1/otlp/traces" +AGENTA_APP_ID="0192b441-ab58-7af3-91d0-2f1818690828" +AGENTA_API_KEY="xxx" +os.environ["OTEL_HEADERS"]=f"AG-APP-ID={AGENTA_APP_ID}" + +async def generate_completion(): + litellm.callbacks = ["otel"] + messages = [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Write a short story about AI Engineering."}] + temperature = 0.2 + max_tokens = 100 + chat_completion = await litellm.acompletion( + model="gpt-3.5-turbo", + messages=messages, + temperature=temperature, + max_tokens=max_tokens, + ) + return chat_completion + +if __name__ == "__main__": + import asyncio + asyncio.run(generate_completion()) \ No newline at end of file diff --git a/agenta-cli/tests/observability_sdk/integrations/litellm/requirements.txt b/agenta-cli/tests/observability_sdk/integrations/litellm/requirements.txt new file mode 100644 index 0000000000..4a264bbf14 --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/litellm/requirements.txt @@ -0,0 +1,5 @@ +opentelemetry-api +opentelemetry-sdk +opentelemetry-exporter-otlp +agenta==0.27.0a9 +litellm \ No newline at end of file diff --git a/agenta-cli/tests/observability_sdk/integrations/openai/requirements.txt b/agenta-cli/tests/observability_sdk/integrations/openai/requirements.txt index 3dd64bb756..296e74c13b 100644 --- a/agenta-cli/tests/observability_sdk/integrations/openai/requirements.txt +++ b/agenta-cli/tests/observability_sdk/integrations/openai/requirements.txt @@ -1,3 +1,3 @@ -agenta==0.27.0a0 +agenta==0.27.0a9 opentelemetry.instrumentation.openai==0.31.2 openai \ No newline at end of file diff --git a/agenta-cli/tests/observability_sdk/sanity_check/requirements.txt b/agenta-cli/tests/observability_sdk/sanity_check/requirements.txt index 2fe0ce82c7..8f749fd51c 100644 --- a/agenta-cli/tests/observability_sdk/sanity_check/requirements.txt +++ b/agenta-cli/tests/observability_sdk/sanity_check/requirements.txt @@ -1 +1 @@ -agenta==0.27.0a5 \ No newline at end of file +agenta==0.27.0a9 \ No newline at end of file From a7804687bf3ac281d52b8457b0a92b47f96211c4 Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Tue, 5 Nov 2024 18:52:26 +0100 Subject: [PATCH 259/305] test(sdk): update litellm tests --- ..._check.py => 01_litellm_implementation.py} | 0 .../litellm/02_agenta_implementation.py | 45 +++++++++++++++++++ 2 files changed, 45 insertions(+) rename agenta-cli/tests/observability_sdk/integrations/litellm/{01_sanity_check.py => 01_litellm_implementation.py} (100%) create mode 100644 agenta-cli/tests/observability_sdk/integrations/litellm/02_agenta_implementation.py diff --git a/agenta-cli/tests/observability_sdk/integrations/litellm/01_sanity_check.py b/agenta-cli/tests/observability_sdk/integrations/litellm/01_litellm_implementation.py similarity index 100% rename from agenta-cli/tests/observability_sdk/integrations/litellm/01_sanity_check.py rename to agenta-cli/tests/observability_sdk/integrations/litellm/01_litellm_implementation.py diff --git a/agenta-cli/tests/observability_sdk/integrations/litellm/02_agenta_implementation.py b/agenta-cli/tests/observability_sdk/integrations/litellm/02_agenta_implementation.py new file mode 100644 index 0000000000..b49dcc1cbf --- /dev/null +++ b/agenta-cli/tests/observability_sdk/integrations/litellm/02_agenta_implementation.py @@ -0,0 +1,45 @@ +import litellm +import agenta as ag +# os.environ["OTEL_EXPORTER"]="otlp_http" +# os.environ["OTEL_ENDPOINT"]="http://localhost/api/observability/v1/otlp/traces" +# AGENTA_APP_ID="0192b441-ab58-7af3-91d0-2f1818690828" +# AGENTA_API_KEY="xxx" +# os.environ["OTEL_HEADERS"]=f"AG-APP-ID={AGENTA_APP_ID}" +from tmphandler import litellm_handler +ag.init() + +@ag.instrument() +async def agenerate_completion(): + litellm.callbacks = [ag.callbacks.litellm_handler()] + + messages = [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Write a short story about AI Engineering."}] + temperature = 0.2 + max_tokens = 100 + chat_completion = await litellm.acompletion( + model="gpt-3.5-turbo", + messages=messages, + temperature=temperature, + max_tokens=max_tokens, + ) + return chat_completion + +@ag.instrument() +def generate_completion(): + litellm.callbacks = [litellm_handler()] + + messages = [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Write a short story about AI Engineering."}] + temperature = 0.2 + max_tokens = 100 + chat_completion = litellm.completion( + model="gpt-3.5-turbo", + messages=messages, + temperature=temperature, + max_tokens=max_tokens, + ) + return chat_completion + + +if __name__ == "__main__": + import asyncio + # asyncio.run(agenerate_completion()) + generate_completion() \ No newline at end of file From dc4d0ae95feb91c871862977ed2e08c7483beb4a Mon Sep 17 00:00:00 2001 From: Mahmoud Mabrouk Date: Tue, 5 Nov 2024 18:53:29 +0100 Subject: [PATCH 260/305] chore(app): fomatting --- .../dbs/postgres/observability/dao.py | 2 -- agenta-cli/agenta/sdk/tracing/exporters.py | 1 - agenta-cli/agenta/sdk/tracing/processors.py | 1 - .../litellm/01_litellm_implementation.py | 21 ++++++++++++------- .../litellm/02_agenta_implementation.py | 17 ++++++++++++--- 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py index fb4302b2ea..0c6f8981e4 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py @@ -299,7 +299,6 @@ async def delete_one( ) if span_dbe: - # COULD BE REPLACED WITH A CASCADE children_dbes = await self.read_children( project_id=project_id, @@ -332,7 +331,6 @@ async def delete_many( if span_dbes: for span_dbe in span_dbes: - # COULD BE REPLACED WITH A CASCADE children_dbes = await self.read_children( project_id=project_id, diff --git a/agenta-cli/agenta/sdk/tracing/exporters.py b/agenta-cli/agenta/sdk/tracing/exporters.py index 3cf3f75820..11c96df815 100644 --- a/agenta-cli/agenta/sdk/tracing/exporters.py +++ b/agenta-cli/agenta/sdk/tracing/exporters.py @@ -50,7 +50,6 @@ def fetch( self, trace_id: int, ) -> List[ReadableSpan]: - trace = self._registry.get(trace_id, []) if trace_id in self._registry: diff --git a/agenta-cli/agenta/sdk/tracing/processors.py b/agenta-cli/agenta/sdk/tracing/processors.py index 2aaffe98ae..2d44b54179 100644 --- a/agenta-cli/agenta/sdk/tracing/processors.py +++ b/agenta-cli/agenta/sdk/tracing/processors.py @@ -16,7 +16,6 @@ class TraceProcessor(BatchSpanProcessor): - def __init__( self, span_exporter: SpanExporter, diff --git a/agenta-cli/tests/observability_sdk/integrations/litellm/01_litellm_implementation.py b/agenta-cli/tests/observability_sdk/integrations/litellm/01_litellm_implementation.py index bf93909ed7..9bedc41087 100644 --- a/agenta-cli/tests/observability_sdk/integrations/litellm/01_litellm_implementation.py +++ b/agenta-cli/tests/observability_sdk/integrations/litellm/01_litellm_implementation.py @@ -1,14 +1,19 @@ import litellm import os -os.environ["OTEL_EXPORTER"]="otlp_http" -os.environ["OTEL_ENDPOINT"]="http://localhost/api/observability/v1/otlp/traces" -AGENTA_APP_ID="0192b441-ab58-7af3-91d0-2f1818690828" -AGENTA_API_KEY="xxx" -os.environ["OTEL_HEADERS"]=f"AG-APP-ID={AGENTA_APP_ID}" + +os.environ["OTEL_EXPORTER"] = "otlp_http" +os.environ["OTEL_ENDPOINT"] = "http://localhost/api/observability/v1/otlp/traces" +AGENTA_APP_ID = "0192b441-ab58-7af3-91d0-2f1818690828" +AGENTA_API_KEY = "xxx" +os.environ["OTEL_HEADERS"] = f"AG-APP-ID={AGENTA_APP_ID}" + async def generate_completion(): litellm.callbacks = ["otel"] - messages = [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Write a short story about AI Engineering."}] + messages = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Write a short story about AI Engineering."}, + ] temperature = 0.2 max_tokens = 100 chat_completion = await litellm.acompletion( @@ -19,6 +24,8 @@ async def generate_completion(): ) return chat_completion + if __name__ == "__main__": import asyncio - asyncio.run(generate_completion()) \ No newline at end of file + + asyncio.run(generate_completion()) diff --git a/agenta-cli/tests/observability_sdk/integrations/litellm/02_agenta_implementation.py b/agenta-cli/tests/observability_sdk/integrations/litellm/02_agenta_implementation.py index b49dcc1cbf..e156c723b5 100644 --- a/agenta-cli/tests/observability_sdk/integrations/litellm/02_agenta_implementation.py +++ b/agenta-cli/tests/observability_sdk/integrations/litellm/02_agenta_implementation.py @@ -1,18 +1,24 @@ import litellm import agenta as ag + # os.environ["OTEL_EXPORTER"]="otlp_http" # os.environ["OTEL_ENDPOINT"]="http://localhost/api/observability/v1/otlp/traces" # AGENTA_APP_ID="0192b441-ab58-7af3-91d0-2f1818690828" # AGENTA_API_KEY="xxx" # os.environ["OTEL_HEADERS"]=f"AG-APP-ID={AGENTA_APP_ID}" from tmphandler import litellm_handler + ag.init() + @ag.instrument() async def agenerate_completion(): litellm.callbacks = [ag.callbacks.litellm_handler()] - messages = [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Write a short story about AI Engineering."}] + messages = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Write a short story about AI Engineering."}, + ] temperature = 0.2 max_tokens = 100 chat_completion = await litellm.acompletion( @@ -23,11 +29,15 @@ async def agenerate_completion(): ) return chat_completion + @ag.instrument() def generate_completion(): litellm.callbacks = [litellm_handler()] - messages = [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Write a short story about AI Engineering."}] + messages = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Write a short story about AI Engineering."}, + ] temperature = 0.2 max_tokens = 100 chat_completion = litellm.completion( @@ -41,5 +51,6 @@ def generate_completion(): if __name__ == "__main__": import asyncio + # asyncio.run(agenerate_completion()) - generate_completion() \ No newline at end of file + generate_completion() From dcb52119d5650b53e4a3292bb581ec72d3bd5b94 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Tue, 5 Nov 2024 19:00:02 +0100 Subject: [PATCH 261/305] Fix otlp receiver when refs is None, exception DTO, JSONB None as null and not 'null', dbe mapper when otel is None, pylint in SDK tracing, litellm callbacks --- .../agenta_backend/core/observability/dtos.py | 7 -- .../dbs/postgres/observability/dbas.py | 16 ++-- .../dbs/postgres/observability/mappings.py | 4 +- agenta-cli/agenta/sdk/decorators/tracing.py | 29 ++++--- agenta-cli/agenta/sdk/litellm/litellm.py | 83 +++++++++++-------- 5 files changed, 74 insertions(+), 65 deletions(-) diff --git a/agenta-backend/agenta_backend/core/observability/dtos.py b/agenta-backend/agenta_backend/core/observability/dtos.py index 001a215cb5..9a70f94d43 100644 --- a/agenta-backend/agenta_backend/core/observability/dtos.py +++ b/agenta-backend/agenta_backend/core/observability/dtos.py @@ -2,7 +2,6 @@ from enum import Enum from datetime import datetime from uuid import UUID -from json import loads from pydantic import BaseModel @@ -84,12 +83,6 @@ class ExceptionDTO(BaseModel): stacktrace: Optional[str] = None attributes: Optional[Attributes] = None - class Config: - json_encoders = {datetime: lambda dt: dt.isoformat()} - - def to_json(self) -> dict: - return loads(self.model_dump_json(exclude_none=True)) - Data = Dict[str, Any] Metrics = Dict[str, Any] diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py index c5c6146dcc..00e1da4002 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dbas.py @@ -43,28 +43,28 @@ class TimeDBA: class StatusDBA: __abstract__ = True - status = Column(JSONB, nullable=True) + status = Column(JSONB(none_as_null=True), nullable=True) class AttributesDBA: __abstract__ = True - data = Column(JSONB, nullable=True) - metrics = Column(JSONB, nullable=True) - meta = Column(JSONB, nullable=True) - refs = Column(JSONB, nullable=True) + data = Column(JSONB(none_as_null=True), nullable=True) + metrics = Column(JSONB(none_as_null=True), nullable=True) + meta = Column(JSONB(none_as_null=True), nullable=True) + refs = Column(JSONB(none_as_null=True), nullable=True) class EventsDBA: __abstract__ = True - exception = Column(JSONB, nullable=True) + exception = Column(JSONB(none_as_null=True), nullable=True) class LinksDBA: __abstract__ = True - links = Column(JSONB, nullable=True) + links = Column(JSONB(none_as_null=True), nullable=True) class FullTextSearchDBA: @@ -76,7 +76,7 @@ class FullTextSearchDBA: class OTelDBA: __abstract__ = True - otel = Column(JSONB, nullable=False) + otel = Column(JSONB(none_as_null=True), nullable=True) class SpanDBA( diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py index 90554b105f..82dca876c4 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py @@ -70,7 +70,7 @@ def map_span_dbe_to_dto(span: InvocationSpanDBE) -> SpanDTO: # LINKS links=span.links, # OTEL - otel=OTelExtraDTO(**span.otel), + otel=OTelExtraDTO(**span.otel) if span.otel else None, ) @@ -109,7 +109,7 @@ def map_span_dto_to_dbe( meta=span_dto.encode(span_dto.meta), refs=span_dto.encode(span_dto.refs), # EVENTS - exception=(span_dto.exception.to_json() if span_dto.exception else None), + exception=span_dto.encode(span_dto.exception) if span_dto.exception else None, # LINKS links=span_dto.encode(span_dto.links), # FULL TEXT SEARCH diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 29e3bbe9f9..28dc1a4938 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -10,12 +10,12 @@ import agenta as ag -class instrument: +class instrument: # pylint: disable=invalid-name DEFAULT_KEY = "__default__" def __init__( self, - type: str = "task", + type: str = "task", # pylint: disable=redefined-builtin config: Optional[Dict[str, Any]] = None, ignore_inputs: Optional[bool] = None, ignore_outputs: Optional[bool] = None, @@ -196,12 +196,16 @@ def _redact( ignore: Union[List[str], bool] = False, ) -> Dict[str, Any]: """ - Redact user-defined sensitive information from inputs and outputs as defined by the ignore list or boolean flag. + Redact user-defined sensitive information + from inputs and outputs as defined by the ignore list or boolean flag. Example: - - ignore = ["password"] -> {"username": "admin", "password": "********"} -> {"username": "admin"} - - ignore = True -> {"username": "admin", "password": "********"} -> {} - - ignore = False -> {"username": "admin", "password": "********"} -> {"username": "admin", "password": "********"} + - ignore = ["password"] -> {"username": "admin", "password": "********"} + -> {"username": "admin"} + - ignore = True -> {"username": "admin", "password": "********"} + -> {} + - ignore = False -> {"username": "admin", "password": "********"} + -> {"username": "admin", "password": "********"} """ io = { key: value @@ -210,9 +214,7 @@ def _redact( not in ( ignore if isinstance(ignore, list) - else io.keys() - if ignore is True - else [] + else io.keys() if ignore is True else [] ) } @@ -226,9 +228,12 @@ def _patch( Patch the result to ensure that it is a dictionary, with a default key when necessary. Example: - - result = "Hello, World!" -> {"__default__": "Hello, World!"} - - result = {"message": "Hello, World!", "cost": 0.0, "usage": {}} -> {"__default__": "Hello, World!"} - - result = {"message": "Hello, World!"} -> {"message": "Hello, World!"} + - result = "Hello, World!" + -> {"__default__": "Hello, World!"} + - result = {"message": "Hello, World!", "cost": 0.0, "usage": {}} + -> {"__default__": "Hello, World!"} + - result = {"message": "Hello, World!"} + -> {"message": "Hello, World!"} """ outputs = ( {instrument.DEFAULT_KEY: result} diff --git a/agenta-cli/agenta/sdk/litellm/litellm.py b/agenta-cli/agenta/sdk/litellm/litellm.py index f51be93f44..4c03669f1d 100644 --- a/agenta-cli/agenta/sdk/litellm/litellm.py +++ b/agenta-cli/agenta/sdk/litellm/litellm.py @@ -1,16 +1,15 @@ -import agenta as ag - from opentelemetry.trace import SpanKind +import agenta as ag + from agenta.sdk.tracing.spans import CustomSpan -from agenta.sdk.utils.exceptions import suppress +from agenta.sdk.utils.exceptions import suppress # TODO: use it ! from agenta.sdk.utils.logging import log def litellm_handler(): try: - from litellm.utils import ModelResponse - from litellm.integrations.custom_logger import ( + from litellm.integrations.custom_logger import ( # pylint: disable=import-outside-toplevel CustomLogger as LitellmCustomLogger, ) except ImportError as exc: @@ -18,18 +17,23 @@ def litellm_handler(): "The litellm SDK is not installed. Please install it using `pip install litellm`." ) from exc except Exception as exc: - raise Exception( - "Unexpected error occurred when importing litellm: {}".format(exc) + raise Exception( # pylint: disable=broad-exception-raised + f"Unexpected error occurred when importing litellm: {exc}" ) from exc class LitellmHandler(LitellmCustomLogger): - """This handler is responsible for instrumenting certain events when using litellm to call LLMs. + """ + This handler is responsible for instrumenting certain events, + when using litellm to call LLMs. Args: - LitellmCustomLogger (object): custom logger that allows us to override the events to capture. + LitellmCustomLogger (object): custom logger that allows us + to override the events to capture. """ def __init__(self): + super().__init__() + self.span = None def log_pre_api_call( @@ -38,7 +42,7 @@ def log_pre_api_call( messages, kwargs, ): - type = ( + type = ( # pylint: disable=redefined-builtin "chat" if kwargs.get("call_type") in ["completion", "acompletion"] else "embedding" @@ -59,10 +63,8 @@ def log_pre_api_call( log.error("LiteLLM callback error: span not found.") return - log.info(f"log_pre_api_call({hex(self.span.context.span_id)[2:]})") - self.span.set_attributes( - attributes={"inputs": {"messages": kwargs["messages"]}}, + attributes={"inputs": {"prompt": kwargs["messages"]}}, namespace="data", ) @@ -87,12 +89,14 @@ def log_stream_event( log.error("LiteLLM callback error: span not found.") return - # log.info(f"log_stream({hex(self.span.context.span_id)[2:]})") + result = kwargs.get("complete_streaming_response") + + outputs = ( + {"__default__": result} if not isinstance(result, dict) else result + ) self.span.set_attributes( - attributes={ - "output": {"__default__": kwargs.get("complete_streaming_response")} - }, + attributes={"outputs": outputs}, namespace="data", ) @@ -127,14 +131,20 @@ def log_success_event( log.error("LiteLLM callback error: span not found.") return - # log.info(f"log_success({hex(self.span.context.span_id)[2:]})") + try: + result = [] + for choice in response_obj.choices: + message = choice.message.__dict__ + result.append(message) - self.span.set_attributes( - attributes={ - "output": {"__default__": response_obj.choices[0].message.content} - }, - namespace="data", - ) + outputs = {"completion": result} + self.span.set_attributes( + attributes={"outputs": outputs}, + namespace="data", + ) + + except Exception as e: + pass self.span.set_attributes( attributes={"total": kwargs.get("response_cost")}, @@ -167,8 +177,6 @@ def log_failure_event( log.error("LiteLLM callback error: span not found.") return - # log.info(f"log_failure({hex(self.span.context.span_id)[2:]})") - self.span.record_exception(kwargs["exception"]) self.span.set_status(status="ERROR") @@ -186,12 +194,14 @@ async def async_log_stream_event( log.error("LiteLLM callback error: span not found.") return - # log.info(f"async_log_stream({hex(self.span.context.span_id)[2:]})") + result = kwargs.get("complete_streaming_response") + + outputs = ( + {"__default__": result} if not isinstance(result, dict) else result + ) self.span.set_attributes( - attributes={ - "output": {"__default__": kwargs.get("complete_streaming_response")} - }, + attributes={"outputs": outputs}, namespace="data", ) @@ -226,12 +236,15 @@ async def async_log_success_event( log.error("LiteLLM callback error: span not found.") return - log.info(f"async_log_success({hex(self.span.context.span_id)[2:]})") + # result = kwargs.get("complete_streaming_response") + result = response_obj.choices[0].message.content + + outputs = ( + {"__default__": result} if not isinstance(result, dict) else result + ) self.span.set_attributes( - attributes={ - "output": {"__default__": kwargs.get("complete_streaming_response")} - }, + attributes={"outputs": outputs}, namespace="data", ) @@ -266,8 +279,6 @@ async def async_log_failure_event( log.error("LiteLLM callback error: span not found.") return - # log.info(f"async_log_failure({hex(self.span.context.span_id)[2:]})") - self.span.record_exception(kwargs["exception"]) self.span.set_status(status="ERROR") From e81f94a1ff6f41aa4e74e6c21aa2b317a3e3cee0 Mon Sep 17 00:00:00 2001 From: jp-agenta Date: Tue, 5 Nov 2024 20:29:51 +0100 Subject: [PATCH 262/305] bump pre-release --- agenta-cli/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index 09f14e9927..cbb05b5593 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.27.0a10" +version = "0.27.0a11" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] From ee67bf9178f79de9db84db30aa35f31ae6607e08 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Wed, 6 Nov 2024 13:25:02 +0100 Subject: [PATCH 263/305] fix(frontend): added max height and scroll to tooltip --- agenta-web/src/styles/globals.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/agenta-web/src/styles/globals.css b/agenta-web/src/styles/globals.css index 89bb5e3723..5888402b3a 100644 --- a/agenta-web/src/styles/globals.css +++ b/agenta-web/src/styles/globals.css @@ -60,6 +60,11 @@ body { opacity: 1; } +.ant-tooltip-inner { + max-height: 300px; + overflow-y: auto; +} + @media screen and (max-width: 1572px) { .evalaution-title { min-height: 80px; From 05e939211c0e33f38fb9d8ab31370c33b28c7b5f Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Thu, 7 Nov 2024 10:55:49 +0100 Subject: [PATCH 264/305] fix(frontend): select node and hover effect seems to be large than the figma design --- .../src/components/pages/observability/drawer/TraceTree.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx index 02c261b3e9..541a4bb8bd 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx @@ -26,6 +26,8 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ padding: "1px 0", "& .ant-tree-node-content-wrapper": { minWidth: 240, + marginTop: 6, + marginBottom: 6, }, "& .ant-tree-node-selected": { outline: `1px solid ${theme.colorBorder}`, From bf0c3c781269f809e4bf52680ed2e8755c7a1241 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 7 Nov 2024 13:34:10 +0100 Subject: [PATCH 265/305] Fix migrations, breaks PR until main is merged. --- .../dbs/postgres/observability/dao.py | 53 ++++---- .../dbs/postgres/observability/dbes.py | 4 +- .../dbs/postgres/observability/mappings.py | 8 +- .../205d3cc5d8e1_fix_invocation_span_types.py | 109 ---------------- ...392da9_clean_up_observability_interface.py | 106 --------------- .../7d8643d3d672_cast_data_to_string.py | 43 ------- .../versions/847972cfa14a_add_nodes_dbe.py | 121 ++++++++++++++++++ .../b7af1b4dc016_add_observability_in_oss.py | 103 --------------- .../d0dca996e123_add_hstore_extension.py | 19 --- .../ddf8f6e431e6_expose_span_exception.py | 34 ----- 10 files changed, 152 insertions(+), 448 deletions(-) delete mode 100644 agenta-backend/agenta_backend/migrations/postgres/versions/205d3cc5d8e1_fix_invocation_span_types.py delete mode 100644 agenta-backend/agenta_backend/migrations/postgres/versions/7693ab392da9_clean_up_observability_interface.py delete mode 100644 agenta-backend/agenta_backend/migrations/postgres/versions/7d8643d3d672_cast_data_to_string.py create mode 100644 agenta-backend/agenta_backend/migrations/postgres/versions/847972cfa14a_add_nodes_dbe.py delete mode 100644 agenta-backend/agenta_backend/migrations/postgres/versions/b7af1b4dc016_add_observability_in_oss.py delete mode 100644 agenta-backend/agenta_backend/migrations/postgres/versions/d0dca996e123_add_hstore_extension.py delete mode 100644 agenta-backend/agenta_backend/migrations/postgres/versions/ddf8f6e431e6_expose_span_exception.py diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py index 0c6f8981e4..1d40850347 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dao.py @@ -9,7 +9,7 @@ from sqlalchemy.dialects import postgresql from agenta_backend.dbs.postgres.shared.engine import engine -from agenta_backend.dbs.postgres.observability.dbes import InvocationSpanDBE +from agenta_backend.dbs.postgres.observability.dbes import NodesDBE from agenta_backend.dbs.postgres.observability.mappings import ( map_span_dto_to_dbe, map_span_dbe_to_dto, @@ -52,7 +52,7 @@ async def query( try: async with engine.session() as session: # BASE (SUB-)QUERY - query = select(InvocationSpanDBE) + query = select(NodesDBE) # ---------------- # GROUPING @@ -61,12 +61,13 @@ async def query( # -------- if grouping and grouping.focus.value != "node": grouping_column = getattr( - InvocationSpanDBE, grouping.focus.value + "_id" + NodesDBE, + grouping.focus.value + "_id", ) query = select( distinct(grouping_column).label("grouping_key"), - InvocationSpanDBE.created_at, + NodesDBE.created_at, ) # -------- @@ -81,14 +82,10 @@ async def query( # --------- if windowing: if windowing.oldest: - query = query.filter( - InvocationSpanDBE.created_at >= windowing.oldest - ) + query = query.filter(NodesDBE.created_at >= windowing.oldest) if windowing.newest: - query = query.filter( - InvocationSpanDBE.created_at < windowing.newest - ) + query = query.filter(NodesDBE.created_at < windowing.newest) # --------- # FILTERING @@ -108,7 +105,7 @@ async def query( # SORTING query = query.order_by( - InvocationSpanDBE.created_at.desc(), + NodesDBE.created_at.desc(), ) # ------- @@ -134,21 +131,21 @@ async def query( if grouping and grouping_column: subquery = query.subquery() - query = select(InvocationSpanDBE) + query = select(NodesDBE) query = query.filter( grouping_column.in_(select(subquery.c["grouping_key"])) ) # SORTING query = query.order_by( - InvocationSpanDBE.created_at.desc(), - InvocationSpanDBE.time_start.asc(), + NodesDBE.created_at.desc(), + NodesDBE.time_start.asc(), ) # ------- else: # SORTING query = query.order_by( - InvocationSpanDBE.time_start.desc(), + NodesDBE.time_start.desc(), ) # ------- # -------- @@ -216,10 +213,10 @@ async def read_one( project_id: UUID, node_id: UUID, to_dto: bool = True, - ) -> Union[Optional[SpanDTO], Optional[InvocationSpanDBE]]: + ) -> Union[Optional[SpanDTO], Optional[NodesDBE]]: span_dbe = None async with engine.session() as session: - query = select(InvocationSpanDBE) + query = select(NodesDBE) query = query.filter_by( project_id=project_id, @@ -242,14 +239,14 @@ async def read_many( project_id: UUID, node_ids: List[UUID], to_dto: bool = True, - ) -> Union[List[SpanDTO], List[InvocationSpanDBE]]: + ) -> Union[List[SpanDTO], List[NodesDBE]]: span_dbes = [] async with engine.session() as session: - query = select(InvocationSpanDBE) + query = select(NodesDBE) query = query.filter_by(project_id=project_id) - query = query.filter(InvocationSpanDBE.node_id.in_(node_ids)) + query = query.filter(NodesDBE.node_id.in_(node_ids)) span_dbes = (await session.execute(query)).scalars().all() @@ -267,10 +264,10 @@ async def read_children( project_id: UUID, parent_id: UUID, to_dto: bool = True, - ) -> Union[List[SpanDTO], List[InvocationSpanDBE]]: + ) -> Union[List[SpanDTO], List[NodesDBE]]: span_dbes = [] async with engine.session() as session: - query = select(InvocationSpanDBE) + query = select(NodesDBE) query = query.filter_by(project_id=project_id) @@ -368,14 +365,14 @@ def _chunk( # 2. WHERE next > created_at LIMIT size # -> unstable if created_at is not unique elif next and size: - query = query.filter(InvocationSpanDBE.created_at < next) + query = query.filter(NodesDBE.created_at < next) query = query.limit(size) # 3. WHERE next > created_at AND created_at >= stop # -> stable thanks to the = stop) + query = query.filter(NodesDBE.created_at < next) + query = query.filter(NodesDBE.created_at >= stop) # 4. WHERE LIMIT size # -> useful as a starter query @@ -385,12 +382,12 @@ def _chunk( # 5. WHERE created_at >= stop # -> useful as a starter query elif stop: - query = query.filter(InvocationSpanDBE.created_at >= stop) + query = query.filter(NodesDBE.created_at >= stop) # 6. WHERE next > created_at # -> rather useless elif next: - query = query.filter(InvocationSpanDBE.created_at < next) + query = query.filter(NodesDBE.created_at < next) return query @@ -452,7 +449,7 @@ def _filters(filtering: FilteringDTO) -> list: key = _split[1] if len(_split) > 1 else None # GET COLUMN AS ATTRIBUTE - attribute: Column = getattr(InvocationSpanDBE, field) + attribute: Column = getattr(NodesDBE, field) if isinstance(attribute.type, JSONB) and key: if field in _NESTED_FIELDS: diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py b/agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py index a119e70970..dab8468b1d 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/dbes.py @@ -5,8 +5,8 @@ from agenta_backend.dbs.postgres.observability.dbas import SpanDBA -class InvocationSpanDBE(Base, SpanDBA): - __tablename__ = "invocation_span" +class NodesDBE(Base, SpanDBA): + __tablename__ = "nodes" __table_args__ = ( PrimaryKeyConstraint( diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py index 82dca876c4..02308e5af1 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py @@ -13,10 +13,10 @@ SpanDTO, ) -from agenta_backend.dbs.postgres.observability.dbes import InvocationSpanDBE +from agenta_backend.dbs.postgres.observability.dbes import NodesDBE -def map_span_dbe_to_dto(span: InvocationSpanDBE) -> SpanDTO: +def map_span_dbe_to_dto(span: NodesDBE) -> SpanDTO: return SpanDTO( lifecycle=LifecycleDTO( created_at=span.created_at, @@ -77,8 +77,8 @@ def map_span_dbe_to_dto(span: InvocationSpanDBE) -> SpanDTO: def map_span_dto_to_dbe( project_id: str, span_dto: SpanDTO, -) -> InvocationSpanDBE: - span_dbe = InvocationSpanDBE( +) -> NodesDBE: + span_dbe = NodesDBE( # SCOPE project_id=project_id, # LIFECYCLE diff --git a/agenta-backend/agenta_backend/migrations/postgres/versions/205d3cc5d8e1_fix_invocation_span_types.py b/agenta-backend/agenta_backend/migrations/postgres/versions/205d3cc5d8e1_fix_invocation_span_types.py deleted file mode 100644 index c885ad8901..0000000000 --- a/agenta-backend/agenta_backend/migrations/postgres/versions/205d3cc5d8e1_fix_invocation_span_types.py +++ /dev/null @@ -1,109 +0,0 @@ -"""fix_invocation_span_types - -Revision ID: 205d3cc5d8e1 -Revises: 7693ab392da9 -Create Date: 2024-11-04 10:08:59.046483 - -""" - -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import postgresql - -# revision identifiers, used by Alembic. -revision: str = "205d3cc5d8e1" -down_revision: Union[str, None] = "7693ab392da9" -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.add_column("invocation_span", sa.Column("content", sa.String(), nullable=True)) - op.alter_column( - "invocation_span", - "created_at", - existing_type=postgresql.TIMESTAMP(), - type_=sa.TIMESTAMP(timezone=True), - existing_nullable=False, - existing_server_default=sa.text("CURRENT_TIMESTAMP"), - ) - op.alter_column( - "invocation_span", - "updated_at", - existing_type=postgresql.TIMESTAMP(), - type_=sa.TIMESTAMP(timezone=True), - existing_nullable=True, - ) - op.alter_column( - "invocation_span", - "data", - existing_type=sa.VARCHAR(), - type_=postgresql.JSONB(astext_type=sa.Text()), - existing_nullable=True, - postgresql_using="data::jsonb", - ) - op.alter_column( - "invocation_span", - "refs", - existing_type=postgresql.HSTORE(text_type=sa.Text()), - type_=postgresql.JSONB(astext_type=sa.Text()), - existing_nullable=True, - postgresql_using="refs::jsonb", - ) - op.alter_column( - "invocation_span", - "links", - existing_type=postgresql.HSTORE(text_type=sa.Text()), - type_=postgresql.JSONB(astext_type=sa.Text()), - existing_nullable=True, - postgresql_using="links::jsonb", - ) - # ### end Alembic commands ### - - -def downgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.alter_column( - "invocation_span", - "links", - existing_type=postgresql.JSONB(astext_type=sa.Text()), - type_=postgresql.HSTORE(text_type=sa.Text()), - existing_nullable=True, - postgresql_using="links::text", - ) - op.alter_column( - "invocation_span", - "refs", - existing_type=postgresql.JSONB(astext_type=sa.Text()), - type_=postgresql.HSTORE(text_type=sa.Text()), - existing_nullable=True, - postgresql_using="refs::text", - ) - op.alter_column( - "invocation_span", - "data", - existing_type=postgresql.JSONB(astext_type=sa.Text()), - type_=sa.VARCHAR(), - existing_nullable=True, - postgresql_using="data::text", - ) - op.alter_column( - "invocation_span", - "updated_at", - existing_type=sa.TIMESTAMP(timezone=True), - type_=postgresql.TIMESTAMP(), - existing_nullable=True, - ) - op.alter_column( - "invocation_span", - "created_at", - existing_type=sa.TIMESTAMP(timezone=True), - type_=postgresql.TIMESTAMP(), - existing_nullable=False, - existing_server_default=sa.text("CURRENT_TIMESTAMP"), - ) - op.drop_column("invocation_span", "content") - # ### end Alembic commands ### diff --git a/agenta-backend/agenta_backend/migrations/postgres/versions/7693ab392da9_clean_up_observability_interface.py b/agenta-backend/agenta_backend/migrations/postgres/versions/7693ab392da9_clean_up_observability_interface.py deleted file mode 100644 index 7662f584b4..0000000000 --- a/agenta-backend/agenta_backend/migrations/postgres/versions/7693ab392da9_clean_up_observability_interface.py +++ /dev/null @@ -1,106 +0,0 @@ -"""clean_up_observability_interface - -Revision ID: 7693ab392da9 -Revises: 7d8643d3d672 -Create Date: 2024-10-24 08:46:46.212987 - -""" - -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import postgresql - -# revision identifiers, used by Alembic. -revision: str = "7693ab392da9" -down_revision: Union[str, None] = "7d8643d3d672" -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.add_column( - "invocation_span", - sa.Column("status", postgresql.JSONB(astext_type=sa.Text()), nullable=True), - ) - op.alter_column( - "invocation_span", - "exception", - existing_type=postgresql.JSON(astext_type=sa.Text()), - type_=postgresql.JSONB(astext_type=sa.Text()), - existing_nullable=True, - ) - op.alter_column( - "invocation_span", - "metrics", - existing_type=postgresql.JSON(astext_type=sa.Text()), - type_=postgresql.JSONB(astext_type=sa.Text()), - existing_nullable=True, - ) - op.alter_column( - "invocation_span", - "meta", - existing_type=postgresql.JSON(astext_type=sa.Text()), - type_=postgresql.JSONB(astext_type=sa.Text()), - existing_nullable=True, - ) - op.drop_column("invocation_span", "status_code") - op.drop_column("invocation_span", "status_message") - op.drop_column("invocation_span", "tags") - op.drop_column("invocation_span", "time_span") - # ### end Alembic commands ### - - -def downgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.add_column( - "invocation_span", - sa.Column("time_span", sa.BIGINT(), autoincrement=False, nullable=True), - ) - op.add_column( - "invocation_span", - sa.Column( - "tags", - postgresql.HSTORE(text_type=sa.Text()), - autoincrement=False, - nullable=True, - ), - ) - op.add_column( - "invocation_span", - sa.Column("status_message", sa.VARCHAR(), autoincrement=False, nullable=True), - ) - op.add_column( - "invocation_span", - sa.Column( - "status_code", - postgresql.ENUM("UNSET", "OK", "ERROR", name="statuscode"), - autoincrement=False, - nullable=False, - ), - ) - op.alter_column( - "invocation_span", - "meta", - existing_type=postgresql.JSONB(astext_type=sa.Text()), - type_=postgresql.JSON(astext_type=sa.Text()), - existing_nullable=True, - ) - op.alter_column( - "invocation_span", - "metrics", - existing_type=postgresql.JSONB(astext_type=sa.Text()), - type_=postgresql.JSON(astext_type=sa.Text()), - existing_nullable=True, - ) - op.alter_column( - "invocation_span", - "exception", - existing_type=postgresql.JSONB(astext_type=sa.Text()), - type_=postgresql.JSON(astext_type=sa.Text()), - existing_nullable=True, - ) - op.drop_column("invocation_span", "status") - # ### end Alembic commands ### diff --git a/agenta-backend/agenta_backend/migrations/postgres/versions/7d8643d3d672_cast_data_to_string.py b/agenta-backend/agenta_backend/migrations/postgres/versions/7d8643d3d672_cast_data_to_string.py deleted file mode 100644 index 2940b3aea3..0000000000 --- a/agenta-backend/agenta_backend/migrations/postgres/versions/7d8643d3d672_cast_data_to_string.py +++ /dev/null @@ -1,43 +0,0 @@ -"""cast_data_to_string - -Revision ID: 7d8643d3d672 -Revises: ddf8f6e431e6 -Create Date: 2024-10-22 10:21:52.468451 - -""" - -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import postgresql - -# revision identifiers, used by Alembic. -revision: str = "7d8643d3d672" -down_revision: Union[str, None] = "ddf8f6e431e6" -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.alter_column( - "invocation_span", - "data", - existing_type=postgresql.JSON(astext_type=sa.Text()), - type_=sa.String(), - existing_nullable=True, - ) - # ### end Alembic commands ### - - -def downgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.alter_column( - "invocation_span", - "data", - existing_type=sa.String(), - type_=postgresql.JSON(astext_type=sa.Text()), - existing_nullable=True, - ) - # ### end Alembic commands ### diff --git a/agenta-backend/agenta_backend/migrations/postgres/versions/847972cfa14a_add_nodes_dbe.py b/agenta-backend/agenta_backend/migrations/postgres/versions/847972cfa14a_add_nodes_dbe.py new file mode 100644 index 0000000000..1376642ed9 --- /dev/null +++ b/agenta-backend/agenta_backend/migrations/postgres/versions/847972cfa14a_add_nodes_dbe.py @@ -0,0 +1,121 @@ +"""add_nodes_dbe + +Revision ID: 847972cfa14a +Revises: 55bdd2e9a465 +Create Date: 2024-11-07 12:21:19.080345 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision: str = "847972cfa14a" +down_revision: Union[str, None] = "55bdd2e9a465" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "nodes", + sa.Column("project_id", sa.UUID(), nullable=False), + sa.Column( + "created_at", + sa.TIMESTAMP(timezone=True), + server_default=sa.text("CURRENT_TIMESTAMP"), + nullable=False, + ), + sa.Column("updated_at", sa.TIMESTAMP(timezone=True), nullable=True), + sa.Column("updated_by_id", sa.UUID(), nullable=True), + sa.Column("root_id", sa.UUID(), nullable=False), + sa.Column("tree_id", sa.UUID(), nullable=False), + sa.Column("tree_type", sa.Enum("INVOCATION", name="treetype"), nullable=True), + sa.Column("node_id", sa.UUID(), nullable=False), + sa.Column("node_name", sa.String(), nullable=False), + sa.Column( + "node_type", + sa.Enum( + "AGENT", + "WORKFLOW", + "CHAIN", + "TASK", + "TOOL", + "EMBEDDING", + "QUERY", + "COMPLETION", + "CHAT", + "RERANK", + name="nodetype", + ), + nullable=True, + ), + sa.Column("parent_id", sa.UUID(), nullable=True), + sa.Column("time_start", sa.TIMESTAMP(), nullable=False), + sa.Column("time_end", sa.TIMESTAMP(), nullable=False), + sa.Column( + "status", + postgresql.JSONB(none_as_null=True, astext_type=sa.Text()), + nullable=True, + ), + sa.Column( + "data", + postgresql.JSONB(none_as_null=True, astext_type=sa.Text()), + nullable=True, + ), + sa.Column( + "metrics", + postgresql.JSONB(none_as_null=True, astext_type=sa.Text()), + nullable=True, + ), + sa.Column( + "meta", + postgresql.JSONB(none_as_null=True, astext_type=sa.Text()), + nullable=True, + ), + sa.Column( + "refs", + postgresql.JSONB(none_as_null=True, astext_type=sa.Text()), + nullable=True, + ), + sa.Column( + "exception", + postgresql.JSONB(none_as_null=True, astext_type=sa.Text()), + nullable=True, + ), + sa.Column( + "links", + postgresql.JSONB(none_as_null=True, astext_type=sa.Text()), + nullable=True, + ), + sa.Column("content", sa.String(), nullable=True), + sa.Column( + "otel", + postgresql.JSONB(none_as_null=True, astext_type=sa.Text()), + nullable=True, + ), + sa.PrimaryKeyConstraint("project_id", "node_id"), + ) + op.create_index( + "index_project_id_node_id", "nodes", ["project_id", "created_at"], unique=False + ) + op.create_index( + "index_project_id_root_id", "nodes", ["project_id", "root_id"], unique=False + ) + op.create_index( + "index_project_id_tree_id", "nodes", ["project_id", "tree_id"], unique=False + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index("index_project_id_tree_id", table_name="nodes") + op.drop_index("index_project_id_root_id", table_name="nodes") + op.drop_index("index_project_id_node_id", table_name="nodes") + op.drop_table("nodes") + # ### end Alembic commands ### diff --git a/agenta-backend/agenta_backend/migrations/postgres/versions/b7af1b4dc016_add_observability_in_oss.py b/agenta-backend/agenta_backend/migrations/postgres/versions/b7af1b4dc016_add_observability_in_oss.py deleted file mode 100644 index 2ad7cefbf1..0000000000 --- a/agenta-backend/agenta_backend/migrations/postgres/versions/b7af1b4dc016_add_observability_in_oss.py +++ /dev/null @@ -1,103 +0,0 @@ -"""add_observability_in_oss - -Revision ID: b7af1b4dc016 -Revises: d0dca996e123 -Create Date: 2024-10-11 10:19:12.331361 - -""" - -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import postgresql - -# revision identifiers, used by Alembic. -revision: str = "b7af1b4dc016" -down_revision: Union[str, None] = "d0dca996e123" -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.create_table( - "invocation_span", - sa.Column("project_id", sa.UUID(), nullable=False), - sa.Column( - "created_at", - sa.TIMESTAMP(), - server_default=sa.text("CURRENT_TIMESTAMP"), - nullable=False, - ), - sa.Column("updated_at", sa.TIMESTAMP(), nullable=True), - sa.Column("updated_by_id", sa.UUID(), nullable=True), - sa.Column("root_id", sa.UUID(), nullable=False), - sa.Column("tree_id", sa.UUID(), nullable=False), - sa.Column("tree_type", sa.Enum("INVOCATION", name="treetype"), nullable=True), - sa.Column("node_id", sa.UUID(), nullable=False), - sa.Column("node_name", sa.String(), nullable=False), - sa.Column( - "node_type", - sa.Enum( - "AGENT", - "WORKFLOW", - "CHAIN", - "TASK", - "TOOL", - "EMBEDDING", - "QUERY", - "COMPLETION", - "CHAT", - "RERANK", - name="nodetype", - ), - nullable=True, - ), - sa.Column("parent_id", sa.UUID(), nullable=True), - sa.Column("time_start", sa.TIMESTAMP(), nullable=False), - sa.Column("time_end", sa.TIMESTAMP(), nullable=False), - sa.Column("time_span", sa.BigInteger(), nullable=True), - sa.Column( - "status_code", - sa.Enum("UNSET", "OK", "ERROR", name="statuscode"), - nullable=False, - ), - sa.Column("status_message", sa.String(), nullable=True), - sa.Column("data", postgresql.JSON(astext_type=sa.Text()), nullable=True), - sa.Column("metrics", postgresql.JSON(astext_type=sa.Text()), nullable=True), - sa.Column("meta", postgresql.JSON(astext_type=sa.Text()), nullable=True), - sa.Column("tags", postgresql.HSTORE(text_type=sa.Text()), nullable=True), - sa.Column("refs", postgresql.HSTORE(text_type=sa.Text()), nullable=True), - sa.Column("links", postgresql.HSTORE(text_type=sa.Text()), nullable=True), - sa.Column("otel", postgresql.JSONB(astext_type=sa.Text()), nullable=False), - sa.PrimaryKeyConstraint("project_id", "node_id"), - ) - op.create_index( - "index_project_id_node_id", - "invocation_span", - ["project_id", "created_at"], - unique=False, - ) - op.create_index( - "index_project_id_root_id", - "invocation_span", - ["project_id", "root_id"], - unique=False, - ) - op.create_index( - "index_project_id_tree_id", - "invocation_span", - ["project_id", "tree_id"], - unique=False, - ) - # ### end Alembic commands ### - - -def downgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.drop_index("index_project_id_tree_id", table_name="invocation_span") - op.drop_index("index_project_id_root_id", table_name="invocation_span") - op.drop_index("index_project_id_node_id", table_name="invocation_span") - op.drop_table("invocation_span") - # ### end Alembic commands ### diff --git a/agenta-backend/agenta_backend/migrations/postgres/versions/d0dca996e123_add_hstore_extension.py b/agenta-backend/agenta_backend/migrations/postgres/versions/d0dca996e123_add_hstore_extension.py deleted file mode 100644 index 623339404d..0000000000 --- a/agenta-backend/agenta_backend/migrations/postgres/versions/d0dca996e123_add_hstore_extension.py +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import postgresql - -# revision identifiers, used by Alembic. -revision: str = "d0dca996e123" -down_revision: Union[str, None] = "55bdd2e9a465" -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - op.execute("CREATE EXTENSION IF NOT EXISTS hstore;") - - -def downgrade() -> None: - op.execute("DROP EXTENSION IF EXISTS hstore;") diff --git a/agenta-backend/agenta_backend/migrations/postgres/versions/ddf8f6e431e6_expose_span_exception.py b/agenta-backend/agenta_backend/migrations/postgres/versions/ddf8f6e431e6_expose_span_exception.py deleted file mode 100644 index 7438ab5b55..0000000000 --- a/agenta-backend/agenta_backend/migrations/postgres/versions/ddf8f6e431e6_expose_span_exception.py +++ /dev/null @@ -1,34 +0,0 @@ -"""expose_span_exception - -Revision ID: ddf8f6e431e6 -Revises: b7af1b4dc016 -Create Date: 2024-10-22 08:58:58.356103 - -""" - -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import postgresql - -# revision identifiers, used by Alembic. -revision: str = "ddf8f6e431e6" -down_revision: Union[str, None] = "b7af1b4dc016" -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.add_column( - "invocation_span", - sa.Column("exception", postgresql.JSON(astext_type=sa.Text()), nullable=True), - ) - # ### end Alembic commands ### - - -def downgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.drop_column("invocation_span", "exception") - # ### end Alembic commands ### From 5d083cebd3fca6b53159d889427b110995039a20 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Thu, 7 Nov 2024 18:37:03 +0100 Subject: [PATCH 266/305] bump prerelease --- agenta-cli/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index 019ec0f589..110eb4e56e 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.27.0a11" +version = "0.27.0a12" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] From aced747df2c463aa3b440769b31fbc410cc122fb Mon Sep 17 00:00:00 2001 From: Abraham 'Abram' Israel Date: Thu, 7 Nov 2024 18:29:14 +0100 Subject: [PATCH 267/305] fix (bug): resolve 'Engine' object has no attribute 'get_session' when deleting an app --- .../agenta_backend/services/db_manager.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/agenta-backend/agenta_backend/services/db_manager.py b/agenta-backend/agenta_backend/services/db_manager.py index cb78ce4404..aae6b6c100 100644 --- a/agenta-backend/agenta_backend/services/db_manager.py +++ b/agenta-backend/agenta_backend/services/db_manager.py @@ -300,7 +300,7 @@ async def fetch_app_environment_revision_by_environment( assert app_environment_id is not None, "app_environment_id cannot be None" assert revision is not None, "revision cannot be None" - async with engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppEnvironmentRevisionDB).filter_by( environment_id=uuid.UUID(app_environment_id), @@ -335,7 +335,7 @@ async def fetch_app_environment_revision_by_version( raise Exception("version cannot be negative") elif version and version > 0: - async with engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppEnvironmentRevisionDB) .filter_by( @@ -355,7 +355,7 @@ async def fetch_app_environment_revision_by_version( return app_environment_revision, version else: - async with engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppEnvironmentRevisionDB) .filter_by( @@ -448,7 +448,7 @@ async def fetch_app_variant_by_config_name_and_appid( AppVariantDB: the instance of the app variant """ - async with engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantDB).filter_by( config_name=config_name, app_id=uuid.UUID(app_id) @@ -825,7 +825,7 @@ async def list_app_variants_by_app_slug( if app_db is None: raise NoResultFound(f"App with name {app_slug} not found.") - async with engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantDB) .filter_by(app_id=app_db.id) @@ -853,7 +853,7 @@ async def fetch_app_variant_by_slug(variant_slug: str, app_id: str) -> AppVarian AppVariantDB: the instance of the app variant """ - async with engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantDB).filter_by( config_name=variant_slug, app_id=uuid.UUID(app_id) @@ -1437,7 +1437,7 @@ async def fetch_app_environment_by_id( AppEnvironmentDB: app environment object """ - async with engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppEnvironmentDB).filter_by(id=uuid.UUID(environment_id)) ) @@ -1457,7 +1457,7 @@ async def fetch_app_environment_revision_by_app_variant_revision_id( AppEnvironmentRevisionDB: app environment object """ - async with engine.get_session() as session: + async with engine.session() as session: query = select(AppEnvironmentRevisionDB).filter_by( deployed_app_variant_revision_id=uuid.UUID(app_variant_revision_id), ) @@ -1729,7 +1729,7 @@ async def list_app_variant_revisions_by_variant( List[AppVariantRevisionsDB]: A list of AppVariantRevisionsDB objects. """ - async with engine.get_session() as session: + async with engine.session() as session: base_query = ( select(AppVariantRevisionsDB) .filter_by(variant_id=app_variant.id, project_id=uuid.UUID(project_id)) @@ -2792,7 +2792,7 @@ async def fetch_app_by_name_and_parameters( if project_id is None: raise ValueError("Either workspace_id or project_id must be provided.") - async with engine.get_session() as session: + async with engine.session() as session: query = select(AppDB).filter_by( app_name=app_name, project_id=uuid.UUID(project_id) ) From c199285f01dafe91a56013687d88cfa5cbcee597 Mon Sep 17 00:00:00 2001 From: Abraham 'Abram' Israel Date: Thu, 7 Nov 2024 18:47:01 +0100 Subject: [PATCH 268/305] fix (bug): resolve ImportNotFound when importing 'router_context' instead of 'routing_context' from sdk.decorators --- agenta-cli/agenta/sdk/decorators/tracing.py | 4 +++- agenta-cli/agenta/sdk/managers/config.py | 14 +++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/agenta-cli/agenta/sdk/decorators/tracing.py b/agenta-cli/agenta/sdk/decorators/tracing.py index 28dc1a4938..2f82728743 100644 --- a/agenta-cli/agenta/sdk/decorators/tracing.py +++ b/agenta-cli/agenta/sdk/decorators/tracing.py @@ -214,7 +214,9 @@ def _redact( not in ( ignore if isinstance(ignore, list) - else io.keys() if ignore is True else [] + else io.keys() + if ignore is True + else [] ) } diff --git a/agenta-cli/agenta/sdk/managers/config.py b/agenta-cli/agenta/sdk/managers/config.py index 9e98f6da0c..c21eb19ab6 100644 --- a/agenta-cli/agenta/sdk/managers/config.py +++ b/agenta-cli/agenta/sdk/managers/config.py @@ -7,7 +7,7 @@ from pydantic import BaseModel from agenta.sdk.managers.shared import SharedManager -from agenta.sdk.decorators.routing import route_context +from agenta.sdk.decorators.routing import routing_context T = TypeVar("T", bound=BaseModel) @@ -45,7 +45,7 @@ def get_from_route( Only one of these should be provided. """ - context = route_context.get() + context = routing_context.get() parameters = None @@ -120,7 +120,7 @@ async def async_get_from_route( Only one of these should be provided. """ - context = route_context.get() + context = routing_context.get() parameters = None @@ -209,9 +209,9 @@ def get_from_registry( ) if schema: - return schema(**config.parameters) + return schema(**config.params) - return config.parameters + return config.params @staticmethod async def async_get_from_registry( @@ -255,9 +255,9 @@ async def async_get_from_registry( ) if schema: - return schema(**config.parameters) + return schema(**config.params) - return config.parameters + return config.params @staticmethod def get_from_yaml( From cafb1ccc9eb98dbc686a3a1560322c49537564be Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Thu, 7 Nov 2024 19:30:52 +0100 Subject: [PATCH 269/305] fix(frontend): Fix type error in custom capture function by returning CaptureResult --- agenta-web/src/hooks/usePostHogAg.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agenta-web/src/hooks/usePostHogAg.ts b/agenta-web/src/hooks/usePostHogAg.ts index 99fee441d6..8b3a4655e6 100644 --- a/agenta-web/src/hooks/usePostHogAg.ts +++ b/agenta-web/src/hooks/usePostHogAg.ts @@ -12,8 +12,9 @@ export const usePostHogAg = () => { const capture: typeof posthog.capture = (...args) => { if (trackingEnabled && user?.id) { - posthog.capture(...args) + return posthog.capture(...args) } + return undefined } const identify: typeof posthog.identify = (id, ...args) => { From 8dfe350733808af4ae5ad4e9b8a2fff52a4a5baa Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Fri, 8 Nov 2024 01:56:01 +0100 Subject: [PATCH 270/305] fix(frontend): updated package-lock file --- agenta-web/package-lock.json | 638 ----------------------------------- 1 file changed, 638 deletions(-) diff --git a/agenta-web/package-lock.json b/agenta-web/package-lock.json index 9814a540fa..a8e21e5472 100644 --- a/agenta-web/package-lock.json +++ b/agenta-web/package-lock.json @@ -1705,57 +1705,6 @@ "@napi-rs/nice-win32-x64-msvc": "1.0.1" } }, - "node_modules/@napi-rs/nice-android-arm-eabi": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.0.1.tgz", - "integrity": "sha512-5qpvOu5IGwDo7MEKVqqyAxF90I6aLj4n07OzpARdgDRfz8UbBztTByBp0RC59r3J1Ij8uzYi6jI7r5Lws7nn6w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/nice-android-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.0.1.tgz", - "integrity": "sha512-GqvXL0P8fZ+mQqG1g0o4AO9hJjQaeYG84FRfZaYjyJtZZZcMjXW5TwkL8Y8UApheJgyE13TQ4YNUssQaTgTyvA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/nice-darwin-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.0.1.tgz", - "integrity": "sha512-91k3HEqUl2fsrz/sKkuEkscj6EAj3/eZNCLqzD2AA0TtVbkQi8nqxZCZDMkfklULmxLkMxuUdKe7RvG/T6s2AA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@napi-rs/nice-darwin-x64": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.0.1.tgz", @@ -1773,210 +1722,6 @@ "node": ">= 10" } }, - "node_modules/@napi-rs/nice-freebsd-x64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.0.1.tgz", - "integrity": "sha512-j+iJ/ezONXRQsVIB/FJfwjeQXX7A2tf3gEXs4WUGFrJjpe/z2KB7sOv6zpkm08PofF36C9S7wTNuzHZ/Iiccfw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/nice-linux-arm-gnueabihf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.0.1.tgz", - "integrity": "sha512-G8RgJ8FYXYkkSGQwywAUh84m946UTn6l03/vmEXBYNJxQJcD+I3B3k5jmjFG/OPiU8DfvxutOP8bi+F89MCV7Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/nice-linux-arm64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.0.1.tgz", - "integrity": "sha512-IMDak59/W5JSab1oZvmNbrms3mHqcreaCeClUjwlwDr0m3BoR09ZiN8cKFBzuSlXgRdZ4PNqCYNeGQv7YMTjuA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/nice-linux-arm64-musl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.0.1.tgz", - "integrity": "sha512-wG8fa2VKuWM4CfjOjjRX9YLIbysSVV1S3Kgm2Fnc67ap/soHBeYZa6AGMeR5BJAylYRjnoVOzV19Cmkco3QEPw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/nice-linux-ppc64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.0.1.tgz", - "integrity": "sha512-lxQ9WrBf0IlNTCA9oS2jg/iAjQyTI6JHzABV664LLrLA/SIdD+I1i3Mjf7TsnoUbgopBcCuDztVLfJ0q9ubf6Q==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/nice-linux-riscv64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.0.1.tgz", - "integrity": "sha512-3xs69dO8WSWBb13KBVex+yvxmUeEsdWexxibqskzoKaWx9AIqkMbWmE2npkazJoopPKX2ULKd8Fm9veEn0g4Ig==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/nice-linux-s390x-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.0.1.tgz", - "integrity": "sha512-lMFI3i9rlW7hgToyAzTaEybQYGbQHDrpRkg+1gJWEpH0PLAQoZ8jiY0IzakLfNWnVda1eTYYlxxFYzW8Rqczkg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/nice-linux-x64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.0.1.tgz", - "integrity": "sha512-XQAJs7DRN2GpLN6Fb+ZdGFeYZDdGl2Fn3TmFlqEL5JorgWKrQGRUrpGKbgZ25UeZPILuTKJ+OowG2avN8mThBA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/nice-linux-x64-musl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.0.1.tgz", - "integrity": "sha512-/rodHpRSgiI9o1faq9SZOp/o2QkKQg7T+DK0R5AkbnI/YxvAIEHf2cngjYzLMQSQgUhxym+LFr+UGZx4vK4QdQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/nice-win32-arm64-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.0.1.tgz", - "integrity": "sha512-rEcz9vZymaCB3OqEXoHnp9YViLct8ugF+6uO5McifTedjq4QMQs3DHz35xBEGhH3gJWEsXMUbzazkz5KNM5YUg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/nice-win32-ia32-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.0.1.tgz", - "integrity": "sha512-t7eBAyPUrWL8su3gDxw9xxxqNwZzAqKo0Szv3IjVQd1GpXXVkb6vBBQUuxfIYaXMzZLwlxRQ7uzM2vdUE9ULGw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/nice-win32-x64-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.0.1.tgz", - "integrity": "sha512-JlF+uDcatt3St2ntBG8H02F1mM45i5SF9W+bIKiReVE6wiy3o16oBP/yxt+RZ+N6LbCImJXJ6bXNO2kn9AXicg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@next/env": { "version": "14.2.17", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.17.tgz", @@ -2053,22 +1798,6 @@ } } }, - "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.17", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.17.tgz", - "integrity": "sha512-WiOf5nElPknrhRMTipXYTJcUz7+8IAjOYw3vXzj3BYRcVY0hRHKWgTgQ5439EvzQyHEko77XK+yN9x9OJ0oOog==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@next/swc-darwin-x64": { "version": "14.2.17", "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.17.tgz", @@ -2085,118 +1814,6 @@ "node": ">= 10" } }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.17", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.17.tgz", - "integrity": "sha512-SSHLZls3ZwNEHsc+d0ynKS+7Af0Nr8+KTUBAy9pm6xz9SHkJ/TeuEg6W3cbbcMSh6j4ITvrjv3Oi8n27VR+IPw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.17", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.17.tgz", - "integrity": "sha512-VFge37us5LNPatB4F7iYeuGs9Dprqe4ZkW7lOEJM91r+Wf8EIdViWHLpIwfdDXinvCdLl6b4VyLpEBwpkctJHA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.17", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.17.tgz", - "integrity": "sha512-aaQlpxUVb9RZ41adlTYVQ3xvYEfBPUC8+6rDgmQ/0l7SvK8S1YNJzPmDPX6a4t0jLtIoNk7j+nroS/pB4nx7vQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.17", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.17.tgz", - "integrity": "sha512-HSyEiFaEY3ay5iATDqEup5WAfrhMATNJm8dYx3ZxL+e9eKv10XKZCwtZByDoLST7CyBmyDz+OFJL1wigyXeaoA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.17", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.17.tgz", - "integrity": "sha512-h5qM9Btqv87eYH8ArrnLoAHLyi79oPTP2vlGNSg4CDvUiXgi7l0+5KuEGp5pJoMhjuv9ChRdm7mRlUUACeBt4w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.17", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.17.tgz", - "integrity": "sha512-BD/G++GKSLexQjdyoEUgyo5nClU7er5rK0sE+HlEqnldJSm96CIr/+YOTT063LVTT/dUOeQsNgp5DXr86/K7/A==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.17", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.17.tgz", - "integrity": "sha512-vkQfN1+4V4KqDibkW2q0sJ6CxQuXq5l2ma3z0BRcfIqkAMZiiW67T9yCpwqJKP68QghBtPEFjPAlaqe38O6frw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -4240,108 +3857,6 @@ "node": ">=10" } }, - "node_modules/@sentry/cli-linux-arm": { - "version": "2.38.2", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.38.2.tgz", - "integrity": "sha512-+AiKDBQKIdQe4NhBiHSHGl0KR+b//HHTrnfK1SaTrOm9HtM4ELXAkjkRF3bmbpSzSQCS5WzcbIxxCJOeaUaO0A==", - "cpu": [ - "arm" - ], - "license": "BSD-3-Clause", - "optional": true, - "os": [ - "linux", - "freebsd" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@sentry/cli-linux-arm64": { - "version": "2.38.2", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.38.2.tgz", - "integrity": "sha512-4Fp/jjQpNZj4Th+ZckMQvldAuuP0ZcyJ9tJCP1CCOn5poIKPYtY6zcbTP036R7Te14PS4ALOcDNX3VNKfpsifA==", - "cpu": [ - "arm64" - ], - "license": "BSD-3-Clause", - "optional": true, - "os": [ - "linux", - "freebsd" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@sentry/cli-linux-i686": { - "version": "2.38.2", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.38.2.tgz", - "integrity": "sha512-6zVJN10dHIn4R1v+fxuzlblzVBhIVwsaN/S7aBED6Vn1HhAyAcNG2tIzeCLGeDfieYjXlE2sCI82sZkQBCbAGw==", - "cpu": [ - "x86", - "ia32" - ], - "license": "BSD-3-Clause", - "optional": true, - "os": [ - "linux", - "freebsd" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@sentry/cli-linux-x64": { - "version": "2.38.2", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.38.2.tgz", - "integrity": "sha512-4UiLu9zdVtqPeltELR5MDGKcuqAdQY9xz3emISuA6bm+MXGbt2W1WgX+XY3GElwjZbmH8qpyLUEd34sw6sdcbQ==", - "cpu": [ - "x64" - ], - "license": "BSD-3-Clause", - "optional": true, - "os": [ - "linux", - "freebsd" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@sentry/cli-win32-i686": { - "version": "2.38.2", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.38.2.tgz", - "integrity": "sha512-DYfSvd5qLPerLpIxj3Xu2rRe3CIlpGOOfGSNI6xvJ5D8j6hqbOHlCzvfC4oBWYVYGtxnwQLMeDGJ7o7RMYulig==", - "cpu": [ - "x86", - "ia32" - ], - "license": "BSD-3-Clause", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@sentry/cli-win32-x64": { - "version": "2.38.2", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.38.2.tgz", - "integrity": "sha512-W5UX58PKY1hNUHo9YJxWNhGvgvv2uOYHI27KchRiUvFYBIqlUUcIdPZDfyzetDfd8qBCxlAsFnkL2VJSNdpA9A==", - "cpu": [ - "x64" - ], - "license": "BSD-3-Clause", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, "node_modules/@sentry/core": { "version": "8.37.1", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.37.1.tgz", @@ -4718,23 +4233,6 @@ } } }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.9.1.tgz", - "integrity": "sha512-2/ncHSCdAh5OHem1fMITrWEzzl97OdMK1PHc9CkxSJnphLjRubfxB5sbc5tDhcO68a5tVy+DxwaBgDec3PXnOg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, "node_modules/@swc/core-darwin-x64": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.9.1.tgz", @@ -4752,142 +4250,6 @@ "node": ">=10" } }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.9.1.tgz", - "integrity": "sha512-eVW/BjRW8/HpLe3+1jRU7w7PdRLBgnEEYTkHJISU8805/EKT03xNZn6CfaBpKfeAloY4043hbGzE/NP9IahdpQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.9.1.tgz", - "integrity": "sha512-8m3u1v8R8NgI/9+cHMkzk14w87blSy3OsQPWPfhOL+XPwhyLPvat+ahQJb2nZmltjTgkB4IbzKFSfbuA34LmNA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.9.1.tgz", - "integrity": "sha512-hpT0sQAZnW8l02I289yeyFfT9llGO9PzKDxUq8pocKtioEHiElRqR53juCWoSmzuWi+6KX7zUJ0NKCBrc8pmDg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.9.1.tgz", - "integrity": "sha512-sGFdpdAYusk/ropHiwtXom2JrdaKPxl8MqemRv6dvxZq1Gm/GdmOowxdXIPjCgBGMgoXVcgNviH6CgiO5q+UtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.9.1.tgz", - "integrity": "sha512-YtNLNwIWs0Z2+XgBs6+LrCIGtfCDtNr4S4b6Q5HDOreEIGzSvhkef8eyBI5L+fJ2eGov4b7iEo61C4izDJS5RA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.9.1.tgz", - "integrity": "sha512-qSxD3uZW2vSiHqUt30vUi0PB92zDh9bjqh5YKpfhhVa7h1vt/xXhlid8yMvSNToTfzhRrTEffOAPUr7WVoyQUA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.9.1.tgz", - "integrity": "sha512-C3fPEwyX/WRPlX6zIToNykJuz1JkZX0sk8H1QH2vpnKuySUkt/Ur5K2FzLgSWzJdbfxstpgS151/es0VGAD+ZA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.9.1.tgz", - "integrity": "sha512-2XZ+U1AyVsOAXeH6WK1syDm7+gwTjA8fShs93WcbxnK7HV+NigDlvr4124CeJLTHyh3fMh1o7+CnQnaBJhlysQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", From e59ce136a631d71e7f41b601a3db057250ba2dc7 Mon Sep 17 00:00:00 2001 From: Ashraf Chowdury Date: Fri, 8 Nov 2024 11:18:49 +0600 Subject: [PATCH 271/305] fix(frontend): avatar profiles color --- agenta-web/src/components/Avatar/Avatar.tsx | 27 +++++++++++ agenta-web/src/components/Sidebar/Sidebar.tsx | 39 +++------------- .../evaluationCompare/EvaluationCompare.tsx | 10 ++--- agenta-web/src/lib/helpers/colors.ts | 45 ++++++++----------- 4 files changed, 56 insertions(+), 65 deletions(-) create mode 100644 agenta-web/src/components/Avatar/Avatar.tsx diff --git a/agenta-web/src/components/Avatar/Avatar.tsx b/agenta-web/src/components/Avatar/Avatar.tsx new file mode 100644 index 0000000000..8ac926b56c --- /dev/null +++ b/agenta-web/src/components/Avatar/Avatar.tsx @@ -0,0 +1,27 @@ +import React from "react" +import {Avatar as MainAvatar} from "antd" +import {getColorPairFromStr} from "@/lib/helpers/colors" +import {getInitials} from "@/lib/helpers/utils" + +type Props = { + name: string +} & React.ComponentProps + +const Avatar: React.FC = ({name, ...props}) => { + const color = getColorPairFromStr(name || "") + + return ( + + {getInitials(name)} + + ) +} + +export default Avatar diff --git a/agenta-web/src/components/Sidebar/Sidebar.tsx b/agenta-web/src/components/Sidebar/Sidebar.tsx index d9106b2aa5..a90e43fef6 100644 --- a/agenta-web/src/components/Sidebar/Sidebar.tsx +++ b/agenta-web/src/components/Sidebar/Sidebar.tsx @@ -1,17 +1,6 @@ import React, {useEffect, useMemo, useState} from "react" import {useRouter} from "next/router" -import { - Avatar, - Button, - Divider, - Dropdown, - Layout, - Menu, - Space, - Tag, - Tooltip, - Typography, -} from "antd" +import {Button, Divider, Dropdown, Layout, Menu, Space, Tag, Tooltip, Typography} from "antd" import Logo from "../Logo/Logo" import Link from "next/link" import {useAppTheme} from "../Layout/ThemeContextProvider" @@ -20,13 +9,13 @@ import {createUseStyles} from "react-jss" import {useLocalStorage} from "usehooks-ts" import {SidebarConfig, useSidebarConfig} from "./config" import {JSSTheme} from "@/lib/Types" -import {getColorFromStr} from "@/lib/helpers/colors" -import {getInitials, isDemo} from "@/lib/helpers/utils" +import {isDemo} from "@/lib/helpers/utils" import {useProfileData} from "@/contexts/profile.context" import {useSession} from "@/hooks/useSession" import {CaretDown, Gear, SignOut} from "@phosphor-icons/react" import AlertPopup from "../AlertPopup/AlertPopup" import {dynamicContext} from "@/lib/helpers/dynamic" +import Avatar from "@/components/Avatar/Avatar" const {Sider} = Layout const {Text} = Typography @@ -117,10 +106,6 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ }, }, }, - userAvatar: { - backgroundColor: theme.colorPrimaryBgHover, - color: theme.colorPrimary, - }, menuHeader: { padding: `${theme.paddingXS}px ${theme.padding}px`, color: theme.colorTextDescription, @@ -375,13 +360,7 @@ const Sidebar: React.FC = () => { key: org.id, label: ( - - {getInitials(org.name)} - + {org.name} ), @@ -428,15 +407,7 @@ const Sidebar: React.FC = () => { > diff --git a/agenta-web/src/contexts/observability.context.tsx b/agenta-web/src/contexts/observability.context.tsx index dee93da392..2ef23af639 100644 --- a/agenta-web/src/contexts/observability.context.tsx +++ b/agenta-web/src/contexts/observability.context.tsx @@ -132,9 +132,14 @@ const ObservabilityContextProvider: React.FC = ({children}) = if (sort) { if (sort.type === "standard") { params.oldest = sort.sorted - } else if (sort.type === "custom" && sort.customRange?.startTime) { - params.oldest = sort.customRange.startTime - params.newest = sort.customRange.endTime + } else if ( + sort.type === "custom" && + (sort.customRange?.startTime || sort.customRange?.endTime) + ) { + const {startTime, endTime} = sort.customRange + + if (startTime) params.oldest = startTime + if (endTime) params.newest = endTime } } From 7af7b5006ab82cc7dab64ec8f24c386780ab7216 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Sat, 9 Nov 2024 16:49:46 +0600 Subject: [PATCH 284/305] fix(frontend): trace drawer time, cost font style --- .../observability/drawer/TraceContent.tsx | 18 ++++++++++++------ .../pages/observability/drawer/TraceTree.tsx | 16 +++++++++++----- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx index a81f7d2b0e..2c76c0ed1a 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx @@ -72,6 +72,12 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ fontWeight: 400, }, }, + resultTag: { + display: "flex", + alignItems: "center", + fontFamily: "monospace", + gap: 4, + }, })) const TraceContent = ({activeTrace}: TraceContentProps) => { @@ -289,19 +295,19 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { +
{" "} {formatLatency(activeTrace?.metrics?.acc?.duration.total / 1000)} - +
} /> +
{formatTokenUsage(activeTrace.metrics?.acc?.tokens?.total)} /{" "} {formatCurrency(activeTrace.metrics?.acc?.costs?.total)} - +
} popoverContent={ @@ -324,11 +330,11 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { /> +
{dayjs(activeTrace.time.start).format("DD/MM/YYYY, hh:mm:ss A")} {" "} {dayjs(activeTrace.time.end).format("DD/MM/YYYY, hh:mm:ss A")} - +
} /> diff --git a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx index 541a4bb8bd..b3371c8f60 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceTree.tsx @@ -57,7 +57,7 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ height: "100%", width: "calc(210px - 40px)", }, - treeContent: { + treeContentContainer: { color: theme.colorTextSecondary, "& div": { display: "flex", @@ -66,6 +66,12 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ fontSize: theme.fontSize, }, }, + treeContent: { + display: "flex", + alignItems: "center", + fontFamily: "monospace", + gap: 2, + }, })) const TreeContent = ({value}: {value: _AgentaRootsResponse}) => { @@ -87,21 +93,21 @@ const TreeContent = ({value}: {value: _AgentaRootsResponse}) => { > {node.name} - -
+ +
{formatLatency(metrics?.acc?.duration.total / 1000)}
{metrics?.acc?.costs?.total && ( -
+
{formatCurrency(metrics?.acc?.costs?.total)}
)} {!!metrics?.acc?.tokens?.total && ( -
+
{formatTokenUsage(metrics?.acc?.tokens?.total)}
From e34462d45aca8fac2b0257896a4f5c544dc60fba Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sat, 9 Nov 2024 14:27:45 +0100 Subject: [PATCH 285/305] fix(frontend): stringified variant parameter values --- .../components/pages/overview/variants/VariantDrawer.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/agenta-web/src/components/pages/overview/variants/VariantDrawer.tsx b/agenta-web/src/components/pages/overview/variants/VariantDrawer.tsx index 87fc78fb80..86da7c8ceb 100644 --- a/agenta-web/src/components/pages/overview/variants/VariantDrawer.tsx +++ b/agenta-web/src/components/pages/overview/variants/VariantDrawer.tsx @@ -1,6 +1,10 @@ import ResultTag from "@/components/ResultTag/ResultTag" import {useAppId} from "@/hooks/useAppId" -import {filterVariantParameters, formatVariantIdWithHash} from "@/lib/helpers/utils" +import { + filterVariantParameters, + formatVariantIdWithHash, + getStringOrJson, +} from "@/lib/helpers/utils" import {variantNameWithRev} from "@/lib/helpers/variantHelper" import {Environment, JSSTheme, Variant} from "@/lib/Types" import {CloseOutlined, MoreOutlined} from "@ant-design/icons" @@ -153,7 +157,6 @@ const VariantDrawer = ({ label: "Configuration", children: (
- {" "} {selectedVariant.parameters && Object.keys(selectedVariant.parameters).length ? (
@@ -173,7 +176,7 @@ const VariantDrawer = ({ ))}
From 1fb5d3972265d1e1d54dd9e88664dc9119e3dd64 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sat, 9 Nov 2024 14:43:51 +0100 Subject: [PATCH 286/305] fix(frontend): displayed data in raw data accordion --- .../observability/drawer/TraceContent.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx index a81f7d2b0e..e213792080 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx @@ -75,7 +75,7 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ })) const TraceContent = ({activeTrace}: TraceContentProps) => { - const {data, key, children, ...filteredTrace} = activeTrace + const {key, children, ...filteredTrace} = activeTrace const classes = useStyles() const [tab, setTab] = useState("overview") const {icon, bgColor, color} = statusMapper(activeTrace.node.type) @@ -131,16 +131,16 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { )} - {data && data?.inputs ? ( + {activeTrace.data && activeTrace.data?.inputs ? ( {activeTrace.node.type !== "chat" ? ( ) : ( - Object.entries(transformDataInputs(data.inputs)).map( + Object.entries(transformDataInputs(activeTrace.data?.inputs)).map( ([key, values]) => { if (key === "prompt") { return Array.isArray(values) @@ -171,16 +171,16 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { ) : null} - {data && data?.outputs ? ( + {activeTrace.data && activeTrace.data?.outputs ? ( {activeTrace.node.type !== "chat" ? ( ) : ( - Object.values(data.outputs).map((item) => + Object.values(activeTrace.data.outputs).map((item) => Array.isArray(item) ? item.map((param, index) => !!param.content && @@ -206,12 +206,12 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { ) : null} - {data && data?.internals && ( + {activeTrace.data && activeTrace.data?.internals && ( {activeTrace.node.type !== "chat" && ( )} From 02efb08fd5361b602baf4c73573a8e25581dfc74 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Sun, 10 Nov 2024 12:41:47 +0600 Subject: [PATCH 287/305] fix(frontend): evaluation result delete redirection url --- .../evaluations/evaluationScenarios/EvaluationScenarios.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx b/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx index 6b23740923..f03fde7dc8 100644 --- a/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx +++ b/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx @@ -334,7 +334,7 @@ const EvaluationScenarios: React.FC = () => { message: "Are you sure you want to delete this evaluation?", onOk: () => deleteEvaluations([evaluationId]) - .then(() => router.push(`/apps/${appId}/evaluations/results`)) + .then(() => router.push(`/apps/${appId}/evaluations`)) .catch(console.error), }) } From fba794a7b75b6dd8e2509229bf66ef1b3586a1c0 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Sun, 10 Nov 2024 15:10:56 +0600 Subject: [PATCH 288/305] fix(frontend): made initial app seletor ui responsive --- agenta-web/src/components/AppSelector/AppSelector.tsx | 2 +- agenta-web/src/components/AppSelector/AppTemplateCard.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agenta-web/src/components/AppSelector/AppSelector.tsx b/agenta-web/src/components/AppSelector/AppSelector.tsx index 7a0527d2a2..6282d8d8b2 100644 --- a/agenta-web/src/components/AppSelector/AppSelector.tsx +++ b/agenta-web/src/components/AppSelector/AppSelector.tsx @@ -369,7 +369,7 @@ const AppSelector: React.FC = () => { footer={null} title={null} className={classes.modal} - width={steps.length === 3 && current == 0 ? 845 : 480} + width={steps.length === 3 && current == 0 ? 855 : 480} centered > {steps[current]?.content} diff --git a/agenta-web/src/components/AppSelector/AppTemplateCard.tsx b/agenta-web/src/components/AppSelector/AppTemplateCard.tsx index 864cc1ef7f..eab31fb542 100644 --- a/agenta-web/src/components/AppSelector/AppTemplateCard.tsx +++ b/agenta-web/src/components/AppSelector/AppTemplateCard.tsx @@ -64,7 +64,7 @@ const AppTemplateCard: React.FC = ({onWriteOwnApp, onCreateFromTemplate}) "Evaluate workflows end-to-end in the web UI", ] return ( -
+
Date: Sun, 10 Nov 2024 12:16:21 +0100 Subject: [PATCH 289/305] fix (tests): resolve failing backend tests --- .../tests/configs_prompt/conftest.py | 20 +++++++++---------- .../tests/variants_main_router/conftest.py | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/agenta-backend/agenta_backend/tests/configs_prompt/conftest.py b/agenta-backend/agenta_backend/tests/configs_prompt/conftest.py index 99b0a13f85..f435749e7d 100644 --- a/agenta-backend/agenta_backend/tests/configs_prompt/conftest.py +++ b/agenta-backend/agenta_backend/tests/configs_prompt/conftest.py @@ -7,7 +7,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from agenta_backend.services import db_manager -from agenta_backend.models.db.postgres_engine import db_engine +from agenta_backend.dbs.postgres.shared.engine import engine from agenta_backend.models.db_models import ( ProjectDB, AppDB, @@ -42,7 +42,7 @@ @pytest.fixture() async def get_or_create_user_from_db(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(UserDB).filter_by(uid="0")) user = result.scalars().first() if user is None: @@ -56,7 +56,7 @@ async def get_or_create_user_from_db(): @pytest.fixture() async def get_or_create_project_from_db(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(ProjectDB).filter_by(is_default=True)) project = result.scalars().first() if project is None: @@ -70,7 +70,7 @@ async def get_or_create_project_from_db(): @pytest.fixture() async def get_app_variant_by_slug(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantDB).filter_by(config_name="from_pytest") ) @@ -80,7 +80,7 @@ async def get_app_variant_by_slug(): @pytest.fixture() async def get_app_by_name(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppDB).filter_by(app_name="test_prompt_client") ) @@ -90,7 +90,7 @@ async def get_app_by_name(): @pytest.fixture() async def get_production_environment_by_name(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppEnvironmentDB).filter_by(name="production") ) @@ -100,7 +100,7 @@ async def get_production_environment_by_name(): @pytest.fixture() async def get_user_from_db(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute(select(UserDB).filter_by(uid="0")) user = result.scalars().first() return user @@ -113,7 +113,7 @@ async def get_app_variant_revision_by_variant_id(get_app_variant_by_slug): variant, AppVariantDB ), "App variant gotten from fixture is not of type AppVariantDB" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppVariantRevisionsDB).filter_by(variant_id=variant.id) ) @@ -130,7 +130,7 @@ async def get_environment_revision_by_environment_id( environment, AppEnvironmentDB ), "Production environment gotten from fixture is not of type AppEnvironmentDB" - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(AppEnvironmentRevisionDB).filter_by(environment_id=environment.id) ) @@ -172,7 +172,7 @@ async def create_app_variant_for_prompt_management( project_db, ProjectDB ), "Project gotten from fixture is not of type ProjectDB" - async with db_engine.get_session() as session: + async with engine.session() as session: app = AppDB( id=uuid.UUID(APP_ID), app_name="test_prompt_client", diff --git a/agenta-backend/agenta_backend/tests/variants_main_router/conftest.py b/agenta-backend/agenta_backend/tests/variants_main_router/conftest.py index d0da1cc4b2..e2098fea2a 100644 --- a/agenta-backend/agenta_backend/tests/variants_main_router/conftest.py +++ b/agenta-backend/agenta_backend/tests/variants_main_router/conftest.py @@ -75,7 +75,7 @@ async def get_second_user_object(): @pytest.fixture() async def get_or_create_project_from_db(): - async with db_engine.get_session() as session: + async with engine.session() as session: result = await session.execute( select(ProjectDB).filter_by(project_name="default", is_default=True) ) @@ -94,7 +94,7 @@ async def get_first_user_app(get_first_user_object, get_or_create_project_from_d user = await get_first_user_object project = await get_or_create_project_from_db - async with engine.get_session() as session: + async with engine.session() as session: app = AppDB(app_name="myapp", project_id=project.id) session.add(app) await session.commit() From 7090cb7152daa67f7fdf9bb1a65f14c76e2ccefe Mon Sep 17 00:00:00 2001 From: Abraham 'Abram' Israel Date: Mon, 11 Nov 2024 09:54:25 +0100 Subject: [PATCH 290/305] cleanup (backend): resolve pydantic v1 deprecated warnings from the backend --- agenta-backend/agenta_backend/models/api/testset_model.py | 5 ++--- .../agenta_backend/routers/human_evaluation_router.py | 4 ++-- docs/docs/reference/openapi.json | 3 ++- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/agenta-backend/agenta_backend/models/api/testset_model.py b/agenta-backend/agenta_backend/models/api/testset_model.py index 02ccbe62b9..60eae9560c 100644 --- a/agenta-backend/agenta_backend/models/api/testset_model.py +++ b/agenta-backend/agenta_backend/models/api/testset_model.py @@ -1,4 +1,3 @@ -from datetime import datetime from typing import Any, List, Dict from pydantic import BaseModel, Field @@ -10,7 +9,7 @@ class TestsetModel(BaseModel): app_id: str = Field(...) class Config: - schema_extra = { + json_schema_extra = { "example": { "column_name": "column1", "column_value": "value1", @@ -51,4 +50,4 @@ class TestSetOutputResponse(BaseModel): updated_at: str class Config: - allow_population_by_field_name = True + populate_by_name = True diff --git a/agenta-backend/agenta_backend/routers/human_evaluation_router.py b/agenta-backend/agenta_backend/routers/human_evaluation_router.py index a5573923e3..7cd04f7c99 100644 --- a/agenta-backend/agenta_backend/routers/human_evaluation_router.py +++ b/agenta-backend/agenta_backend/routers/human_evaluation_router.py @@ -42,9 +42,9 @@ @router.post( - "/", response_model=SimpleEvaluationOutput, operation_id="create_evaluation" + "/", response_model=SimpleEvaluationOutput, operation_id="create_human_evaluation" ) -async def create_evaluation( +async def create_human_evaluation( payload: NewHumanEvaluation, request: Request, ): diff --git a/docs/docs/reference/openapi.json b/docs/docs/reference/openapi.json index 123feb46f7..247f50032e 100644 --- a/docs/docs/reference/openapi.json +++ b/docs/docs/reference/openapi.json @@ -2707,7 +2707,7 @@ ], "summary": "Create Evaluation", "description": "Creates a new comparison table document\nRaises:\n HTTPException: _description_\nReturns:\n _description_", - "operationId": "create_evaluation", + "operationId": "create_human_evaluation", "requestBody": { "content": { "application/json": { @@ -5957,6 +5957,7 @@ "run_evaluations", "edit_evaluation", "create_evaluation", + "create_human_evaluation", "delete_evaluation", "deploy_application", "view_workspace", From 309f1a67e27b7f9b6bfa0f786779a2082a0217b1 Mon Sep 17 00:00:00 2001 From: Abraham 'Abram' Israel Date: Mon, 11 Nov 2024 10:28:41 +0100 Subject: [PATCH 291/305] cleanup (backend): update operation_id for fetch_human_evaluation_scenario API function name --- .../agenta_backend/routers/human_evaluation_router.py | 4 ++-- docs/docs/reference/openapi.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/agenta-backend/agenta_backend/routers/human_evaluation_router.py b/agenta-backend/agenta_backend/routers/human_evaluation_router.py index 7cd04f7c99..c91f0a55dd 100644 --- a/agenta-backend/agenta_backend/routers/human_evaluation_router.py +++ b/agenta-backend/agenta_backend/routers/human_evaluation_router.py @@ -168,9 +168,9 @@ async def fetch_human_evaluation( @router.get( "/{evaluation_id}/evaluation_scenarios/", response_model=List[HumanEvaluationScenario], - operation_id="fetch_evaluation_scenarios", + operation_id="fetch_human_evaluation_scenarios", ) -async def fetch_evaluation_scenarios( +async def fetch_human_evaluation_scenarios( evaluation_id: str, request: Request, ): diff --git a/docs/docs/reference/openapi.json b/docs/docs/reference/openapi.json index 247f50032e..300bd62509 100644 --- a/docs/docs/reference/openapi.json +++ b/docs/docs/reference/openapi.json @@ -2885,7 +2885,7 @@ ], "summary": "Fetch Evaluation Scenarios", "description": "Fetches evaluation scenarios for a given evaluation ID.\n\nArguments:\n evaluation_id (str): The ID of the evaluation for which to fetch scenarios.\n\nRaises:\n HTTPException: If the evaluation is not found or access is denied.\n\nReturns:\n List[EvaluationScenario]: A list of evaluation scenarios.", - "operationId": "fetch_evaluation_scenarios", + "operationId": "fetch_human_evaluation_scenarios", "parameters": [ { "required": true, From 1c3c73d7727054b649b98d50ad4a7bf18ff46da1 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Mon, 11 Nov 2024 10:44:47 +0100 Subject: [PATCH 292/305] fix issue with exceptions --- .../agenta_backend/core/observability/dtos.py | 9 +++++++++ .../dbs/postgres/observability/mappings.py | 14 ++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/agenta-backend/agenta_backend/core/observability/dtos.py b/agenta-backend/agenta_backend/core/observability/dtos.py index 9a70f94d43..6375aec13d 100644 --- a/agenta-backend/agenta_backend/core/observability/dtos.py +++ b/agenta-backend/agenta_backend/core/observability/dtos.py @@ -83,6 +83,12 @@ class ExceptionDTO(BaseModel): stacktrace: Optional[str] = None attributes: Optional[Attributes] = None + class Config: + json_encoders = { + UUID: lambda v: str(v), # pylint: disable=unnecessary-lambda + datetime: lambda dt: dt.isoformat(), + } + Data = Dict[str, Any] Metrics = Dict[str, Any] @@ -97,6 +103,9 @@ class LinkDTO(BaseModel): class Config: use_enum_values = True + json_encoders = { + UUID: lambda v: str(v), # pylint: disable=unnecessary-lambda + } class OTelSpanKind(Enum): diff --git a/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py index 02308e5af1..fa160de8b6 100644 --- a/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py +++ b/agenta-backend/agenta_backend/dbs/postgres/observability/mappings.py @@ -1,4 +1,4 @@ -from json import dumps +from json import dumps, loads from agenta_backend.core.shared.dtos import LifecycleDTO from agenta_backend.core.observability.dtos import ( @@ -109,13 +109,19 @@ def map_span_dto_to_dbe( meta=span_dto.encode(span_dto.meta), refs=span_dto.encode(span_dto.refs), # EVENTS - exception=span_dto.encode(span_dto.exception) if span_dto.exception else None, + exception=( + loads(span_dto.exception.model_dump_json()) if span_dto.exception else None + ), # LINKS - links=span_dto.encode(span_dto.links), + links=( + [loads(link.model_dump_json()) for link in span_dto.links] + if span_dto.links + else None + ), # FULL TEXT SEARCH content=dumps(span_dto.data), # OTEL - otel=span_dto.otel.model_dump(exclude_none=True), + otel=loads(span_dto.otel.model_dump_json()) if span_dto.otel else None, ) return span_dbe From c67faabed89fec4b873278f3f3e03a61a2096375 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Mon, 11 Nov 2024 15:46:30 +0600 Subject: [PATCH 293/305] fix(backend): predefined app enviroment order --- agenta-backend/agenta_backend/routers/app_router.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/agenta-backend/agenta_backend/routers/app_router.py b/agenta-backend/agenta_backend/routers/app_router.py index c059d89cf2..a076f5a239 100644 --- a/agenta-backend/agenta_backend/routers/app_router.py +++ b/agenta-backend/agenta_backend/routers/app_router.py @@ -678,8 +678,13 @@ async def list_environments( app_id=app_id, project_id=request.state.project_id ) logger.debug(f"environments_db: {environments_db}") + + fixed_order = ["development", "staging", "production"] + + sorted_environments = sorted(environments_db, key=lambda env: (fixed_order + [env.name]).index(env.name)) + return [ - await converters.environment_db_to_output(env) for env in environments_db + await converters.environment_db_to_output(env) for env in sorted_environments ] except Exception as e: logger.exception(f"An error occurred: {str(e)}") From 327cf6aaf30fa56c4d1c012d9a6ca4dc3022efcc Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Mon, 11 Nov 2024 15:57:34 +0600 Subject: [PATCH 294/305] fix(backend): code format --- agenta-backend/agenta_backend/routers/app_router.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/agenta-backend/agenta_backend/routers/app_router.py b/agenta-backend/agenta_backend/routers/app_router.py index a076f5a239..86204c8724 100644 --- a/agenta-backend/agenta_backend/routers/app_router.py +++ b/agenta-backend/agenta_backend/routers/app_router.py @@ -681,10 +681,13 @@ async def list_environments( fixed_order = ["development", "staging", "production"] - sorted_environments = sorted(environments_db, key=lambda env: (fixed_order + [env.name]).index(env.name)) + sorted_environments = sorted( + environments_db, key=lambda env: (fixed_order + [env.name]).index(env.name) + ) return [ - await converters.environment_db_to_output(env) for env in sorted_environments + await converters.environment_db_to_output(env) + for env in sorted_environments ] except Exception as e: logger.exception(f"An error occurred: {str(e)}") From 47993a76ae3b299fe8c2ea850d0a0863aae01b08 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Mon, 11 Nov 2024 11:08:23 +0100 Subject: [PATCH 295/305] fix error where invalid node_type makes the ingestion of traces fails, by setting 'TASK' as default node_type --- .../agenta_backend/apis/fastapi/observability/utils.py | 9 +++++++-- agenta-cli/agenta/sdk/tracing/inline.py | 8 ++++++-- agenta-cli/pyproject.toml | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py index b4dc37c79b..f2af84df65 100644 --- a/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py +++ b/agenta-backend/agenta_backend/apis/fastapi/observability/utils.py @@ -24,6 +24,7 @@ OTelSpanDTO, OTelContextDTO, OTelLinkDTO, + NodeType, ) from agenta_backend.core.observability.dtos import ( GroupingDTO, @@ -465,11 +466,15 @@ def parse_from_otel_span_dto( node_id = UUID(tree_id.hex[16:] + otel_span_dto.context.span_id[2:]) - node_type: str = types.get("node") + node_type = NodeType.TASK + try: + node_type = NodeType(types.get("node", "").lower()) + except: # pylint: disable=bare-except + pass node = NodeDTO( id=node_id, - type=node_type.lower() if node_type else None, + type=node_type, name=otel_span_dto.name, ) diff --git a/agenta-cli/agenta/sdk/tracing/inline.py b/agenta-cli/agenta/sdk/tracing/inline.py index 11100d4217..8c4165ea6f 100644 --- a/agenta-cli/agenta/sdk/tracing/inline.py +++ b/agenta-cli/agenta/sdk/tracing/inline.py @@ -821,11 +821,15 @@ def parse_from_otel_span_dto( node_id = UUID(tree_id.hex[16:] + otel_span_dto.context.span_id[2:]) - node_type: str = types.get("node") + node_type = NodeType.TASK + try: + node_type = NodeType(types.get("node", "").lower()) + except: # pylint: disable=bare-except + pass node = NodeDTO( id=node_id, - type=node_type.lower() if node_type else None, + type=node_type, name=otel_span_dto.name, ) diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index 0616c4f491..ce498613ce 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.27.0a13" +version = "0.27.0a14" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] From 9917f25165bb6b784447dccf1c02dcaf7f643ad7 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Mon, 11 Nov 2024 16:37:58 +0600 Subject: [PATCH 296/305] fix(frontend): removed autocomplete from llm as a judge --- .../EvaluatorsModal/ConfigureEvaluator/Messages.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/ConfigureEvaluator/Messages.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/ConfigureEvaluator/Messages.tsx index 204cb18c21..350c8552c3 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/ConfigureEvaluator/Messages.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/ConfigureEvaluator/Messages.tsx @@ -111,6 +111,7 @@ export const Messages: React.FC = ({value = [], onChange}) => { glyphMargin: false, lineNumbersMinChars: 0, // Reduces the space for line numbers folding: false, + quickSuggestions: false, }} onChange={(newValue) => { const currentMessages = From 58992aa716e0be0666663090d0c9fe8fd0bc0429 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Mon, 11 Nov 2024 11:45:05 +0100 Subject: [PATCH 297/305] fix(frontend): changed default language in code editor to markdown --- .../EvaluatorsModal/ConfigureEvaluator/Messages.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/ConfigureEvaluator/Messages.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/ConfigureEvaluator/Messages.tsx index 204cb18c21..2cec7a9e6c 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/ConfigureEvaluator/Messages.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/ConfigureEvaluator/Messages.tsx @@ -95,7 +95,7 @@ export const Messages: React.FC = ({value = [], onChange}) => { > = ({value = [], onChange}) => { }} beforeMount={(monaco) => { // Add custom token provider for highlighting text between curly braces - monaco.languages.setMonarchTokensProvider("plaintext", { + monaco.languages.setMonarchTokensProvider("markdown", { tokenizer: { root: [[/{[^}]*}/, "variable"]], }, From 7c4bf2055f351d58191ec6377ec678125189e4a9 Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Mon, 11 Nov 2024 11:48:44 +0100 Subject: [PATCH 298/305] app_id not mandatory anymore --- agenta-cli/agenta/sdk/agenta_init.py | 14 +++++++------- agenta-cli/agenta/sdk/tracing/tracing.py | 12 +++--------- agenta-cli/pyproject.toml | 2 +- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/agenta-cli/agenta/sdk/agenta_init.py b/agenta-cli/agenta/sdk/agenta_init.py index f4f4b47e4d..006fa5941a 100644 --- a/agenta-cli/agenta/sdk/agenta_init.py +++ b/agenta-cli/agenta/sdk/agenta_init.py @@ -79,13 +79,13 @@ def init( ) self.app_id = app_id or config.get("app_id") or os.environ.get("AGENTA_APP_ID") - if not self.app_id: - raise ValueError( - "App ID must be specified. You can provide it in one of the following ways:\n" - "1. As an argument when calling ag.init(app_id='your_app_id').\n" - "2. In the configuration file specified by config_fname.\n" - "3. As an environment variable 'AGENTA_APP_ID'." - ) + # if not self.app_id: + # raise ValueError( + # "App ID must be specified. You can provide it in one of the following ways:\n" + # "1. As an argument when calling ag.init(app_id='your_app_id').\n" + # "2. In the configuration file specified by config_fname.\n" + # "3. As an environment variable 'AGENTA_APP_ID'." + # ) self.api_key = ( api_key or os.environ.get("AGENTA_API_KEY") or config.get("api_key") diff --git a/agenta-cli/agenta/sdk/tracing/tracing.py b/agenta-cli/agenta/sdk/tracing/tracing.py index dcd231a0bb..bc083e86e3 100644 --- a/agenta-cli/agenta/sdk/tracing/tracing.py +++ b/agenta-cli/agenta/sdk/tracing/tracing.py @@ -35,8 +35,6 @@ def __init__( ) -> None: # ENDPOINT (OTLP) self.otlp_url = url - # AITH (OTLP) - self.api_key: Optional[str] = None # HEADERS (OTLP) self.headers: Dict[str, str] = dict() # REFERENCES @@ -59,16 +57,12 @@ def configure( # DEPRECATING app_id: Optional[str] = None, ): - # AUTH (OTLP) - self.api_key = api_key # HEADERS (OTLP) - self.headers = {} - if app_id: - self.headers.update(**{"AG-APP-ID": app_id}) if api_key: - self.headers.update(**{"Authorization": self.api_key}) + self.headers["Authorization"] = api_key # REFERENCES - self.references["application.id"] = app_id + if app_id: + self.references["application.id"] = app_id # TRACER PROVIDER self.tracer_provider = TracerProvider( diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index ce498613ce..db29c885d5 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.27.0a14" +version = "0.27.0a15" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] From f52c9e5e5dba9bb3e481c39266526e2a2b111d85 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Mon, 11 Nov 2024 17:28:19 +0600 Subject: [PATCH 299/305] fix(frontend): observability windowing time zone issue --- agenta-web/src/components/Filters/Sort.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/agenta-web/src/components/Filters/Sort.tsx b/agenta-web/src/components/Filters/Sort.tsx index 46d0da3b6e..a9b35be3f7 100644 --- a/agenta-web/src/components/Filters/Sort.tsx +++ b/agenta-web/src/components/Filters/Sort.tsx @@ -99,10 +99,18 @@ const Sort: React.FC = ({onSortApply, defaultSortValue}) => { .toISOString() .split(".")[0] } else if (sortData === "custom" && (customRange?.startTime || customRange?.endTime)) { - if (customRange.startTime) - customRangeTime.startTime = customRange.startTime.toISOString().split(".")[0] - if (customRange.endTime) - customRangeTime.endTime = customRange.endTime.toISOString().split(".")[0] + if (customRange.startTime) { + customRangeTime.startTime = dayjs(customRange.startTime) + .utc() + .toISOString() + .split(".")[0] + } + if (customRange.endTime) { + customRangeTime.endTime = dayjs(customRange.endTime) + .utc() + .toISOString() + .split(".")[0] + } } else if (sortData === "all time") { sortedTime = "1970-01-01T00:00:00" } From d6e7a1729f95b22cc94a3fe0c48543781e1ae45b Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Mon, 11 Nov 2024 17:43:55 +0600 Subject: [PATCH 300/305] fix(frontend): converted utc to local time zone before diplaying to user --- .../components/pages/observability/ObservabilityDashboard.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx index cf5a804c0d..f1c6c75d5a 100644 --- a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx +++ b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx @@ -213,7 +213,9 @@ const ObservabilityDashboard = () => { }), render: (_, record) => { return ( -
{dayjs(record.lifecycle?.created_at).format("HH:mm:ss DD MMM YYYY")}
+
+ {dayjs(record.lifecycle?.created_at).local().format("HH:mm:ss DD MMM YYYY")} +
) }, }, From e1e2c9f74014fd880885741efeb5dbc146be1ec6 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Mon, 11 Nov 2024 17:59:56 +0600 Subject: [PATCH 301/305] fix(frontend): trace drawer timezone format --- .../pages/observability/drawer/TraceContent.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx index 1b0cd4732f..f0c1387eaf 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx @@ -331,9 +331,13 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { - {dayjs(activeTrace.time.start).format("DD/MM/YYYY, hh:mm:ss A")} + {dayjs(activeTrace.time.start) + .local() + .format("DD/MM/YYYY, hh:mm:ss A")} {" "} - {dayjs(activeTrace.time.end).format("DD/MM/YYYY, hh:mm:ss A")} + {dayjs(activeTrace.time.end) + .local() + .format("DD/MM/YYYY, hh:mm:ss A")}
} /> From a9b840d2c46ead6c9781313bbc0cadbe95a56071 Mon Sep 17 00:00:00 2001 From: Abraham 'Abram' Israel Date: Mon, 11 Nov 2024 14:08:12 +0100 Subject: [PATCH 302/305] fix (bug): Resolve error in app variant revisions query by correcting ORM relationships - Resolved issue with ORM error related to "AppVariantDB_.app" relationship path in list_app_variant_revisions_by_variant - Refactored query to adjust `joinedload` options for AppVariantRevisionsDB variant_revision and modified_by relationships --- .../agenta_backend/services/db_manager.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/agenta-backend/agenta_backend/services/db_manager.py b/agenta-backend/agenta_backend/services/db_manager.py index dead7421da..0baf67510d 100644 --- a/agenta-backend/agenta_backend/services/db_manager.py +++ b/agenta-backend/agenta_backend/services/db_manager.py @@ -1719,29 +1719,26 @@ async def create_environment_revision( async def list_app_variant_revisions_by_variant( app_variant: AppVariantDB, project_id: str ) -> List[AppVariantRevisionsDB]: - """Returns list of app variant revision for the given app variant + """Returns list of app variant revisions for the given app variant. Args: - app_variant (AppVariantDB): The app variant to retrieve environments for. + app_variant (AppVariantDB): The app variant to retrieve revisions for. project_id (str): The ID of the project. Returns: List[AppVariantRevisionsDB]: A list of AppVariantRevisionsDB objects. """ - async with engine.session() as session: base_query = ( select(AppVariantRevisionsDB) .filter_by(variant_id=app_variant.id, project_id=uuid.UUID(project_id)) .options( - joinedload(AppVariantRevisionsDB.variant_revision) - .joinedload(AppVariantDB.app) - .load_only(AppDB.app_name) - ) - .options( - joinedload(AppVariantRevisionsDB.modified_by).load_only( + joinedload(AppVariantRevisionsDB.variant_revision.of_type(AppVariantDB)) + .joinedload(AppVariantDB.app.of_type(AppDB)) + .load_only(AppDB.app_name), + joinedload(AppVariantRevisionsDB.modified_by.of_type(UserDB)).load_only( UserDB.username, UserDB.email - ) + ), ) ) From 65b329b9e07a48ade3a6550b2748041cbfe30026 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Mon, 11 Nov 2024 19:52:24 +0600 Subject: [PATCH 303/305] fix(frontend): observability export data format --- .../observability/ObservabilityDashboard.tsx | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx index f1c6c75d5a..1c2251f875 100644 --- a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx +++ b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx @@ -338,7 +338,7 @@ const ObservabilityDashboard = () => { try { if (traces.length) { const {currentApp} = getAppValues() - const filename = `${currentApp?.app_name}_observability.csv` + const filename = `${currentApp?.app_name || ""}_observability.csv` const convertToStringOrJson = (value: any) => { return typeof value === "string" ? value : JSON.stringify(value) @@ -348,15 +348,14 @@ const ObservabilityDashboard = () => { const createTraceObject = (trace: any) => ({ "Trace ID": trace.key, Name: trace.node.name, - Timestamp: dayjs(trace.time.start).format("HH:mm:ss DD MMM YYYY"), - Inputs: trace?.data?.inputs?.topic || "N/A", + "Span type": trace.node.type || "N/A", + Inputs: convertToStringOrJson(trace?.data?.inputs?.topic) || "N/A", Outputs: convertToStringOrJson(trace?.data?.outputs) || "N/A", - Status: trace.status.code, - Latency: formatLatency(trace.metrics?.acc?.duration.total), - Usage: formatTokenUsage(trace.metrics?.acc?.tokens?.total || 0), - "Total Cost": formatCurrency(trace.metrics?.acc?.costs?.total || 0), - "Span Type": trace.node.type || "N/A", - "Span ID": trace.node.id, + Duration: formatLatency(trace?.metrics?.acc?.duration.total / 1000), + Cost: formatCurrency(trace.metrics?.acc?.costs?.total), + Usage: formatTokenUsage(trace.metrics?.acc?.tokens?.total), + Timestamp: dayjs(trace.time.start).local().format("HH:mm:ss DD MMM YYYY"), + Status: trace.status.code === "failed" ? "ERROR" : "SUCCESS", }) const csvData = convertToCsv( @@ -366,13 +365,7 @@ const ObservabilityDashboard = () => { ? [parentTrace, ...trace.children.map(createTraceObject)] : [parentTrace] }), - [ - ...columns.map((col) => - col.title === "ID" ? "Trace ID" : (col.title as string), - ), - "Span ID", - "Span Type", - ], + columns.map((col) => (col.title === "ID" ? "Trace ID" : (col.title as string))), ) downloadCsv(csvData, filename) From 803370fae0ebe1ecf659171caf60d2058b6cd797 Mon Sep 17 00:00:00 2001 From: aakrem <6608260+aakrem@users.noreply.github.com> Date: Tue, 12 Nov 2024 09:27:24 +0000 Subject: [PATCH 304/305] v0.27.0 --- agenta-backend/pyproject.toml | 2 +- agenta-cli/pyproject.toml | 2 +- agenta-web/package-lock.json | 4 ++-- agenta-web/package.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/agenta-backend/pyproject.toml b/agenta-backend/pyproject.toml index 6b81d7f9c2..7e3f15263e 100644 --- a/agenta-backend/pyproject.toml +++ b/agenta-backend/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta_backend" -version = "0.26.0" +version = "0.27.0" description = "" authors = ["Mahmoud Mabrouk "] readme = "README.md" diff --git a/agenta-cli/pyproject.toml b/agenta-cli/pyproject.toml index d731451c2a..a5953e3f72 100644 --- a/agenta-cli/pyproject.toml +++ b/agenta-cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.26.0" +version = "0.27.0" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] diff --git a/agenta-web/package-lock.json b/agenta-web/package-lock.json index 2edc096eab..c3af1a8a7b 100644 --- a/agenta-web/package-lock.json +++ b/agenta-web/package-lock.json @@ -1,12 +1,12 @@ { "name": "agenta", - "version": "0.26.0", + "version": "0.27.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "agenta", - "version": "0.26.0", + "version": "0.27.0", "dependencies": { "@ant-design/colors": "^7.0.0", "@ant-design/icons": "^5.3.7", diff --git a/agenta-web/package.json b/agenta-web/package.json index 94b77dc5f2..400095907b 100644 --- a/agenta-web/package.json +++ b/agenta-web/package.json @@ -1,6 +1,6 @@ { "name": "agenta", - "version": "0.26.0", + "version": "0.27.0", "private": true, "engines": { "node": ">=18" From 199d7ee026286617a43fe520985f1dda22a2a3e9 Mon Sep 17 00:00:00 2001 From: Abraham 'Abram' Israel Date: Tue, 12 Nov 2024 12:26:36 +0100 Subject: [PATCH 305/305] refactor (cli): add Prompt to SDK types --- agenta-cli/agenta/__init__.py | 1 + agenta-cli/agenta/sdk/__init__.py | 1 + agenta-cli/agenta/sdk/types.py | 16 +++++++++++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/agenta-cli/agenta/__init__.py b/agenta-cli/agenta/__init__.py index db2523837b..5a03fec167 100644 --- a/agenta-cli/agenta/__init__.py +++ b/agenta-cli/agenta/__init__.py @@ -14,6 +14,7 @@ TextParam, FileInputURL, BinaryParam, + Prompt, ) from .sdk.utils.logging import log as logging diff --git a/agenta-cli/agenta/sdk/__init__.py b/agenta-cli/agenta/sdk/__init__.py index 75b5c62065..65886105db 100644 --- a/agenta-cli/agenta/sdk/__init__.py +++ b/agenta-cli/agenta/sdk/__init__.py @@ -16,6 +16,7 @@ MessagesInput, FileInputURL, BinaryParam, + Prompt, ) from .tracing import Tracing, get_tracer diff --git a/agenta-cli/agenta/sdk/types.py b/agenta-cli/agenta/sdk/types.py index b34f380bae..cab9fb4b2c 100644 --- a/agenta-cli/agenta/sdk/types.py +++ b/agenta-cli/agenta/sdk/types.py @@ -222,7 +222,10 @@ class LifecyclesResponse(ReferencesResponse): deployed_by_id: Optional[str] = None def __str__(self): - return str(self.model_dump(exclude_none=True)) + return self.model_dump_json(indent=4) + + def __repr__(self): + return self.__str__() class ConfigurationResponse(LifecyclesResponse): @@ -231,3 +234,14 @@ class ConfigurationResponse(LifecyclesResponse): class DeploymentResponse(LifecyclesResponse): pass + + +class Prompt(BaseModel): + temperature: float + model: str + max_tokens: int + prompt_system: str + prompt_user: str + top_p: float + frequency_penalty: float + presence_penalty: float