From 310dfb81947a8102b3f1f106db6e27423368785a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9mence=20Lesn=C3=A9?= Date: Tue, 17 Dec 2024 13:11:38 +0100 Subject: [PATCH] breaking: Use AI Factory SDK and LLM tracing --- README.md | 31 ---- app/helpers/call_llm.py | 15 +- app/helpers/config_models/llm.py | 119 +++---------- app/helpers/config_models/prompts.py | 30 +--- app/helpers/llm_utils.py | 10 +- app/helpers/llm_worker.py | 229 +++++++++--------------- app/helpers/monitoring.py | 11 +- app/models/message.py | 47 +++-- cicd/bicep/app.bicep | 81 +++++++-- config-local-example.yaml | 20 +-- pyproject.toml | 4 +- tests/conftest.py | 3 +- uv.lock | 249 ++++++++++++--------------- 13 files changed, 329 insertions(+), 520 deletions(-) diff --git a/README.md b/README.md index 8a6b75d3..d5d2fe46 100644 --- a/README.md +++ b/README.md @@ -484,37 +484,6 @@ Conversation options are represented as features. They can be configured from Ap | `vad_silence_timeout_ms` | Silence to trigger voice activity detection in milliseconds. | `int` | 500 | | `vad_threshold` | The threshold for voice activity detection. Between 0.1 and 1. | `float` | 0.5 | -### Use an OpenAI compatible model for the LLM - -To use a model compatible with the OpenAI completion API, you need to create an account and get the following information: - -- API key -- Context window size -- Endpoint URL -- Model name -- Streaming capability - -Then, add the following in the `config.yaml` file: - -```yaml -# config.yaml -llm: - fast: - mode: openai - openai: - context: 128000 - endpoint: https://api.openai.com - model: gpt-4o-mini - streaming: true - slow: - mode: openai - openai: - context: 128000 - endpoint: https://api.openai.com - model: gpt-4o - streaming: true -``` - ### Use Twilio for SMS To use Twilio for SMS, you need to create an account and get the following information: diff --git a/app/helpers/call_llm.py b/app/helpers/call_llm.py index 794fdb8a..3f806871 100644 --- a/app/helpers/call_llm.py +++ b/app/helpers/call_llm.py @@ -9,7 +9,6 @@ SpeechSynthesizer, ) from azure.communication.callautomation.aio import CallAutomationClient -from openai import APIError from app.helpers.call_utils import ( AECStream, @@ -427,7 +426,7 @@ def _clear_tasks() -> None: # TODO: Refacto, this function is too long @tracer.start_as_current_span("call_generate_chat_completion") -async def _generate_chat_completion( # noqa: PLR0913, PLR0911, PLR0912, PLR0915 +async def _generate_chat_completion( # noqa: PLR0913, PLR0912, PLR0915 call: CallStateModel, client: CallAutomationClient, post_callback: Callable[[CallStateModel], Awaitable[None]], @@ -495,7 +494,7 @@ async def _content_callback(buffer: str) -> None: # Execute LLM inference maximum_tokens_reached = False content_buffer_pointer = 0 - tool_calls_buffer: dict[int, MessageToolModel] = {} + tool_calls_buffer: dict[str, MessageToolModel] = {} try: async for delta in completion_stream( max_tokens=160, # Lowest possible value for 90% of the cases, if not sufficient, retry will be triggered, 100 tokens ~= 75 words, 20 words ~= 1 sentence, 6 sentences ~= 160 tokens @@ -505,10 +504,10 @@ async def _content_callback(buffer: str) -> None: ): if not delta.content: for piece in delta.tool_calls or []: - tool_calls_buffer[piece.index] = tool_calls_buffer.get( - piece.index, MessageToolModel() + tool_calls_buffer[piece.id] = tool_calls_buffer.get( + piece.id, MessageToolModel() ) - tool_calls_buffer[piece.index] += piece + tool_calls_buffer[piece.id] += piece else: # Store whole content content_full += delta.content @@ -522,10 +521,6 @@ async def _content_callback(buffer: str) -> None: except MaximumTokensReachedError: logger.warning("Maximum tokens reached for this completion, retry asked") maximum_tokens_reached = True - # Retry on API error - except APIError as e: - logger.warning("OpenAI API call error: %s", e) - return True, True, call # Error, retry # Last user message is trash, remove it except SafetyCheckError as e: logger.warning("Safety Check error: %s", e) diff --git a/app/helpers/config_models/llm.py b/app/helpers/config_models/llm.py index a7ba8ba9..7538fa73 100644 --- a/app/helpers/config_models/llm.py +++ b/app/helpers/config_models/llm.py @@ -1,117 +1,40 @@ -from abc import abstractmethod -from enum import Enum -from typing import Any - -from openai import AsyncAzureOpenAI, AsyncOpenAI -from pydantic import BaseModel, Field, SecretStr, ValidationInfo, field_validator +from azure.ai.inference.aio import ChatCompletionsClient +from pydantic import BaseModel from app.helpers.cache import async_lru_cache -from app.helpers.identity import token - - -class ModeEnum(str, Enum): - AZURE_OPENAI = "azure_openai" - """Use Azure OpenAI.""" - OPENAI = "openai" - """Use OpenAI.""" +from app.helpers.http import azure_transport +from app.helpers.identity import credential -class AbstractPlatformModel(BaseModel, frozen=True): - _client_kwargs: dict[str, Any] = { - # Reliability - "max_retries": 0, # Retries are managed manually - "timeout": 60, - } +class DeploymentModel(BaseModel, frozen=True): + api_version: str = "2024-10-21" # See: https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#api-specs context: int endpoint: str model: str seed: int = 42 # Reproducible results - streaming: bool temperature: float = 0.0 # Most focused and deterministic - @abstractmethod - async def instance( - self, - ) -> tuple[AsyncAzureOpenAI | AsyncOpenAI, "AbstractPlatformModel"]: - pass - - -class AzureOpenaiPlatformModel(AbstractPlatformModel, frozen=True): - api_version: str = "2024-06-01" - deployment: str - @async_lru_cache() - async def instance(self) -> tuple[AsyncAzureOpenAI, AbstractPlatformModel]: - return AsyncAzureOpenAI( - **self._client_kwargs, + async def instance(self) -> tuple[ChatCompletionsClient, "DeploymentModel"]: + return ChatCompletionsClient( + # Reliability + seed=self.seed, + temperature=self.temperature, # Deployment api_version=self.api_version, - azure_deployment=self.deployment, - azure_endpoint=self.endpoint, + endpoint=self.endpoint, + model=self.model, + # Performance + transport=await azure_transport(), # Authentication - azure_ad_token_provider=await token( - "https://cognitiveservices.azure.com/.default" - ), + credential_scopes=["https://cognitiveservices.azure.com/.default"], + credential=await credential(), ), self -class OpenaiPlatformModel(AbstractPlatformModel, frozen=True): - api_key: SecretStr - - @async_lru_cache() - async def instance(self) -> tuple[AsyncOpenAI, AbstractPlatformModel]: - return AsyncOpenAI( - **self._client_kwargs, - # API root URL - base_url=self.endpoint, - # Authentication - api_key=self.api_key.get_secret_value(), - ), self - - -class SelectedPlatformModel(BaseModel): - azure_openai: AzureOpenaiPlatformModel | None = None - mode: ModeEnum - openai: OpenaiPlatformModel | None = None - - @field_validator("azure_openai") - @classmethod - def _validate_azure_openai( - cls, - azure_openai: AzureOpenaiPlatformModel | None, - info: ValidationInfo, - ) -> AzureOpenaiPlatformModel | None: - if not azure_openai and info.data.get("mode", None) == ModeEnum.AZURE_OPENAI: - raise ValueError("Azure OpenAI config required") - return azure_openai - - @field_validator("openai") - @classmethod - def _validate_openai( - cls, - openai: OpenaiPlatformModel | None, - info: ValidationInfo, - ) -> OpenaiPlatformModel | None: - if not openai and info.data.get("mode", None) == ModeEnum.OPENAI: - raise ValueError("OpenAI config required") - return openai - - def selected(self) -> AzureOpenaiPlatformModel | OpenaiPlatformModel: - platform = ( - self.azure_openai if self.mode == ModeEnum.AZURE_OPENAI else self.openai - ) - assert platform - return platform - - class LlmModel(BaseModel): - fast: SelectedPlatformModel = Field( - serialization_alias="backup", # Backwards compatibility with v6 - ) - slow: SelectedPlatformModel = Field( - serialization_alias="primary", # Backwards compatibility with v6 - ) + fast: DeploymentModel + slow: DeploymentModel - def selected(self, is_fast: bool) -> AzureOpenaiPlatformModel | OpenaiPlatformModel: - platform = self.fast if is_fast else self.slow - return platform.selected() + def selected(self, is_fast: bool) -> DeploymentModel: + return self.fast if is_fast else self.slow diff --git a/app/helpers/config_models/prompts.py b/app/helpers/config_models/prompts.py index 10149609..e0f44e54 100644 --- a/app/helpers/config_models/prompts.py +++ b/app/helpers/config_models/prompts.py @@ -6,8 +6,8 @@ from logging import Logger from textwrap import dedent +from azure.ai.inference.models import SystemMessage from azure.core.exceptions import HttpResponseError -from openai.types.chat import ChatCompletionSystemMessageParam from pydantic import BaseModel, TypeAdapter from app.models.call import CallStateModel @@ -329,7 +329,7 @@ def default_system(self, call: CallStateModel) -> str: def chat_system( self, call: CallStateModel, trainings: list[TrainingModel] - ) -> list[ChatCompletionSystemMessageParam]: + ) -> list[SystemMessage]: from app.models.message import ( ActionEnum as MessageActionEnum, StyleEnum as MessageStyleEnum, @@ -352,9 +352,7 @@ def chat_system( call=call, ) - def sms_summary_system( - self, call: CallStateModel - ) -> list[ChatCompletionSystemMessageParam]: + def sms_summary_system(self, call: CallStateModel) -> list[SystemMessage]: return self._messages( self._format( self.sms_summary_system_tpl, @@ -373,9 +371,7 @@ def sms_summary_system( call=call, ) - def synthesis_system( - self, call: CallStateModel - ) -> list[ChatCompletionSystemMessageParam]: + def synthesis_system(self, call: CallStateModel) -> list[SystemMessage]: return self._messages( self._format( self.synthesis_system_tpl, @@ -392,9 +388,7 @@ def synthesis_system( call=call, ) - def citations_system( - self, call: CallStateModel, text: str - ) -> list[ChatCompletionSystemMessageParam]: + def citations_system(self, call: CallStateModel, text: str) -> list[SystemMessage]: """ Return the formatted prompt. Prompt is used to add citations to the text, without cluttering the content itself. @@ -412,9 +406,7 @@ def citations_system( call=call, ) - def next_system( - self, call: CallStateModel - ) -> list[ChatCompletionSystemMessageParam]: + def next_system(self, call: CallStateModel) -> list[SystemMessage]: return self._messages( self._format( self.next_system_tpl, @@ -461,17 +453,13 @@ def _format( # self.logger.debug("Formatted prompt: %s", formatted_prompt) return formatted_prompt - def _messages( - self, system: str, call: CallStateModel - ) -> list[ChatCompletionSystemMessageParam]: + def _messages(self, system: str, call: CallStateModel) -> list[SystemMessage]: messages = [ - ChatCompletionSystemMessageParam( + SystemMessage( content=self.default_system(call), - role="system", ), - ChatCompletionSystemMessageParam( + SystemMessage( content=system, - role="system", ), ] # self.logger.debug("Messages: %s", messages) diff --git a/app/helpers/llm_utils.py b/app/helpers/llm_utils.py index 807c35b6..7ef7bf29 100644 --- a/app/helpers/llm_utils.py +++ b/app/helpers/llm_utils.py @@ -14,14 +14,13 @@ from typing import Annotated, Any, ForwardRef, TypeVar from aiojobs import Scheduler +from azure.ai.inference.models import ChatCompletionsToolDefinition, FunctionDefinition from azure.cognitiveservices.speech import ( SpeechSynthesizer, ) from azure.communication.callautomation.aio import CallAutomationClient from jinja2 import Environment from json_repair import repair_json -from openai.types.chat import ChatCompletionToolParam -from openai.types.shared_params.function_definition import FunctionDefinition from pydantic import BaseModel, TypeAdapter from pydantic._internal._typing_extra import eval_type_lenient from pydantic.json_schema import JsonSchemaValue @@ -77,7 +76,7 @@ def __init__( # noqa: PLR0913 async def to_openai( self, blacklist: frozenset[str], - ) -> list[ChatCompletionToolParam]: + ) -> list[ChatCompletionsToolDefinition]: """ Get the OpenAI SDK schema for all functions of the plugin, excluding the ones in the blacklist. """ @@ -257,7 +256,7 @@ async def wrapper( async def _function_schema( f: Callable[..., Any], **kwargs: Any, -) -> ChatCompletionToolParam: +) -> ChatCompletionsToolDefinition: """ Take a function and return a JSON schema for it as defined by the OpenAI API. @@ -303,8 +302,7 @@ async def _function_schema( ) ).model_dump() - return ChatCompletionToolParam( - type="function", + return ChatCompletionsToolDefinition( function=FunctionDefinition( description=description, name=name, diff --git a/app/helpers/llm_worker.py b/app/helpers/llm_worker.py index 3bfe22e9..dec184f0 100644 --- a/app/helpers/llm_worker.py +++ b/app/helpers/llm_worker.py @@ -5,32 +5,21 @@ from typing import TypeVar import tiktoken -from json_repair import repair_json -from openai import ( - APIConnectionError, - APIResponseValidationError, - AsyncAzureOpenAI, - AsyncOpenAI, - AsyncStream, - BadRequestError, - InternalServerError, - RateLimitError, -) -from openai.types.chat import ( - ChatCompletion, - ChatCompletionAssistantMessageParam, - ChatCompletionChunk, - ChatCompletionSystemMessageParam, - ChatCompletionToolMessageParam, - ChatCompletionToolParam, - ChatCompletionUserMessageParam, +from azure.ai.inference._model_base import Model, SdkJSONEncoder +from azure.ai.inference.aio import ChatCompletionsClient +from azure.ai.inference.models import ( + AssistantMessage, + ChatCompletionsResponseFormatJSON, + ChatCompletionsToolDefinition, + ChatRequestMessage, + StreamingChatResponseMessageUpdate, + SystemMessage, + UserMessage, ) -from openai.types.chat.chat_completion_chunk import ( - ChoiceDelta, - ChoiceDeltaToolCall, - ChoiceDeltaToolCallFunction, +from azure.core.exceptions import ( + ServiceResponseError, ) -from opentelemetry.instrumentation.openai import OpenAIInstrumentor +from json_repair import repair_json from pydantic import ValidationError from tenacity import ( AsyncRetrying, @@ -42,20 +31,13 @@ ) from app.helpers.config import CONFIG -from app.helpers.config_models.llm import ( - AbstractPlatformModel as LlmAbstractPlatformModel, -) +from app.helpers.config_models.llm import DeploymentModel as LlmDeploymentModel from app.helpers.features import slow_llm_for_chat from app.helpers.logging import logger from app.helpers.monitoring import tracer from app.helpers.resources import resources_dir from app.models.message import MessageModel -environ["TRACELOOP_TRACE_CONTENT"] = str( - True -) # Instrumentation logs prompts, completions, and embeddings to span attributes, set to False to lower monitoring costs or to avoid logging PII -OpenAIInstrumentor().instrument() # Instrument OpenAI - # tiktoken cache environ["TIKTOKEN_CACHE_DIR"] = resources_dir("tiktoken") @@ -77,10 +59,7 @@ class MaximumTokensReachedError(Exception): _retried_exceptions = [ - APIConnectionError, - APIResponseValidationError, - InternalServerError, - RateLimitError, + ServiceResponseError, ] @@ -88,9 +67,9 @@ class MaximumTokensReachedError(Exception): async def completion_stream( max_tokens: int, messages: list[MessageModel], - system: list[ChatCompletionSystemMessageParam], - tools: list[ChatCompletionToolParam] | None = None, -) -> AsyncGenerator[ChoiceDelta, None]: + system: list[SystemMessage], + tools: list[ChatCompletionsToolDefinition] = [], +) -> AsyncGenerator[StreamingChatResponseMessageUpdate, None]: """ Returns a stream of completions. @@ -140,21 +119,20 @@ async def completion_stream( # TODO: Refacto, too long (and remove PLR0912 ignore) -async def _completion_stream_worker( # noqa: PLR0912 +async def _completion_stream_worker( is_fast: bool, max_tokens: int, messages: list[MessageModel], - system: list[ChatCompletionSystemMessageParam], - tools: list[ChatCompletionToolParam] | None = None, -) -> AsyncGenerator[ChoiceDelta, None]: + system: list[SystemMessage], + tools: list[ChatCompletionsToolDefinition] = [], +) -> AsyncGenerator[StreamingChatResponseMessageUpdate, None]: """ Returns a stream of completions. """ + # Init client client, platform = await _use_llm(is_fast) - extra = {} - if tools: - extra["tools"] = tools # Add tools if any + # Build context and limit to 20 messages for quick response and avoid hallucinations prompt = _limit_messages( context_window=platform.context, max_messages=20, # Quick response @@ -163,83 +141,32 @@ async def _completion_stream_worker( # noqa: PLR0912 model=platform.model, system=system, tools=tools, - ) # Limit to 20 messages for quick response and avoid hallucinations - chat_kwargs = { - "max_tokens": max_tokens, - "messages": prompt, - "model": platform.model, - "seed": platform.seed, - "temperature": platform.temperature, - **extra, - } # Shared kwargs for both streaming and non-streaming - maximum_tokens_reached = False + ) - try: - # Streaming - if platform.streaming: - stream: AsyncStream[ - ChatCompletionChunk - ] = await client.chat.completions.create( - **chat_kwargs, - stream=True, - ) - async for chunck in stream: - choices = chunck.choices - # Skip empty choices, happens sometimes with GPT-4 Turbo - if not choices: - continue - choice = choices[0] - delta = choice.delta - # Azure OpenAI content filter - if choice.finish_reason == "content_filter": - raise SafetyCheckError(f"Issue detected in text: {delta.content}") - if choice.finish_reason == "length": - logger.warning( - "Maximum tokens reached %s, should be fixed", max_tokens - ) - maximum_tokens_reached = True - if delta: - yield delta - - # Non-streaming, emulate streaming with a single completion - else: - completion: ChatCompletion = await client.chat.completions.create( - **chat_kwargs - ) - choice = completion.choices[0] - # Azure OpenAI content filter - if choice.finish_reason == "content_filter": - raise SafetyCheckError( - f"Issue detected in generation: {choice.message.content}" - ) - if choice.finish_reason == "length": - logger.warning("Maximum tokens reached %s, should be fixed", max_tokens) - maximum_tokens_reached = True - message = choice.message - delta = ChoiceDelta( - content=message.content, - role=message.role, - tool_calls=[ - ChoiceDeltaToolCall( - id=tool.id, - index=0, - type=tool.type, - function=ChoiceDeltaToolCallFunction( - arguments=tool.function.arguments, - name=tool.function.name, - ), - ) - for tool in message.tool_calls or [] - ], - ) - yield delta - except BadRequestError as e: - if e.code == "content_filter": - raise SafetyCheckError("Issue detected in prompt") from e - raise e + # Start completion + stream = await client.complete( + max_tokens=max_tokens, + messages=prompt, + stream=True, + tools=tools, + ) - if maximum_tokens_reached: - raise MaximumTokensReachedError(f"Maximum tokens reached {max_tokens}") + # Yield chuncks + async for chunck in stream: + choices = chunck.choices + # Skip empty choices, happens sometimes with GPT-4 Turbo + if not choices: + continue + choice = choices[0] + delta = choice.delta + # Azure OpenAI content filter + if choice.finish_reason == "content_filter": + raise SafetyCheckError(f"Issue detected in text: {delta.content}") + if choice.finish_reason == "length": + logger.warning("Maximum tokens reached %s, should be fixed", max_tokens) + raise MaximumTokensReachedError(f"Maximum tokens reached {max_tokens}") + if delta: + yield delta @retry( @@ -251,7 +178,7 @@ async def _completion_stream_worker( # noqa: PLR0912 @tracer.start_as_current_span("llm_completion_sync") async def completion_sync( res_type: type[T], - system: list[ChatCompletionSystemMessageParam], + system: list[SystemMessage], validation_callback: Callable[[str | None], tuple[bool, str | None, T | None]], validate_json: bool = False, _previous_result: str | None = None, @@ -262,13 +189,11 @@ async def completion_sync( messages = system if _validation_error: messages += [ - ChatCompletionAssistantMessageParam( + AssistantMessage( content=_previous_result or "", - role="assistant", ), - ChatCompletionUserMessageParam( + UserMessage( content=f"A validation error occurred, please retry: {_validation_error}", - role="user", ), ] @@ -310,18 +235,17 @@ async def completion_sync( async def _completion_sync_worker( is_fast: bool, - system: list[ChatCompletionSystemMessageParam], + system: list[SystemMessage], json_output: bool = False, max_tokens: int | None = None, ) -> str | None: """ Returns a completion. """ + # Init client client, platform = await _use_llm(is_fast) - extra = {} - if json_output: - extra["response_format"] = {"type": "json_object"} + # Build context prompt = _limit_messages( context_window=platform.context, max_tokens=max_tokens, @@ -344,20 +268,19 @@ async def _completion_sync_worker( choice = None async for attempt in retryed: with attempt: - try: - res = await client.chat.completions.create( + # Start completion + choice = ( + await client.complete( max_tokens=max_tokens, messages=prompt, model=platform.model, seed=platform.seed, temperature=platform.temperature, - **extra, + response_format=ChatCompletionsResponseFormatJSON() + if json_output + else None, ) - except BadRequestError as e: - if e.code == "content_filter": - raise SafetyCheckError("Issue detected in prompt") from e - raise e - choice = res.choices[0] + ).choices[0] # Azure OpenAI content filter if choice.finish_reason == "content_filter": raise SafetyCheckError( @@ -374,15 +297,10 @@ def _limit_messages( # noqa: PLR0913 max_tokens: int | None, messages: list[MessageModel], model: str, - system: list[ChatCompletionSystemMessageParam], + system: list[SystemMessage], max_messages: int = 1000, - tools: list[ChatCompletionToolParam] | None = None, -) -> list[ - ChatCompletionAssistantMessageParam - | ChatCompletionSystemMessageParam - | ChatCompletionToolMessageParam - | ChatCompletionUserMessageParam -]: + tools: list[ChatCompletionsToolDefinition] | None = None, +) -> list[ChatRequestMessage]: """ Returns a list of messages limited by the context size. @@ -398,18 +316,18 @@ def _limit_messages( # noqa: PLR0913 # Add system messages for message in system: - tokens += _count_tokens(json.dumps(message), model) + tokens += _count_tokens(_dump_sdk_model(message), model) counter += 1 # Add tools for tool in tools or []: - tokens += _count_tokens(json.dumps(tool), model) + tokens += _count_tokens(_dump_sdk_model(tool), model) # Add user messages until the available context is reached, from the newest to the oldest for message in messages[::-1]: openai_message = message.to_openai() new_tokens = _count_tokens( - "".join([json.dumps(x) for x in openai_message]), + "".join([_dump_sdk_model(x) for x in openai_message]), model, ) if tokens + new_tokens >= max_context: @@ -442,9 +360,20 @@ def _count_tokens(content: str, model: str) -> int: return len(tiktoken.get_encoding(encoding_name).encode(content)) +def _dump_sdk_model(message: Model) -> str: + """ + Returns a JSON representation of the AI Inference SDK data model. + """ + return json.dumps( + cls=SdkJSONEncoder, + exclude_readonly=True, + obj=message, + ) + + async def _use_llm( is_fast: bool, -) -> tuple[AsyncAzureOpenAI | AsyncOpenAI, LlmAbstractPlatformModel]: +) -> tuple[ChatCompletionsClient, LlmDeploymentModel]: """ Returns an LLM client and platform model. diff --git a/app/helpers/monitoring.py b/app/helpers/monitoring.py index 291ffa61..510b4c31 100644 --- a/app/helpers/monitoring.py +++ b/app/helpers/monitoring.py @@ -4,7 +4,6 @@ from azure.monitor.opentelemetry import configure_azure_monitor from opentelemetry import metrics, trace from opentelemetry.instrumentation.aiohttp_client import AioHttpClientInstrumentor -from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor from opentelemetry.metrics._internal.instrument import Counter, Gauge from opentelemetry.semconv.attributes import service_attributes from opentelemetry.trace.span import INVALID_SPAN @@ -98,9 +97,13 @@ def gauge( try: - configure_azure_monitor() # Configure Azure Application Insights exporter - AioHttpClientInstrumentor().instrument() # Instrument aiohttp - HTTPXClientInstrumentor().instrument() # Instrument httpx + # Capture LLM prompt and completion contents + # See: https://learn.microsoft.com/en-us/azure/ai-studio/how-to/develop/trace-local-sdk?tabs=python#configuration + environ["AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED"] = "true" + # Configure Azure Application Insights exporter + configure_azure_monitor() + # Instrument aiohttp + AioHttpClientInstrumentor().instrument() except ValueError as e: print( # noqa: T201 "Azure Application Insights instrumentation failed, likely due to a missing APPLICATIONINSIGHTS_CONNECTION_STRING environment variable.", diff --git a/app/models/message.py b/app/models/message.py index 842a0c87..04c7a96c 100644 --- a/app/models/message.py +++ b/app/models/message.py @@ -2,13 +2,15 @@ from datetime import UTC, datetime from enum import Enum -from openai.types.chat import ( - ChatCompletionAssistantMessageParam, - ChatCompletionMessageToolCallParam, - ChatCompletionToolMessageParam, - ChatCompletionUserMessageParam, +from azure.ai.inference.models import ( + AssistantMessage, + ChatCompletionsToolCall, + ChatRequestMessage, + FunctionCall, + StreamingChatResponseToolCallUpdate, + ToolMessage, + UserMessage, ) -from openai.types.chat.chat_completion_chunk import ChoiceDeltaToolCall from pydantic import BaseModel, Field, field_validator _FUNC_NAME_SANITIZER_R = r"[^a-zA-Z0-9_-]" @@ -58,7 +60,7 @@ class ToolModel(BaseModel): tool_id: str = "" def __add__(self, other: object) -> "ToolModel": - if not isinstance(other, ChoiceDeltaToolCall): + if not isinstance(other, StreamingChatResponseToolCallUpdate): return NotImplemented if other.id: self.tool_id = other.id @@ -77,20 +79,19 @@ def __eq__(self, other: object) -> bool: return False return self.tool_id == other.tool_id - def to_openai(self) -> ChatCompletionMessageToolCallParam: - return ChatCompletionMessageToolCallParam( + def to_openai(self) -> ChatCompletionsToolCall: + return ChatCompletionsToolCall( id=self.tool_id, - type="function", - function={ - "arguments": self.function_arguments, - "name": "-".join( + function=FunctionCall( + arguments=self.function_arguments, + name="-".join( re.sub( _FUNC_NAME_SANITIZER_R, "-", self.function_name, ).split("-") ), # Sanitize with dashes then deduplicate dashes, backward compatibility with old models - }, + ), ) @@ -118,43 +119,35 @@ def _validate_created_at(cls, created_at: datetime) -> datetime: def to_openai( self, - ) -> list[ - ChatCompletionAssistantMessageParam - | ChatCompletionToolMessageParam - | ChatCompletionUserMessageParam - ]: + ) -> list[ChatRequestMessage]: # Removing newlines from the content to avoid hallucinations issues with GPT-4 Turbo content = " ".join([line.strip() for line in self.content.splitlines()]) if self.persona == PersonaEnum.HUMAN: return [ - ChatCompletionUserMessageParam( + UserMessage( content=f"action={self.action.value} {content}", - role="user", ) ] if self.persona == PersonaEnum.ASSISTANT: if not self.tool_calls: return [ - ChatCompletionAssistantMessageParam( + AssistantMessage( content=f"action={self.action.value} style={self.style.value} {content}", - role="assistant", ) ] res = [] res.append( - ChatCompletionAssistantMessageParam( + AssistantMessage( content=f"action={self.action.value} style={self.style.value} {content}", - role="assistant", tool_calls=[tool_call.to_openai() for tool_call in self.tool_calls], ) ) res.extend( - ChatCompletionToolMessageParam( + ToolMessage( content=tool_call.content, - role="tool", tool_call_id=tool_call.tool_id, ) for tool_call in self.tool_calls diff --git a/cicd/bicep/app.bicep b/cicd/bicep/app.bicep index 60d42066..e867abc1 100644 --- a/cicd/bicep/app.bicep +++ b/cicd/bicep/app.bicep @@ -72,24 +72,14 @@ var config = { } llm: { fast: { - mode: 'azure_openai' - azure_openai: { - context: llmFastContext - deployment: llmFast.name - endpoint: cognitiveOpenai.properties.endpoint - model: llmFastModel - streaming: true - } + context: llmFastContext + endpoint: '${cognitiveOpenai.properties.endpoint}/openai/deployments/${llmFast.name}' + model: llmFastModel } slow: { - mode: 'azure_openai' - azure_openai: { - context: llmSlowContext - deployment: llmSlow.name - endpoint: cognitiveOpenai.properties.endpoint - model: llmSlowModel - streaming: true - } + context: llmSlowContext + endpoint: '${cognitiveOpenai.properties.endpoint}/openai/deployments/${llmSlow.name}' + model: llmSlowModel } } ai_search: { @@ -504,6 +494,65 @@ resource cognitiveCommunication 'Microsoft.CognitiveServices/accounts@2024-06-01 } } +var aiFoundryName = '${prefix}-${openaiLocation}-foundry' + +resource aiFoundry 'Microsoft.MachineLearningServices/workspaces@2024-10-01' = { + name: aiFoundryName + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + kind: 'hub' + properties: { + applicationInsights: applicationInsights.id + friendlyName: appName + } + + resource aiServicesConnection 'connections@2024-10-01' = { + name: '${aiFoundryName}-connection-openai' + properties: { + authType: 'AAD' + category: 'AzureOpenAI' + isSharedToAll: true + target: cognitiveOpenai.properties.endpoint + metadata: { + ApiType: 'Azure' + ResourceId: cognitiveOpenai.id + } + } + } +} + +// Log Analytics Contributor +resource roleLogAnalyticsContributor 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: '92aaf0da-9dab-42b6-94a3-d43ce8d16293' +} + +resource assignmentsAiProjectLogAnalyticsContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, prefix, aiProject.name, 'assignmentsAiProjectLogAnalyticsContributor') + scope: logAnalytics + properties: { + principalId: aiProject.identity.principalId + principalType: 'ServicePrincipal' + roleDefinitionId: roleLogAnalyticsContributor.id + } +} + +resource aiProject 'Microsoft.MachineLearningServices/workspaces@2024-10-01' = { + name: appName + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + kind: 'project' + properties: { + friendlyName: appName + hubResourceId: aiFoundry.id + } +} + // Cognitive Services OpenAI Contributor resource roleOpenaiContributor 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { name: 'a001fd3d-188f-4b5d-821b-7da978bf7442' diff --git a/config-local-example.yaml b/config-local-example.yaml index b5f1ab8d..ee2a52e4 100644 --- a/config-local-example.yaml +++ b/config-local-example.yaml @@ -24,21 +24,13 @@ cognitive_service: llm: fast: - mode: azure_openai - azure_openai: - context: 16385 - deployment: gpt-4o-mini-2024-07-18 - endpoint: https://xxx.openai.azure.com - model: gpt-4o-mini - streaming: true + context: 16385 + endpoint: https://xxx.openai.azure.com/openai/deployments/gpt-4o-mini-2024-07-18 + model: gpt-4o-mini slow: - mode: azure_openai - azure_openai: - context: 128000 - deployment: gpt-4o-2024-08-06 - endpoint: https://xxx.openai.azure.com - model: gpt-4o - streaming: true + context: 128000 + endpoint: https://xxx.openai.azure.com/openai/deployments/gpt-4o-2024-08-06 + model: gpt-4o ai_search: embedding_deployment: text-embedding-3-large-1 diff --git a/pyproject.toml b/pyproject.toml index 7fd75e1b..23b275c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,7 @@ dependencies = [ "aiohttp-retry~=2.9", # Retry middleware for aiohttp, used with Twilio SDK "aiohttp[speedups]~=3.10", # Async HTTP client for Azure and Twilio SDKs, plus async DNS resolver and async Brotli compression "aiojobs~=1.3", # Async job scheduler + "azure-ai-inference[opentelemetry]~=1.0.0a0", # Azure AI Foundry LLM inference "azure-ai-translation-text~=1.0", # Azure Cognitive Services Text Translation "azure-appconfiguration~=1.7", # Outsourced configuration for live updates "azure-cognitiveservices-speech~=1.41", # Azure AI Speech @@ -27,10 +28,7 @@ dependencies = [ "json-repair~=0.30", # Repair JSON files from LLM "mistune~=3.0", # Markdown parser for web views "noisereduce~=3.0", # Noise reduction - "openai~=1.52", # OpenAI client "opentelemetry-instrumentation-aiohttp-client~=0.0a0", # OpenTelemetry instrumentation for aiohttp client - "opentelemetry-instrumentation-httpx~=0.0a0", # OpenTelemetry instrumentation for HTTPX - "opentelemetry-instrumentation-openai~=0.0a0", # OpenTelemetry instrumentation for OpenAI "opentelemetry-instrumentation-redis~=0.0a0", # OpenTelemetry instrumentation for Redis "opentelemetry-semantic-conventions~=0.0a0", # OpenTelemetry conventions, to standardize telemetry data "phonenumbers~=8.13", # Phone number parsing and formatting, used with Pydantic diff --git a/tests/conftest.py b/tests/conftest.py index c8e54d86..8b23f981 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -192,7 +192,7 @@ def __init__( cache: pytest.Cache, **kwargs, ): - platform = CONFIG.llm.fast.azure_openai + platform = CONFIG.llm.fast assert platform _langchain_kwargs = { @@ -203,7 +203,6 @@ def __init__( "temperature": platform.temperature, # Deployment "api_version": platform.api_version, - "azure_deployment": platform.deployment, "azure_endpoint": platform.endpoint, "model": platform.model, # Authentication, either RBAC or API diff --git a/uv.lock b/uv.lock index 75352b2f..f1f2dda6 100644 --- a/uv.lock +++ b/uv.lock @@ -103,14 +103,14 @@ wheels = [ [[package]] name = "aiosignal" -version = "1.3.1" +version = "1.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "frozenlist" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ae/67/0952ed97a9793b4958e5736f6d2b346b414a2cd63e82d05940032f45b32f/aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc", size = 19422 } +sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/ac/a7305707cb852b7e16ff80eaf5692309bde30e2b1100a1fcacdc8f731d97/aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17", size = 7617 }, + { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 }, ] [[package]] @@ -156,11 +156,30 @@ wheels = [ [[package]] name = "attrs" -version = "24.2.0" +version = "24.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/48/c8/6260f8ccc11f0917360fc0da435c5c9c7504e3db174d5a12a1494887b045/attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", size = 805984 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/aa/ab0f7891a01eeb2d2e338ae8fecbe57fcebea1a24dbb64d45801bfab481d/attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308", size = 63397 }, +] + +[[package]] +name = "azure-ai-inference" +version = "1.0.0b6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/0f/aafca9af9315aee06a89ffde799a10a582fe8de76c563ee80bbcdc08b3fb/attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", size = 792678 } +dependencies = [ + { name = "azure-core" }, + { name = "isodate" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/c9/264ae0ef0460dbd7c7efe1d3a093ad6a00fb2823d341ac457459396df2d6/azure_ai_inference-1.0.0b6.tar.gz", hash = "sha256:b8ac941de1e69151bad464191e18856d4e74f962ae03235da137a9a326143676", size = 145414 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/21/5b6702a7f963e95456c0de2d495f67bf5fd62840ac655dc451586d23d39a/attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2", size = 63001 }, + { url = "https://files.pythonhosted.org/packages/5a/aa/47459ab2e67c55ff98dbb9694c47cf98e484ce1ae1acb244d28b25a8c1c1/azure_ai_inference-1.0.0b6-py3-none-any.whl", hash = "sha256:5699ad78d70ec2d227a5eff2c1bafc845018f6624edc5b03589dfff861c54958", size = 115312 }, +] + +[package.optional-dependencies] +opentelemetry = [ + { name = "azure-core-tracing-opentelemetry" }, ] [[package]] @@ -454,6 +473,7 @@ dependencies = [ { name = "aiohttp", extra = ["speedups"] }, { name = "aiohttp-retry" }, { name = "aiojobs" }, + { name = "azure-ai-inference", extra = ["opentelemetry"] }, { name = "azure-ai-translation-text" }, { name = "azure-appconfiguration" }, { name = "azure-cognitiveservices-speech" }, @@ -472,10 +492,7 @@ dependencies = [ { name = "json-repair" }, { name = "mistune" }, { name = "noisereduce" }, - { name = "openai" }, { name = "opentelemetry-instrumentation-aiohttp-client" }, - { name = "opentelemetry-instrumentation-httpx" }, - { name = "opentelemetry-instrumentation-openai" }, { name = "opentelemetry-instrumentation-redis" }, { name = "opentelemetry-semantic-conventions" }, { name = "phonenumbers" }, @@ -514,6 +531,7 @@ requires-dist = [ { name = "aiohttp", extras = ["speedups"], specifier = "~=3.10" }, { name = "aiohttp-retry", specifier = "~=2.9" }, { name = "aiojobs", specifier = "~=1.3" }, + { name = "azure-ai-inference", extras = ["opentelemetry"], specifier = "~=1.0.0a0" }, { name = "azure-ai-translation-text", specifier = "~=1.0" }, { name = "azure-appconfiguration", specifier = "~=1.7" }, { name = "azure-cognitiveservices-speech", specifier = "~=1.41" }, @@ -534,10 +552,7 @@ requires-dist = [ { name = "json-repair", specifier = "~=0.30" }, { name = "mistune", specifier = "~=3.0" }, { name = "noisereduce", specifier = "~=3.0" }, - { name = "openai", specifier = "~=1.52" }, { name = "opentelemetry-instrumentation-aiohttp-client", specifier = "~=0.0a0" }, - { name = "opentelemetry-instrumentation-httpx", specifier = "~=0.0a0" }, - { name = "opentelemetry-instrumentation-openai", specifier = "~=0.0a0" }, { name = "opentelemetry-instrumentation-redis", specifier = "~=0.0a0" }, { name = "opentelemetry-semantic-conventions", specifier = "~=0.0a0" }, { name = "phonenumbers", specifier = "~=8.13" }, @@ -567,11 +582,11 @@ requires-dist = [ [[package]] name = "certifi" -version = "2024.8.30" +version = "2024.12.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, + { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 }, ] [[package]] @@ -723,7 +738,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/98/65/13d9e76ca19b0ba5603d71ac8424b5694415b348e719db277b5edc985ff5/cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", size = 3915420 }, { url = "https://files.pythonhosted.org/packages/b1/07/40fe09ce96b91fc9276a9ad272832ead0fddedcba87f1190372af8e3039c/cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", size = 4154498 }, { url = "https://files.pythonhosted.org/packages/75/ea/af65619c800ec0a7e4034207aec543acdf248d9bffba0533342d1bd435e1/cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", size = 3932569 }, - { url = "https://files.pythonhosted.org/packages/4e/d5/9cc182bf24c86f542129565976c21301d4ac397e74bf5a16e48241aab8a6/cryptography-44.0.0-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385", size = 4164756 }, { url = "https://files.pythonhosted.org/packages/c7/af/d1deb0c04d59612e3d5e54203159e284d3e7a6921e565bb0eeb6269bdd8a/cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", size = 4016721 }, { url = "https://files.pythonhosted.org/packages/bd/69/7ca326c55698d0688db867795134bdfac87136b80ef373aaa42b225d6dd5/cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", size = 4240915 }, { url = "https://files.pythonhosted.org/packages/ef/d4/cae11bf68c0f981e0413906c6dd03ae7fa864347ed5fac40021df1ef467c/cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", size = 2757925 }, @@ -734,7 +748,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/c7/c656eb08fd22255d21bc3129625ed9cd5ee305f33752ef2278711b3fa98b/cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", size = 3915417 }, { url = "https://files.pythonhosted.org/packages/ef/82/72403624f197af0db6bac4e58153bc9ac0e6020e57234115db9596eee85d/cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", size = 4155160 }, { url = "https://files.pythonhosted.org/packages/a2/cd/2f3c440913d4329ade49b146d74f2e9766422e1732613f57097fea61f344/cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", size = 3932331 }, - { url = "https://files.pythonhosted.org/packages/31/d9/90409720277f88eb3ab72f9a32bfa54acdd97e94225df699e7713e850bd4/cryptography-44.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba", size = 4165207 }, { url = "https://files.pythonhosted.org/packages/7f/df/8be88797f0a1cca6e255189a57bb49237402b1880d6e8721690c5603ac23/cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", size = 4017372 }, { url = "https://files.pythonhosted.org/packages/af/36/5ccc376f025a834e72b8e52e18746b927f34e4520487098e283a719c205e/cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", size = 4239657 }, { url = "https://files.pythonhosted.org/packages/46/b0/f4f7d0d0bcfbc8dd6296c1449be326d04217c57afb8b2594f017eed95533/cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", size = 2758672 }, @@ -1194,7 +1207,7 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "0.26.5" +version = "0.27.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -1205,9 +1218,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/51/21/2be5c66f29e798650a3e66bb350dee63bd9ab02cfc3ed7197cf4a905203e/huggingface_hub-0.26.5.tar.gz", hash = "sha256:1008bd18f60bfb65e8dbc0a97249beeeaa8c99d3c2fa649354df9fa5a13ed83b", size = 375951 } +sdist = { url = "https://files.pythonhosted.org/packages/36/c6/e3709b61de8e7832dbe19f0d9637e81356cede733d99359fbce125423774/huggingface_hub-0.27.0.tar.gz", hash = "sha256:902cce1a1be5739f5589e560198a65a8edcfd3b830b1666f36e4b961f0454fac", size = 379286 } wheels = [ - { url = "https://files.pythonhosted.org/packages/44/5a/dc6af87c61f89b23439eb95521e4e99862636cfd538ae12fd36be5483e5f/huggingface_hub-0.26.5-py3-none-any.whl", hash = "sha256:fb7386090bbe892072e64b85f7c4479fd2d65eea5f2543327c970d5169e83924", size = 447766 }, + { url = "https://files.pythonhosted.org/packages/61/8c/fbdc0a88a622d9fa54e132d7bf3ee03ec602758658a2db5b339a65be2cfe/huggingface_hub-0.27.0-py3-none-any.whl", hash = "sha256:8f2e834517f1f1ddf1ecc716f91b120d7333011b7485f665a9a412eacb1a2a81", size = 450537 }, ] [[package]] @@ -1307,11 +1320,11 @@ wheels = [ [[package]] name = "json-repair" -version = "0.30.3" +version = "0.31.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/7a/7745d0d908563a478421c7520649dfd6a5c551858e2233ff7caf20cb8df7/json_repair-0.30.3.tar.gz", hash = "sha256:0ac56e7ae9253ee9c507a7e1a3a26799c9b0bbe5e2bec1b2cc5053e90d5b05e3", size = 27803 } +sdist = { url = "https://files.pythonhosted.org/packages/68/0c/afa04988a2b26164b3987b24facbe48671bc172dda0b8484e472456b6cec/json_repair-0.31.0.tar.gz", hash = "sha256:3539dbb1857fa2c64404e1cf3e53b091738e74fcf3f12762d4199a0e2af657f5", size = 28053 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/2d/79a46330c4b97ee90dd403fb0d267da7b25b24d7db604c5294e5c57d5f7c/json_repair-0.30.3-py3-none-any.whl", hash = "sha256:63bb588162b0958ae93d85356ecbe54c06b8c33f8a4834f93fa2719ea669804e", size = 18951 }, + { url = "https://files.pythonhosted.org/packages/e4/f6/03e7f4f9bb133e6410bc6f1cc37481fc6da645c4395d53dd604d7c1fa633/json_repair-0.31.0-py3-none-any.whl", hash = "sha256:82a4f60a9a836ed12b103367477af96f94fc6a14310d70a06e5354c3eb287d11", size = 19065 }, ] [[package]] @@ -1377,7 +1390,7 @@ wheels = [ [[package]] name = "langchain" -version = "0.3.11" +version = "0.3.12" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -1391,14 +1404,14 @@ dependencies = [ { name = "sqlalchemy" }, { name = "tenacity" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/03/6d/df5afcab81750ee1f9895ae33dd2f59f8f7b8a826611e2d73a9f1909d6ce/langchain-0.3.11.tar.gz", hash = "sha256:17868ea3f0cf5a46b4b88bf1961c4a12d32ea0778930e7d2eb5103e0287ff478", size = 420290 } +sdist = { url = "https://files.pythonhosted.org/packages/0a/f0/4a1e712a45285ea52c4c075d73ebc4d6d7bee610db17e4422d34133b4586/langchain-0.3.12.tar.gz", hash = "sha256:0d8247afbf37beb263b4adc29f7aa8a5ae83c43a6941894e2f9ba39d5c869e3b", size = 420316 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/4a/26620afcff880f6058756786d9b858d348ac29c815e44f57b6c2c07bf86d/langchain-0.3.11-py3-none-any.whl", hash = "sha256:6655feded1f7569e5a4bd11e38de0a26c7c86646c0dea49afccceba42df60ad7", size = 1009036 }, + { url = "https://files.pythonhosted.org/packages/de/48/f1457a56444134cd4531a8fc31a80b529ce157fce9c9cb7bb79cda7362d9/langchain-0.3.12-py3-none-any.whl", hash = "sha256:581ad93a9de12e4b957bc2af9ba8482eb86e3930e84c4ee20ed677da5e2311cd", size = 1009094 }, ] [[package]] name = "langchain-community" -version = "0.3.11" +version = "0.3.12" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -1414,14 +1427,14 @@ dependencies = [ { name = "sqlalchemy" }, { name = "tenacity" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/11/55/cd4076894cce736efc48d787be68275f9ffc736449403cf4c1b11347dae3/langchain_community-0.3.11.tar.gz", hash = "sha256:31a96de1578f6037cd49acf287227d54e88e81f82e3e49cb4d90bfe05b1cdc32", size = 1681136 } +sdist = { url = "https://files.pythonhosted.org/packages/cf/15/97439b96bd3f8407ef0f049ea3fed7e5b2b00fcd9679428b94cafa3baf8e/langchain_community-0.3.12.tar.gz", hash = "sha256:b4694f34c7214dede03fe5a75e9f335e16bd788dfa6ca279302ad357bf0d0fc4", size = 1691757 } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/c2/aeb43e7f879d429df40f742228dba747d790655f3eb0e2082b7d9854f0fd/langchain_community-0.3.11-py3-none-any.whl", hash = "sha256:c67091dc7652f44161bbea915c03a296f3c1ef2a8dfbcb475cdf23a1deb9790e", size = 2459045 }, + { url = "https://files.pythonhosted.org/packages/2f/b9/9d52879a1c1ee090d47530aa0d9d9a0cc5ca7f5ee9f1d8de784fec8fbff0/langchain_community-0.3.12-py3-none-any.whl", hash = "sha256:5a993c931d46dc07fcdfcdfa4d87095c5a15d37ff32b0c16e9ecf6f5caa58c9c", size = 2471646 }, ] [[package]] name = "langchain-core" -version = "0.3.24" +version = "0.3.25" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch" }, @@ -1432,9 +1445,9 @@ dependencies = [ { name = "tenacity" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2f/fc/8ccbaea3d6efa3aca973188f4d765024ecad618ab9eddd22bcc97761ada2/langchain_core-0.3.24.tar.gz", hash = "sha256:460851e8145327f70b70aad7dce2cdbd285e144d14af82b677256b941fc99656", size = 329650 } +sdist = { url = "https://files.pythonhosted.org/packages/a2/cf/fca2b987f8f43ff4e82c171fbacbc0b562047e29d013050ba88c1af54905/langchain_core-0.3.25.tar.gz", hash = "sha256:fdb8df41e5cdd928c0c2551ebbde1cea770ee3c64598395367ad77ddf9acbae7", size = 330368 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/eb/b5681dfa46a8f994d2b8d23610d8545ff80c6ad3fe6cabf76cea3256a8ea/langchain_core-0.3.24-py3-none-any.whl", hash = "sha256:97192552ef882a3dd6ae3b870a180a743801d0137a1159173f51ac555eeb7eec", size = 410643 }, + { url = "https://files.pythonhosted.org/packages/ce/7a/65c154c0cb3638757658b997963defc93c5dd704602f69e559fd78c0e8de/langchain_core-0.3.25-py3-none-any.whl", hash = "sha256:e10581c6c74ba16bdc6fdf16b00cced2aa447cc4024ed19746a1232918edde38", size = 411175 }, ] [[package]] @@ -1453,19 +1466,19 @@ wheels = [ [[package]] name = "langchain-text-splitters" -version = "0.3.2" +version = "0.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/47/63/0f7dae88d87e924d819e6a6375043499e3bc9931e306edd48b396abb4e42/langchain_text_splitters-0.3.2.tar.gz", hash = "sha256:81e6515d9901d6dd8e35fb31ccd4f30f76d44b771890c789dc835ef9f16204df", size = 20229 } +sdist = { url = "https://files.pythonhosted.org/packages/a9/03/75c2ef6e3f9360d1404f24a6dfa4e9a6bba04259c1d5a7369e189f4ce7cd/langchain_text_splitters-0.3.3.tar.gz", hash = "sha256:c596958dcab15fdfe0627fd36ce9d588d0a7e35593af70cd10d0a4a06d69b3ee", size = 21946 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/c6/5ba25c8bad647e92a92b3066177ab10d78efbd16c0b9919948cdcd18b027/langchain_text_splitters-0.3.2-py3-none-any.whl", hash = "sha256:0db28c53f41d1bc024cdb3b1646741f6d46d5371e90f31e7e7c9fbe75d01c726", size = 25564 }, + { url = "https://files.pythonhosted.org/packages/7b/c6/1dc14d2932b45ec8dd78478473b76185377ad4e0556ff62b0716d92b34be/langchain_text_splitters-0.3.3-py3-none-any.whl", hash = "sha256:c2f8650457685072971edc8c52c9f8826496b3307f28004a7fd09eb32d4d819f", size = 27547 }, ] [[package]] name = "langsmith" -version = "0.2.2" +version = "0.2.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -1474,9 +1487,9 @@ dependencies = [ { name = "requests" }, { name = "requests-toolbelt" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/a0/1cdb5505c3794df0b9bf31ab3498a487145b1e1b225c37ac7f3c1a8ac0fc/langsmith-0.2.2.tar.gz", hash = "sha256:6f515ee41ae80968a7d552be1154414ccde57a0a534c960c8c3cd1835734095f", size = 308883 } +sdist = { url = "https://files.pythonhosted.org/packages/70/29/c34a2f19565a8115d1b9a186c6f4c1c61e0aaabbba01c9641a409a0c11d8/langsmith-0.2.3.tar.gz", hash = "sha256:54c231b07fdeb0f8472925074a0ec0ed2cb654a0437d63c6ccf76a9da635900d", size = 308910 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/a6/8c1c1d22512cac8c74a50d68f2cbe144973a824583770ced38b980f4d1dc/langsmith-0.2.2-py3-none-any.whl", hash = "sha256:4786d7dcdbc25e43d4a1bf70bbe12938a9eb2364feec8f6fc4d967162519b367", size = 320633 }, + { url = "https://files.pythonhosted.org/packages/a1/f0/9040d7adff12c3d4bebedb561aed3235b76d04051aee1f267124ef061a53/langsmith-0.2.3-py3-none-any.whl", hash = "sha256:4958b6e918f57fedba6ddc55b8534d1e06478bb44c779aa73713ce898ca6ae87", size = 320665 }, ] [[package]] @@ -1543,7 +1556,7 @@ wheels = [ [[package]] name = "matplotlib" -version = "3.9.3" +version = "3.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "contourpy" }, @@ -1556,26 +1569,26 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/75/9f/562ed484b11ac9f4bb4f9d2d7546954ec106a8c0f06cc755d6f63e519274/matplotlib-3.9.3.tar.gz", hash = "sha256:cd5dbbc8e25cad5f706845c4d100e2c8b34691b412b93717ce38d8ae803bcfa5", size = 36113438 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/d5/eb2338d21b2d36511f9417230413fa0c30fc82283b33dc0e3643969f3b50/matplotlib-3.9.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0a361bd5583bf0bcc08841df3c10269617ee2a36b99ac39d455a767da908bbbc", size = 7883049 }, - { url = "https://files.pythonhosted.org/packages/e5/52/3910833a073e7182ab3ae03810ed418f71c7fdcd65e2862cda1c6a14ffc1/matplotlib-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e14485bb1b83eeb3d55b6878f9560240981e7bbc7a8d4e1e8c38b9bd6ec8d2de", size = 7768285 }, - { url = "https://files.pythonhosted.org/packages/92/67/69df4b6636e40e964788b003535561ea3e98e33e46df4d96fa8c34ef99e6/matplotlib-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a8d279f78844aad213c4935c18f8292a9432d51af2d88bca99072c903948045", size = 8192626 }, - { url = "https://files.pythonhosted.org/packages/40/d6/70a196b0cf62e0a5bc64ccab07816ab4f6c98db0414a55280331a481a5bf/matplotlib-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6c12514329ac0d03128cf1dcceb335f4fbf7c11da98bca68dca8dcb983153a9", size = 8305687 }, - { url = "https://files.pythonhosted.org/packages/c3/43/ef6ab78dd2d8eb362c1e5a31f9cec5ece5761e6143a519153d716d85e590/matplotlib-3.9.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6e9de2b390d253a508dd497e9b5579f3a851f208763ed67fdca5dc0c3ea6849c", size = 9087208 }, - { url = "https://files.pythonhosted.org/packages/30/cb/36844affc69490652b5a99296b9fcee530b96621e23d3143a4839f30fb22/matplotlib-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:d796272408f8567ff7eaa00eb2856b3a00524490e47ad505b0b4ca6bb8a7411f", size = 7833105 }, - { url = "https://files.pythonhosted.org/packages/60/04/949640040982822416c471d9ebe4e9e6c69ca9f9bb6ba82ed30808863c02/matplotlib-3.9.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:203d18df84f5288973b2d56de63d4678cc748250026ca9e1ad8f8a0fd8a75d83", size = 7883417 }, - { url = "https://files.pythonhosted.org/packages/9f/90/ebd37143cd3150b6c650ee1580024df3dd649d176e68d346f826b8d24e37/matplotlib-3.9.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b651b0d3642991259109dc0351fc33ad44c624801367bb8307be9bfc35e427ad", size = 7768720 }, - { url = "https://files.pythonhosted.org/packages/dc/84/6591e6b55d755d16dacdc113205067031867c1f5e3c08b32c01aad831420/matplotlib-3.9.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66d7b171fecf96940ce069923a08ba3df33ef542de82c2ff4fe8caa8346fa95a", size = 8192723 }, - { url = "https://files.pythonhosted.org/packages/29/09/146a17d37e32313507f11ac984e65311f2d5805d731eb981d4f70eb928dc/matplotlib-3.9.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be0ba61f6ff2e6b68e4270fb63b6813c9e7dec3d15fc3a93f47480444fd72f0", size = 8305801 }, - { url = "https://files.pythonhosted.org/packages/85/cb/d2690572c08f19ca7c0f44b1fb4d11c121d63467a57b508cc3656ff80b43/matplotlib-3.9.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d6b2e8856dec3a6db1ae51aec85c82223e834b228c1d3228aede87eee2b34f9", size = 9086564 }, - { url = "https://files.pythonhosted.org/packages/28/dd/0a5176027c1cb94fe75f69f76cb274180c8abf740df6fc0e6a1e4cbaec3f/matplotlib-3.9.3-cp313-cp313-win_amd64.whl", hash = "sha256:90a85a004fefed9e583597478420bf904bb1a065b0b0ee5b9d8d31b04b0f3f70", size = 7833257 }, - { url = "https://files.pythonhosted.org/packages/42/d4/e477d50a8e4b437c2afbb5c665cb8e5d79b06abe6fe3c6915d6f7f0c2ef2/matplotlib-3.9.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3119b2f16de7f7b9212ba76d8fe6a0e9f90b27a1e04683cd89833a991682f639", size = 7911906 }, - { url = "https://files.pythonhosted.org/packages/ae/a1/ba5ab89666c42ace8e31b4ff5a2c76a17e4d6f91aefce476b064c56ff61d/matplotlib-3.9.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:87ad73763d93add1b6c1f9fcd33af662fd62ed70e620c52fcb79f3ac427cf3a6", size = 7801336 }, - { url = "https://files.pythonhosted.org/packages/77/59/4dcdb3a6695af6c698a95aec13016a550ef2f85144d22f61f81d1e064148/matplotlib-3.9.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:026bdf3137ab6022c866efa4813b6bbeddc2ed4c9e7e02f0e323a7bca380dfa0", size = 8218178 }, - { url = "https://files.pythonhosted.org/packages/4f/27/7c72db0d0ee35d9237572565ffa3c0eb25fc46a3f47e0f16412a587bc9d8/matplotlib-3.9.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760a5e89ebbb172989e8273024a1024b0f084510b9105261b3b00c15e9c9f006", size = 8327768 }, - { url = "https://files.pythonhosted.org/packages/de/ad/213eee624feadba7b77e881c9d2c04c1e036efe69d19031e3fa927fdb5dc/matplotlib-3.9.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a42b9dc42de2cfe357efa27d9c50c7833fc5ab9b2eb7252ccd5d5f836a84e1e4", size = 9094075 }, - { url = "https://files.pythonhosted.org/packages/19/1b/cb8e99a5fe2e2b14e3b8234cb1649a675be63f74a5224a648ae4ab61f60c/matplotlib-3.9.3-cp313-cp313t-win_amd64.whl", hash = "sha256:e0fcb7da73fbf67b5f4bdaa57d85bb585a4e913d4a10f3e15b32baea56a67f0a", size = 7888937 }, +sdist = { url = "https://files.pythonhosted.org/packages/68/dd/fa2e1a45fce2d09f4aea3cee169760e672c8262325aa5796c49d543dc7e6/matplotlib-3.10.0.tar.gz", hash = "sha256:b886d02a581b96704c9d1ffe55709e49b4d2d52709ccebc4be42db856e511278", size = 36686418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/c7/6b2d8cb7cc251d53c976799cacd3200add56351c175ba89ab9cbd7c1e68a/matplotlib-3.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4659665bc7c9b58f8c00317c3c2a299f7f258eeae5a5d56b4c64226fca2f7c59", size = 8172465 }, + { url = "https://files.pythonhosted.org/packages/42/2a/6d66d0fba41e13e9ca6512a0a51170f43e7e7ed3a8dfa036324100775612/matplotlib-3.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d44cb942af1693cced2604c33a9abcef6205601c445f6d0dc531d813af8a2f5a", size = 8043300 }, + { url = "https://files.pythonhosted.org/packages/90/60/2a60342b27b90a16bada939a85e29589902b41073f59668b904b15ea666c/matplotlib-3.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a994f29e968ca002b50982b27168addfd65f0105610b6be7fa515ca4b5307c95", size = 8448936 }, + { url = "https://files.pythonhosted.org/packages/a7/b2/d872fc3d753516870d520595ddd8ce4dd44fa797a240999f125f58521ad7/matplotlib-3.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b0558bae37f154fffda54d779a592bc97ca8b4701f1c710055b609a3bac44c8", size = 8594151 }, + { url = "https://files.pythonhosted.org/packages/f4/bd/b2f60cf7f57d014ab33e4f74602a2b5bdc657976db8196bbc022185f6f9c/matplotlib-3.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:503feb23bd8c8acc75541548a1d709c059b7184cde26314896e10a9f14df5f12", size = 9400347 }, + { url = "https://files.pythonhosted.org/packages/9f/6e/264673e64001b99d747aff5a288eca82826c024437a3694e19aed1decf46/matplotlib-3.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:c40ba2eb08b3f5de88152c2333c58cee7edcead0a2a0d60fcafa116b17117adc", size = 8039144 }, + { url = "https://files.pythonhosted.org/packages/72/11/1b2a094d95dcb6e6edd4a0b238177c439006c6b7a9fe8d31801237bf512f/matplotlib-3.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96f2886f5c1e466f21cc41b70c5a0cd47bfa0015eb2d5793c88ebce658600e25", size = 8173073 }, + { url = "https://files.pythonhosted.org/packages/0d/c4/87b6ad2723070511a411ea719f9c70fde64605423b184face4e94986de9d/matplotlib-3.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:12eaf48463b472c3c0f8dbacdbf906e573013df81a0ab82f0616ea4b11281908", size = 8043892 }, + { url = "https://files.pythonhosted.org/packages/57/69/cb0812a136550b21361335e9ffb7d459bf6d13e03cb7b015555d5143d2d6/matplotlib-3.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fbbabc82fde51391c4da5006f965e36d86d95f6ee83fb594b279564a4c5d0d2", size = 8450532 }, + { url = "https://files.pythonhosted.org/packages/ea/3a/bab9deb4fb199c05e9100f94d7f1c702f78d3241e6a71b784d2b88d7bebd/matplotlib-3.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad2e15300530c1a94c63cfa546e3b7864bd18ea2901317bae8bbf06a5ade6dcf", size = 8593905 }, + { url = "https://files.pythonhosted.org/packages/8b/66/742fd242f989adc1847ddf5f445815f73ad7c46aa3440690cc889cfa423c/matplotlib-3.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3547d153d70233a8496859097ef0312212e2689cdf8d7ed764441c77604095ae", size = 9399609 }, + { url = "https://files.pythonhosted.org/packages/fa/d6/54cee7142cef7d910a324a7aedf335c0c147b03658b54d49ec48166f10a6/matplotlib-3.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:c55b20591ced744aa04e8c3e4b7543ea4d650b6c3c4b208c08a05b4010e8b442", size = 8039076 }, + { url = "https://files.pythonhosted.org/packages/43/14/815d072dc36e88753433bfd0385113405efb947e6895ff7b4d2e8614a33b/matplotlib-3.10.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ade1003376731a971e398cc4ef38bb83ee8caf0aee46ac6daa4b0506db1fd06", size = 8211000 }, + { url = "https://files.pythonhosted.org/packages/9a/76/34e75f364194ec352678adcb540964be6f35ec7d3d8c75ebcb17e6839359/matplotlib-3.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:95b710fea129c76d30be72c3b38f330269363fbc6e570a5dd43580487380b5ff", size = 8087707 }, + { url = "https://files.pythonhosted.org/packages/c3/2b/b6bc0dff6a72d333bc7df94a66e6ce662d224e43daa8ad8ae4eaa9a77f55/matplotlib-3.10.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdbaf909887373c3e094b0318d7ff230b2ad9dcb64da7ade654182872ab2593", size = 8477384 }, + { url = "https://files.pythonhosted.org/packages/c2/2d/b5949fb2b76e9b47ab05e25a5f5f887c70de20d8b0cbc704a4e2ee71c786/matplotlib-3.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d907fddb39f923d011875452ff1eca29a9e7f21722b873e90db32e5d8ddff12e", size = 8610334 }, + { url = "https://files.pythonhosted.org/packages/d6/9a/6e3c799d5134d9af44b01c787e1360bee38cf51850506ea2e743a787700b/matplotlib-3.10.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3b427392354d10975c1d0f4ee18aa5844640b512d5311ef32efd4dd7db106ede", size = 9406777 }, + { url = "https://files.pythonhosted.org/packages/0e/dd/e6ae97151e5ed648ab2ea48885bc33d39202b640eec7a2910e2c843f7ac0/matplotlib-3.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5fd41b0ec7ee45cd960a8e71aea7c946a28a0b8a4dcee47d2856b2af051f334c", size = 8109742 }, ] [[package]] @@ -1786,7 +1799,7 @@ wheels = [ [[package]] name = "openai" -version = "1.57.2" +version = "1.57.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1798,9 +1811,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/b6/9a404e1d1043cabbffb020f9d850a44a31f30f284444a528d9a1f0eec9df/openai-1.57.2.tar.gz", hash = "sha256:5f49fd0f38e9f2131cda7deb45dafdd1aee4f52a637e190ce0ecf40147ce8cee", size = 315752 } +sdist = { url = "https://files.pythonhosted.org/packages/29/e4/267d7f3f93432454f6dde95f08c737f48f031090693e558e2da7e63909ec/openai-1.57.4.tar.gz", hash = "sha256:a8f071a3e9198e2818f63aade68e759417b9f62c0971bdb83de82504b70b77f7", size = 316459 } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/e7/95437fb676381e927d4cb3f9f8dd90ed24cfd264f572db4d395037428594/openai-1.57.2-py3-none-any.whl", hash = "sha256:f7326283c156fdee875746e7e54d36959fb198eadc683952ee05e3302fbd638d", size = 389873 }, + { url = "https://files.pythonhosted.org/packages/3e/be/b466c8b64b224d285a338fbc705dc9d58cd60068bbfb8be2e47b1691e55c/openai-1.57.4-py3-none-any.whl", hash = "sha256:7def1ab2d52f196357ce31b9cfcf4181529ce00838286426bb35be81c035dafb", size = 390267 }, ] [[package]] @@ -1957,37 +1970,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/3d/fcde4f8f0bf9fa1ee73a12304fa538076fb83fe0a2ae966ab0f0b7da5109/opentelemetry_instrumentation_flask-0.48b0-py3-none-any.whl", hash = "sha256:26b045420b9d76e85493b1c23fcf27517972423480dc6cf78fd6924248ba5808", size = 14588 }, ] -[[package]] -name = "opentelemetry-instrumentation-httpx" -version = "0.48b0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-instrumentation" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "opentelemetry-util-http" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d3/d9/c65d818607c16d1b7ea8d2de6111c6cecadf8d2fd38c1885a72733a7c6d3/opentelemetry_instrumentation_httpx-0.48b0.tar.gz", hash = "sha256:ee977479e10398931921fb995ac27ccdeea2e14e392cb27ef012fc549089b60a", size = 16931 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/fe/f2daa9d6d988c093b8c7b1d35df675761a8ece0b600b035dc04982746c9d/opentelemetry_instrumentation_httpx-0.48b0-py3-none-any.whl", hash = "sha256:d94f9d612c82d09fe22944d1904a30a464c19bea2ba76be656c99a28ad8be8e5", size = 13900 }, -] - -[[package]] -name = "opentelemetry-instrumentation-openai" -version = "0.33.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-instrumentation" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "opentelemetry-semantic-conventions-ai" }, - { name = "tiktoken" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/75/8c/9c30f9ac0ac6f9f8af655e6d8e4375f5c91f6a91cd3bbaaa52bed2b4bbda/opentelemetry_instrumentation_openai-0.33.9.tar.gz", hash = "sha256:5989a6049e63a09a6e9d699c077f7bbc932c0bda5a08f9ec0f4e88fd0c38d8b7", size = 14630 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/5b/6037e904a30360b2efa965c4d3bee5d3ff4257a6bf518388476e26c7eb6c/opentelemetry_instrumentation_openai-0.33.9-py3-none-any.whl", hash = "sha256:9a54ec31a66c212cd42b7f02701beecea4068effdf227b11c96fecfbc6544f40", size = 22075 }, -] - [[package]] name = "opentelemetry-instrumentation-psycopg2" version = "0.48b0" @@ -2129,15 +2111,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/7a/4f0063dbb0b6c971568291a8bc19a4ca70d3c185db2d956230dd67429dfc/opentelemetry_semantic_conventions-0.48b0-py3-none-any.whl", hash = "sha256:a0de9f45c413a8669788a38569c7e0a11ce6ce97861a628cca785deecdc32a1f", size = 149685 }, ] -[[package]] -name = "opentelemetry-semantic-conventions-ai" -version = "0.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/92/5f/76a9f82b08cdc05482a162d2bf67b5c0bbcc4118d4654f4b366f10fd71af/opentelemetry_semantic_conventions_ai-0.4.2.tar.gz", hash = "sha256:90b969c7d838e03e30a9150ffe46543d8e58e9d7370c7221fd30d4ce4d7a1b96", size = 4570 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/bb/6b578a23c46ec87f364c809343cd8e80fcbcc7fc22129ee3dd1461aada81/opentelemetry_semantic_conventions_ai-0.4.2-py3-none-any.whl", hash = "sha256:0a5432aacd441eb7dbdf62e0de3f3d90ed4f69595b687a6dd2ccc4c5b94c5861", size = 5262 }, -] - [[package]] name = "opentelemetry-util-http" version = "0.48b0" @@ -2222,11 +2195,11 @@ wheels = [ [[package]] name = "phonenumbers" -version = "8.13.51" +version = "8.13.52" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/88/70/bdcc356aa0460c490bc7dd59ef4510611b737eea6b55e9bfdcedde588e15/phonenumbers-8.13.51.tar.gz", hash = "sha256:e8f4969841a163a3df3cb3ed8c499f0e00d58b2a1ecaa661e84e1d5fee67335f", size = 2297685 } +sdist = { url = "https://files.pythonhosted.org/packages/2f/be/8d1698beaed180df58685a3d0e1aacac86a7b90e74a954ca489bd2a0a247/phonenumbers-8.13.52.tar.gz", hash = "sha256:fdc371ea6a4da052beb1225de63963d5a2fddbbff2bb53e3a957f360e0185f80", size = 2296342 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/b7/098745e60d2303ff51f27b929880ee1b300884c07bd6b0aaf5778ee83e68/phonenumbers-8.13.51-py2.py3-none-any.whl", hash = "sha256:3bdacc0a155c8761c2a0ba7fc5632fe1541e5291ab70a4f345ab80a5742874b6", size = 2583157 }, + { url = "https://files.pythonhosted.org/packages/b0/fa/ccc322224bbe27434f67d8fae917affcf6af4a6ef55f82f16ae87d9c784d/phonenumbers-8.13.52-py2.py3-none-any.whl", hash = "sha256:e803210038ece9d208b129e3023dc20e656a820d6bf6f1cb0471d4164f54bada", size = 2582018 }, ] [[package]] @@ -2502,15 +2475,15 @@ wheels = [ [[package]] name = "pydantic-settings" -version = "2.6.1" +version = "2.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "python-dotenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/d4/9dfbe238f45ad8b168f5c96ee49a3df0598ce18a0795a983b419949ce65b/pydantic_settings-2.6.1.tar.gz", hash = "sha256:e0f92546d8a9923cb8941689abf85d6601a8c19a23e97a34b2964a2e3f813ca0", size = 75646 } +sdist = { url = "https://files.pythonhosted.org/packages/86/41/19b62b99e7530cfa1d6ccd16199afd9289a12929bef1a03aa4382b22e683/pydantic_settings-2.7.0.tar.gz", hash = "sha256:ac4bfd4a36831a48dbf8b2d9325425b549a0a6f18cea118436d728eb4f1c4d66", size = 79743 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/f9/ff95fd7d760af42f647ea87f9b8a383d891cdb5e5dbd4613edaeb094252a/pydantic_settings-2.6.1-py3-none-any.whl", hash = "sha256:7fb0637c786a558d3103436278a7c4f1cfd29ba8973238a50c5bb9a55387da87", size = 28595 }, + { url = "https://files.pythonhosted.org/packages/f9/00/57b4540deb5c3a39ba689bb519a4e03124b24ab8589e618be4aac2c769bd/pydantic_settings-2.7.0-py3-none-any.whl", hash = "sha256:e00c05d5fa6cbbb227c84bd7487c5c1065084119b750df7c8c1a554aed236eb5", size = 29549 }, ] [[package]] @@ -2596,14 +2569,14 @@ wheels = [ [[package]] name = "pytest-asyncio" -version = "0.24.0" +version = "0.25.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/6d/c6cf50ce320cf8611df7a1254d86233b3df7cc07f9b5f5cbcb82e08aa534/pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276", size = 49855 } +sdist = { url = "https://files.pythonhosted.org/packages/94/18/82fcb4ee47d66d99f6cd1efc0b11b2a25029f303c599a5afda7c1bca4254/pytest_asyncio-0.25.0.tar.gz", hash = "sha256:8c0610303c9e0442a5db8604505fc0f545456ba1528824842b37b4a626cbf609", size = 53298 } wheels = [ - { url = "https://files.pythonhosted.org/packages/96/31/6607dab48616902f76885dfcf62c08d929796fc3b2d2318faf9fd54dbed9/pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b", size = 18024 }, + { url = "https://files.pythonhosted.org/packages/88/56/2ee0cab25c11d4e38738a2a98c645a8f002e2ecf7b5ed774c70d53b92bb1/pytest_asyncio-0.25.0-py3-none-any.whl", hash = "sha256:db5432d18eac6b7e28b46dcd9b69921b55c3b1086e85febfe04e70b18d9e81b3", size = 19245 }, ] [[package]] @@ -2659,11 +2632,11 @@ wheels = [ [[package]] name = "python-multipart" -version = "0.0.19" +version = "0.0.20" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c1/19/93bfb43a3c41b1dd0fa1fa66a08286f6467d36d30297a7aaab8c0b176a26/python_multipart-0.0.19.tar.gz", hash = "sha256:905502ef39050557b7a6af411f454bc19526529ca46ae6831508438890ce12cc", size = 36886 } +sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/f4/ddd0fcdc454cf3870153ae16a818256523d31c3c8136e216bc6836ed4cd1/python_multipart-0.0.19-py3-none-any.whl", hash = "sha256:f8d5b0b9c618575bf9df01c684ded1d94a338839bdd8223838afacfb4bb2082d", size = 24448 }, + { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 }, ] [[package]] @@ -2852,27 +2825,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.8.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/2b/01245f4f3a727d60bebeacd7ee6d22586c7f62380a2597ddb22c2f45d018/ruff-0.8.2.tar.gz", hash = "sha256:b84f4f414dda8ac7f75075c1fa0b905ac0ff25361f42e6d5da681a465e0f78e5", size = 3349020 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/29/366be70216dba1731a00a41f2f030822b0c96c7c4f3b2c0cdce15cbace74/ruff-0.8.2-py3-none-linux_armv6l.whl", hash = "sha256:c49ab4da37e7c457105aadfd2725e24305ff9bc908487a9bf8d548c6dad8bb3d", size = 10530649 }, - { url = "https://files.pythonhosted.org/packages/63/82/a733956540bb388f00df5a3e6a02467b16c0e529132625fe44ce4c5fb9c7/ruff-0.8.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ec016beb69ac16be416c435828be702ee694c0d722505f9c1f35e1b9c0cc1bf5", size = 10274069 }, - { url = "https://files.pythonhosted.org/packages/3d/12/0b3aa14d1d71546c988a28e1b412981c1b80c8a1072e977a2f30c595cc4a/ruff-0.8.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f05cdf8d050b30e2ba55c9b09330b51f9f97d36d4673213679b965d25a785f3c", size = 9909400 }, - { url = "https://files.pythonhosted.org/packages/23/08/f9f08cefb7921784c891c4151cce6ed357ff49e84b84978440cffbc87408/ruff-0.8.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60f578c11feb1d3d257b2fb043ddb47501ab4816e7e221fbb0077f0d5d4e7b6f", size = 10766782 }, - { url = "https://files.pythonhosted.org/packages/e4/71/bf50c321ec179aa420c8ec40adac5ae9cc408d4d37283a485b19a2331ceb/ruff-0.8.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cbd5cf9b0ae8f30eebc7b360171bd50f59ab29d39f06a670b3e4501a36ba5897", size = 10286316 }, - { url = "https://files.pythonhosted.org/packages/f2/83/c82688a2a6117539aea0ce63fdf6c08e60fe0202779361223bcd7f40bd74/ruff-0.8.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b402ddee3d777683de60ff76da801fa7e5e8a71038f57ee53e903afbcefdaa58", size = 11338270 }, - { url = "https://files.pythonhosted.org/packages/7f/d7/bc6a45e5a22e627640388e703160afb1d77c572b1d0fda8b4349f334fc66/ruff-0.8.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:705832cd7d85605cb7858d8a13d75993c8f3ef1397b0831289109e953d833d29", size = 12058579 }, - { url = "https://files.pythonhosted.org/packages/da/3b/64150c93946ec851e6f1707ff586bb460ca671581380c919698d6a9267dc/ruff-0.8.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:32096b41aaf7a5cc095fa45b4167b890e4c8d3fd217603f3634c92a541de7248", size = 11615172 }, - { url = "https://files.pythonhosted.org/packages/e4/9e/cf12b697ea83cfe92ec4509ae414dc4c9b38179cc681a497031f0d0d9a8e/ruff-0.8.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e769083da9439508833cfc7c23e351e1809e67f47c50248250ce1ac52c21fb93", size = 12882398 }, - { url = "https://files.pythonhosted.org/packages/a9/27/96d10863accf76a9c97baceac30b0a52d917eb985a8ac058bd4636aeede0/ruff-0.8.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fe716592ae8a376c2673fdfc1f5c0c193a6d0411f90a496863c99cd9e2ae25d", size = 11176094 }, - { url = "https://files.pythonhosted.org/packages/eb/10/cd2fd77d4a4e7f03c29351be0f53278a393186b540b99df68beb5304fddd/ruff-0.8.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:81c148825277e737493242b44c5388a300584d73d5774defa9245aaef55448b0", size = 10771884 }, - { url = "https://files.pythonhosted.org/packages/71/5d/beabb2ff18870fc4add05fa3a69a4cb1b1d2d6f83f3cf3ae5ab0d52f455d/ruff-0.8.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d261d7850c8367704874847d95febc698a950bf061c9475d4a8b7689adc4f7fa", size = 10382535 }, - { url = "https://files.pythonhosted.org/packages/ae/29/6b3fdf3ad3e35b28d87c25a9ff4c8222ad72485ab783936b2b267250d7a7/ruff-0.8.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1ca4e3a87496dc07d2427b7dd7ffa88a1e597c28dad65ae6433ecb9f2e4f022f", size = 10886995 }, - { url = "https://files.pythonhosted.org/packages/e9/dc/859d889b4d9356a1a2cdbc1e4a0dda94052bc5b5300098647e51a58c430b/ruff-0.8.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:729850feed82ef2440aa27946ab39c18cb4a8889c1128a6d589ffa028ddcfc22", size = 11220750 }, - { url = "https://files.pythonhosted.org/packages/0b/08/e8f519f61f1d624264bfd6b8829e4c5f31c3c61193bc3cff1f19dbe7626a/ruff-0.8.2-py3-none-win32.whl", hash = "sha256:ac42caaa0411d6a7d9594363294416e0e48fc1279e1b0e948391695db2b3d5b1", size = 8729396 }, - { url = "https://files.pythonhosted.org/packages/f8/d4/ba1c7ab72aba37a2b71fe48ab95b80546dbad7a7f35ea28cf66fc5cea5f6/ruff-0.8.2-py3-none-win_amd64.whl", hash = "sha256:2aae99ec70abf43372612a838d97bfe77d45146254568d94926e8ed5bbb409ea", size = 9594729 }, - { url = "https://files.pythonhosted.org/packages/23/34/db20e12d3db11b8a2a8874258f0f6d96a9a4d631659d54575840557164c8/ruff-0.8.2-py3-none-win_arm64.whl", hash = "sha256:fb88e2a506b70cfbc2de6fae6681c4f944f7dd5f2fe87233a7233d888bad73e8", size = 9035131 }, +version = "0.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/5e/683c7ef7a696923223e7d95ca06755d6e2acbc5fd8382b2912a28008137c/ruff-0.8.3.tar.gz", hash = "sha256:5e7558304353b84279042fc584a4f4cb8a07ae79b2bf3da1a7551d960b5626d3", size = 3378522 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/c4/bfdbb8b9c419ff3b52479af8581026eeaac3764946fdb463dec043441b7d/ruff-0.8.3-py3-none-linux_armv6l.whl", hash = "sha256:8d5d273ffffff0acd3db5bf626d4b131aa5a5ada1276126231c4174543ce20d6", size = 10535860 }, + { url = "https://files.pythonhosted.org/packages/ef/c5/0aabdc9314b4b6f051168ac45227e2aa8e1c6d82718a547455e40c9c9faa/ruff-0.8.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e4d66a21de39f15c9757d00c50c8cdd20ac84f55684ca56def7891a025d7e939", size = 10346327 }, + { url = "https://files.pythonhosted.org/packages/1a/78/4843a59e7e7b398d6019cf91ab06502fd95397b99b2b858798fbab9151f5/ruff-0.8.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c356e770811858bd20832af696ff6c7e884701115094f427b64b25093d6d932d", size = 9942585 }, + { url = "https://files.pythonhosted.org/packages/91/5a/642ed8f1ba23ffc2dd347697e01eef3c42fad6ac76603be4a8c3a9d6311e/ruff-0.8.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c0a60a825e3e177116c84009d5ebaa90cf40dfab56e1358d1df4e29a9a14b13", size = 10797597 }, + { url = "https://files.pythonhosted.org/packages/30/25/2e654bc7226da09a49730a1a2ea6e89f843b362db80b4b2a7a4f948ac986/ruff-0.8.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75fb782f4db39501210ac093c79c3de581d306624575eddd7e4e13747e61ba18", size = 10307244 }, + { url = "https://files.pythonhosted.org/packages/c0/2d/a224d56bcd4383583db53c2b8f410ebf1200866984aa6eb9b5a70f04e71f/ruff-0.8.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f26bc76a133ecb09a38b7868737eded6941b70a6d34ef53a4027e83913b6502", size = 11362439 }, + { url = "https://files.pythonhosted.org/packages/82/01/03e2857f9c371b8767d3e909f06a33bbdac880df17f17f93d6f6951c3381/ruff-0.8.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:01b14b2f72a37390c1b13477c1c02d53184f728be2f3ffc3ace5b44e9e87b90d", size = 12078538 }, + { url = "https://files.pythonhosted.org/packages/af/ae/ff7f97b355da16d748ceec50e1604a8215d3659b36b38025a922e0612e9b/ruff-0.8.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53babd6e63e31f4e96ec95ea0d962298f9f0d9cc5990a1bbb023a6baf2503a82", size = 11616172 }, + { url = "https://files.pythonhosted.org/packages/6a/d0/6156d4d1e53ebd17747049afe801c5d7e3014d9b2f398b9236fe36ba4320/ruff-0.8.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ae441ce4cf925b7f363d33cd6570c51435972d697e3e58928973994e56e1452", size = 12919886 }, + { url = "https://files.pythonhosted.org/packages/4e/84/affcb30bacb94f6036a128ad5de0e29f543d3f67ee42b490b17d68e44b8a/ruff-0.8.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7c65bc0cadce32255e93c57d57ecc2cca23149edd52714c0c5d6fa11ec328cd", size = 11212599 }, + { url = "https://files.pythonhosted.org/packages/60/b9/5694716bdefd8f73df7c0104334156c38fb0f77673d2966a5a1345bab94d/ruff-0.8.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5be450bb18f23f0edc5a4e5585c17a56ba88920d598f04a06bd9fd76d324cb20", size = 10784637 }, + { url = "https://files.pythonhosted.org/packages/24/7e/0e8f835103ac7da81c3663eedf79dec8359e9ae9a3b0d704bae50be59176/ruff-0.8.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8faeae3827eaa77f5721f09b9472a18c749139c891dbc17f45e72d8f2ca1f8fc", size = 10390591 }, + { url = "https://files.pythonhosted.org/packages/27/da/180ec771fc01c004045962ce017ca419a0281f4bfaf867ed0020f555b56e/ruff-0.8.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:db503486e1cf074b9808403991663e4277f5c664d3fe237ee0d994d1305bb060", size = 10894298 }, + { url = "https://files.pythonhosted.org/packages/6d/f8/29f241742ed3954eb2222314b02db29f531a15cab3238d1295e8657c5f18/ruff-0.8.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6567be9fb62fbd7a099209257fef4ad2c3153b60579818b31a23c886ed4147ea", size = 11275965 }, + { url = "https://files.pythonhosted.org/packages/79/e9/5b81dc9afc8a80884405b230b9429efeef76d04caead904bd213f453b973/ruff-0.8.3-py3-none-win32.whl", hash = "sha256:19048f2f878f3ee4583fc6cb23fb636e48c2635e30fb2022b3a1cd293402f964", size = 8807651 }, + { url = "https://files.pythonhosted.org/packages/ea/67/7291461066007617b59a707887b90e319b6a043c79b4d19979f86b7a20e7/ruff-0.8.3-py3-none-win_amd64.whl", hash = "sha256:f7df94f57d7418fa7c3ffb650757e0c2b96cf2501a0b192c18e4fb5571dfada9", size = 9625289 }, + { url = "https://files.pythonhosted.org/packages/03/8f/e4fa95288b81233356d9a9dcaed057e5b0adc6399aa8fd0f6d784041c9c3/ruff-0.8.3-py3-none-win_arm64.whl", hash = "sha256:fe2756edf68ea79707c8d68b78ca9a58ed9af22e430430491ee03e718b5e4936", size = 9078754 }, ] [[package]] @@ -3145,15 +3118,15 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.32.1" +version = "0.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6a/3c/21dba3e7d76138725ef307e3d7ddd29b763119b3aa459d02cc05fefcff75/uvicorn-0.32.1.tar.gz", hash = "sha256:ee9519c246a72b1c084cea8d3b44ed6026e78a4a309cbedae9c37e4cb9fbb175", size = 77630 } +sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/c1/2d27b0a15826c2b71dcf6e2f5402181ef85acf439617bb2f1453125ce1f3/uvicorn-0.32.1-py3-none-any.whl", hash = "sha256:82ad92fd58da0d12af7482ecdb5f2470a04c9c9a53ced65b9bbb4a205377602e", size = 63828 }, + { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 }, ] [package.optional-dependencies]