From 0cae1e0adfa5dc49738fbcb7a99982f33f46ee84 Mon Sep 17 00:00:00 2001 From: Jacob Alber Date: Mon, 8 Sep 2025 15:11:53 -0400 Subject: [PATCH 01/32] .NET: Improve DevEx for simple Executors (#626) * feat: Improve DevEx for simple Executors * Add abstract types for executors that will only handle one type of message * Add FunctionExecutor and configuration capability on delegates * Add support for late-instantiated Executors * refactor: Remove open-typed extension method * refactor: Switch to TaskFactory pattern for async--from-sync * docs: Update XML docs for publics and fix formatting * refactor: Better naming for ExecutorIsh configuration methods * docs: Fix typo in ExecutorIshConfigurationExtensions.ConfigureFactory --- .../src/Microsoft.Agents.Workflows/Config.cs | 32 ++++ .../ConfigurationExtensions.cs | 32 ++++ .../Microsoft.Agents.Workflows/Configured.cs | 153 ++++++++++++++++++ .../Microsoft.Agents.Workflows/Executor.cs | 56 +++++-- .../Microsoft.Agents.Workflows/ExecutorIsh.cs | 114 +++++++++++-- .../ExecutorOptions.cs | 2 +- .../ExecutorRegistration.cs | 7 +- .../FunctionExecutor.cs | 72 +++++++++ .../InProc/InProcessRunnerContext.cs | 2 +- .../Reflection/ReflectingExecutor.cs | 4 +- .../Specialized/RequestInfoExecutor.cs | 2 +- .../WorkflowBuilder.cs | 33 +++- .../RepresentationTests.cs | 31 ++-- 13 files changed, 498 insertions(+), 42 deletions(-) create mode 100644 dotnet/src/Microsoft.Agents.Workflows/Config.cs create mode 100644 dotnet/src/Microsoft.Agents.Workflows/ConfigurationExtensions.cs create mode 100644 dotnet/src/Microsoft.Agents.Workflows/Configured.cs create mode 100644 dotnet/src/Microsoft.Agents.Workflows/FunctionExecutor.cs diff --git a/dotnet/src/Microsoft.Agents.Workflows/Config.cs b/dotnet/src/Microsoft.Agents.Workflows/Config.cs new file mode 100644 index 0000000000..cdd2ce1534 --- /dev/null +++ b/dotnet/src/Microsoft.Agents.Workflows/Config.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace Microsoft.Agents.Workflows; + +/// +/// Configuration for an object with a string identifier. For example, object. +/// +/// A unique identifier for the configurable object. +public class Config(string? id = null) +{ + /// + /// A unique identifier for the configurable object. + /// + /// + /// If not provided, the configured object will generate its own identifier. + /// + public string? Id => id; +} + +/// +/// Configuration for an object with a string identifier and options of type . +/// +/// The type of options for the configurable object. +/// The options for the configurable object. +/// A unique identifier for the configurable object. +public class Config(TOptions? options = default, string? id = null) : Config(id) +{ + /// + /// Options for the configured object. + /// + public TOptions? Options => options; +} diff --git a/dotnet/src/Microsoft.Agents.Workflows/ConfigurationExtensions.cs b/dotnet/src/Microsoft.Agents.Workflows/ConfigurationExtensions.cs new file mode 100644 index 0000000000..c3309116c6 --- /dev/null +++ b/dotnet/src/Microsoft.Agents.Workflows/ConfigurationExtensions.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace Microsoft.Agents.Workflows; + +/// +/// Extensions methods for creating Configured objects +/// +public static class ConfigurationExtensions +{ + /// + /// Creates a new configuration that treats the subject as its base type, allowing configuration to be applied at + /// the parent type level. + /// + /// The type of the original subject being configured. Must inherit from or implement TParent. + /// The base type or interface to which the configuration will be upcast. + /// The existing configuration for the subject type to be upcast to its parent type. Cannot be null. + /// A new instance that applies the original configuration logic to the parent type. + public static Configured Super(this Configured configured) where TSubject : TParent + => new(async config => await configured.FactoryAsync(config).ConfigureAwait(false), configured.Id, configured.Raw); + + /// + /// Creates a new configuration that treats the subject as its base type, allowing configuration to be applied at + /// the parent type level. + /// + /// The type of the original subject being configured. Must inherit from or implement TParent. + /// The base type or interface to which the configuration will be upcast. + /// The type of configuration options for the original subject being configured. + /// The existing configuration for the subject type to be upcast to its parent type. Cannot be null. + /// A new instance that applies the original configuration logic to the parent type. + public static Configured Super(this Configured configured) where TSubject : TParent + => configured.Memoize().Super(); +} diff --git a/dotnet/src/Microsoft.Agents.Workflows/Configured.cs b/dotnet/src/Microsoft.Agents.Workflows/Configured.cs new file mode 100644 index 0000000000..d88da9277f --- /dev/null +++ b/dotnet/src/Microsoft.Agents.Workflows/Configured.cs @@ -0,0 +1,153 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Threading.Tasks; + +namespace Microsoft.Agents.Workflows; + +/// +/// Helper methods for creating instances. +/// +public static class Configured +{ + /// + /// Creates a instance from an existing subject instance. + /// + /// + /// The subject instance. If the subject implements , its ID will be used + /// and checked against the provided ID (if any). + /// + /// + /// A unique identifier for the configured subject. This is required if the subject does not implement + /// + /// + /// + /// The raw representation of the subject instance. + /// + /// + public static Configured FromInstance(TSubject subject, string? id = null, object? raw = null) + { + if (subject is IIdentified identified) + { + if (id != null && identified.Id != id) + { + throw new ArgumentException($"Provided ID '{id}' does not match subject's ID '{identified.Id}'.", nameof(id)); + } + + return new Configured((_) => new(subject), id: identified.Id, raw: raw ?? subject); + } + + if (id == null) + { + throw new ArgumentNullException(nameof(id), "ID must be provided when the subject does not implement IIdentified."); + } + + return new Configured((_) => new(subject), id, raw: raw ?? subject); + } +} + +/// +/// A representation of a preconfigured, lazy-instantiatable instance of . +/// +/// The type of the preconfigured subject. +/// A factory to intantiate the subject when desired. +/// The unique identifier for the configured subject. +/// +public class Configured(Func> factoryAsync, string id, object? raw = null) +{ + /// + /// The raw representation of the configured object, if any. + /// + public object? Raw => raw; + + /// + /// Gets the configured identifier for the subject. + /// + public string Id => id; + + /// + /// Gets the factory function to create an instance of given a . + /// + public Func> FactoryAsync => factoryAsync; + + /// + /// The configuration for this configured instance. + /// + public Config Configuration => new(this.Id); + + /// + /// Gets a "partially" applied factory function that only requires no parameters to create an instance of + /// with the provided instance. + /// + internal Func> BoundFactoryAsync => () => this.FactoryAsync(this.Configuration); +} + +/// +/// A representation of a preconfigured, lazy-instantiatable instance of . +/// +/// The type of the preconfigured subject. +/// The type of configuration options for the preconfigured subject. +/// A factory to intantiate the subject when desired. +/// The unique identifier for the configured subject. +/// Additional configuration options for the subject. +/// +public class Configured(Func, ValueTask> factoryAsync, string id, TOptions? options = default, object? raw = null) +{ + /// + /// The raw representation of the configured object, if any. + /// + public object? Raw => raw; + + /// + /// Gets the configured identifier for the subject. + /// + public string Id => id; + + /// + /// Gets the options associated with this instance. + /// + public TOptions? Options => options; + + /// + /// Gets the factory function to create an instance of given a . + /// + public Func, ValueTask> FactoryAsync => factoryAsync; + + /// + /// The configuration for this configured instance. + /// + public Config Configuration => new(this.Options, this.Id); + + /// + /// Gets a "partially" applied factory function that only requires no parameters to create an instance of + /// with the provided instance. + /// + internal Func> BoundFactoryAsync => () => this.CreateValidatingMemoizedFactory()(this.Configuration); + + private Func> CreateValidatingMemoizedFactory() + { + return FactoryAsync; + + async ValueTask FactoryAsync(Config configuration) + { + if (this.Id != configuration.Id) + { + throw new InvalidOperationException($"Requested instance ID '{configuration.Id}' does not match configured ID '{this.Id}'."); + } + + TSubject subject = await this.FactoryAsync(this.Configuration).ConfigureAwait(false); + + if (this.Id != null && subject is IIdentified identified && identified.Id != this.Id) + { + throw new InvalidOperationException($"Created instance ID '{identified.Id}' does not match configured ID '{this.Id}'."); + } + + return subject; + } + } + + /// + /// Memoizes and erases the typed configuration options for the subject. + /// + public Configured Memoize() => new(this.CreateValidatingMemoizedFactory(), this.Id); +} diff --git a/dotnet/src/Microsoft.Agents.Workflows/Executor.cs b/dotnet/src/Microsoft.Agents.Workflows/Executor.cs index 97519ec0ad..c3e704ed69 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Executor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Executor.cs @@ -7,6 +7,7 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.Agents.Workflows.Execution; +using Microsoft.Agents.Workflows.Reflection; namespace Microsoft.Agents.Workflows; @@ -21,20 +22,17 @@ public abstract class Executor : IIdentified /// public string Id { get; } + private readonly ExecutorOptions _options; + /// /// Initialize the executor with a unique identifier /// - /// A optional unique identifier for the executor. If null, a type-tagged - /// UUID will be generated. - protected Executor(string? id = null) : this(ExecutorOptions.Default, id) - { - } - - private readonly ExecutorOptions _options; - internal Executor(ExecutorOptions options, string? id = null) + /// A optional unique identifier for the executor. If null, a type-tagged UUID will be generated. + /// Configuration options for the executor. If null, default options will be used. + protected Executor(string? id = null, ExecutorOptions? options = null) { this.Id = id ?? $"{this.GetType().Name}/{Guid.NewGuid():N}"; - this._options = options; + this._options = options ?? ExecutorOptions.Default; } /// @@ -144,3 +142,43 @@ public abstract class Executor : IIdentified /// public bool CanHandle(Type messageType) => this.Router.CanHandle(messageType); } + +/// +/// Provides a simple executor implementation that uses a single message handler function to process incoming messages. +/// +/// The type of input message. +/// A optional unique identifier for the executor. If null, a type-tagged UUID will be generated. +/// Configuration options for the executor. If null, default options will be used. +public abstract class Executor(string? id = null, ExecutorOptions? options = null) + : Executor(id, options), IMessageHandler +{ + /// + protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) + { + return routeBuilder.AddHandler(this.HandleAsync); + } + + /// + public abstract ValueTask HandleAsync(TInput message, IWorkflowContext context); +} + +/// +/// Provides a simple executor implementation that uses a single message handler function to process incoming messages. +/// +/// The type of input message. +/// The type of output message. +/// A optional unique identifier for the executor. If null, a type-tagged UUID will be generated. +/// Configuration options for the executor. If null, default options will be used. +public abstract class Executor(string? id = null, ExecutorOptions? options = null) + : Executor(id, options ?? ExecutorOptions.Default), + IMessageHandler +{ + /// + protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) + { + return routeBuilder.AddHandler(this.HandleAsync); + } + + /// + public abstract ValueTask HandleAsync(TInput message, IWorkflowContext context); +} diff --git a/dotnet/src/Microsoft.Agents.Workflows/ExecutorIsh.cs b/dotnet/src/Microsoft.Agents.Workflows/ExecutorIsh.cs index dd026f541d..53a2fb201f 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/ExecutorIsh.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/ExecutorIsh.cs @@ -1,12 +1,86 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Agents.Workflows.Specialized; using Microsoft.Extensions.AI.Agents; using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows; +/// +/// Extension methods for configuring executors and functions as instances. +/// +public static class ExecutorIshConfigurationExtensions +{ + /// + /// Configures a factory method for creating an of type , with + /// the specified id and options. + /// + /// + /// Although this will generally result in a delay-instantiated once messages are available + /// for it, if this is used as a start node of a typed via , + /// it will be instantiated as part of the workflow's construction, to validate that its input type matches the + /// demanded TInput. + /// + /// The type of the resulting executor + /// The type of options object to be passed to the factory method. + /// The factory method. + /// An id for the executor to be instantiated. + /// An optional parameter specifying the options. + /// An ExecutorIsh instance that resolves to the result of the factory call when messages get sent to it. + public static ExecutorIsh ConfigureFactory(this Func, ValueTask> factoryAsync, string id, TOptions? options = null) + where TExecutor : Executor + where TOptions : ExecutorOptions + { + Configured configured = new(factoryAsync, id, options); + + return new ExecutorIsh(configured.Super(), typeof(TExecutor), ExecutorIsh.Type.Executor); + } + + private static ExecutorIsh ToExecutorIsh(this FunctionExecutor executor, Delegate raw) + { + return new ExecutorIsh(Configured.FromInstance(executor, raw: raw) + .Super, Executor>(), + typeof(FunctionExecutor), + ExecutorIsh.Type.Function); + } + + private static ExecutorIsh ToExecutorIsh(this FunctionExecutor executor, Delegate raw) + { + return new ExecutorIsh(Configured.FromInstance(executor, raw: raw) + .Super, Executor>(), + typeof(FunctionExecutor), + ExecutorIsh.Type.Function); + } + + /// + /// Configures a function-based asynchronous message handler as an executor with the specified identifier and + /// options. + /// + /// The type of input message. + /// A delegate that defines the asynchronous function to execute for each input message. + /// A optional unique identifier for the executor. If null, a type-tagged UUID will be generated. + /// Configuration options for the executor. If null, default options will be used. + /// An ExecutorIsh instance that wraps the provided asynchronous message handler and configuration. + public static ExecutorIsh AsExecutor(this Func messageHandlerAsync, string id, ExecutorOptions? options = null) + => new FunctionExecutor(messageHandlerAsync, id, options).ToExecutorIsh(messageHandlerAsync); + + /// + /// Configures a function-based asynchronous message handler as an executor with the specified identifier and + /// options. + /// + /// The type of input message. + /// The type of output message. + /// A delegate that defines the asynchronous function to execute for each input message. + /// A optional unique identifier for the executor. If null, a type-tagged UUID will be generated. + /// Configuration options for the executor. If null, default options will be used. + /// An ExecutorIsh instance that wraps the provided asynchronous message handler and configuration. + public static ExecutorIsh AsExecutor(this Func> messageHandlerAsync, string id, ExecutorOptions? options = null) + => new FunctionExecutor(messageHandlerAsync, id, options).ToExecutorIsh(messageHandlerAsync); +} + /// /// A tagged union representing an object that can function like an in a , /// or a reference to one by ID. @@ -31,6 +105,10 @@ public sealed class ExecutorIsh : /// Executor, /// + /// A function delegate to be wrapped as an executor. + /// + Function, + /// /// An for servicing external requests. /// InputPort, @@ -46,7 +124,10 @@ public sealed class ExecutorIsh : public Type ExecutorType { get; init; } private readonly string? _idValue; - private readonly Executor? _executorValue; + + private readonly Configured? _configuredExecutor; + private readonly System.Type? _configuredExecutorType; + internal readonly InputPort? _inputPortValue; private readonly AIAgent? _aiAgentValue; @@ -60,6 +141,13 @@ public sealed class ExecutorIsh : this._idValue = Throw.IfNull(id); } + internal ExecutorIsh(Configured configured, System.Type configuredExecutorType, ExecutorIsh.Type type) + { + this.ExecutorType = type; + this._configuredExecutor = configured; + this._configuredExecutorType = configuredExecutorType; + } + /// /// Initializes a new instance of the ExecutorIsh class using the specified executor. /// @@ -67,7 +155,8 @@ public sealed class ExecutorIsh : public ExecutorIsh(Executor executor) { this.ExecutorType = Type.Executor; - this._executorValue = Throw.IfNull(executor); + this._configuredExecutor = Configured.FromInstance(Throw.IfNull(executor)); + this._configuredExecutorType = executor.GetType(); } /// @@ -96,18 +185,20 @@ public sealed class ExecutorIsh : public string Id => this.ExecutorType switch { Type.Unbound => this._idValue ?? throw new InvalidOperationException("This ExecutorIsh is unbound and has no ID."), - Type.Executor => this._executorValue!.Id, + Type.Executor => this._configuredExecutor!.Id, Type.InputPort => this._inputPortValue!.Id, Type.Agent => this._aiAgentValue!.Id, + Type.Function => this._configuredExecutor!.Id, _ => throw new InvalidOperationException($"Unknown ExecutorIsh type: {this.ExecutorType}") }; internal object? RawData => this.ExecutorType switch { Type.Unbound => this._idValue, - Type.Executor => this._executorValue, + Type.Executor => this._configuredExecutor!.Raw ?? this._configuredExecutor, Type.InputPort => this._inputPortValue, Type.Agent => this._aiAgentValue, + Type.Function => this._configuredExecutor!.Raw ?? this._configuredExecutor, _ => throw new InvalidOperationException($"Unknown ExecutorIsh type: {this.ExecutorType}") }; @@ -122,9 +213,10 @@ public sealed class ExecutorIsh : private System.Type RuntimeType => this.ExecutorType switch { Type.Unbound => throw new InvalidOperationException($"ExecutorIsh with ID '{this.Id}' is unbound."), - Type.Executor => this._executorValue!.GetType(), + Type.Executor => this._configuredExecutorType!, Type.InputPort => typeof(RequestInfoExecutor), Type.Agent => typeof(AIAgentHostExecutor), + Type.Function => this._configuredExecutorType!, _ => throw new InvalidOperationException($"Unknown ExecutorIsh type: {this.ExecutorType}") }; @@ -132,12 +224,13 @@ public sealed class ExecutorIsh : /// Gets an that can be used to obtain an instance /// corresponding to this . /// - private Func ExecutorProvider => this.ExecutorType switch + private Func> ExecutorProvider => this.ExecutorType switch { Type.Unbound => throw new InvalidOperationException($"Executor with ID '{this.Id}' is unbound."), - Type.Executor => () => this._executorValue!, - Type.InputPort => () => new RequestInfoExecutor(this._inputPortValue!), - Type.Agent => () => new AIAgentHostExecutor(this._aiAgentValue!), + Type.Executor => this._configuredExecutor!.BoundFactoryAsync, + Type.InputPort => () => new(new RequestInfoExecutor(this._inputPortValue!)), + Type.Agent => () => new(new AIAgentHostExecutor(this._aiAgentValue!)), + Type.Function => this._configuredExecutor!.BoundFactoryAsync, _ => throw new InvalidOperationException($"Unknown ExecutorIsh type: {this.ExecutorType}") }; @@ -225,9 +318,10 @@ public sealed class ExecutorIsh : return this.ExecutorType switch { Type.Unbound => $"'{this.Id}':", - Type.Executor => $"'{this.Id}':{this._executorValue!.GetType().Name}", + Type.Executor => $"'{this.Id}':{this._configuredExecutorType!.Name}", Type.InputPort => $"'{this.Id}':Input({this._inputPortValue!.Request.Name}->{this._inputPortValue!.Response.Name})", Type.Agent => $"{this.Id}':AIAgent(@{this._aiAgentValue!.GetType().Name})", + Type.Function => $"'{this.Id}':{this._configuredExecutorType!.Name}", _ => $"'{this.Id}':" }; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/ExecutorOptions.cs b/dotnet/src/Microsoft.Agents.Workflows/ExecutorOptions.cs index a19630bf31..02cadf601f 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/ExecutorOptions.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/ExecutorOptions.cs @@ -5,7 +5,7 @@ namespace Microsoft.Agents.Workflows; /// /// Configuration options for Executor behavior. /// -public sealed class ExecutorOptions +public class ExecutorOptions { /// /// The default runner configuration. diff --git a/dotnet/src/Microsoft.Agents.Workflows/ExecutorRegistration.cs b/dotnet/src/Microsoft.Agents.Workflows/ExecutorRegistration.cs index bb1e060107..76dbba8ed3 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/ExecutorRegistration.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/ExecutorRegistration.cs @@ -1,9 +1,10 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Threading.Tasks; using Microsoft.Shared.Diagnostics; -using ExecutorFactoryF = System.Func; +using ExecutorFactoryF = System.Func>; namespace Microsoft.Agents.Workflows; @@ -11,7 +12,7 @@ internal class ExecutorRegistration(string id, Type executorType, ExecutorFactor { public string Id { get; } = Throw.IfNullOrEmpty(id); public Type ExecutorType { get; } = Throw.IfNull(executorType); - public ExecutorFactoryF Provider { get; } = Throw.IfNull(provider); + public ExecutorFactoryF ProviderAsync { get; } = Throw.IfNull(provider); internal object? RawExecutorishData { get; } = rawData; @@ -28,5 +29,5 @@ internal class ExecutorRegistration(string id, Type executorType, ExecutorFactor return executor; } - public Executor CreateInstance() => this.CheckId(this.Provider()); + public async ValueTask CreateInstanceAsync() => this.CheckId(await this.ProviderAsync().ConfigureAwait(false)); } diff --git a/dotnet/src/Microsoft.Agents.Workflows/FunctionExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows/FunctionExecutor.cs new file mode 100644 index 0000000000..502e58008b --- /dev/null +++ b/dotnet/src/Microsoft.Agents.Workflows/FunctionExecutor.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Agents.Workflows; + +/// +/// Executes a user-provided asynchronous function in response to workflow messages of the specified input type. +/// +/// The type of input message. +/// A delegate that defines the asynchronous function to execute for each input message. +/// A optional unique identifier for the executor. If null, a type-tagged UUID will be generated. +/// Configuration options for the executor. If null, default options will be used. +public class FunctionExecutor(Func handlerAsync, + string? id = null, + ExecutorOptions? options = null) : Executor(id, options) +{ + internal static Func WrapAction(Action handlerSync) + { + return RunActionAsync; + + ValueTask RunActionAsync(TInput input, IWorkflowContext workflowContext, CancellationToken cancellation) + { + handlerSync(input, workflowContext, cancellation); + return default; + } + } + + /// + public override ValueTask HandleAsync(TInput message, IWorkflowContext context) => handlerAsync(message, context, default); + + /// + /// Creates a new instance of the class. + /// + /// A synchronous function to execute for each input message and workflow context. + public FunctionExecutor(Action handlerSync) : this(WrapAction(handlerSync)) + { } +} + +/// +/// Executes a user-provided asynchronous function in response to workflow messages of the specified input type, +/// +/// The type of input message. +/// The type of output message. +/// A delegate that defines the asynchronous function to execute for each input message. +/// A optional unique identifier for the executor. If null, a type-tagged UUID will be generated. +/// Configuration options for the executor. If null, default options will be used. +public class FunctionExecutor(Func> handlerAsync, + string? id = null, + ExecutorOptions? options = null) : Executor(id, options) +{ + internal static Func> WrapFunc(Func handlerSync) + { + return RunFuncAsync; + + ValueTask RunFuncAsync(TInput input, IWorkflowContext workflowContext, CancellationToken cancellation) + { + TOutput result = handlerSync(input, workflowContext, cancellation); + return new ValueTask(result); + } + } + /// + public override ValueTask HandleAsync(TInput message, IWorkflowContext context) => handlerAsync(message, context, default); + /// + /// Creates a new instance of the class. + /// + /// A synchronous function to execute for each input message and workflow context. + public FunctionExecutor(Func handlerSync) : this(WrapFunc(handlerSync)) + { } +} diff --git a/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs b/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs index 15c4b9c8b2..3a57dd9302 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs @@ -35,7 +35,7 @@ internal class InProcessRunnerContext : IRunnerContext throw new InvalidOperationException($"Executor with ID '{executorId}' is not registered."); } - this._executors[executorId] = executor = registration.Provider(); + this._executors[executorId] = executor = await registration.ProviderAsync().ConfigureAwait(false); tracer?.TraceActivated(executorId); if (executor is RequestInfoExecutor requestInputExecutor) diff --git a/dotnet/src/Microsoft.Agents.Workflows/Reflection/ReflectingExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows/Reflection/ReflectingExecutor.cs index 9b0ca4a74e..f5854bf29d 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Reflection/ReflectingExecutor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Reflection/ReflectingExecutor.cs @@ -16,8 +16,8 @@ public class ReflectingExecutor< ] TExecutor > : Executor where TExecutor : ReflectingExecutor { - /// - protected ReflectingExecutor(string? id = null) : base(id) + /// + protected ReflectingExecutor(string? id = null, ExecutorOptions? options = null) : base(id, options) { } /// diff --git a/dotnet/src/Microsoft.Agents.Workflows/Specialized/RequestInfoExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows/Specialized/RequestInfoExecutor.cs index 4cb151666f..9b12ff1515 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Specialized/RequestInfoExecutor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Specialized/RequestInfoExecutor.cs @@ -20,7 +20,7 @@ internal class RequestInfoExecutor : Executor }; private readonly bool _allowWrapped; - public RequestInfoExecutor(InputPort port, bool allowWrapped = true) : base(RequestInfoExecutor.DefaultOptions, port.Id) + public RequestInfoExecutor(InputPort port, bool allowWrapped = true) : base(port.Id, RequestInfoExecutor.DefaultOptions) { this.Port = port; diff --git a/dotnet/src/Microsoft.Agents.Workflows/WorkflowBuilder.cs b/dotnet/src/Microsoft.Agents.Workflows/WorkflowBuilder.cs index 1a37415d19..49ad08c33b 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/WorkflowBuilder.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/WorkflowBuilder.cs @@ -2,7 +2,10 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.Workflows; @@ -205,6 +208,32 @@ public class WorkflowBuilder return this; } + [SuppressMessage("Reliability", "CA2008:Do not create tasks without passing a TaskScheduler", + Justification = "We explicitly set the TaskScheduler when we create the TaskFactory")] + [SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", + Justification = "This runs the thread on the thread pool")] + private static TResult RunSync(Func> funcAsync) + { + TaskFactory factory = new(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default); + + // See ASP.Net.Identity's implementation of AsyncHelper + // https://github.com/aspnet/AspNetIdentity/blob/main/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs + + // Capture the current culture and UI culture + var culture = System.Globalization.CultureInfo.CurrentCulture; + var uiCulture = System.Globalization.CultureInfo.CurrentUICulture; + + return factory.StartNew(PropagateCultureAndInvoke).Unwrap().GetAwaiter().GetResult(); + + Task PropagateCultureAndInvoke() + { + // Set the culture and UI culture to the captured values + System.Globalization.CultureInfo.CurrentCulture = culture; + System.Globalization.CultureInfo.CurrentUICulture = uiCulture; + return funcAsync().AsTask(); + } + } + /// /// Builds and returns a workflow instance configured to process messages of the specified input type. /// @@ -228,9 +257,7 @@ public class WorkflowBuilder throw new InvalidOperationException($"Start executor with ID '{this._startExecutorId}' is not bound."); } - // TODO: Delay-instantiate the start executor, and ensure it take input of type T - Executor startExecutor = startRegistration.Provider(); - + Executor startExecutor = RunSync(startRegistration.CreateInstanceAsync); if (!startExecutor.InputTypes.Any(t => t.IsAssignableFrom(typeof(T)))) { // We have no handlers for the input type T, which means the built workflow will not be able to diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/RepresentationTests.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/RepresentationTests.cs index 1809d0d908..294b58a4de 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/RepresentationTests.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/RepresentationTests.cs @@ -47,42 +47,49 @@ public class RepresentationTests return current; } - private static void RunExecutorishInfoMatchTest(ExecutorIsh target) + private static async ValueTask RunExecutorishInfoMatchTestAsync(ExecutorIsh target) { ExecutorRegistration registration = target.Registration; ExecutorInfo info = registration.ToExecutorInfo(); - info.IsMatch(registration.Provider()).Should().BeTrue(); + info.IsMatch(await registration.ProviderAsync()).Should().BeTrue(); } [Fact] - public void Test_Executorish_Infos() + public async Task Test_Executorish_InfosAsync() { int testsRun = 0; - RunExecutorishTest(new TestExecutor()); - RunExecutorishTest(TestInputPort); - RunExecutorishTest(new TestAgent()); + await RunExecutorishTest(new TestExecutor()); + await RunExecutorishTest(TestInputPort); + await RunExecutorishTest(new TestAgent()); + + Func function = MessageHandlerAsync; + await RunExecutorishTest(function.AsExecutor("FunctionExecutor")); if (Enum.GetValues(typeof(ExecutorIsh.Type)).Length > testsRun + 1) { Assert.Fail("Not all ExecutorIsh types were tested."); } - void RunExecutorishTest(ExecutorIsh executorish) + async ValueTask RunExecutorishTest(ExecutorIsh executorish) { - RunExecutorishInfoMatchTest(executorish); + await RunExecutorishInfoMatchTestAsync(executorish); testsRun++; } + + async ValueTask MessageHandlerAsync(int message, IWorkflowContext workflowContext, CancellationToken cancellation = default) + { + } } [Fact] - public void Test_SpecializedExecutor_Infos() + public async Task Test_SpecializedExecutor_InfosAsync() { - RunExecutorishInfoMatchTest(new AIAgentHostExecutor(new TestAgent())); - RunExecutorishInfoMatchTest(new RequestInfoExecutor(TestInputPort)); + await RunExecutorishInfoMatchTestAsync(new AIAgentHostExecutor(new TestAgent())); + await RunExecutorishInfoMatchTestAsync(new RequestInfoExecutor(TestInputPort)); OutputCollectorExecutor> outputCollector = new(StreamingAggregators.Union()); - RunExecutorishInfoMatchTest(outputCollector); + await RunExecutorishInfoMatchTestAsync(outputCollector); } private static string Source(string id) => $"Source/{id}"; From b4d8ad1bd27deb7c7075fb1c20190f3c97c0b8b7 Mon Sep 17 00:00:00 2001 From: Ben Thomas Date: Mon, 8 Sep 2025 12:20:08 -0700 Subject: [PATCH 02/32] Adding workflows label to PRs when appropriate. (#643) * Adding workflows label to issues when appropriate. * Adding declarative workflows path. --- .github/labeler.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index 32ae6c9b1e..99a38a6d9d 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -16,3 +16,13 @@ documentation: - any-glob-to-any-file: - docs/** - '**/*.md' + +# Add 'workflows' label to any change within the dotnet or python workflows src or samples +workflows: +- changed-files: + - any-glob-to-any-file: + - dotnet/src/Microsoft.Agents.Workflows/** + - dotnet/src/Microsoft.Agents.Workflows.Declarative/** + - dotnet/samples/GettingStarted/Workflow/** + - python/packages/workflows/** + - python/samples/getting_started/workflow/** \ No newline at end of file From e8f1f4e78512a1efc35715c6c28d2ce896a861c9 Mon Sep 17 00:00:00 2001 From: Jacob Alber Date: Mon, 8 Sep 2025 15:22:41 -0400 Subject: [PATCH 03/32] fix: Fix Checkpoint Restore when Rehydrating Run (#642) When checkpointing we did not persist the set of instantiated executors. This means, in turn, when we restore from a checkpoint when using Resume(Stream) rather than restoring a checkpoint in the context of an already existing (Streaming)Run, the executors never got reinstantiated and there were no executors to notify that a state should be loaded. The fix is to ensure we persist the list and reinstantiate the executors on rehydration. * Also adds a rehydration restore test --- .../Execution/RunnerStateData.cs | 3 ++- .../InProc/InProcessRunner.cs | 6 +++--- .../InProc/InProcessRunnerContext.cs | 14 ++++++++++---- .../05_Simple_Workflow_Checkpointing.cs | 16 ++++++++++++++-- .../SampleSmokeTest.cs | 19 +++++++++++++++++++ 5 files changed, 48 insertions(+), 10 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.Workflows/Execution/RunnerStateData.cs b/dotnet/src/Microsoft.Agents.Workflows/Execution/RunnerStateData.cs index 9677ce6e57..877518718a 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Execution/RunnerStateData.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Execution/RunnerStateData.cs @@ -5,8 +5,9 @@ using Microsoft.Agents.Workflows.Checkpointing; namespace Microsoft.Agents.Workflows.Execution; -internal class RunnerStateData(Dictionary> queuedMessages, List outstandingRequests) +internal class RunnerStateData(HashSet instantiatedExecutors, Dictionary> queuedMessages, List outstandingRequests) { + public HashSet InstantiatedExecutors { get; } = instantiatedExecutors; public Dictionary> QueuedMessages { get; } = queuedMessages; public List OutstandingRequests { get; } = outstandingRequests; } diff --git a/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunner.cs b/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunner.cs index a6d7081c1c..4bf2f50e8c 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunner.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunner.cs @@ -228,12 +228,12 @@ internal class InProcessRunner : ISuperStepRunner, ICheckpointingRunner this._workflowInfoCache = this.Workflow.ToWorkflowInfo(); } - RunnerStateData runnerData = await this.RunContext.ExportStateAsync().ConfigureAwait(false); Dictionary edgeData = await this.EdgeMap.ExportStateAsync().ConfigureAwait(false); await prepareTask.ConfigureAwait(false); await this.RunContext.StateManager.PublishUpdatesAsync(this.StepTracer).ConfigureAwait(false); + RunnerStateData runnerData = await this.RunContext.ExportStateAsync().ConfigureAwait(false); Dictionary stateData = await this.RunContext.StateManager.ExportStateAsync().ConfigureAwait(false); Checkpoint checkpoint = new(this.StepTracer.StepNumber, this._workflowInfoCache, runnerData, stateData, edgeData); @@ -261,9 +261,9 @@ internal class InProcessRunner : ISuperStepRunner, ICheckpointingRunner } await this.RunContext.StateManager.ImportStateAsync(checkpoint).ConfigureAwait(false); - Task executorNotifyTask = this.RunContext.NotifyCheckpointLoadedAsync(cancellation); - await this.RunContext.ImportStateAsync(checkpoint).ConfigureAwait(false); + + Task executorNotifyTask = this.RunContext.NotifyCheckpointLoadedAsync(cancellation); ValueTask republishRequestsTask = this.RunContext.RepublishUnservicedRequestsAsync(cancellation); await this.EdgeMap.ImportStateAsync(checkpoint).ConfigureAwait(false); diff --git a/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs b/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs index 3a57dd9302..b2d30f21f4 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/InProc/InProcessRunnerContext.cs @@ -136,8 +136,9 @@ internal class InProcessRunnerContext : IRunnerContext } Dictionary> queuedMessages = this._nextStep.ExportMessages(); - - RunnerStateData result = new(queuedMessages, this._externalRequests.Values.ToList()); + RunnerStateData result = new(instantiatedExecutors: [.. this._executors.Keys], + queuedMessages, + outstandingRequests: [.. this._externalRequests.Values]); return new(result); } @@ -154,7 +155,7 @@ internal class InProcessRunnerContext : IRunnerContext } } - internal ValueTask ImportStateAsync(Checkpoint checkpoint) + internal async ValueTask ImportStateAsync(Checkpoint checkpoint) { if (this.QueuedEvents.Count > 0) { @@ -163,6 +164,11 @@ internal class InProcessRunnerContext : IRunnerContext RunnerStateData importedState = checkpoint.RunnerData; + Task[] executorTasks = importedState.InstantiatedExecutors + .Where(id => !this._executors.ContainsKey(id)) + .Select(id => this.EnsureExecutorAsync(id, tracer: null).AsTask()) + .ToArray(); + this._nextStep = new StepContext(); this._nextStep.ImportMessages(importedState.QueuedMessages); @@ -176,6 +182,6 @@ internal class InProcessRunnerContext : IRunnerContext this._externalRequests[request.RequestId] = request; } - return default; + await Task.WhenAll(executorTasks).ConfigureAwait(false); } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/05_Simple_Workflow_Checkpointing.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/05_Simple_Workflow_Checkpointing.cs index f0755c064a..7658871f79 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/05_Simple_Workflow_Checkpointing.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/05_Simple_Workflow_Checkpointing.cs @@ -13,7 +13,7 @@ internal static class Step5EntryPoint { private static CheckpointManager CheckpointManager { get; } = new(); - public static async ValueTask RunAsync(TextWriter writer, Func userGuessCallback) + public static async ValueTask RunAsync(TextWriter writer, Func userGuessCallback, bool rehydrateToRestore = false) { Workflow workflow = Step4EntryPoint.CreateWorkflowInstance(out JudgeExecutor judge); Checkpointed> checkpointed = @@ -30,7 +30,19 @@ internal static class Step5EntryPoint checkpoints.Should().HaveCount(6, "we should have two checkpoints, one for each step"); judge.Tries.Should().Be(2); - await checkpointed.RestoreCheckpointAsync(checkpoints[2], CancellationToken.None).ConfigureAwait(false); + CheckpointInfo targetCheckpoint = checkpoints[2]; + + if (rehydrateToRestore) + { + checkpointed = await InProcessExecution.ResumeStreamAsync(workflow, targetCheckpoint, CheckpointManager, CancellationToken.None) + .ConfigureAwait(false); + handle = checkpointed.Run; + } + else + { + await checkpointed.RestoreCheckpointAsync(checkpoints[2], CancellationToken.None).ConfigureAwait(false); + } + judge.Tries.Should().Be(1); cancellationSource.Dispose(); diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SampleSmokeTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SampleSmokeTest.cs index 9e3277dfaf..ed1fec105b 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SampleSmokeTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SampleSmokeTest.cs @@ -103,6 +103,25 @@ public class SampleSmokeTest Assert.Equal("You guessed correctly! You Win!", guessResult); } + [Fact] + public async Task Test_RunSample_Step5aAsync() + { + using StringWriter writer = new(); + + VerifyingPlaybackResponder responder = new( + // Iteration 1 + ("Guess the number.", 50), + ("Your guess was too high. Try again.", 23), + + // Iteration 2 + ("Your guess was too high. Try again.", 23), + ("Your guess was too low. Try again.", 42) + ); + + string guessResult = await Step5EntryPoint.RunAsync(writer, userGuessCallback: responder.InvokeNext, rehydrateToRestore: true); + Assert.Equal("You guessed correctly! You Win!", guessResult); + } + [Fact] public async Task Test_RunSample_Step6Async() { From 726eb6698242796e7b5d2b7b7f402b0d0a7b6a66 Mon Sep 17 00:00:00 2001 From: e-straight <142330569+e-straight@users.noreply.github.com> Date: Mon, 8 Sep 2025 15:02:21 -0700 Subject: [PATCH 04/32] Update README (#644) * Update README with clearer setup instructions and direct links to getting started guides * Update README.md Co-authored-by: Tao Chen --------- Co-authored-by: Tao Chen --- README.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 26eea1db11..5a59ebdeab 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,25 @@ You're getting early access to Microsoft's comprehensive multi-language framework for building, orchestrating, and deploying AI agents with support for both .NET and Python implementations. This framework provides everything from simple chat agents to complex multi-agent workflows with graph-based orchestration. -**A few important notes:** -- There currently are not public pypi or nuget packages for the SDKs. In order for the code and samples to work, please clone this repo and run the code here. -- The repo is an active project so make sure to sync regularly. +### 📋 Important Setup Information +**Package Availability:** Public PyPI and NuGet packages are not yet available. You have two options: -**We want your feedback!** +**Option 1: Run samples directly from this repository (no package installation needed)** +- Clone this repository +- For .NET: Run samples with `dotnet run` from any sample directory (e.g., `dotnet/samples/GettingStarted/Agents/Agent_Step01_Running`) +- For Python: Run samples from any sample directory (e.g., [`python/samples/getting_started/minimal_sample.py`](python/samples/getting_started/minimal_sample.py)) after setting up the local dev environment following this [guide](python/DEV_SETUP.md). + +**Option 2: Install packages in your own project** +- **[.NET Getting Started Guide](./user-documentation-dotnet/getting-started/README.md)** - Instructions for using nightly packages +- **[Python Package Installation Guide](./user-documentation-python/getting-started/package_installation.md)** - Install packages directly from GitHub + +**Stay Updated:** This is an active project - sync your local repository regularly to get the latest updates. + +### 💬 **We want your feedback!** - For bugs, please file a [GitHub issue](https://github.com/microsoft/agent-framework/issues). - For feedback and suggestions for the team, please fill out [this survey](https://forms.office.com/Pages/ResponsePage.aspx?id=v4j5cvGGr0GRqy180BHbR9huAe5pW55CqgnnimXONJJUMlVMUzdCN1ZGOURXODlBSVJOSkxERVNCNS4u). -**Highlights** +### ✨ **Highlights** - Flexible Agent Framework: build, orchestrate, and deploy AI agents and workflows - Multi-Agent Orchestration: group chat, sequential, concurrent, and handoff patterns - Graph-based Workflows: connect agents and deterministic functions using data flows with streaming, checkpointing, time-travel, and Human-in-the-loop. From 240edb00cddb5744352bbe641c7f2f3754e75163 Mon Sep 17 00:00:00 2001 From: Chris <66376200+crickman@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:16:29 -0700 Subject: [PATCH 05/32] .NET Workflows - Update structure of samples (#645) * Updated * Typos * Update readme fwiw --- dotnet/agent-framework-dotnet.slnx | 38 +-- .../Concurrent/Concurrent.csproj | 0 .../Concurrent/Program.cs | 0 .../01_EdgeCondition/01_EdgeCondition.csproj | 0 .../01_EdgeCondition/Program.cs | 0 .../01_EdgeCondition/Resources.cs | 0 .../02_SwitchCase/02_SwitchCase.csproj | 0 .../ConditionalEdges/02_SwitchCase/Program.cs | 0 .../02_SwitchCase/Resources.cs | 0 .../03_MultiSelection.csproj | 0 .../03_MultiSelection/Program.cs | 0 .../03_MultiSelection/Resources.cs | 0 .../Declarative/DeclarativeWorkflow.csproj | 33 ++ .../Workflows/Declarative/Program.cs | 304 ++++++++++++++++++ .../Workflows/Declarative/README.md | 87 +++++ .../01_ExecutorsAndEdges.csproj | 0 .../01_ExecutorsAndEdges/Program.cs | 0 .../02_Streaming/02_Streaming.csproj | 0 .../Foundational/02_Streaming/Program.cs | 0 .../03_AgentsInWorkflows.csproj | 0 .../03_AgentsInWorkflows/Program.cs | 0 .../{Workflow => Workflows}/README.md | 8 + .../Resources/Lorem_Ipsum.txt | 0 .../Resources/ambiguous_email.txt | 0 .../Resources/email.txt | 0 .../Resources/spam.txt | 0 .../SharedStates/Program.cs | 0 .../SharedStates/Resources.cs | 0 .../SharedStates/SharedStates.csproj | 0 29 files changed, 451 insertions(+), 19 deletions(-) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/Concurrent/Concurrent.csproj (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/Concurrent/Program.cs (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/ConditionalEdges/01_EdgeCondition/01_EdgeCondition.csproj (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/ConditionalEdges/01_EdgeCondition/Program.cs (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/ConditionalEdges/01_EdgeCondition/Resources.cs (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/ConditionalEdges/02_SwitchCase/02_SwitchCase.csproj (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/ConditionalEdges/02_SwitchCase/Program.cs (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/ConditionalEdges/02_SwitchCase/Resources.cs (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/ConditionalEdges/03_MultiSelection/03_MultiSelection.csproj (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/ConditionalEdges/03_MultiSelection/Program.cs (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/ConditionalEdges/03_MultiSelection/Resources.cs (100%) create mode 100644 dotnet/samples/GettingStarted/Workflows/Declarative/DeclarativeWorkflow.csproj create mode 100644 dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs create mode 100644 dotnet/samples/GettingStarted/Workflows/Declarative/README.md rename dotnet/samples/GettingStarted/{Workflow => Workflows}/Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/Foundational/01_ExecutorsAndEdges/Program.cs (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/Foundational/02_Streaming/02_Streaming.csproj (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/Foundational/02_Streaming/Program.cs (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/Foundational/03_AgentsInWorkflows/Program.cs (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/README.md (92%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/Resources/Lorem_Ipsum.txt (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/Resources/ambiguous_email.txt (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/Resources/email.txt (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/Resources/spam.txt (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/SharedStates/Program.cs (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/SharedStates/Resources.cs (100%) rename dotnet/samples/GettingStarted/{Workflow => Workflows}/SharedStates/SharedStates.csproj (100%) diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index 8d550c3031..e764f4c936 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -48,29 +48,21 @@ - - + + - - + + - - - - + + + + - - - - + + - - - - - - - + @@ -78,6 +70,14 @@ + + + + + + + + diff --git a/dotnet/samples/GettingStarted/Workflow/Concurrent/Concurrent.csproj b/dotnet/samples/GettingStarted/Workflows/Concurrent/Concurrent.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/Concurrent/Concurrent.csproj rename to dotnet/samples/GettingStarted/Workflows/Concurrent/Concurrent.csproj diff --git a/dotnet/samples/GettingStarted/Workflow/Concurrent/Program.cs b/dotnet/samples/GettingStarted/Workflows/Concurrent/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/Concurrent/Program.cs rename to dotnet/samples/GettingStarted/Workflows/Concurrent/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflow/ConditionalEdges/01_EdgeCondition/01_EdgeCondition.csproj b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/01_EdgeCondition.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/ConditionalEdges/01_EdgeCondition/01_EdgeCondition.csproj rename to dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/01_EdgeCondition.csproj diff --git a/dotnet/samples/GettingStarted/Workflow/ConditionalEdges/01_EdgeCondition/Program.cs b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/ConditionalEdges/01_EdgeCondition/Program.cs rename to dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflow/ConditionalEdges/01_EdgeCondition/Resources.cs b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Resources.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/ConditionalEdges/01_EdgeCondition/Resources.cs rename to dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Resources.cs diff --git a/dotnet/samples/GettingStarted/Workflow/ConditionalEdges/02_SwitchCase/02_SwitchCase.csproj b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/02_SwitchCase.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/ConditionalEdges/02_SwitchCase/02_SwitchCase.csproj rename to dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/02_SwitchCase.csproj diff --git a/dotnet/samples/GettingStarted/Workflow/ConditionalEdges/02_SwitchCase/Program.cs b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/ConditionalEdges/02_SwitchCase/Program.cs rename to dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflow/ConditionalEdges/02_SwitchCase/Resources.cs b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Resources.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/ConditionalEdges/02_SwitchCase/Resources.cs rename to dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Resources.cs diff --git a/dotnet/samples/GettingStarted/Workflow/ConditionalEdges/03_MultiSelection/03_MultiSelection.csproj b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/03_MultiSelection.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/ConditionalEdges/03_MultiSelection/03_MultiSelection.csproj rename to dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/03_MultiSelection.csproj diff --git a/dotnet/samples/GettingStarted/Workflow/ConditionalEdges/03_MultiSelection/Program.cs b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/ConditionalEdges/03_MultiSelection/Program.cs rename to dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflow/ConditionalEdges/03_MultiSelection/Resources.cs b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Resources.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/ConditionalEdges/03_MultiSelection/Resources.cs rename to dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Resources.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/DeclarativeWorkflow.csproj b/dotnet/samples/GettingStarted/Workflows/Declarative/DeclarativeWorkflow.csproj new file mode 100644 index 0000000000..96a9d5df1d --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Declarative/DeclarativeWorkflow.csproj @@ -0,0 +1,33 @@ + + + + Exe + net9.0 + net9.0 + $(ProjectsDebugTargetFrameworks) + enable + disable + 5ee045b0-aea3-4f08-8d31-32d1a6f8fed0 + $(NoWarn);CA1812 + + + + true + + + + + + + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs b/dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs new file mode 100644 index 0000000000..d2301c0311 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs @@ -0,0 +1,304 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Azure.AI.Agents.Persistent; +using Azure.Identity; +using Microsoft.Agents.Workflows; +using Microsoft.Agents.Workflows.Declarative; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.Configuration; + +namespace Demo.DeclarativeWorkflow; + +/// +/// HOW TO: Create a workflow from a declarative (yaml based) definition. +/// +/// +/// Configuration +/// Define FOUNDRY_PROJECT_ENDPOINT as a user-secret or environment variable that +/// points to your Foundry project endpoint. +/// Usage +/// Provide the path to the workflow definition file as the first argument. +/// All other arguments are intepreted as a queue of inputs. +/// When no input is queued, interactive input is requested from the console. +/// +internal sealed class Program +{ + public static async Task Main(string[] args) + { + Program program = new(args); + await program.ExecuteAsync(); + } + + private async Task ExecuteAsync() + { + // Read and parse the declarative workflow. + Notify($"WORKFLOW: Parsing {Path.GetFullPath(this.WorkflowFile)}"); + + Stopwatch timer = Stopwatch.StartNew(); + + // Use DeclarativeWorkflowBuilder to build a workflow based on a YAML file. + DeclarativeWorkflowOptions options = + new(new FoundryAgentProvider(this.FoundryEndpoint, new AzureCliCredential())) + { + Configuration = this.Configuration + }; + Workflow workflow = DeclarativeWorkflowBuilder.Build(this.WorkflowFile, options); + + Notify($"\nWORKFLOW: Defined {timer.Elapsed}"); + + Notify("\nWORKFLOW: Starting..."); + + // Run the workflow, just like any other workflow + string input = this.GetWorkflowInput(); + StreamingRun run = await InProcessExecution.StreamAsync(workflow, input); + await this.MonitorWorkflowRunAsync(run); + + Notify("\nWORKFLOW: Done!"); + } + + private const string DefaultWorkflow = "HelloWorld.yaml"; + private const string ConfigKeyFoundryEndpoint = "FOUNDRY_PROJECT_ENDPOINT"; + + private static readonly Dictionary s_nameCache = []; + private static readonly HashSet s_fileCache = []; + + private string WorkflowFile { get; } + private string? WorkflowInput { get; } + private string FoundryEndpoint { get; } + private PersistentAgentsClient FoundryClient { get; } + private IConfiguration Configuration { get; } + + private Program(string[] args) + { + this.WorkflowFile = ParseWorkflowFile(args); + this.WorkflowInput = ParseWorkflowInput(args); + + this.Configuration = InitializeConfig(); + + this.FoundryEndpoint = this.Configuration[ConfigKeyFoundryEndpoint] ?? throw new InvalidOperationException($"Undefined configuration setting: {ConfigKeyFoundryEndpoint}"); + this.FoundryClient = new PersistentAgentsClient(this.FoundryEndpoint, new AzureCliCredential()); + } + + private async Task MonitorWorkflowRunAsync(StreamingRun run) + { + string? messageId = null; + + await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false)) + { + if (evt is ExecutorInvokeEvent executorInvoked) + { + Debug.WriteLine($"STEP ENTER #{executorInvoked.ExecutorId}"); + } + else if (evt is ExecutorCompleteEvent executorComplete) + { + Debug.WriteLine($"STEP EXIT #{executorComplete.ExecutorId}"); + } + else if (evt is ExecutorFailureEvent executorFailure) + { + Debug.WriteLine($"STEP ERROR #{executorFailure.ExecutorId}: {executorFailure.Data?.Message ?? "Unknown"}"); + } + else if (evt is ConversationUpdateEvent invokeEvent) + { + Debug.WriteLine($"CONVERSATION: {invokeEvent.Data}"); + } + else if (evt is AgentRunUpdateEvent streamEvent) + { + if (!string.Equals(messageId, streamEvent.Update.MessageId, StringComparison.Ordinal)) + { + messageId = streamEvent.Update.MessageId; + + if (messageId is not null) + { + string? agentId = streamEvent.Update.AuthorName; + if (agentId is not null) + { + if (!s_nameCache.TryGetValue(agentId, out string? realName)) + { + PersistentAgent agent = await this.FoundryClient.Administration.GetAgentAsync(agentId); + s_nameCache[agentId] = agent.Name; + realName = agent.Name; + } + agentId = realName; + } + agentId ??= nameof(ChatRole.Assistant); + Console.ForegroundColor = ConsoleColor.Cyan; + Console.Write($"\n{agentId.ToUpperInvariant()}:"); + Console.ForegroundColor = ConsoleColor.DarkGray; + Console.WriteLine($" [{messageId}]"); + } + } + + ChatResponseUpdate? chatUpdate = streamEvent.Update.RawRepresentation as ChatResponseUpdate; + switch (chatUpdate?.RawRepresentation) + { + case MessageContentUpdate messageUpdate: + string? fileId = messageUpdate.ImageFileId ?? messageUpdate.TextAnnotation?.OutputFileId; + if (fileId is not null && s_fileCache.Add(fileId)) + { + BinaryData content = await this.FoundryClient.Files.GetFileContentAsync(fileId); + await DownloadFileContentAsync(Path.GetFileName(messageUpdate.TextAnnotation?.TextToReplace ?? "response.png"), content); + } + break; + } + try + { + Console.ResetColor(); + Console.Write(streamEvent.Data); + } + finally + { + Console.ResetColor(); + } + } + else if (evt is AgentRunResponseEvent messageEvent) + { + try + { + Console.WriteLine(); + if (messageEvent.Response.AgentId is null) + { + Console.ForegroundColor = ConsoleColor.Cyan; + Console.WriteLine("ACTIVITY:"); + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine(messageEvent.Response?.Text.Trim()); + } + else + { + if (messageEvent.Response.Usage is not null) + { + Console.ForegroundColor = ConsoleColor.DarkGray; + Console.WriteLine($"[Tokens Total: {messageEvent.Response.Usage.TotalTokenCount}, Input: {messageEvent.Response.Usage.InputTokenCount}, Output: {messageEvent.Response.Usage.OutputTokenCount}]"); + } + } + } + finally + { + Console.ResetColor(); + } + } + } + } + + private static string ParseWorkflowFile(string[] args) + { + string workflowFile = args.FirstOrDefault() ?? DefaultWorkflow; + + if (!File.Exists(workflowFile) && !Path.IsPathFullyQualified(workflowFile)) + { + string? repoFolder = GetRepoFolder(); + if (repoFolder is not null) + { + workflowFile = Path.Combine(repoFolder, "Workflows", workflowFile); + workflowFile = Path.ChangeExtension(workflowFile, ".yaml"); + } + } + + if (!File.Exists(workflowFile)) + { + throw new InvalidOperationException($"Unable to locate workflow: {Path.GetFullPath(workflowFile)}."); + } + + return workflowFile; + + static string? GetRepoFolder() + { + DirectoryInfo? current = new(Directory.GetCurrentDirectory()); + + while (current is not null) + { + if (Directory.Exists(Path.Combine(current.FullName, ".git"))) + { + return current.FullName; + } + + current = current.Parent; + } + + return null; + } + } + + private string GetWorkflowInput() + { + string? input = this.WorkflowInput; + + try + { + Console.ForegroundColor = ConsoleColor.DarkGreen; + + Console.Write("\nINPUT: "); + + Console.ForegroundColor = ConsoleColor.White; + + if (!string.IsNullOrWhiteSpace(input)) + { + Console.WriteLine(input); + return input; + } + while (string.IsNullOrWhiteSpace(input)) + { + input = Console.ReadLine(); + } + + return input.Trim(); + } + finally + { + Console.ResetColor(); + } + } + + private static string? ParseWorkflowInput(string[] args) + { + if (args.Length == 0) + { + return null; + } + + string[] workflowInput = [.. args.Skip(1)]; + + return workflowInput.FirstOrDefault(); + } + + // Load configuration from user-secrets + private static IConfigurationRoot InitializeConfig() => + new ConfigurationBuilder() + .AddUserSecrets(Assembly.GetExecutingAssembly()) + .AddEnvironmentVariables() + .Build(); + + private static void Notify(string message) + { + Console.ForegroundColor = ConsoleColor.Cyan; + try + { + Console.WriteLine(message); + } + finally + { + Console.ResetColor(); + } + } + + private static async ValueTask DownloadFileContentAsync(string filename, BinaryData content) + { + string filePath = Path.Combine(Path.GetTempPath(), Path.GetFileName(filename)); + filePath = Path.ChangeExtension(filePath, ".png"); + + await File.WriteAllBytesAsync(filePath, content.ToArray()); + + Process.Start( + new ProcessStartInfo + { + FileName = "cmd.exe", + Arguments = $"/C start {filePath}" + }); + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/README.md b/dotnet/samples/GettingStarted/Workflows/Declarative/README.md new file mode 100644 index 0000000000..57101c9b21 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Declarative/README.md @@ -0,0 +1,87 @@ +# Summary + +This demo showcases the ability to parse a declarative Foundry Workflow file (YAML) to build a `Workflow<>` +be executed using the same pattern as any code-based workflow. + +## Configuration + +This demo requires configuration to access agents an [Azure Foundry Project](https://learn.microsoft.com/azure/ai-foundry). + +#### Settings + +We suggest using .NET [Secret Manager](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets) +to avoid the risk of leaking secrets into the repository, branches and pull requests. +You can also use environment variables if you prefer. + +To set your secrets as an environment variable (PowerShell): + +```pwsh +$env:FOUNDRY_PROJECT_ENDPOINT="https://..." +``` + +To set your secrets with .NET Secret Manager: + +1. From the root of the repository, navigate the console to the project folder: + + ``` + cd dotnet/samples/GettingStarted/Workflows/Declarative + ``` + +2. Examine existing secret definitions: + + ``` + dotnet user-secrets list + ``` + +3. If needed, perform first time initialization: + + ``` + dotnet user-secrets init + ``` + +4. Define setting that identifies your Azure Foundry Project (endpoint): + + ``` + dotnet user-secrets set "FOUNDRY_PROJECT_ENDPOINT" "https://..." + ``` + +#### Authorization + +Use [_Azure CLI_](https://learn.microsoft.com/cli/azure/authenticate-azure-cli) to authorize access to your Azure Foundry Project: + +``` +az login +az account get-access-token +``` + +#### Agents + +The sample workflows rely on agents defined in your Azure Foundry Project. + +To create agents, run the [`Create.ps1`](../../../../../workflows/) script. +This will create the agents used in the sample workflows in your Azure Foundry Project and format a script you can copy and use to configure your environment. + +> Note: `Create.ps1` relies upon the `FOUNDRY_PROJECT_ENDPOINT` setting. + +## Execution + +Run the demo from the console by specifying a path to a declarative (YAML) workflow file. +The repository has example workflows available in the root [`/workflows`](../../../../../workflows) folder. + +1. From the root of the repository, navigate the console to the project folder: + + ```sh + cd dotnet/samples/GettingStarted/Workflows/Declarative + ``` + +2. Run the demo referencing a sample workflow by name: + + ```sh + dotnet run HelloWorld + ``` + +3. Run the demo with a path to any workflow file: + + ```sh + dotnet run c:/myworkflows/HelloWorld.yaml + ``` diff --git a/dotnet/samples/GettingStarted/Workflow/Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj b/dotnet/samples/GettingStarted/Workflows/Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj rename to dotnet/samples/GettingStarted/Workflows/Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj diff --git a/dotnet/samples/GettingStarted/Workflow/Foundational/01_ExecutorsAndEdges/Program.cs b/dotnet/samples/GettingStarted/Workflows/Foundational/01_ExecutorsAndEdges/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/Foundational/01_ExecutorsAndEdges/Program.cs rename to dotnet/samples/GettingStarted/Workflows/Foundational/01_ExecutorsAndEdges/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflow/Foundational/02_Streaming/02_Streaming.csproj b/dotnet/samples/GettingStarted/Workflows/Foundational/02_Streaming/02_Streaming.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/Foundational/02_Streaming/02_Streaming.csproj rename to dotnet/samples/GettingStarted/Workflows/Foundational/02_Streaming/02_Streaming.csproj diff --git a/dotnet/samples/GettingStarted/Workflow/Foundational/02_Streaming/Program.cs b/dotnet/samples/GettingStarted/Workflows/Foundational/02_Streaming/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/Foundational/02_Streaming/Program.cs rename to dotnet/samples/GettingStarted/Workflows/Foundational/02_Streaming/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflow/Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj b/dotnet/samples/GettingStarted/Workflows/Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj rename to dotnet/samples/GettingStarted/Workflows/Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj diff --git a/dotnet/samples/GettingStarted/Workflow/Foundational/03_AgentsInWorkflows/Program.cs b/dotnet/samples/GettingStarted/Workflows/Foundational/03_AgentsInWorkflows/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/Foundational/03_AgentsInWorkflows/Program.cs rename to dotnet/samples/GettingStarted/Workflows/Foundational/03_AgentsInWorkflows/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflow/README.md b/dotnet/samples/GettingStarted/Workflows/README.md similarity index 92% rename from dotnet/samples/GettingStarted/Workflow/README.md rename to dotnet/samples/GettingStarted/Workflows/README.md index 05476e7b8b..13dda87e01 100644 --- a/dotnet/samples/GettingStarted/Workflow/README.md +++ b/dotnet/samples/GettingStarted/Workflows/README.md @@ -39,3 +39,11 @@ Once completed, please proceed to other samples listed below. | [Multi-Selection Routing](./ConditionalEdges/03_MultiSelection) | Demonstrates multi-selection routing where one executor can trigger multiple downstream executors | > These 3 samples build upon each other. It's recommended to explore them in sequence to fully grasp the concepts. + + +### Declarative Workflows + +| Sample | Concepts | +|--------|----------| +| [DeclarativeWorkflow](./DeclarativeWorkflow) | Demonstrates execution of declartive workflows. | + diff --git a/dotnet/samples/GettingStarted/Workflow/Resources/Lorem_Ipsum.txt b/dotnet/samples/GettingStarted/Workflows/Resources/Lorem_Ipsum.txt similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/Resources/Lorem_Ipsum.txt rename to dotnet/samples/GettingStarted/Workflows/Resources/Lorem_Ipsum.txt diff --git a/dotnet/samples/GettingStarted/Workflow/Resources/ambiguous_email.txt b/dotnet/samples/GettingStarted/Workflows/Resources/ambiguous_email.txt similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/Resources/ambiguous_email.txt rename to dotnet/samples/GettingStarted/Workflows/Resources/ambiguous_email.txt diff --git a/dotnet/samples/GettingStarted/Workflow/Resources/email.txt b/dotnet/samples/GettingStarted/Workflows/Resources/email.txt similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/Resources/email.txt rename to dotnet/samples/GettingStarted/Workflows/Resources/email.txt diff --git a/dotnet/samples/GettingStarted/Workflow/Resources/spam.txt b/dotnet/samples/GettingStarted/Workflows/Resources/spam.txt similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/Resources/spam.txt rename to dotnet/samples/GettingStarted/Workflows/Resources/spam.txt diff --git a/dotnet/samples/GettingStarted/Workflow/SharedStates/Program.cs b/dotnet/samples/GettingStarted/Workflows/SharedStates/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/SharedStates/Program.cs rename to dotnet/samples/GettingStarted/Workflows/SharedStates/Program.cs diff --git a/dotnet/samples/GettingStarted/Workflow/SharedStates/Resources.cs b/dotnet/samples/GettingStarted/Workflows/SharedStates/Resources.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/SharedStates/Resources.cs rename to dotnet/samples/GettingStarted/Workflows/SharedStates/Resources.cs diff --git a/dotnet/samples/GettingStarted/Workflow/SharedStates/SharedStates.csproj b/dotnet/samples/GettingStarted/Workflows/SharedStates/SharedStates.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Workflow/SharedStates/SharedStates.csproj rename to dotnet/samples/GettingStarted/Workflows/SharedStates/SharedStates.csproj From ee56314a26b44f495b526bb04e31275aa6477dd2 Mon Sep 17 00:00:00 2001 From: Giles Odigwe <79032838+giles17@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:45:51 -0700 Subject: [PATCH 06/32] Python: Samples Integration Tests (#615) * Samples Tests * small fixes * job fix * telemetry dependency fix * job error fix * sorting provider specific tests * telemetry fixes * openai file search fix --------- Co-authored-by: Giles Odigwe --- .github/workflows/python-merge-tests.yml | 14 + .../openai/test_openai_assistants_client.py | 2 + .../openai/test_openai_responses_client.py | 2 + python/pyproject.toml | 5 + .../threads/suspend_resume_thread.py | 46 +- python/tests/__init__.py | 3 + python/tests/sample_utils.py | 43 ++ python/tests/samples/__init__.py | 3 + .../tests/samples/getting_started/__init__.py | 3 + .../samples/getting_started/test_agents.py | 559 ++++++++++++++++++ .../getting_started/test_chat_client.py | 135 +++++ .../samples/getting_started/test_telemetry.py | 0 .../samples/getting_started/test_threads.py | 54 ++ 13 files changed, 842 insertions(+), 27 deletions(-) create mode 100644 python/tests/__init__.py create mode 100644 python/tests/sample_utils.py create mode 100644 python/tests/samples/__init__.py create mode 100644 python/tests/samples/getting_started/__init__.py create mode 100644 python/tests/samples/getting_started/test_agents.py create mode 100644 python/tests/samples/getting_started/test_chat_client.py create mode 100644 python/tests/samples/getting_started/test_telemetry.py create mode 100644 python/tests/samples/getting_started/test_threads.py diff --git a/.github/workflows/python-merge-tests.yml b/.github/workflows/python-merge-tests.yml index 6378e4520e..819822ae27 100644 --- a/.github/workflows/python-merge-tests.yml +++ b/.github/workflows/python-merge-tests.yml @@ -17,6 +17,7 @@ env: # Configure a constant location for the uv cache UV_CACHE_DIR: /tmp/.uv-cache RUN_INTEGRATION_TESTS: "true" + RUN_SAMPLES_TESTS: ${{ vars.RUN_SAMPLES_TESTS }} jobs: paths-filter: @@ -79,6 +80,10 @@ jobs: - name: Test with pytest run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test --junitxml=coverage.xml working-directory: ./python + - name: Test openai samples + if: env.RUN_SAMPLES_TESTS == 'true' + run: uv run pytest tests/samples/ -m "openai" --junitxml=coverage_samples_main.xml + working-directory: ./python - name: Move coverage file run: | mv ./packages/${{ env.PACKAGE_NAME }}/coverage.xml coverage_${{ env.PACKAGE_NAME }}.xml @@ -141,6 +146,10 @@ jobs: - name: Test with pytest run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test --junitxml=coverage.xml working-directory: ./python + - name: Test azure samples + if: env.RUN_SAMPLES_TESTS == 'true' + run: uv run pytest tests/samples/ -m "azure" --junitxml=coverage_samples_azure.xml + working-directory: ./python - name: Move coverage file run: | mv ./packages/${{ env.PACKAGE_NAME }}/coverage.xml coverage_${{ env.PACKAGE_NAME }}.xml @@ -202,6 +211,10 @@ jobs: - name: Test with pytest run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test --junitxml=coverage.xml working-directory: ./python + - name: Test foundry samples + if: env.RUN_SAMPLES_TESTS == 'true' + run: uv run pytest tests/samples/ -m "foundry" --junitxml=coverage_samples_foundry.xml + working-directory: ./python - name: Move coverage file run: | mv ./packages/${{ env.PACKAGE_NAME }}/coverage.xml coverage_${{ env.PACKAGE_NAME }}.xml @@ -220,6 +233,7 @@ jobs: display-options: fEX fail-on-empty: true title: Test results + python-integration-tests-check: if: always() runs-on: ubuntu-latest diff --git a/python/packages/main/tests/openai/test_openai_assistants_client.py b/python/packages/main/tests/openai/test_openai_assistants_client.py index 7e3b455bc9..c78a45d1fb 100644 --- a/python/packages/main/tests/openai/test_openai_assistants_client.py +++ b/python/packages/main/tests/openai/test_openai_assistants_client.py @@ -1049,6 +1049,7 @@ async def test_openai_assistants_client_with_existing_assistant() -> None: @skip_if_openai_integration_tests_disabled +@pytest.mark.skip(reason="OpenAI file search functionality is currently broken - tracked in GitHub issue") async def test_openai_assistants_client_file_search() -> None: """Test OpenAI Assistants Client response.""" async with OpenAIAssistantsClient() as openai_assistants_client: @@ -1071,6 +1072,7 @@ async def test_openai_assistants_client_file_search() -> None: @skip_if_openai_integration_tests_disabled +@pytest.mark.skip(reason="OpenAI file search functionality is currently broken - tracked in GitHub issue") async def test_openai_assistants_client_file_search_streaming() -> None: """Test OpenAI Assistants Client response.""" async with OpenAIAssistantsClient() as openai_assistants_client: diff --git a/python/packages/main/tests/openai/test_openai_responses_client.py b/python/packages/main/tests/openai/test_openai_responses_client.py index 51d8633b79..f9e7c18ab6 100644 --- a/python/packages/main/tests/openai/test_openai_responses_client.py +++ b/python/packages/main/tests/openai/test_openai_responses_client.py @@ -1012,6 +1012,7 @@ async def test_openai_responses_client_web_search_streaming() -> None: @skip_if_openai_integration_tests_disabled +@pytest.mark.skip(reason="OpenAI file search functionality is currently broken - tracked in GitHub issue") async def test_openai_responses_client_file_search() -> None: openai_responses_client = OpenAIResponsesClient() @@ -1036,6 +1037,7 @@ async def test_openai_responses_client_file_search() -> None: @skip_if_openai_integration_tests_disabled +@pytest.mark.skip(reason="OpenAI file search functionality is currently broken - tracked in GitHub issue") async def test_openai_responses_client_streaming_file_search() -> None: openai_responses_client = OpenAIResponsesClient() diff --git a/python/pyproject.toml b/python/pyproject.toml index 0b04f3dd21..503de388d4 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -131,6 +131,11 @@ asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" filterwarnings = [] timeout = 120 +markers = [ + "azure: marks tests as Azure provider specific", + "foundry: marks tests as Foundry provider specific", + "openai: marks tests as OpenAI provider specific", +] [tool.coverage.run] omit = [ diff --git a/python/samples/getting_started/threads/suspend_resume_thread.py b/python/samples/getting_started/threads/suspend_resume_thread.py index 28f445816e..88065aa987 100644 --- a/python/samples/getting_started/threads/suspend_resume_thread.py +++ b/python/samples/getting_started/threads/suspend_resume_thread.py @@ -2,46 +2,38 @@ import asyncio -from agent_framework.foundry import FoundryChatClient from agent_framework.openai import OpenAIChatClient -from azure.identity.aio import AzureCliCredential async def suspend_resume_service_managed_thread() -> None: """Demonstrates how to suspend and resume a service-managed thread.""" print("=== Suspend-Resume Service-Managed Thread ===") - # Foundry Chat Client is used as an example here, + # OpenAI Chat Client is used as an example here, # other chat clients can be used as well. - # For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred - # authentication option. - async with ( - AzureCliCredential() as credential, - FoundryChatClient(async_credential=credential).create_agent( - name="Joker", instructions="You are good at telling jokes." - ) as agent, - ): - # Start a new thread for the agent conversation. - thread = agent.get_new_thread() + agent = OpenAIChatClient().create_agent(name="Joker", instructions="You are good at telling jokes.") - # Respond to user input. - query = "Tell me a joke about a pirate." - print(f"User: {query}") - print(f"Agent: {await agent.run(query, thread=thread)}\n") + # Start a new thread for the agent conversation. + thread = agent.get_new_thread() - # Serialize the thread state, so it can be stored for later use. - serialized_thread = await thread.serialize() + # Respond to user input. + query = "Tell me a joke about a pirate." + print(f"User: {query}") + print(f"Agent: {await agent.run(query, thread=thread)}\n") - # The thread can now be saved to a database, file, or any other storage mechanism and loaded again later. - print(f"Serialized thread: {serialized_thread}\n") + # Serialize the thread state, so it can be stored for later use. + serialized_thread = await thread.serialize() - # Deserialize the thread state after loading from storage. - resumed_thread = await agent.deserialize_thread(serialized_thread) + # The thread can now be saved to a database, file, or any other storage mechanism and loaded again later. + print(f"Serialized thread: {serialized_thread}\n") - # Respond to user input. - query = "Now tell the same joke in the voice of a pirate, and add some emojis to the joke." - print(f"User: {query}") - print(f"Agent: {await agent.run(query, thread=resumed_thread)}\n") + # Deserialize the thread state after loading from storage. + resumed_thread = await agent.deserialize_thread(serialized_thread) + + # Respond to user input. + query = "Now tell the same joke in the voice of a pirate, and add some emojis to the joke." + print(f"User: {query}") + print(f"Agent: {await agent.run(query, thread=resumed_thread)}\n") async def suspend_resume_in_memory_thread() -> None: diff --git a/python/tests/__init__.py b/python/tests/__init__.py new file mode 100644 index 0000000000..58b29ef7f3 --- /dev/null +++ b/python/tests/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) Microsoft. All rights reserved. + +"""Test utilities for sample testing.""" diff --git a/python/tests/sample_utils.py b/python/tests/sample_utils.py new file mode 100644 index 0000000000..878566e223 --- /dev/null +++ b/python/tests/sample_utils.py @@ -0,0 +1,43 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +import logging +from collections.abc import Awaitable, Callable +from typing import Any + +logger = logging.getLogger(__name__) + + +async def retry( + func: Callable[[], Awaitable[Any]], + retries: int = 3, + reset: Callable[[], None] | None = None, + name: str | None = None, +) -> None: + """Retry function with reset capability and proper logging. + + Args: + func: The function to retry. + retries: Number of retries. + reset: Function to reset the state of any variables used in the function. + name: Optional name for logging purposes. + """ + func_name = name or func.__module__ + logger.info(f"Running {retries} retries with func: {func_name}") + + for i in range(retries): + logger.info(f" Try {i + 1} for {func_name}") + try: + if reset: + reset() + await func() + return + except Exception as e: + logger.warning(f" On try {i + 1} got this error: {e}") + if i == retries - 1: # Last retry + raise + + # Binary exponential backoff like Semantic Kernel + backoff = 2**i + logger.info(f" Sleeping for {backoff} seconds before retrying") + await asyncio.sleep(backoff) diff --git a/python/tests/samples/__init__.py b/python/tests/samples/__init__.py new file mode 100644 index 0000000000..1857030e0e --- /dev/null +++ b/python/tests/samples/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) Microsoft. All rights reserved. + +"""Sample tests package.""" diff --git a/python/tests/samples/getting_started/__init__.py b/python/tests/samples/getting_started/__init__.py new file mode 100644 index 0000000000..9c75ed9a9b --- /dev/null +++ b/python/tests/samples/getting_started/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) Microsoft. All rights reserved. + +"""Getting started sample tests.""" diff --git a/python/tests/samples/getting_started/test_agents.py b/python/tests/samples/getting_started/test_agents.py new file mode 100644 index 0000000000..5e04367061 --- /dev/null +++ b/python/tests/samples/getting_started/test_agents.py @@ -0,0 +1,559 @@ +# Copyright (c) Microsoft. All rights reserved. + +import copy +import os +from collections.abc import Awaitable, Callable +from typing import Any + +import pytest +from pytest import MonkeyPatch, mark, param + +from samples.getting_started.agents.azure_assistants_client.azure_assistants_basic import ( + main as azure_assistants_basic, +) +from samples.getting_started.agents.azure_assistants_client.azure_assistants_with_code_interpreter import ( + main as azure_assistants_with_code_interpreter, +) +from samples.getting_started.agents.azure_assistants_client.azure_assistants_with_existing_assistant import ( + main as azure_assistants_with_existing_assistant, +) +from samples.getting_started.agents.azure_assistants_client.azure_assistants_with_explicit_settings import ( + main as azure_assistants_with_explicit_settings, +) +from samples.getting_started.agents.azure_assistants_client.azure_assistants_with_function_tools import ( + main as azure_assistants_with_function_tools, +) +from samples.getting_started.agents.azure_assistants_client.azure_assistants_with_thread import ( + main as azure_assistants_with_thread, +) +from samples.getting_started.agents.azure_chat_client.azure_chat_client_basic import ( + main as azure_chat_client_basic, +) +from samples.getting_started.agents.azure_chat_client.azure_chat_client_with_explicit_settings import ( + main as azure_chat_client_with_explicit_settings, +) +from samples.getting_started.agents.azure_chat_client.azure_chat_client_with_function_tools import ( + main as azure_chat_client_with_function_tools, +) +from samples.getting_started.agents.azure_chat_client.azure_chat_client_with_thread import ( + main as azure_chat_client_with_thread, +) +from samples.getting_started.agents.azure_responses_client.azure_responses_client_basic import ( + main as azure_responses_client_basic, +) +from samples.getting_started.agents.azure_responses_client.azure_responses_client_with_code_interpreter import ( + main as azure_responses_client_with_code_interpreter, +) +from samples.getting_started.agents.azure_responses_client.azure_responses_client_with_explicit_settings import ( + main as azure_responses_client_with_explicit_settings, +) +from samples.getting_started.agents.azure_responses_client.azure_responses_client_with_function_tools import ( + main as azure_responses_client_with_function_tools, +) +from samples.getting_started.agents.azure_responses_client.azure_responses_client_with_thread import ( + main as azure_responses_client_with_thread, +) +from samples.getting_started.agents.foundry.foundry_basic import ( + main as foundry_basic, +) +from samples.getting_started.agents.foundry.foundry_with_code_interpreter import ( + main as foundry_with_code_interpreter, +) +from samples.getting_started.agents.foundry.foundry_with_existing_agent import ( + main as foundry_with_existing_agent, +) +from samples.getting_started.agents.foundry.foundry_with_explicit_settings import ( + main as foundry_with_explicit_settings, +) +from samples.getting_started.agents.foundry.foundry_with_function_tools import ( + main as foundry_with_function_tools, +) +from samples.getting_started.agents.foundry.foundry_with_thread import ( + main as foundry_with_thread, +) +from samples.getting_started.agents.openai_assistants_client.openai_assistants_basic import ( + main as openai_assistants_basic, +) +from samples.getting_started.agents.openai_assistants_client.openai_assistants_with_code_interpreter import ( + main as openai_assistants_with_code_interpreter, +) +from samples.getting_started.agents.openai_assistants_client.openai_assistants_with_existing_assistant import ( + main as openai_assistants_with_existing_assistant, +) +from samples.getting_started.agents.openai_assistants_client.openai_assistants_with_explicit_settings import ( + main as openai_assistants_with_explicit_settings, +) +from samples.getting_started.agents.openai_assistants_client.openai_assistants_with_file_search import ( + main as openai_assistants_with_file_search, +) +from samples.getting_started.agents.openai_assistants_client.openai_assistants_with_function_tools import ( + main as openai_assistants_with_function_tools, +) +from samples.getting_started.agents.openai_assistants_client.openai_assistants_with_thread import ( + main as openai_assistants_with_thread, +) +from samples.getting_started.agents.openai_chat_client.openai_chat_client_basic import ( + main as openai_chat_client_basic, +) +from samples.getting_started.agents.openai_chat_client.openai_chat_client_with_explicit_settings import ( + main as openai_chat_client_with_explicit_settings, +) +from samples.getting_started.agents.openai_chat_client.openai_chat_client_with_function_tools import ( + main as openai_chat_client_with_function_tools, +) +from samples.getting_started.agents.openai_chat_client.openai_chat_client_with_local_mcp import ( + main as openai_chat_client_with_local_mcp, +) +from samples.getting_started.agents.openai_chat_client.openai_chat_client_with_thread import ( + main as openai_chat_client_with_thread, +) +from samples.getting_started.agents.openai_chat_client.openai_chat_client_with_web_search import ( + main as openai_chat_client_with_web_search, +) +from samples.getting_started.agents.openai_responses_client.openai_responses_client_basic import ( + main as openai_responses_client_basic, +) +from samples.getting_started.agents.openai_responses_client.openai_responses_client_reasoning import ( + main as openai_responses_client_reasoning, +) +from samples.getting_started.agents.openai_responses_client.openai_responses_client_with_code_interpreter import ( + main as openai_responses_client_with_code_interpreter, +) +from samples.getting_started.agents.openai_responses_client.openai_responses_client_with_explicit_settings import ( + main as openai_responses_client_with_explicit_settings, +) +from samples.getting_started.agents.openai_responses_client.openai_responses_client_with_file_search import ( + main as openai_responses_client_with_file_search, +) +from samples.getting_started.agents.openai_responses_client.openai_responses_client_with_function_tools import ( + main as openai_responses_client_with_function_tools, +) +from samples.getting_started.agents.openai_responses_client.openai_responses_client_with_local_mcp import ( + main as openai_responses_client_with_local_mcp, +) +from samples.getting_started.agents.openai_responses_client.openai_responses_client_with_thread import ( + main as openai_responses_client_with_thread, +) +from samples.getting_started.agents.openai_responses_client.openai_responses_client_with_web_search import ( + main as openai_responses_client_with_web_search, +) +from tests.sample_utils import retry + +# Environment variable for controlling sample tests +RUN_SAMPLES_TESTS = "RUN_SAMPLES_TESTS" + +# All agent samples across providers +agent_samples = [ + # Azure Assistants Agent samples + param( + azure_assistants_basic, + [], # Non-interactive sample + id="azure_assistants_basic", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + azure_assistants_with_code_interpreter, + [], # Non-interactive sample + id="azure_assistants_with_code_interpreter", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + azure_assistants_with_function_tools, + [], # Non-interactive sample + id="azure_assistants_with_function_tools", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + azure_assistants_with_existing_assistant, + [], # Non-interactive sample + id="azure_assistants_with_existing_assistant", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + azure_assistants_with_explicit_settings, + [], # Non-interactive sample + id="azure_assistants_with_explicit_settings", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + azure_assistants_with_thread, + [], # Non-interactive sample + id="azure_assistants_with_thread", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + # Azure Chat Client Agent samples + param( + azure_chat_client_basic, + [], # Non-interactive sample + id="azure_chat_client_basic", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + azure_chat_client_with_explicit_settings, + [], # Non-interactive sample + id="azure_chat_client_with_explicit_settings", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + azure_chat_client_with_function_tools, + [], # Non-interactive sample + id="azure_chat_client_with_function_tools", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + azure_chat_client_with_thread, + [], # Non-interactive sample + id="azure_chat_client_with_thread", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + # Azure Responses Client Agent samples + param( + azure_responses_client_basic, + [], # Non-interactive sample + id="azure_responses_client_basic", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + azure_responses_client_with_code_interpreter, + [], # Non-interactive sample + id="azure_responses_client_with_code_interpreter", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + azure_responses_client_with_explicit_settings, + [], # Non-interactive sample + id="azure_responses_client_with_explicit_settings", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + azure_responses_client_with_function_tools, + [], # Non-interactive sample + id="azure_responses_client_with_function_tools", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + azure_responses_client_with_thread, + [], # Non-interactive sample + id="azure_responses_client_with_thread", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + # Foundry Agent samples + param( + foundry_basic, + [], # Non-interactive sample + id="foundry_basic", + marks=[ + pytest.mark.foundry, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + foundry_with_code_interpreter, + [], # Non-interactive sample + id="foundry_with_code_interpreter", + marks=[ + pytest.mark.foundry, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + foundry_with_existing_agent, + [], # Non-interactive sample + id="foundry_with_existing_agent", + marks=[ + pytest.mark.foundry, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + foundry_with_explicit_settings, + [], # Non-interactive sample + id="foundry_with_explicit_settings", + marks=[ + pytest.mark.foundry, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + foundry_with_function_tools, + [], # Non-interactive sample + id="foundry_with_function_tools", + marks=[ + pytest.mark.foundry, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + foundry_with_thread, + [], # Non-interactive sample + id="foundry_with_thread", + marks=[ + pytest.mark.foundry, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + # OpenAI Assistants Agent samples + param( + openai_assistants_basic, + [], # Non-interactive sample + id="openai_assistants_basic", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_assistants_with_code_interpreter, + [], # Non-interactive sample + id="openai_assistants_with_code_interpreter", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_assistants_with_existing_assistant, + [], # Non-interactive sample + id="openai_assistants_with_existing_assistant", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_assistants_with_explicit_settings, + [], # Non-interactive sample + id="openai_assistants_with_explicit_settings", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_assistants_with_file_search, + [], # Non-interactive sample + id="openai_assistants_with_file_search", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + pytest.mark.skip(reason="OpenAI file search functionality is currently broken - tracked in GitHub issue"), + ], + ), + param( + openai_assistants_with_function_tools, + [], # Non-interactive sample + id="openai_assistants_with_function_tools", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_assistants_with_thread, + [], # Non-interactive sample + id="openai_assistants_with_thread", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + # OpenAI Chat Client Agent samples + param( + openai_chat_client_basic, + [], # Non-interactive sample + id="openai_chat_client_basic", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_chat_client_with_explicit_settings, + [], # Non-interactive sample + id="openai_chat_client_with_explicit_settings", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_chat_client_with_function_tools, + [], # Non-interactive sample + id="openai_chat_client_with_function_tools", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_chat_client_with_local_mcp, + [], # Non-interactive sample + id="openai_chat_client_with_local_mcp", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_chat_client_with_thread, + [], # Non-interactive sample + id="openai_chat_client_with_thread", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_chat_client_with_web_search, + [], # Non-interactive sample + id="openai_chat_client_with_web_search", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + # OpenAI Responses Client Agent samples + param( + openai_responses_client_basic, + [], # Non-interactive sample + id="openai_responses_client_basic", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_responses_client_reasoning, + [], # Non-interactive sample + id="openai_responses_client_reasoning", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_responses_client_with_code_interpreter, + [], # Non-interactive sample + id="openai_responses_client_with_code_interpreter", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_responses_client_with_explicit_settings, + [], # Non-interactive sample + id="openai_responses_client_with_explicit_settings", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_responses_client_with_file_search, + [], # Non-interactive sample + id="openai_responses_client_with_file_search", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + pytest.mark.skip(reason="OpenAI file search functionality is currently broken - tracked in GitHub issue"), + ], + ), + param( + openai_responses_client_with_function_tools, + [], # Non-interactive sample + id="openai_responses_client_with_function_tools", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_responses_client_with_local_mcp, + [], # Non-interactive sample + id="openai_responses_client_with_local_mcp", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_responses_client_with_thread, + [], # Non-interactive sample + id="openai_responses_client_with_thread", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_responses_client_with_web_search, + [], # Non-interactive sample + id="openai_responses_client_with_web_search", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), +] + + +@mark.parametrize("sample, responses", agent_samples) +async def test_agent_samples(sample: Callable[..., Awaitable[Any]], responses: list[str], monkeypatch: MonkeyPatch): + """Test agent samples with input mocking and retry logic.""" + saved_responses = copy.deepcopy(responses) + + def reset(): + responses.clear() + responses.extend(saved_responses) + + def mock_input(prompt: str = "") -> str: + return responses.pop(0) if responses else "exit" + + monkeypatch.setattr("builtins.input", mock_input) + await retry(sample, retries=3, reset=reset) diff --git a/python/tests/samples/getting_started/test_chat_client.py b/python/tests/samples/getting_started/test_chat_client.py new file mode 100644 index 0000000000..2c93ee0804 --- /dev/null +++ b/python/tests/samples/getting_started/test_chat_client.py @@ -0,0 +1,135 @@ +# Copyright (c) Microsoft. All rights reserved. + +import copy +import os +from collections.abc import Awaitable, Callable +from typing import Any + +import pytest +from pytest import MonkeyPatch, mark, param + +from samples.getting_started.chat_client.azure_assistants_client import ( + main as azure_assistants_client, +) +from samples.getting_started.chat_client.azure_chat_client import ( + main as azure_chat_client, +) +from samples.getting_started.chat_client.azure_responses_client import ( + main as azure_responses_client, +) +from samples.getting_started.chat_client.chat_response_cancellation import ( + main as chat_response_cancellation, +) +from samples.getting_started.chat_client.foundry_chat_client import ( + main as foundry_chat_client, +) +from samples.getting_started.chat_client.openai_assistants_client import ( + main as openai_assistants_client, +) +from samples.getting_started.chat_client.openai_chat_client import ( + main as openai_chat_client, +) +from samples.getting_started.chat_client.openai_responses_client import ( + main as openai_responses_client, +) +from tests.sample_utils import retry + +# Environment variable for controlling sample tests +RUN_SAMPLES_TESTS = "RUN_SAMPLES_TESTS" + +# All chat client samples across providers +chat_client_samples = [ + # Azure Chat Client samples + param( + azure_assistants_client, + [], # Non-interactive sample + id="azure_assistants_client", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + azure_chat_client, + [], # Non-interactive sample + id="azure_chat_client", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + azure_responses_client, + [], # Non-interactive sample + id="azure_responses_client", + marks=[ + pytest.mark.azure, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + # Foundry Chat Client samples + param( + foundry_chat_client, + [], # Non-interactive sample + id="foundry_chat_client", + marks=[ + pytest.mark.foundry, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + # OpenAI Chat Client samples + param( + openai_assistants_client, + [], # Non-interactive sample + id="openai_assistants_client", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_chat_client, + [], # Non-interactive sample + id="openai_chat_client", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + openai_responses_client, + [], # Non-interactive sample + id="openai_responses_client", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + # General Chat Client samples (no provider-specific environment variable) + param( + chat_response_cancellation, + [], # Non-interactive sample + id="chat_response_cancellation", + marks=pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ), +] + + +@mark.parametrize("sample, responses", chat_client_samples) +async def test_chat_client_samples( + sample: Callable[..., Awaitable[Any]], + responses: list[str], + monkeypatch: MonkeyPatch, +): + """Test chat client samples with input mocking and retry logic.""" + saved_responses = copy.deepcopy(responses) + + def reset(): + responses.clear() + responses.extend(saved_responses) + + def mock_input(prompt: str = "") -> str: + return responses.pop(0) if responses else "exit" + + monkeypatch.setattr("builtins.input", mock_input) + await retry(sample, retries=3, reset=reset) diff --git a/python/tests/samples/getting_started/test_telemetry.py b/python/tests/samples/getting_started/test_telemetry.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/tests/samples/getting_started/test_threads.py b/python/tests/samples/getting_started/test_threads.py new file mode 100644 index 0000000000..ec530efa6e --- /dev/null +++ b/python/tests/samples/getting_started/test_threads.py @@ -0,0 +1,54 @@ +# Copyright (c) Microsoft. All rights reserved. + +import copy +import os +from collections.abc import Awaitable, Callable +from typing import Any + +import pytest +from pytest import MonkeyPatch, mark, param + +from samples.getting_started.threads.custom_chat_message_store_thread import main as threads_custom_store +from samples.getting_started.threads.suspend_resume_thread import main as threads_suspend_resume +from tests.sample_utils import retry + +# Environment variable for controlling sample tests +RUN_SAMPLES_TESTS = "RUN_SAMPLES_TESTS" + +# All thread samples +thread_samples = [ + param( + threads_custom_store, + [], # Non-interactive sample + id="threads_custom_store", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + threads_suspend_resume, + [], # Non-interactive sample + id="threads_suspend_resume", + marks=[ + pytest.mark.openai, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), +] + + +@mark.parametrize("sample, responses", thread_samples) +async def test_thread_samples(sample: Callable[..., Awaitable[Any]], responses: list[str], monkeypatch: MonkeyPatch): + """Test thread samples with input mocking and retry logic.""" + saved_responses = copy.deepcopy(responses) + + def reset(): + responses.clear() + responses.extend(saved_responses) + + def mock_input(prompt: str = "") -> str: + return responses.pop(0) if responses else "exit" + + monkeypatch.setattr("builtins.input", mock_input) + await retry(sample, retries=3, reset=reset) From 2472f35876058930527e3422beb6db067536eb92 Mon Sep 17 00:00:00 2001 From: Alex Lavaee <57336517+lavaman131@users.noreply.github.com> Date: Mon, 8 Sep 2025 17:23:02 -0700 Subject: [PATCH 07/32] fixed incorrect tool.uv.sources config in python package installation instructions (#651) Co-authored-by: Alex Lavaee --- .../getting-started/package_installation.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/user-documentation-python/getting-started/package_installation.md b/user-documentation-python/getting-started/package_installation.md index 48fafdd5a0..51b9d8dd5c 100644 --- a/user-documentation-python/getting-started/package_installation.md +++ b/user-documentation-python/getting-started/package_installation.md @@ -69,10 +69,10 @@ dependencies = [ [tool.uv] prerelease = "if-necessary-or-explicit" [tool.uv.sources] -"agent-framework" = { git = "https://github.com/microsoft/agent-framework.git", ref = "main", subdirectory = "python/packages/main" } -"agent-framework-azure" = { git = "https://github.com/microsoft/agent-framework.git", ref = "main", subdirectory = "python/packages/azure" } -"agent-framework-foundry" = { git = "https://github.com/microsoft/agent-framework.git", ref = "main", subdirectory = "python/packages/foundry" } -"agent-framework-workflow" = { git = "https://github.com/microsoft/agent-framework.git", ref = "main", subdirectory = "python/packages/workflow" } +"agent-framework" = { git = "https://github.com/microsoft/agent-framework.git", branch = "main", subdirectory = "python/packages/main" } +"agent-framework-azure" = { git = "https://github.com/microsoft/agent-framework.git", branch = "main", subdirectory = "python/packages/azure" } +"agent-framework-foundry" = { git = "https://github.com/microsoft/agent-framework.git", branch = "main", subdirectory = "python/packages/foundry" } +"agent-framework-workflow" = { git = "https://github.com/microsoft/agent-framework.git", branch = "main", subdirectory = "python/packages/workflow" } ``` Then create a virtual environment: ```bash From 2699b8528509063c2104806f209d225bc9dd7cad Mon Sep 17 00:00:00 2001 From: Evan Mattson <35585003+moonbox3@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:06:22 +0900 Subject: [PATCH 08/32] Python: Update getting started with workflows sample structure and README (#653) * Update getting started with workflows sample structure and README * Small updates * Adjust getting started samples. Fix agent executor bug. Add workflow tests to unit test file. * Fix resource links --- .github/workflows/python-tests.yml | 27 +++++++ .../agent_framework_workflow/_executor.py | 8 ++ .../getting_started/workflow/README.md | 79 +++++++++---------- .../step1_executors_and_edges.py | 0 .../step2_agents_in_a_workflow.py | 0 .../step3_streaming.py | 0 .../sub_workflow_basics.py | 0 .../sub_workflow_parallel_requests.py | 0 .../sub_workflow_request_interception.py | 0 .../edge_condition.py | 0 .../multi_selection_edge_group.py | 7 +- .../sequential_executors.py | 0 .../sequential_streaming.py | 0 .../{loop => control-flow}/simple_loop.py | 0 .../switch_case_edge_group.py | 5 +- .../guessing_game_with_human_input.py | 0 .../tracing_basics.py | 0 .../fan_out_fan_in_edges.py | 0 .../map_reduce_and_visualization.py | 0 .../shared_states_with_agents.py | 38 ++++----- 20 files changed, 103 insertions(+), 61 deletions(-) rename python/samples/getting_started/workflow/{foundational => _start-here}/step1_executors_and_edges.py (100%) rename python/samples/getting_started/workflow/{foundational => _start-here}/step2_agents_in_a_workflow.py (100%) rename python/samples/getting_started/workflow/{foundational => _start-here}/step3_streaming.py (100%) rename python/samples/getting_started/workflow/{sub_workflow => composition}/sub_workflow_basics.py (100%) rename python/samples/getting_started/workflow/{sub_workflow => composition}/sub_workflow_parallel_requests.py (100%) rename python/samples/getting_started/workflow/{sub_workflow => composition}/sub_workflow_request_interception.py (100%) rename python/samples/getting_started/workflow/{conditional_edges => control-flow}/edge_condition.py (100%) rename python/samples/getting_started/workflow/{conditional_edges => control-flow}/multi_selection_edge_group.py (97%) rename python/samples/getting_started/workflow/{sequential => control-flow}/sequential_executors.py (100%) rename python/samples/getting_started/workflow/{sequential => control-flow}/sequential_streaming.py (100%) rename python/samples/getting_started/workflow/{loop => control-flow}/simple_loop.py (100%) rename python/samples/getting_started/workflow/{conditional_edges => control-flow}/switch_case_edge_group.py (97%) rename python/samples/getting_started/workflow/{human_in_the_loop => human-in-the-loop}/guessing_game_with_human_input.py (100%) rename python/samples/getting_started/workflow/{tracing => observability}/tracing_basics.py (100%) rename python/samples/getting_started/workflow/{fan_out_fan_in => parallelism}/fan_out_fan_in_edges.py (100%) rename python/samples/getting_started/workflow/{fan_out_fan_in => parallelism}/map_reduce_and_visualization.py (100%) rename python/samples/getting_started/workflow/{shared_states => state-management}/shared_states_with_agents.py (89%) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 2001cb9997..6552b7829c 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -37,6 +37,7 @@ jobs: - name: Install the project run: | uv sync --all-packages --all-extras --dev -U --prerelease=if-necessary-or-explicit + # Main package tests - name: Set environment variables - main - win if: ${{ matrix.os == 'windows-latest' }} @@ -58,6 +59,7 @@ jobs: with: name: coverage-${{ matrix.OS }}-${{ matrix.python-version }}-${{ env.PACKAGE_NAME }} path: ./python/coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml + # Azure package tests - name: Set environment variables - azure - win if: ${{ matrix.os == 'windows-latest' }} @@ -79,6 +81,7 @@ jobs: with: name: coverage-${{ matrix.OS }}-${{ matrix.python-version }}-${{ env.PACKAGE_NAME }} path: ./python/coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml + # Foundry package tests - name: Set environment variables - foundry - win if: ${{ matrix.os == 'windows-latest' }} @@ -100,6 +103,30 @@ jobs: with: name: coverage-${{ matrix.OS }}-${{ matrix.python-version }}-${{ env.PACKAGE_NAME }} path: ./python/coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml + + # Workflow package tests + - name: Set environment variables - workflow - win + if: ${{ matrix.os == 'windows-latest' }} + run: | + echo "PACKAGE_NAME=workflow" | Out-File -FilePath $env:GITHUB_ENV -Append + - name: Set environment variables - workflow + if: ${{ matrix.os != 'windows-latest' }} + run: | + echo "PACKAGE_NAME=workflow" >> $GITHUB_ENV + - name: Test with pytest - workflow + run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test --junitxml=coverage.xml + working-directory: ./python + - name: Move coverage file - workflow + run: | + mv ./packages/${{ env.PACKAGE_NAME }}/coverage.xml coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml + working-directory: ./python + - name: Upload coverage artifact - workflow + uses: actions/upload-artifact@v4 + with: + name: coverage-${{ matrix.OS }}-${{ matrix.python-version }}-${{ env.PACKAGE_NAME }} + path: ./python/coverage_${{ matrix.OS }}_${{ matrix.python-version }}_${{ env.PACKAGE_NAME }}.xml + + # Surface failing tests - name: Surface failing tests if: always() uses: pmeier/pytest-results-action@v0.7.2 diff --git a/python/packages/workflow/agent_framework_workflow/_executor.py b/python/packages/workflow/agent_framework_workflow/_executor.py index 2cd1b1623a..0a7135047f 100644 --- a/python/packages/workflow/agent_framework_workflow/_executor.py +++ b/python/packages/workflow/agent_framework_workflow/_executor.py @@ -902,6 +902,14 @@ class AgentExecutor(Executor): ) await ctx.add_event(AgentRunEvent(self.id, response)) + full_conversation: list[ChatMessage] | None = None + if self._cache: + full_conversation = list(self._cache) + list(response.messages) + + agent_response = AgentExecutorResponse(self.id, response, full_conversation=full_conversation) + await ctx.send_message(agent_response) + self._cache.clear() + @handler async def from_response(self, prior: AgentExecutorResponse, ctx: WorkflowContext[AgentExecutorResponse]) -> None: """Enable seamless chaining: accept a prior AgentExecutorResponse as input. diff --git a/python/samples/getting_started/workflow/README.md b/python/samples/getting_started/workflow/README.md index c453877414..744ad3c9c0 100644 --- a/python/samples/getting_started/workflow/README.md +++ b/python/samples/getting_started/workflow/README.md @@ -2,13 +2,13 @@ ## Installation -To install the base `agent_framework.workflow` package, please run: +To install the base `agent_framework.workflow` package, run: ```bash pip install agent-framework-workflow ``` -You can install the workflow package with visualization dependency: +To install with visualization support: ```bash pip install agent-framework-workflow[viz] @@ -20,15 +20,15 @@ To export visualization images you also need to [install GraphViz](https://graph ## Foundational Concepts - Start Here -Begin with the `foundational` folder in order. These three samples introduce the core ideas of executors, edges, agents in workflows, and streaming. +Begin with the `_start-here` folder in order. These three samples introduce the core ideas of executors, edges, agents in workflows, and streaming. | Sample | File | Concepts | |--------|------|----------| -| Executors and Edges | [foundational/step1_executors_and_edges.py](./foundational/step1_executors_and_edges.py) | Minimal workflow with basic executors and edges | -| Agents in a Workflow | [foundational/step2_agents_in_a_workflow.py](./foundational/step2_agents_in_a_workflow.py) | Introduces `AgentExecutor`; calling agents inside a workflow | -| Streaming | [foundational/step3_streaming.py](./foundational/step3_streaming.py) | Extends workflows with event streaming | +| Executors and Edges | [_start-here/step1_executors_and_edges.py](./_start-here/step1_executors_and_edges.py) | Minimal workflow with basic executors and edges | +| Agents in a Workflow | [_start-here/step2_agents_in_a_workflow.py](./_start-here/step2_agents_in_a_workflow.py) | Introduces adding Agents as nodes; calling agents inside a workflow | +| Streaming (Basics) | [_start-here/step3_streaming.py](./_start-here/step3_streaming.py) | Extends workflows with event streaming | -Once comfortable with these, explore the rest of the samples. +Once comfortable with these, explore the rest of the samples below. --- @@ -37,39 +37,43 @@ Once comfortable with these, explore the rest of the samples. ### agents | Sample | File | Concepts | |---|---|---| -| Azure Chat Agents Streaming | [agents/azure_chat_agents_streaming.py](./agents/azure_chat_agents_streaming.py) | Directly adds Azure agents as edges and handling streaming events | +| Azure Chat Agents (Streaming) | [agents/azure_chat_agents_streaming.py](./agents/azure_chat_agents_streaming.py) | Add Azure agents as edges and handle streaming events | | Custom Agent Executors | [agents/custom_agent_executors.py](./agents/custom_agent_executors.py) | Create executors to handle agent run methods | -| Foundry Chat Agents Streaming | [agents/foundry_chat_agents_streaming.py](./agents/foundry_chat_agents_streaming.py) | Directly adds Foundry agents as edges and handling streaming events | -| Workflow as Agent | [ai_agent/workflow_as_agent.py](./agents/workflow_as_agent.py) | Wrap a workflow so it can behave like an agent | -| Workflow as Agent + HITL | [ai_agent/workflow_as_agent_human_in_the_loop.py](./agents/workflow_as_agent_human_in_the_loop.py) | Extend workflow-as-agent with human-in-the-loop capability | +| Foundry Chat Agents (Streaming) | [agents/foundry_chat_agents_streaming.py](./agents/foundry_chat_agents_streaming.py) | Add Foundry agents as edges and handle streaming events | +| Workflow as Agent (Reflection Pattern) | [agents/workflow_as_agent_reflection_pattern.py](./agents/workflow_as_agent_reflection_pattern.py) | Wrap a workflow so it can behave like an agent (reflection pattern) | +| Workflow as Agent + HITL | [agents/workflow_as_agent_human_in_the_loop.py](./agents/workflow_as_agent_human_in_the_loop.py) | Extend workflow-as-agent with human-in-the-loop capability | ### checkpoint | Sample | File | Concepts | |---|---|---| | Checkpoint & Resume | [checkpoint/checkpoint_with_resume.py](./checkpoint/checkpoint_with_resume.py) | Create checkpoints, inspect them, and resume execution | -### conditional_edges +### composition | Sample | File | Concepts | |---|---|---| -| Edge Condition | [conditional_edges/edge_condition.py](./conditional_edges/edge_condition.py) | Conditional routing based on agent classification | -| Switch-Case Edge Group | [conditional_edges/switch_case_edge_group.py](./conditional_edges/switch_case_edge_group.py) | Switch-case branching using classifier outputs | -| Multi-Selection Edge Group | [conditional_edges/multi_selection_edge_group.py](./conditional_edges/multi_selection_edge_group.py) | Select one or many targets dynamically (subset fan-out) | +| Sub-Workflow (Basics) | [composition/sub_workflow_basics.py](./composition/sub_workflow_basics.py) | Wrap a workflow as an executor and orchestrate sub-workflows | +| Sub-Workflow: Request Interception | [composition/sub_workflow_request_interception.py](./composition/sub_workflow_request_interception.py) | Intercept and forward requests with decorators and request handling | +| Sub-Workflow: Parallel Requests | [composition/sub_workflow_parallel_requests.py](./composition/sub_workflow_parallel_requests.py) | Multi-type interception and external forwarding patterns | -### fan_out_fan_in +### control-flow | Sample | File | Concepts | |---|---|---| -| Concurrent (Fan-out/Fan-in) | [fan_out_fan_in/fan_out_fan_in_edges.py](./fan_out_fan_in/fan_out_fan_in_edges.py) | Dispatch to multiple executors and aggregate results | -| Map-Reduce with Visualization | [fan_out_fan_in/map_reduce_and_visualization.py](./fan_out_fan_in/map_reduce_and_visualization.py) | Fan-out/fan-in pattern with GraphViz/diagram export | +| Sequential Executors | [control-flow/sequential_executors.py](./control-flow/sequential_executors.py) | Sequential workflow with explicit executor setup | +| Sequential (Streaming) | [control-flow/sequential_streaming.py](./control-flow/sequential_streaming.py) | Stream events from a simple sequential run | +| Edge Condition | [control-flow/edge_condition.py](./control-flow/edge_condition.py) | Conditional routing based on agent classification | +| Switch-Case Edge Group | [control-flow/switch_case_edge_group.py](./control-flow/switch_case_edge_group.py) | Switch-case branching using classifier outputs | +| Multi-Selection Edge Group | [control-flow/multi_selection_edge_group.py](./control-flow/multi_selection_edge_group.py) | Select one or many targets dynamically (subset fan-out) | +| Simple Loop | [control-flow/simple_loop.py](./control-flow/simple_loop.py) | Feedback loop where an agent judges ABOVE/BELOW/MATCHED | -### human_in_the_loop +### human-in-the-loop | Sample | File | Concepts | |---|---|---| -| Human-In-The-Loop (Guessing Game) | [human_in_the_loop/guessing_game_with_human_input.py](./human_in_the_loop/guessing_game_with_human_input.py) | Interactive request/response prompts with a human | +| Human-In-The-Loop (Guessing Game) | [human-in-the-loop/guessing_game_with_human_input.py](./human-in-the-loop/guessing_game_with_human_input.py) | Interactive request/response prompts with a human | -### loop +### observability | Sample | File | Concepts | |---|---|---| -| Simple Loop | [loop/simple_loop.py](./loop/simple_loop.py) | Feedback loop where an agent judges ABOVE/BELOW/MATCHED | +| Tracing (Basics) | [observability/tracing_basics.py](./observability/tracing_basics.py) | Use basic tracing for workflow telemetry | ### orchestration | Sample | File | Concepts | @@ -77,36 +81,31 @@ Once comfortable with these, explore the rest of the samples. | Magentic Workflow (Multi-Agent) | [orchestration/magentic.py](./orchestration/magentic.py) | Orchestrate multiple agents with Magentic manager and streaming | | Magentic + Human Plan Review | [orchestration/magentic_human_plan_update.py](./orchestration/magentic_human_plan_update.py) | Human reviews/updates the plan before execution | -### sequential +### parallelism | Sample | File | Concepts | |---|---|---| -| Sequential Executors | [sequential/sequential_executors.py](./sequential/sequential_executors.py) | Sequential workflow with explicit executor setup | -| Sequential (Streaming) | [sequential/sequential_streaming.py](./sequential/sequential_streaming.py) | Stream events from a simple sequential run | +| Concurrent (Fan-out/Fan-in) | [parallelism/fan_out_fan_in_edges.py](./parallelism/fan_out_fan_in_edges.py) | Dispatch to multiple executors and aggregate results | +| Map-Reduce with Visualization | [parallelism/map_reduce_and_visualization.py](./parallelism/map_reduce_and_visualization.py) | Fan-out/fan-in pattern with diagram export | -### shared_states +### state-management | Sample | File | Concepts | |---|---|---| -| Shared States | [shared_states/shared_states_with_agents.py](./shared_states/shared_states_with_agents.py) | Store in shared state once and later reuse across agents | - -### sub_workflow -| Sample | File | Concepts | -|---|---|---| -| Sub-Workflow (Basics) | [sub_workflow/sub_workflow_basics.py](./sub_workflow/sub_workflow.py) | Wrap a workflow as an executor and orchestrate sub-workflows | -| Sub-Workflow: Request Interception | [sub_workflow/sub_workflow_request_interception.py](./sub_workflow/sub_workflow_request_interception.py) | Intercept/forward requests with decorators and request handling | -| Sub-Workflow: Parallel Requests | [sub_workflow/sub_workflow_parallel_requests.py](./sub_workflow/sub_workflow_parallel_requests.py) | Multi-type interception and external forwarding patterns | - -### tracing -| Sample | File | Concepts | -|---|---|---| -| Tracing (Basics) | [tracing/tracing_basics.py](./tracing/tracing_basics.py) | Use basic tracing for workflow telemetry | +| Shared States | [state-management/shared_states_with_agents.py](./state-management/shared_states_with_agents.py) | Store in shared state once and later reuse across agents | ### visualization | Sample | File | Concepts | |---|---|---| | Concurrent with Visualization | [visualization/concurrent_with_visualization.py](./visualization/concurrent_with_visualization.py) | Fan-out/fan-in workflow with diagram export | +### resources +- Sample text inputs used by certain workflows: + - [resources/long_text.txt](./resources/long_text.txt) + - [resources/email.txt](./resources/email.txt) + - [resources/spam.txt](./resources/spam.txt) + - [resources/ambiguous_email.txt](./resources/ambiguous_email.txt) + Notes -- Agent‑based samples use provider SDKs (Azure/OpenAI, etc.). Ensure credentials are configured, or adapt agents accordingly. +- Agent-based samples use provider SDKs (Azure/OpenAI, etc.). Ensure credentials are configured, or adapt agents accordingly. ### Environment Variables diff --git a/python/samples/getting_started/workflow/foundational/step1_executors_and_edges.py b/python/samples/getting_started/workflow/_start-here/step1_executors_and_edges.py similarity index 100% rename from python/samples/getting_started/workflow/foundational/step1_executors_and_edges.py rename to python/samples/getting_started/workflow/_start-here/step1_executors_and_edges.py diff --git a/python/samples/getting_started/workflow/foundational/step2_agents_in_a_workflow.py b/python/samples/getting_started/workflow/_start-here/step2_agents_in_a_workflow.py similarity index 100% rename from python/samples/getting_started/workflow/foundational/step2_agents_in_a_workflow.py rename to python/samples/getting_started/workflow/_start-here/step2_agents_in_a_workflow.py diff --git a/python/samples/getting_started/workflow/foundational/step3_streaming.py b/python/samples/getting_started/workflow/_start-here/step3_streaming.py similarity index 100% rename from python/samples/getting_started/workflow/foundational/step3_streaming.py rename to python/samples/getting_started/workflow/_start-here/step3_streaming.py diff --git a/python/samples/getting_started/workflow/sub_workflow/sub_workflow_basics.py b/python/samples/getting_started/workflow/composition/sub_workflow_basics.py similarity index 100% rename from python/samples/getting_started/workflow/sub_workflow/sub_workflow_basics.py rename to python/samples/getting_started/workflow/composition/sub_workflow_basics.py diff --git a/python/samples/getting_started/workflow/sub_workflow/sub_workflow_parallel_requests.py b/python/samples/getting_started/workflow/composition/sub_workflow_parallel_requests.py similarity index 100% rename from python/samples/getting_started/workflow/sub_workflow/sub_workflow_parallel_requests.py rename to python/samples/getting_started/workflow/composition/sub_workflow_parallel_requests.py diff --git a/python/samples/getting_started/workflow/sub_workflow/sub_workflow_request_interception.py b/python/samples/getting_started/workflow/composition/sub_workflow_request_interception.py similarity index 100% rename from python/samples/getting_started/workflow/sub_workflow/sub_workflow_request_interception.py rename to python/samples/getting_started/workflow/composition/sub_workflow_request_interception.py diff --git a/python/samples/getting_started/workflow/conditional_edges/edge_condition.py b/python/samples/getting_started/workflow/control-flow/edge_condition.py similarity index 100% rename from python/samples/getting_started/workflow/conditional_edges/edge_condition.py rename to python/samples/getting_started/workflow/control-flow/edge_condition.py diff --git a/python/samples/getting_started/workflow/conditional_edges/multi_selection_edge_group.py b/python/samples/getting_started/workflow/control-flow/multi_selection_edge_group.py similarity index 97% rename from python/samples/getting_started/workflow/conditional_edges/multi_selection_edge_group.py rename to python/samples/getting_started/workflow/control-flow/multi_selection_edge_group.py index afed90325b..d14a1ab539 100644 --- a/python/samples/getting_started/workflow/conditional_edges/multi_selection_edge_group.py +++ b/python/samples/getting_started/workflow/control-flow/multi_selection_edge_group.py @@ -250,11 +250,16 @@ async def main() -> None: ) # Read an email sample - resources_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources", "email.txt") + resources_path = os.path.join( + os.path.dirname(os.path.dirname(os.path.realpath(__file__))), + "resources", + "email.txt", + ) if os.path.exists(resources_path): with open(resources_path, encoding="utf-8") as f: # noqa: ASYNC230 email = f.read() else: + print("Unable to find resource file, using default text.") email = "Hello team, here are the updates for this week..." async for event in workflow.run_stream(email): diff --git a/python/samples/getting_started/workflow/sequential/sequential_executors.py b/python/samples/getting_started/workflow/control-flow/sequential_executors.py similarity index 100% rename from python/samples/getting_started/workflow/sequential/sequential_executors.py rename to python/samples/getting_started/workflow/control-flow/sequential_executors.py diff --git a/python/samples/getting_started/workflow/sequential/sequential_streaming.py b/python/samples/getting_started/workflow/control-flow/sequential_streaming.py similarity index 100% rename from python/samples/getting_started/workflow/sequential/sequential_streaming.py rename to python/samples/getting_started/workflow/control-flow/sequential_streaming.py diff --git a/python/samples/getting_started/workflow/loop/simple_loop.py b/python/samples/getting_started/workflow/control-flow/simple_loop.py similarity index 100% rename from python/samples/getting_started/workflow/loop/simple_loop.py rename to python/samples/getting_started/workflow/control-flow/simple_loop.py diff --git a/python/samples/getting_started/workflow/conditional_edges/switch_case_edge_group.py b/python/samples/getting_started/workflow/control-flow/switch_case_edge_group.py similarity index 97% rename from python/samples/getting_started/workflow/conditional_edges/switch_case_edge_group.py rename to python/samples/getting_started/workflow/control-flow/switch_case_edge_group.py index ba6eed5fc2..b23ffafb08 100644 --- a/python/samples/getting_started/workflow/conditional_edges/switch_case_edge_group.py +++ b/python/samples/getting_started/workflow/control-flow/switch_case_edge_group.py @@ -201,11 +201,14 @@ async def main(): ) # Read ambiguous email if available. Otherwise use a simple inline sample. - resources_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources", "ambiguous_email.txt") + resources_path = os.path.join( + os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "resources", "ambiguous_email.txt" + ) if os.path.exists(resources_path): with open(resources_path, encoding="utf-8") as f: # noqa: ASYNC230 email = f.read() else: + print("Unable to find resource file, using default text.") email = ( "Hey there, I noticed you might be interested in our latest offer—no pressure, but it expires soon. " "Let me know if you'd like more details." diff --git a/python/samples/getting_started/workflow/human_in_the_loop/guessing_game_with_human_input.py b/python/samples/getting_started/workflow/human-in-the-loop/guessing_game_with_human_input.py similarity index 100% rename from python/samples/getting_started/workflow/human_in_the_loop/guessing_game_with_human_input.py rename to python/samples/getting_started/workflow/human-in-the-loop/guessing_game_with_human_input.py diff --git a/python/samples/getting_started/workflow/tracing/tracing_basics.py b/python/samples/getting_started/workflow/observability/tracing_basics.py similarity index 100% rename from python/samples/getting_started/workflow/tracing/tracing_basics.py rename to python/samples/getting_started/workflow/observability/tracing_basics.py diff --git a/python/samples/getting_started/workflow/fan_out_fan_in/fan_out_fan_in_edges.py b/python/samples/getting_started/workflow/parallelism/fan_out_fan_in_edges.py similarity index 100% rename from python/samples/getting_started/workflow/fan_out_fan_in/fan_out_fan_in_edges.py rename to python/samples/getting_started/workflow/parallelism/fan_out_fan_in_edges.py diff --git a/python/samples/getting_started/workflow/fan_out_fan_in/map_reduce_and_visualization.py b/python/samples/getting_started/workflow/parallelism/map_reduce_and_visualization.py similarity index 100% rename from python/samples/getting_started/workflow/fan_out_fan_in/map_reduce_and_visualization.py rename to python/samples/getting_started/workflow/parallelism/map_reduce_and_visualization.py diff --git a/python/samples/getting_started/workflow/shared_states/shared_states_with_agents.py b/python/samples/getting_started/workflow/state-management/shared_states_with_agents.py similarity index 89% rename from python/samples/getting_started/workflow/shared_states/shared_states_with_agents.py rename to python/samples/getting_started/workflow/state-management/shared_states_with_agents.py index 82777dd63e..45c71b516c 100644 --- a/python/samples/getting_started/workflow/shared_states/shared_states_with_agents.py +++ b/python/samples/getting_started/workflow/state-management/shared_states_with_agents.py @@ -9,7 +9,6 @@ from uuid import uuid4 from agent_framework import ChatMessage, Role from agent_framework.azure import AzureChatClient from agent_framework.workflow import ( - AgentExecutor, AgentExecutorRequest, AgentExecutorResponse, WorkflowBuilder, @@ -158,26 +157,22 @@ async def main() -> None: # Create chat client and agents. response_format enforces structured JSON from each agent. chat_client = AzureChatClient(credential=AzureCliCredential()) - spam_detection_agent = AgentExecutor( - chat_client.create_agent( - instructions=( - "You are a spam detection assistant that identifies spam emails. " - "Always return JSON with fields is_spam (bool) and reason (string)." - ), - response_format=DetectionResultAgent, + spam_detection_agent = chat_client.create_agent( + instructions=( + "You are a spam detection assistant that identifies spam emails. " + "Always return JSON with fields is_spam (bool) and reason (string)." ), - id="spam_detection_agent", + response_format=DetectionResultAgent, + name="spam_detection_agent", ) - email_assistant_agent = AgentExecutor( - chat_client.create_agent( - instructions=( - "You are an email assistant that helps users draft responses to emails with professionalism. " - "Return JSON with a single field 'response' containing the drafted reply." - ), - response_format=EmailResponse, + email_assistant_agent = chat_client.create_agent( + instructions=( + "You are an email assistant that helps users draft responses to emails with professionalism. " + "Return JSON with a single field 'response' containing the drafted reply." ), - id="email_assistant_agent", + response_format=EmailResponse, + name="email_assistant_agent", ) # Build the workflow graph with conditional edges. @@ -198,17 +193,22 @@ async def main() -> None: ) # Read an email from resources/spam.txt if available; otherwise use a default sample. - resources_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources", "spam.txt") + resources_path = os.path.join( + os.path.dirname(os.path.dirname(os.path.realpath(__file__))), + "resources", + "spam.txt", + ) if os.path.exists(resources_path): with open(resources_path, encoding="utf-8") as f: # noqa: ASYNC230 email = f.read() else: + print("Unable to find resource file, using default text.") email = "You are a WINNER! Click here for a free lottery offer!!!" # Run and print the terminal result. Streaming surfaces intermediate execution events as well. async for event in workflow.run_stream(email): if isinstance(event, WorkflowCompletedEvent): - print(f"{event}") + print(event) """ Sample Output: From 55f0a1b51e27317755bab137f40576595d978ada Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 01:39:23 -0700 Subject: [PATCH 09/32] Bump actions/labeler from 5 to 6 (#630) Bumps [actions/labeler](https://github.com/actions/labeler) from 5 to 6. - [Release notes](https://github.com/actions/labeler/releases) - [Commits](https://github.com/actions/labeler/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/labeler dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/label-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/label-pr.yml b/.github/workflows/label-pr.yml index 11d0f083a8..4aea432e31 100644 --- a/.github/workflows/label-pr.yml +++ b/.github/workflows/label-pr.yml @@ -16,6 +16,6 @@ jobs: pull-requests: write steps: - - uses: actions/labeler@v5 + - uses: actions/labeler@v6 with: repo-token: "${{ secrets.GH_ACTIONS_PR_WRITE }}" From 8c5cba5826292299f7b874a427a1915a4767f1d5 Mon Sep 17 00:00:00 2001 From: Lutz Roeder Date: Tue, 9 Sep 2025 01:40:00 -0700 Subject: [PATCH 10/32] Fix .NET casing (#616) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5a59ebdeab..3f80b85e41 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Below are the basics for each language implementation. For more details on pytho - [Azure Integration](./python/packages/azure): Azure OpenAI and AI Foundry integration - [Getting Started with Workflows](./python/samples/getting_started/workflow): basic workflow creation and integration with agents -### .Net +### .NET - [Getting Started with Agents](./dotnet/samples/GettingStarted/Agents): basic agent creation and tool usage - [Agent Provider Samples](./dotnet/samples/GettingStarted/AgentProviders): samples showing different agent providers - [Orchestration Samples](./dotnet/samples/GettingStarted/Orchestration): advanced multi-agent patterns From b2a6b64d722ac0551dd51eca95847f94194d14f8 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Tue, 9 Sep 2025 12:04:31 +0100 Subject: [PATCH 11/32] .NET: Add Ollama and custom agent samples, plus ONNX improvements (#639) * Add ollama and custom agent samples, plus onnx improvements * Update dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Address PR comments. * Address pr comments. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dotnet/Directory.Packages.props | 1 + dotnet/agent-framework-dotnet.slnx | 3 + .../Agent_With_CustomImplementation.csproj | 16 +++ .../Program.cs | 102 ++++++++++++++++++ .../Agent_With_CustomImplementation/README.md | 3 + .../AgentProviders/Agent_With_ONNX/Program.cs | 1 + .../AgentProviders/Agent_With_ONNX/README.md | 8 ++ .../Agent_With_Ollama.csproj | 20 ++++ .../Agent_With_Ollama/Program.cs | 20 ++++ .../Agent_With_Ollama/README.md | 34 ++++++ .../GettingStarted/AgentProviders/README.md | 3 + dotnet/samples/GettingStarted/README.md | 4 +- 12 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Agent_With_CustomImplementation.csproj create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/README.md create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/Agent_With_Ollama.csproj create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/Program.cs create mode 100644 dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/README.md diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index 85769f892d..a4a699b780 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -79,6 +79,7 @@ + diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index e764f4c936..f46ab37c4f 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -25,6 +25,8 @@ + + @@ -43,6 +45,7 @@ + diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Agent_With_CustomImplementation.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Agent_With_CustomImplementation.csproj new file mode 100644 index 0000000000..4fa7edf3fc --- /dev/null +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Agent_With_CustomImplementation.csproj @@ -0,0 +1,16 @@ + + + + Exe + net9.0 + 12 + + enable + disable + + + + + + + diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs new file mode 100644 index 0000000000..df315dbfaa --- /dev/null +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows all the required steps to create a fully custom agent implementation. +// In this case the agent doesn't use AI at all, and simply parrots back the user input in upper case. +// You can however, build a fully custom agent that uses AI in any way you want. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.AI.Agents; +using SampleApp; + +AIAgent agent = new UpperCaseParrotAgent(); + +// Invoke the agent and output the text result. +Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.")); + +// Invoke the agent with streaming support. +await foreach (var update in agent.RunStreamingAsync("Tell me a joke about a pirate.")) +{ + Console.WriteLine(update); +} + +namespace SampleApp +{ + // Custom agent that parrot's the user input back in upper case. + internal sealed class UpperCaseParrotAgent : AIAgent + { + public override async Task RunAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + { + // Create a thread if the user didn't supply one. + thread ??= this.GetNewThread(); + + // Clone the input messages and turn them into response messages with upper case text. + List responseMessages = CloneAndToUpperCase(messages, this.DisplayName).ToList(); + + // Notify the thread of the input and output messages. + await NotifyThreadOfNewMessagesAsync(thread, messages, cancellationToken); + await NotifyThreadOfNewMessagesAsync(thread, responseMessages, cancellationToken); + + return new AgentRunResponse + { + AgentId = this.Id, + ResponseId = Guid.NewGuid().ToString(), + Messages = responseMessages + }; + } + + public override async IAsyncEnumerable RunStreamingAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + // Create a thread if the user didn't supply one. + thread ??= this.GetNewThread(); + + // Clone the input messages and turn them into response messages with upper case text. + List responseMessages = CloneAndToUpperCase(messages, this.DisplayName).ToList(); + + // Notify the thread of the input and output messages. + await NotifyThreadOfNewMessagesAsync(thread, messages, cancellationToken); + await NotifyThreadOfNewMessagesAsync(thread, responseMessages, cancellationToken); + + foreach (var message in responseMessages) + { + yield return new AgentRunResponseUpdate + { + AgentId = this.Id, + AuthorName = this.DisplayName, + Role = ChatRole.Assistant, + Contents = message.Contents, + ResponseId = Guid.NewGuid().ToString(), + MessageId = Guid.NewGuid().ToString() + }; + } + } + + private static IEnumerable CloneAndToUpperCase(IReadOnlyCollection messages, string agentName) => messages.Select(x => + { + // Clone the message and update its author to be the agent. + var messageClone = x.Clone(); + messageClone.Role = ChatRole.Assistant; + messageClone.MessageId = Guid.NewGuid().ToString(); + messageClone.AuthorName = agentName; + + // Clone and convert any text content to upper case. + messageClone.Contents = x.Contents.Select(c => c switch + { + TextContent tc => new TextContent(tc.Text.ToUpperInvariant()) + { + AdditionalProperties = tc.AdditionalProperties, + Annotations = tc.Annotations, + RawRepresentation = tc.RawRepresentation + }, + _ => c + }).ToList(); + + return messageClone; + }); + } +} diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/README.md b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/README.md new file mode 100644 index 0000000000..97eb3e87d3 --- /dev/null +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/README.md @@ -0,0 +1,3 @@ +# Prerequisites + +This sample has no prerequisites. \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_ONNX/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_ONNX/Program.cs index 94994dd92d..8a2579b1ff 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_ONNX/Program.cs +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_ONNX/Program.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. // This sample shows how to create and use a simple AI agent with ONNX as the backend. +// WARNING: ONNX doesn't support function calling, so any function tools passed to the agent will be ignored. using System; using Microsoft.Extensions.AI.Agents; diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_ONNX/README.md b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_ONNX/README.md index 7bfacf30e9..cb86e0d7c4 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_ONNX/README.md +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_ONNX/README.md @@ -1,10 +1,18 @@ # Prerequisites +WARNING: ONNX doesn't support function calling, so any function tools passed to the agent will be ignored. + Before you begin, ensure you have the following prerequisites: - .NET 8.0 SDK or later - An ONNX model downloaded to your machine +You can download an ONNX model from hugging face, using git clone: + +```powershell +git clone https://huggingface.co/microsoft/Phi-4-mini-instruct-onnx +``` + Set the following environment variables: ```powershell diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/Agent_With_Ollama.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/Agent_With_Ollama.csproj new file mode 100644 index 0000000000..44603cc24c --- /dev/null +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/Agent_With_Ollama.csproj @@ -0,0 +1,20 @@ + + + + Exe + net9.0 + 12 + + enable + disable + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/Program.cs new file mode 100644 index 0000000000..9a8b028f5f --- /dev/null +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/Program.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to create and use a simple AI agent with Ollama as the backend. + +using System; +using Microsoft.Extensions.AI.Agents; +using OllamaSharp; + +var endpoint = Environment.GetEnvironmentVariable("OLLAMA_ENDPOINT") ?? throw new InvalidOperationException("OLLAMA_ENDPOINT is not set."); +var modelName = Environment.GetEnvironmentVariable("OLLAMA_MODEL_NAME") ?? throw new InvalidOperationException("OLLAMA_MODEL_NAME is not set."); + +const string JokerName = "Joker"; +const string JokerInstructions = "You are good at telling jokes."; + +// Get a chat client for Ollama and use it to construct an AIAgent. +using OllamaApiClient chatClient = new(new Uri(endpoint), modelName); +AIAgent agent = new ChatClientAgent(chatClient, JokerInstructions, JokerName); + +// Invoke the agent and output the text result. +Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.")); diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/README.md b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/README.md new file mode 100644 index 0000000000..be76a75de0 --- /dev/null +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Ollama/README.md @@ -0,0 +1,34 @@ +# Prerequisites + +Before you begin, ensure you have the following prerequisites: + +- .NET 8.0 SDK or later +- Docker installed and running on your machine +- An Ollama model downloaded into Ollama + +To download and start Ollama on Docker using CPU, run the following command in your terminal. + +```powershell +docker run -d -v "c:\temp\ollama:/root/.ollama" -p 11434:11434 --name ollama ollama/ollama +``` + +To download and start Ollama on Docker using GPU, run the following command in your terminal. + +```powershell +docker run -d --gpus=all -v "c:\temp\ollama:/root/.ollama" -p 11434:11434 --name ollama ollama/ollama +``` + +After the container has started, launch a Terminal window for the docker container, e.g. if using docker desktop, choose Open in Terminal from actions. + +From this terminal download the required models, e.g. here we are downloading the phi3 model. + +```text +ollama pull gpt-oss +``` + +Set the following environment variables: + +```powershell +$env:OLLAMA_ENDPOINT="http://localhost:11434" +$env:OLLAMA_MODEL_NAME="gpt-oss" +``` diff --git a/dotnet/samples/GettingStarted/AgentProviders/README.md b/dotnet/samples/GettingStarted/AgentProviders/README.md index 981e60d9b8..0b5fbf1a74 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/README.md +++ b/dotnet/samples/GettingStarted/AgentProviders/README.md @@ -9,6 +9,7 @@ see the [Getting Started Steps](../GettingStartedSteps/README.md) samples. ## Prerequisites See the README.md for each sample for the prerequisites for that sample. + ## Samples |Sample|Description| @@ -17,6 +18,8 @@ See the README.md for each sample for the prerequisites for that sample. |[Creating an AIAgent with AzureFoundry](./Agent_With_AzureFoundry/)|This sample demonstrates how to create an Azure Foundry agent and expose it as an AIAgent| |[Creating an AIAgent with Azure OpenAI ChatCompletion](./Agent_With_AzureOpenAIChatCompletion/)|This sample demonstrates how to create an AIAgent using Azure OpenAI ChatCompletion as the underlying inference service| |[Creating an AIAgent with Azure OpenAI Responses](./Agent_With_AzureOpenAIResponses/)|This sample demonstrates how to create an AIAgent using Azure OpenAI Responses as the underlying inference service| +|[Creating an AIAgent with a custom implementation](./Agent_With_CustomImplementation/)|This sample demonstrates how to create an AIAgent with a custom implementation| +|[Creating an AIAgent with Ollama](./Agent_With_Ollama/)|This sample demonstrates how to create an AIAgent using Ollama as the underlying inference service| |[Creating an AIAgent with ONNX](./Agent_With_ONNX/)|This sample demonstrates how to create an AIAgent using ONNX as the underlying inference service| |[Creating an AIAgent with OpenAI ChatCompletion](./Agent_With_OpenAIChatCompletion/)|This sample demonstrates how to create an AIAgent using OpenAI ChatCompletion as the underlying inference service| |[Creating an AIAgent with OpenAI Responses](./Agent_With_OpenAIResponses/)|This sample demonstrates how to create an AIAgent using OpenAI Responses as the underlying inference service| diff --git a/dotnet/samples/GettingStarted/README.md b/dotnet/samples/GettingStarted/README.md index 55cc7f7a97..e95b96c923 100644 --- a/dotnet/samples/GettingStarted/README.md +++ b/dotnet/samples/GettingStarted/README.md @@ -7,6 +7,8 @@ of the agent framework. |Sample|Description| |---|---| -|[Agents](./Agents/README.md)|Getting started with agents| +|[Agents](./Agents/README.md)|Step by step instructions for getting started with agents| |[Agent Providers](./AgentProviders/README.md)|Getting started with creating agents using various providers| |[Agent Open Telemetry](./AgentOpenTelemetry/README.md)|Getting started with OpenTelemetry for agents| +|[Agent With OpenAI exchange types](./AgentWithOpenAI/README.md)|Using OpenAI exchange types with agents| +|[Workflow](./Workflow/README.md)|Getting started with Workflow| From 5c3c2fe63460d3b7d8973341ed0eeaa8c89e3d1f Mon Sep 17 00:00:00 2001 From: Chris Gillum Date: Tue, 9 Sep 2025 08:23:00 -0700 Subject: [PATCH 12/32] .NET: Multiple fixes in Microsoft.Agents.Orchestration to make handoffs work end-to-end (#568) * Multiple fixes in Microsoft.Agents.Orchestration: - Enable multi-turn (via RunAsync) interactions for all orchestrating agents - Fix tool calling in HandoffOrchestration (multiple issues) - Fixes for chat history serialization involving tool calls * GHCP PR feedback * Fix issue with returning old responses --------- Co-authored-by: Chris <66376200+crickman@users.noreply.github.com> --- .../ConcurrentOrchestration.cs | 7 ++-- .../GroupChat/GroupChatOrchestration.cs | 8 +++-- .../Handoffs/HandoffOrchestration.cs | 33 ++++++++++--------- .../OrchestratingAgent.cs | 5 +-- .../OrchestrationJsonContext.cs | 2 ++ .../SequentialOrchestration.cs | 7 ++-- .../OrchestrationResultTests.cs | 2 +- 7 files changed, 39 insertions(+), 25 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs index 670731efe8..3953b7fd0f 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs @@ -61,10 +61,13 @@ public partial class ConcurrentOrchestration : OrchestratingAgent this.ResumeAsync(messages, new AgentRunResponse?[this.Agents.Count], context, cancellationToken); /// - protected override Task ResumeCoreAsync(JsonElement checkpointState, OrchestratingAgentContext context, CancellationToken cancellationToken) + protected override Task ResumeCoreAsync(JsonElement checkpointState, IReadOnlyCollection newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken) { var state = checkpointState.Deserialize(OrchestrationJsonContext.Default.ConcurrentState) ?? throw new InvalidOperationException("The checkpoint state is invalid."); - return this.ResumeAsync(state.Messages, state.Completed, context, cancellationToken); + + // Append the new messages to the checkpoint state + List allMessages = [.. state.Messages, .. newMessages]; + return this.ResumeAsync(allMessages, state.Completed, context, cancellationToken); } /// diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs index 46f258715d..0f0973e768 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs @@ -51,10 +51,14 @@ public sealed partial class GroupChatOrchestration : OrchestratingAgent } /// - protected override Task ResumeCoreAsync(JsonElement checkpointState, OrchestratingAgentContext context, CancellationToken cancellationToken) + protected override Task ResumeCoreAsync(JsonElement checkpointState, IReadOnlyCollection newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken) { var state = checkpointState.Deserialize(OrchestrationJsonContext.Default.GroupChatState) ?? throw new InvalidOperationException("The checkpoint state is invalid."); - return this.ResumeAsync(state.AllMessages, state.OriginalMessageCount, context, cancellationToken); + + // Append the new messages to the checkpoint state + List allMessages = [.. state.AllMessages, .. newMessages]; + + return this.ResumeAsync(allMessages, allMessages.Count, context, cancellationToken); } private async Task ResumeAsync( diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs index d1c9c5568e..321e71b4a2 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs @@ -52,26 +52,28 @@ public sealed partial class HandoffOrchestration : OrchestratingAgent } /// - protected override Task ResumeCoreAsync(JsonElement checkpointState, OrchestratingAgentContext context, CancellationToken cancellationToken) + protected override Task ResumeCoreAsync(JsonElement checkpointState, IReadOnlyCollection newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken) { var state = checkpointState.Deserialize(OrchestrationJsonContext.Default.HandoffState) ?? throw new InvalidOperationException("The checkpoint state is invalid."); AIAgent? nextAgent = null; - foreach (var agent in this.Agents) + if (state.NextAgent is null) { - if (agent.Id == state.NextAgent) + nextAgent = this._handoffs.InitialAgent; + } + else + { + nextAgent = this.Agents.FirstOrDefault(a => a.Id == state.NextAgent); + if (nextAgent is null) { - nextAgent = agent; - break; + Throw.InvalidOperationException($"The next agent '{state.NextAgent}' is not defined in the orchestration."); } } - if (nextAgent is null) - { - Throw.InvalidOperationException($"The next agent '{state.NextAgent}' is not defined in the orchestration."); - } + // Append the new messages to the checkpoint state + List allMessages = [.. state.AllMessages, .. newMessages]; - return this.ResumeAsync(nextAgent, state.AllMessages, state.OriginalMessageCount, context, cancellationToken); + return this.ResumeAsync(nextAgent, allMessages, allMessages.Count, context, cancellationToken); } /// @@ -144,7 +146,7 @@ public sealed partial class HandoffOrchestration : OrchestratingAgent private static void RemoveHandoffFunctionCalls(AgentRunResponse response, List handoffTools) { HashSet? removeToolNames = null; - HashSet? callIds = null; + HashSet? handoffCallIds = null; foreach (var message in response.Messages) { @@ -153,23 +155,22 @@ public sealed partial class HandoffOrchestration : OrchestratingAgent if (message.Contents[i] is FunctionCallContent fcc) { removeToolNames ??= [.. handoffTools.Select(t => t.Name)]; - (callIds ??= new()).Add(fcc.CallId); - if (removeToolNames.Contains(fcc.Name)) { + (handoffCallIds ??= []).Add(fcc.CallId); message.Contents.RemoveAt(i); } } } } - if (callIds is not null) + if (handoffCallIds is not null) { foreach (var message in response.Messages) { for (int i = message.Contents.Count - 1; i >= 0; i--) { - if (message.Contents[i] is FunctionResultContent frc && callIds.Contains(frc.CallId)) + if (message.Contents[i] is FunctionResultContent frc && handoffCallIds.Contains(frc.CallId)) { message.Contents.RemoveAt(i); } @@ -215,7 +216,7 @@ public sealed partial class HandoffOrchestration : OrchestratingAgent static void Terminate() { - if (FunctionInvokingChatClient.CurrentContext is not { } ctx) + if (NewFunctionInvokingChatClient.CurrentContext is not { } ctx) { throw new NotSupportedException($"The agent is not configured with a {nameof(FunctionInvokingChatClient)}. Cease execution."); } diff --git a/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs b/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs index 733003d8c0..dfbe7bddfe 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs @@ -135,7 +135,7 @@ public abstract partial class OrchestratingAgent : AIAgent JsonElement? checkpoint = await this.ReadCheckpointAsync(context, cancellationToken).ConfigureAwait(false); Task completion = checkpoint is null ? this.RunCoreAsync(messages, context, cancellationToken) : - this.ResumeCoreAsync(checkpoint.Value, context, cancellationToken); + this.ResumeCoreAsync(checkpoint.Value, messages, context, cancellationToken); if (logger.IsEnabled(LogLevel.Trace)) { @@ -157,9 +157,10 @@ public abstract partial class OrchestratingAgent : AIAgent /// Resumes processing of the orchestration. /// /// The last checkpoint state available from which to resume the operation. + /// The new messages to be processed in addition to the checkpoint state. /// The context for this operation. /// A cancellation token that can be used to cancel the operation. - protected abstract Task ResumeCoreAsync(JsonElement checkpointState, OrchestratingAgentContext context, CancellationToken cancellationToken); + protected abstract Task ResumeCoreAsync(JsonElement checkpointState, IReadOnlyCollection newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken); /// /// Runs the agent with input messages and respond with both streamed and regular messages. diff --git a/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationJsonContext.cs b/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationJsonContext.cs index 1ef15b385a..5066f3e6dc 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationJsonContext.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationJsonContext.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System.Text.Json; using System.Text.Json.Serialization; namespace Microsoft.Agents.Orchestration; @@ -8,4 +9,5 @@ namespace Microsoft.Agents.Orchestration; [JsonSerializable(typeof(ConcurrentOrchestration.ConcurrentState))] [JsonSerializable(typeof(GroupChatOrchestration.GroupChatState))] [JsonSerializable(typeof(HandoffOrchestration.HandoffState))] +[JsonSerializable(typeof(JsonElement))] internal sealed partial class OrchestrationJsonContext : JsonSerializerContext; diff --git a/dotnet/src/Microsoft.Agents.Orchestration/SequentialOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/SequentialOrchestration.cs index 6f79bb4129..1c113cfbde 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/SequentialOrchestration.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/SequentialOrchestration.cs @@ -32,10 +32,13 @@ public sealed partial class SequentialOrchestration : OrchestratingAgent this.ResumeAsync(0, messages, context, cancellationToken); /// - protected override Task ResumeCoreAsync(JsonElement checkpointState, OrchestratingAgentContext context, CancellationToken cancellationToken) + protected override Task ResumeCoreAsync(JsonElement checkpointState, IReadOnlyCollection newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken) { var state = checkpointState.Deserialize(OrchestrationJsonContext.Default.SequentialState) ?? throw new InvalidOperationException("The checkpoint state is invalid."); - return this.ResumeAsync(state.Index, state.Messages, context, cancellationToken); + + // Append the new messages to the checkpoint state + List allMessages = [.. state.Messages, .. newMessages]; + return this.ResumeAsync(state.Index, allMessages, context, cancellationToken); } /// diff --git a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/OrchestrationResultTests.cs b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/OrchestrationResultTests.cs index bbc79b661c..9d9f238cca 100644 --- a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/OrchestrationResultTests.cs +++ b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/OrchestrationResultTests.cs @@ -83,7 +83,7 @@ public class OrchestrationResultTests protected override Task RunCoreAsync(IReadOnlyCollection messages, OrchestratingAgentContext context, CancellationToken cancellationToken) => throw new NotSupportedException(); - protected override Task ResumeCoreAsync(JsonElement checkpointState, OrchestratingAgentContext context, CancellationToken cancellationToken) => + protected override Task ResumeCoreAsync(JsonElement checkpointState, IReadOnlyCollection newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken) => throw new NotSupportedException(); } From ec5ea3c8a89255f761900133545bd9da88fdcb43 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Tue, 9 Sep 2025 17:40:07 +0100 Subject: [PATCH 13/32] .NET: Remove AgentThread.GetMessagesAsync (#668) * Remove AgentThread.GetMessagesAsync * Remove unecessary using --- .../OrchestratingAgent.cs | 7 +-- .../AgentThread.cs | 18 ------- .../IChatMessageStore.cs | 4 +- .../ChatCompletion/ChatClientAgent.cs | 4 +- .../AgentThreadTests.cs | 50 ------------------- .../OpenAIChatCompletionFixture.cs | 2 +- .../OpenAIResponseFixture.cs | 2 +- 7 files changed, 8 insertions(+), 79 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs b/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs index dfbe7bddfe..8da88c20bc 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; using System.Text.Json; using System.Threading; @@ -75,11 +76,7 @@ public abstract partial class OrchestratingAgent : AIAgent throw new InvalidOperationException("An agent service managed thread is not supported by this agent."); } - List messagesList = []; - await foreach (var threadMessage in thread.GetMessagesAsync(cancellationToken).ConfigureAwait(false)) - { - messagesList.Add(threadMessage); - } + List messagesList = (await thread.MessageStore.GetMessagesAsync(cancellationToken).ConfigureAwait(false)).ToList(); messagesList.AddRange(messages); messages = messagesList; } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs index 63b8f07f69..afc92bc60d 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Runtime.CompilerServices; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -109,23 +108,6 @@ public class AgentThread } } - /// - /// Retrieves any messages stored in the of the thread, otherwise returns an empty collection. - /// - /// The to monitor for cancellation requests. The default is . - /// The messages from the in ascending chronological order, with the oldest message first. - public virtual async IAsyncEnumerable GetMessagesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) - { - if (this._messageStore is not null) - { - var messages = await this._messageStore!.GetMessagesAsync(cancellationToken).ConfigureAwait(false); - foreach (var message in messages) - { - yield return message; - } - } - } - /// /// Serializes the current object's state to a using the specified serialization options. /// diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/IChatMessageStore.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/IChatMessageStore.cs index d1713e9336..ad6303f36e 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/IChatMessageStore.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/IChatMessageStore.cs @@ -34,7 +34,7 @@ public interface IChatMessageStore /// since they may contain state that is specific to a thread. /// /// - Task> GetMessagesAsync(CancellationToken cancellationToken); + Task> GetMessagesAsync(CancellationToken cancellationToken = default); /// /// Adds messages to the store. @@ -42,7 +42,7 @@ public interface IChatMessageStore /// The messages to add. /// The to monitor for cancellation requests. The default is . /// An async task. - Task AddMessagesAsync(IReadOnlyCollection messages, CancellationToken cancellationToken); + Task AddMessagesAsync(IReadOnlyCollection messages, CancellationToken cancellationToken = default); /// /// Deserializes the state contained in the provided into the properties on this store. diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs index 76bb906a8d..f100fee198 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs @@ -344,9 +344,9 @@ public sealed class ChatClientAgent : AIAgent // Add any existing messages from the thread to the messages to be sent to the chat client. List threadMessages = []; - await foreach (ChatMessage message in thread.GetMessagesAsync(cancellationToken).ConfigureAwait(false)) + if (thread.MessageStore is not null) { - threadMessages.Add(message); + threadMessages.AddRange(await thread.MessageStore.GetMessagesAsync(cancellationToken).ConfigureAwait(false)); } // Add the input messages to the end of thread messages. diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentThreadTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentThreadTests.cs index 157fb30fdc..305fc1cd45 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentThreadTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentThreadTests.cs @@ -88,56 +88,6 @@ public class AgentThreadTests #endregion Constructor and Property Tests - #region GetMessagesAsync Tests - - [Fact] - public async Task GetMessagesAsyncReturnsEmptyListWhenNoStoreAsync() - { - // Arrange - var thread = new AgentThread(); - - // Act - var messages = await ToListAsync(thread.GetMessagesAsync(CancellationToken.None)); - - // Assert - Assert.Empty(messages); - } - - [Fact] - public async Task GetMessagesAsyncReturnsEmptyListWhenAgentServiceIdAsync() - { - // Arrange - var thread = new AgentThread { ConversationId = "thread-123" }; - - // Act - var messages = await ToListAsync(thread.GetMessagesAsync(CancellationToken.None)); - - // Assert - Assert.Empty(messages); - } - - [Fact] - public async Task GetMessagesAsyncReturnsMessagesFromStoreAsync() - { - // Arrange - var store = new InMemoryChatMessageStore - { - new ChatMessage(ChatRole.User, "Hello"), - new ChatMessage(ChatRole.Assistant, "Hi there!") - }; - var thread = new AgentThread { MessageStore = store }; - - // Act - var messages = await ToListAsync(thread.GetMessagesAsync(CancellationToken.None)); - - // Assert - Assert.Equal(2, messages.Count); - Assert.Equal("Hello", messages[0].Text); - Assert.Equal("Hi there!", messages[1].Text); - } - - #endregion GetMessagesAsync Tests - #region OnNewMessagesAsync Tests [Fact] diff --git a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs index da5324e60e..feaacca6f8 100644 --- a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs +++ b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs @@ -32,7 +32,7 @@ public class OpenAIChatCompletionFixture : IChatClientAgentFixture public async Task> GetChatHistoryAsync(AgentThread thread) { - return await thread.GetMessagesAsync().ToListAsync(); + return thread.MessageStore is null ? [] : (await thread.MessageStore.GetMessagesAsync()).ToList(); } public Task CreateChatClientAgentAsync( diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs index 304a955b68..1dd8a760f8 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs @@ -50,7 +50,7 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture return [.. previousMessages, responseMessage]; } - return await thread.GetMessagesAsync().ToListAsync(); + return thread.MessageStore is null ? [] : (await thread.MessageStore.GetMessagesAsync()).ToList(); } private static ChatMessage ConvertToChatMessage(ResponseItem item) From 745a05d43c877e89df1ed496fad7f86c2da1fc8e Mon Sep 17 00:00:00 2001 From: Mark Wallace <127216156+markwallace-microsoft@users.noreply.github.com> Date: Tue, 9 Sep 2025 18:03:10 +0100 Subject: [PATCH 14/32] .NET: Add an A2A client server sample (#633) * Add an A2A client server sample * Address code review feedback * Start to fix code review feedback * Start to fix code review feedback * Start to fix code review feedback --- dotnet/Directory.Packages.props | 1 + dotnet/agent-framework-dotnet.slnx | 5 + .../A2AClient/A2AClient.csproj | 26 ++ .../A2AClient/HostClientAgent.cs | 64 ++++ .../A2AClientServer/A2AClient/Program.cs | 80 +++++ .../A2AClientServer/A2AClient/README.md | 26 ++ .../A2AServer/A2AServer.csproj | 26 ++ .../A2AClientServer/A2AServer/A2AServer.http | 82 +++++ .../A2AServer/HostAgentFactory.cs | 148 ++++++++ .../A2AServer/Models/InvoiceQuery.cs | 176 +++++++++ .../A2AClientServer/A2AServer/Program.cs | 107 ++++++ dotnet/samples/A2AClientServer/README.md | 234 ++++++++++++ .../a2a-inspector-agent-card.png | Bin 0 -> 130424 bytes .../a2a-inspector-raw-json-response.png | Bin 0 -> 185701 bytes .../a2a-inspector-send-message.png | Bin 0 -> 55276 bytes .../A2AClientServer/demo-architecture.png | Bin 0 -> 39629 bytes .../rest-client-agent-card.png | Bin 0 -> 484666 bytes .../rest-client-send-message.png | Bin 0 -> 449636 bytes .../A2AAgent.cs | 29 +- .../A2AHostAgent.cs | 112 ++++++ .../Extensions/A2AAgentTaskExtensions.cs | 30 ++ .../Extensions/A2AArtifactExtensions.cs | 28 ++ .../Extensions/A2AMessageExtensions.cs | 2 +- .../Extensions/A2AMetadataExtensions.cs | 2 +- .../Extensions/A2APartExtensions.cs | 2 +- .../Extensions/AIContentExtensions.cs | 4 +- .../Extensions/ChatMessageExtensions.cs | 2 +- .../AgentAIFunctionFactory.cs | 39 ++ .../AgentAIFunctionFactoryTests.cs | 339 ++++++++++++++++++ 29 files changed, 1550 insertions(+), 14 deletions(-) create mode 100644 dotnet/samples/A2AClientServer/A2AClient/A2AClient.csproj create mode 100644 dotnet/samples/A2AClientServer/A2AClient/HostClientAgent.cs create mode 100644 dotnet/samples/A2AClientServer/A2AClient/Program.cs create mode 100644 dotnet/samples/A2AClientServer/A2AClient/README.md create mode 100644 dotnet/samples/A2AClientServer/A2AServer/A2AServer.csproj create mode 100644 dotnet/samples/A2AClientServer/A2AServer/A2AServer.http create mode 100644 dotnet/samples/A2AClientServer/A2AServer/HostAgentFactory.cs create mode 100644 dotnet/samples/A2AClientServer/A2AServer/Models/InvoiceQuery.cs create mode 100644 dotnet/samples/A2AClientServer/A2AServer/Program.cs create mode 100644 dotnet/samples/A2AClientServer/README.md create mode 100644 dotnet/samples/A2AClientServer/a2a-inspector-agent-card.png create mode 100644 dotnet/samples/A2AClientServer/a2a-inspector-raw-json-response.png create mode 100644 dotnet/samples/A2AClientServer/a2a-inspector-send-message.png create mode 100644 dotnet/samples/A2AClientServer/demo-architecture.png create mode 100644 dotnet/samples/A2AClientServer/rest-client-agent-card.png create mode 100644 dotnet/samples/A2AClientServer/rest-client-send-message.png create mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AHostAgent.cs create mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AAgentTaskExtensions.cs create mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AArtifactExtensions.cs create mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents/AgentAIFunctionFactory.cs create mode 100644 dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/AgentAIFunctionFactoryTests.cs diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index a4a699b780..280cdd7743 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -40,6 +40,7 @@ + diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index f46ab37c4f..2bf90eda9b 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -7,6 +7,11 @@ + + + + + diff --git a/dotnet/samples/A2AClientServer/A2AClient/A2AClient.csproj b/dotnet/samples/A2AClientServer/A2AClient/A2AClient.csproj new file mode 100644 index 0000000000..b7699244d4 --- /dev/null +++ b/dotnet/samples/A2AClientServer/A2AClient/A2AClient.csproj @@ -0,0 +1,26 @@ + + + + Exe + net9.0 + enable + enable + 5ee045b0-aea3-4f08-8d31-32d1a6f8fed0 + $(NoWarn);CS1591;VSTHRD111;CA2007;SKEXP0110 + + + + + + + + + + + + + + + + + diff --git a/dotnet/samples/A2AClientServer/A2AClient/HostClientAgent.cs b/dotnet/samples/A2AClientServer/A2AClient/HostClientAgent.cs new file mode 100644 index 0000000000..db95f49af4 --- /dev/null +++ b/dotnet/samples/A2AClientServer/A2AClient/HostClientAgent.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft. All rights reserved. +using System.ClientModel; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.AI.Agents; +using Microsoft.Extensions.AI.Agents.A2A; +using Microsoft.Extensions.Logging; +using OpenAI; + +namespace A2A; + +internal sealed class HostClientAgent +{ + internal HostClientAgent(ILoggerFactory loggerFactory) + { + this._loggerFactory = loggerFactory; + this._logger = loggerFactory.CreateLogger("HostClientAgent"); + } + + internal async Task InitializeAgentAsync(string modelId, string apiKey, string[] agentUrls) + { + try + { + this._logger.LogInformation("Initializing Agent Framework agent with model: {ModelId}", modelId); + + // Connect to the remote agents via A2A + var createAgentTasks = agentUrls.Select(agentUrl => this.CreateAgentAsync(agentUrl)); + var agents = await Task.WhenAll(createAgentTasks); + var tools = agents.Select(agent => (AITool)AgentAIFunctionFactory.CreateFromAgent(agent)).ToList(); + + // Create the agent that uses the remote agents as tools + this.Agent = new OpenAIClient(new ApiKeyCredential(apiKey)) + .GetChatClient(modelId) + .CreateAIAgent(instructions: "You specialize in handling queries for users and using your tools to provide answers.", name: "HostClient", tools: tools); + } + catch (Exception ex) + { + this._logger.LogError(ex, "Failed to initialize HostClientAgent"); + throw; + } + } + + /// + /// The associated + /// + public AIAgent? Agent { get; private set; } + + #region private + private readonly ILoggerFactory _loggerFactory; + private readonly ILogger _logger; + + private async Task CreateAgentAsync(string agentUri) + { + var url = new Uri(agentUri); + var httpClient = new HttpClient + { + Timeout = TimeSpan.FromSeconds(60) + }; + + var agentCardResolver = new A2ACardResolver(url, httpClient); + + return await agentCardResolver.GetAIAgentAsync(); + } + #endregion +} diff --git a/dotnet/samples/A2AClientServer/A2AClient/Program.cs b/dotnet/samples/A2AClientServer/A2AClient/Program.cs new file mode 100644 index 0000000000..2715fdaf70 --- /dev/null +++ b/dotnet/samples/A2AClientServer/A2AClient/Program.cs @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.CommandLine; +using System.CommandLine.Invocation; +using System.Reflection; +using Microsoft.Extensions.AI.Agents; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace A2A; + +public static class Program +{ + public static async Task Main(string[] args) + { + // Create root command with options + var rootCommand = new RootCommand("A2AClient"); + rootCommand.SetHandler(HandleCommandsAsync); + + // Run the command + return await rootCommand.InvokeAsync(args); + } + + public static async Task HandleCommandsAsync(InvocationContext context) + { + // Set up the logging + using var loggerFactory = LoggerFactory.Create(builder => + { + builder.AddConsole(); + builder.SetMinimumLevel(LogLevel.Information); + }); + var logger = loggerFactory.CreateLogger("A2AClient"); + + // Retrieve configuration settings + IConfigurationRoot configRoot = new ConfigurationBuilder() + .AddEnvironmentVariables() + .AddUserSecrets(Assembly.GetExecutingAssembly()) + .Build(); + var apiKey = configRoot["A2AClient:ApiKey"] ?? throw new ArgumentException("A2AClient:ApiKey must be provided"); + var modelId = configRoot["A2AClient:ModelId"] ?? "gpt-4.1"; + var agentUrls = configRoot["A2AClient:AgentUrls"] ?? "http://localhost:5000/;http://localhost:5001/;http://localhost:5002/"; + + // Create the Host agent + var hostAgent = new HostClientAgent(loggerFactory); + await hostAgent.InitializeAgentAsync(modelId, apiKey, agentUrls!.Split(";")); + AgentThread thread = hostAgent.Agent!.GetNewThread(); + try + { + while (true) + { + // Get user message + Console.Write("\nUser (:q or quit to exit): "); + string? message = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(message)) + { + Console.WriteLine("Request cannot be empty."); + continue; + } + + if (message == ":q" || message == "quit") + { + break; + } + + var agentResponse = await hostAgent.Agent!.RunAsync(message, thread); + foreach (var chatMessage in agentResponse.Messages) + { + Console.ForegroundColor = ConsoleColor.Cyan; + Console.WriteLine($"\nAgent: {chatMessage.Text}"); + Console.ResetColor(); + } + } + } + catch (Exception ex) + { + logger.LogError(ex, "An error occurred while running the A2AClient"); + return; + } + } +} diff --git a/dotnet/samples/A2AClientServer/A2AClient/README.md b/dotnet/samples/A2AClientServer/A2AClient/README.md new file mode 100644 index 0000000000..324aa9aeee --- /dev/null +++ b/dotnet/samples/A2AClientServer/A2AClient/README.md @@ -0,0 +1,26 @@ + +# A2A Client Sample +Show how to create an A2A Client with a command line interface which invokes agents using the A2A protocol. + +## Run the Sample + +To run the sample, follow these steps: + +1. Run the A2A client: + ```bash + cd A2AClient + dotnet run + ``` +2. Enter your request e.g. "Show me all invoices for Contoso?" + +## Set Environment Variables + +The agent urls are provided as a ` ` delimited list of strings + +```powershell +cd dotnet/samples/A2AClientServer/A2AClient + +$env:OPENAI_MODEL_ID="gpt-4o-mini" +$env:OPENAI_API_KEY="" +$env:AGENT_URLS="http://localhost:5000/policy;http://localhost:5000/invoice;http://localhost:5000/logistics" +``` diff --git a/dotnet/samples/A2AClientServer/A2AServer/A2AServer.csproj b/dotnet/samples/A2AClientServer/A2AServer/A2AServer.csproj new file mode 100644 index 0000000000..54b038d6b6 --- /dev/null +++ b/dotnet/samples/A2AClientServer/A2AServer/A2AServer.csproj @@ -0,0 +1,26 @@ + + + + Exe + net9.0 + enable + enable + 5ee045b0-aea3-4f08-8d31-32d1a6f8fed0 + $(NoWarn);CS1591;VSTHRD111;CA2007;SKEXP0110 + + + + + + + + + + + + + + + + + diff --git a/dotnet/samples/A2AClientServer/A2AServer/A2AServer.http b/dotnet/samples/A2AClientServer/A2AServer/A2AServer.http new file mode 100644 index 0000000000..7e543ae005 --- /dev/null +++ b/dotnet/samples/A2AClientServer/A2AServer/A2AServer.http @@ -0,0 +1,82 @@ +### Each A2A agent is available at a different host address +@hostInvoice = http://localhost:5000 +@hostPolicy = http://localhost:5001 +@hostLogistics = http://localhost:5002 + +### Query agent card for the invoice agent +GET {{hostInvoice}}/.well-known/agent.json + +### Send a message to the invoice agent +POST {{hostInvoice}} +Content-Type: application/json + +{ + "id": "1", + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "id": "12345", + "message": { + "role": "user", + "messageId": "msg_1", + "parts": [ + { + "kind": "text", + "text": "Show me all invoices for Contoso?" + } + ] + } + } +} + +### Query agent card for the policy agent +GET {{hostPolicy}}/.well-known/agent.json + +### Send a message to the policy agent +POST {{hostPolicy}} +Content-Type: application/json + +{ + "id": "1", + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "id": "12345", + "message": { + "role": "user", + "messageId": "msg_1", + "parts": [ + { + "kind": "text", + "text": "What is the policy for short shipments?" + } + ] + } + } +} + +### Query agent card for the logistics agent +GET {{hostLogistics}}/.well-known/agent.json + +### Send a message to the logistics agent +POST {{hostLogistics}} +Content-Type: application/json + +{ + "id": "1", + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "id": "12345", + "message": { + "role": "user", + "messageId": "msg_1", + "parts": [ + { + "kind": "text", + "text": "What is the status for SHPMT-SAP-001?" + } + ] + } + } +} \ No newline at end of file diff --git a/dotnet/samples/A2AClientServer/A2AServer/HostAgentFactory.cs b/dotnet/samples/A2AClientServer/A2AServer/HostAgentFactory.cs new file mode 100644 index 0000000000..4c912133e1 --- /dev/null +++ b/dotnet/samples/A2AClientServer/A2AServer/HostAgentFactory.cs @@ -0,0 +1,148 @@ +// Copyright (c) Microsoft. All rights reserved. + +using A2A; +using Azure.AI.Agents.Persistent; +using Azure.Identity; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.AI.Agents; +using Microsoft.Extensions.AI.Agents.A2A; +using OpenAI; + +namespace A2AServer; + +internal static class HostAgentFactory +{ + internal static async Task CreateFoundryHostAgentAsync(string agentType, string modelId, string endpoint, string assistantId, IList? tools = null) + { + var persistentAgentsClient = new PersistentAgentsClient(endpoint, new AzureCliCredential()); + PersistentAgent persistentAgent = await persistentAgentsClient.Administration.GetAgentAsync(assistantId); + + AIAgent agent = await persistentAgentsClient + .GetAIAgentAsync(persistentAgent.Id, chatOptions: new() { Tools = tools }); + + AgentCard agentCard = agentType.ToUpperInvariant() switch + { + "INVOICE" => GetInvoiceAgentCard(), + "POLICY" => GetPolicyAgentCard(), + "LOGISTICS" => GetLogisticsAgentCard(), + _ => throw new ArgumentException($"Unsupported agent type: {agentType}"), + }; + + return new A2AHostAgent(agent, agentCard); + } + + internal static async Task CreateChatCompletionHostAgentAsync(string agentType, string modelId, string apiKey, string name, string instructions, IList? tools = null) + { + AIAgent agent = new OpenAIClient(apiKey) + .GetChatClient(modelId) + .CreateAIAgent(instructions, name, tools: tools); + + AgentCard agentCard = agentType.ToUpperInvariant() switch + { + "INVOICE" => GetInvoiceAgentCard(), + "POLICY" => GetPolicyAgentCard(), + "LOGISTICS" => GetLogisticsAgentCard(), + _ => throw new ArgumentException($"Unsupported agent type: {agentType}"), + }; + + return new A2AHostAgent(agent, agentCard); + } + + #region private + private static AgentCard GetInvoiceAgentCard() + { + var capabilities = new AgentCapabilities() + { + Streaming = false, + PushNotifications = false, + }; + + var invoiceQuery = new AgentSkill() + { + Id = "id_invoice_agent", + Name = "InvoiceQuery", + Description = "Handles requests relating to invoices.", + Tags = ["invoice", "semantic-kernel"], + Examples = + [ + "List the latest invoices for Contoso.", + ], + }; + + return new() + { + Name = "InvoiceAgent", + Description = "Handles requests relating to invoices.", + Version = "1.0.0", + DefaultInputModes = ["text"], + DefaultOutputModes = ["text"], + Capabilities = capabilities, + Skills = [invoiceQuery], + }; + } + + private static AgentCard GetPolicyAgentCard() + { + var capabilities = new AgentCapabilities() + { + Streaming = false, + PushNotifications = false, + }; + + var invoiceQuery = new AgentSkill() + { + Id = "id_policy_agent", + Name = "PolicyAgent", + Description = "Handles requests relating to policies and customer communications.", + Tags = ["policy", "semantic-kernel"], + Examples = + [ + "What is the policy for short shipments?", + ], + }; + + return new AgentCard() + { + Name = "PolicyAgent", + Description = "Handles requests relating to policies and customer communications.", + Version = "1.0.0", + DefaultInputModes = ["text"], + DefaultOutputModes = ["text"], + Capabilities = capabilities, + Skills = [invoiceQuery], + }; + } + + private static AgentCard GetLogisticsAgentCard() + { + var capabilities = new AgentCapabilities() + { + Streaming = false, + PushNotifications = false, + }; + + var logisticsQuery = new AgentSkill() + { + Id = "id_logistics_agent", + Name = "LogisticsQuery", + Description = "Handles requests relating to logistics.", + Tags = ["logistics", "semantic-kernel"], + Examples = + [ + "What is the status for SHPMT-SAP-001", + ], + }; + + return new AgentCard() + { + Name = "LogisticsAgent", + Description = "Handles requests relating to logistics.", + Version = "1.0.0", + DefaultInputModes = ["text"], + DefaultOutputModes = ["text"], + Capabilities = capabilities, + Skills = [logisticsQuery], + }; + } + #endregion +} diff --git a/dotnet/samples/A2AClientServer/A2AServer/Models/InvoiceQuery.cs b/dotnet/samples/A2AClientServer/A2AServer/Models/InvoiceQuery.cs new file mode 100644 index 0000000000..499e54aa6e --- /dev/null +++ b/dotnet/samples/A2AClientServer/A2AServer/Models/InvoiceQuery.cs @@ -0,0 +1,176 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.ComponentModel; + +namespace A2A; + +/// +/// A simple invoice plugin that returns mock data. +/// +public class Product +{ + public string Name { get; set; } + public int Quantity { get; set; } + public decimal Price { get; set; } // Price per unit + + public Product(string name, int quantity, decimal price) + { + this.Name = name; + this.Quantity = quantity; + this.Price = price; + } + + public decimal TotalPrice() + { + return this.Quantity * this.Price; // Total price for this product + } +} + +public class Invoice +{ + public string TransactionId { get; set; } + public string InvoiceId { get; set; } + public string CompanyName { get; set; } + public DateTime InvoiceDate { get; set; } + public List Products { get; set; } // List of products + + public Invoice(string transactionId, string invoiceId, string companyName, DateTime invoiceDate, List products) + { + this.TransactionId = transactionId; + this.InvoiceId = invoiceId; + this.CompanyName = companyName; + this.InvoiceDate = invoiceDate; + this.Products = products; + } + + public decimal TotalInvoicePrice() + { + return this.Products.Sum(product => product.TotalPrice()); // Total price of all products in the invoice + } +} + +public class InvoiceQuery +{ + private readonly List _invoices; + private static readonly Random s_random = new(); + + public InvoiceQuery() + { + // Extended mock data with quantities and prices + this._invoices = + [ + new("TICKET-XYZ987", "INV789", "Contoso", GetRandomDateWithinLastTwoMonths(), new List + { + new("T-Shirts", 150, 10.00m), + new("Hats", 200, 15.00m), + new("Glasses", 300, 5.00m) + }), + new("TICKET-XYZ111", "INV111", "XStore", GetRandomDateWithinLastTwoMonths(), new List + { + new("T-Shirts", 2500, 12.00m), + new("Hats", 1500, 8.00m), + new("Glasses", 200, 20.00m) + }), + new("TICKET-XYZ222", "INV222", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(), new List + { + new("T-Shirts", 1200, 14.00m), + new("Hats", 800, 7.00m), + new("Glasses", 500, 25.00m) + }), + new("TICKET-XYZ333", "INV333", "Contoso", GetRandomDateWithinLastTwoMonths(), new List + { + new("T-Shirts", 400, 11.00m), + new("Hats", 600, 15.00m), + new("Glasses", 700, 5.00m) + }), + new("TICKET-XYZ444", "INV444", "XStore", GetRandomDateWithinLastTwoMonths(), new List + { + new("T-Shirts", 800, 10.00m), + new("Hats", 500, 18.00m), + new("Glasses", 300, 22.00m) + }), + new("TICKET-XYZ555", "INV555", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(), new List + { + new("T-Shirts", 1100, 9.00m), + new("Hats", 900, 12.00m), + new("Glasses", 1200, 15.00m) + }), + new("TICKET-XYZ666", "INV666", "Contoso", GetRandomDateWithinLastTwoMonths(), new List + { + new("T-Shirts", 2500, 8.00m), + new("Hats", 1200, 10.00m), + new("Glasses", 1000, 6.00m) + }), + new("TICKET-XYZ777", "INV777", "XStore", GetRandomDateWithinLastTwoMonths(), new List + { + new("T-Shirts", 1900, 13.00m), + new("Hats", 1300, 16.00m), + new("Glasses", 800, 19.00m) + }), + new("TICKET-XYZ888", "INV888", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(), new List + { + new("T-Shirts", 2200, 11.00m), + new("Hats", 1700, 8.50m), + new("Glasses", 600, 21.00m) + }), + new("TICKET-XYZ999", "INV999", "Contoso", GetRandomDateWithinLastTwoMonths(), new List + { + new("T-Shirts", 1400, 10.50m), + new("Hats", 1100, 9.00m), + new("Glasses", 950, 12.00m) + }) + ]; + } + + public static DateTime GetRandomDateWithinLastTwoMonths() + { + // Get the current date and time + DateTime endDate = DateTime.Now; + + // Calculate the start date, which is two months before the current date + DateTime startDate = endDate.AddMonths(-2); + + // Generate a random number of days between 0 and the total number of days in the range + int totalDays = (endDate - startDate).Days; +#pragma warning disable CA5394 // Do not use insecure randomness + int randomDays = s_random.Next(0, totalDays + 1); // +1 to include the end date +#pragma warning restore CA5394 // Do not use insecure randomness + + // Return the random date + return startDate.AddDays(randomDays); + } + + [Description("Retrieves invoices for the specified company and optionally within the specified time range")] + public IEnumerable QueryInvoices(string companyName, DateTime? startDate = null, DateTime? endDate = null) + { + var query = this._invoices.Where(i => i.CompanyName.Equals(companyName, StringComparison.OrdinalIgnoreCase)); + + if (startDate.HasValue) + { + query = query.Where(i => i.InvoiceDate >= startDate.Value); + } + + if (endDate.HasValue) + { + query = query.Where(i => i.InvoiceDate <= endDate.Value); + } + + return query.ToList(); + } + + [Description("Retrieves invoice using the transaction id")] + public IEnumerable QueryByTransactionId(string transactionId) + { + var query = this._invoices.Where(i => i.TransactionId.Equals(transactionId, StringComparison.OrdinalIgnoreCase)); + + return query.ToList(); + } + + [Description("Retrieves invoice using the invoice id")] + public IEnumerable QueryByInvoiceId(string invoiceId) + { + var query = this._invoices.Where(i => i.InvoiceId.Equals(invoiceId, StringComparison.OrdinalIgnoreCase)); + + return query.ToList(); + } +} diff --git a/dotnet/samples/A2AClientServer/A2AServer/Program.cs b/dotnet/samples/A2AClientServer/A2AServer/Program.cs new file mode 100644 index 0000000000..5c34361ced --- /dev/null +++ b/dotnet/samples/A2AClientServer/A2AServer/Program.cs @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft. All rights reserved. +using A2A; +using A2A.AspNetCore; +using A2AServer; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.AI.Agents.A2A; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +string agentId = string.Empty; +string agentType = string.Empty; + +for (var i = 0; i < args.Length; i++) +{ + if (args[i].StartsWith("--agentId", StringComparison.InvariantCultureIgnoreCase) && i + 1 < args.Length) + { + agentId = args[++i]; + } + else if (args[i].StartsWith("--agentType", StringComparison.InvariantCultureIgnoreCase) && i + 1 < args.Length) + { + agentType = args[++i]; + } +} + +var builder = WebApplication.CreateBuilder(args); +builder.Services.AddHttpClient().AddLogging(); +var app = builder.Build(); + +var httpClient = app.Services.GetRequiredService().CreateClient(); +var logger = app.Logger; + +IConfigurationRoot configuration = new ConfigurationBuilder() + .AddEnvironmentVariables() + .AddUserSecrets() + .Build(); + +string? apiKey = configuration["OPENAI_API_KEY"]; +string modelId = configuration["OPENAI_MODEL_ID"] ?? "gpt-4o-mini"; +string? endpoint = configuration["FOUNDRY_PROJECT_ENDPOINT"]; + +var invoiceQueryPlugin = new InvoiceQuery(); +IList tools = + [ + AIFunctionFactory.Create(invoiceQueryPlugin.QueryInvoices), + AIFunctionFactory.Create(invoiceQueryPlugin.QueryByTransactionId), + AIFunctionFactory.Create(invoiceQueryPlugin.QueryByInvoiceId) + ]; + +A2AHostAgent? hostAgent = null; +if (!string.IsNullOrEmpty(endpoint) && !string.IsNullOrEmpty(agentId)) +{ + hostAgent = agentType.ToUpperInvariant() switch + { + "INVOICE" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, modelId, endpoint, agentId, tools), + "POLICY" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, modelId, endpoint, agentId), + "LOGISTICS" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, modelId, endpoint, agentId), + _ => throw new ArgumentException($"Unsupported agent type: {agentType}"), + }; +} +else if (!string.IsNullOrEmpty(apiKey)) +{ + hostAgent = agentType.ToUpperInvariant() switch + { + "INVOICE" => await HostAgentFactory.CreateChatCompletionHostAgentAsync( + agentType, modelId, apiKey, "InvoiceAgent", + """ + You specialize in handling queries related to invoices. + """, tools), + "POLICY" => await HostAgentFactory.CreateChatCompletionHostAgentAsync( + agentType, modelId, apiKey, "PolicyAgent", + """ + You specialize in handling queries related to policies and customer communications. + + Always reply with exactly this text: + + Policy: Short Shipment Dispute Handling Policy V2.1 + + Summary: "For short shipments reported by customers, first verify internal shipment records + (SAP) and physical logistics scan data (BigQuery). If discrepancy is confirmed and logistics data + shows fewer items packed than invoiced, issue a credit for the missing items. Document the + resolution in SAP CRM and notify the customer via email within 2 business days, referencing the + original invoice and the credit memo number. Use the 'Formal Credit Notification' email + template." + """), + "LOGISTICS" => await HostAgentFactory.CreateChatCompletionHostAgentAsync( + agentType, modelId, apiKey, "LogisticsAgent", + """ + You specialize in handling queries related to logistics. + + Always reply with exactly: + + Shipment number: SHPMT-SAP-001 + Item: TSHIRT-RED-L + Quantity: 900 + """), + _ => throw new ArgumentException($"Unsupported agent type: {agentType}"), + }; +} +else +{ + throw new ArgumentException("Either A2AServer:ApiKey or A2AServer:ConnectionString & agentId must be provided"); +} + +app.MapA2A(hostAgent!.TaskManager!, "/"); + +await app.RunAsync(); diff --git a/dotnet/samples/A2AClientServer/README.md b/dotnet/samples/A2AClientServer/README.md new file mode 100644 index 0000000000..0311c0a2b7 --- /dev/null +++ b/dotnet/samples/A2AClientServer/README.md @@ -0,0 +1,234 @@ +# A2A Client and Server samples + +> **Warning** +> The [A2A protocol](https://google.github.io/A2A/) is still under development and changing fast. +> We will try to keep these samples updated as the protocol evolves. + +These samples are built with [official A2A C# SDK](https://www.nuget.org/packages/A2A) and demonstrates: + +1. Creating an A2A Server which makes an agent available via the A2A protocol. +2. Creating an A2A Client with a command line interface which invokes agents using the A2A protocol. + +The demonstration has two components: + +1. `A2AServer` - You will run three instances of the server to correspond to three A2A servers each providing a single Agent i.e., the Invoice, Policy and Logistics agents. +2. `A2AClient` - This represents a client application which will connect to the remote A2A servers using the A2A protocol so that it can use those agents when answering questions you will ask. + +Demo Architecture + +## Configuring Environment Variables + +The samples can be configured to use chat completion agents or Azure AI agents. + +### Configuring for use with Chat Completion Agents + +Provide your OpenAI API key via an environment variable + +```powershell +$env:OPENAI_API_KEY="" +``` + +Use the following commands to run each A2A server: + +Execute the following command to build the sample: + +```powershell +cd A2AServer +dotnet build +``` + +```bash +dotnet run --urls "http://localhost:5000;https://localhost:5010" --agentType "invoice" --no-build +``` + +```bash +dotnet run --urls "http://localhost:5001;https://localhost:5011" --agentType "policy" --no-build +``` + +```bash +dotnet run --urls "http://localhost:5002;https://localhost:5012" --agentType "logistics" --no-build +``` + +### Configuring for use with Azure AI Agents + +You must create the agents in an Azure AI Foundry project and then provide the project endpoint and agents ids. The instructions for each agent are as follows: + +- Invoice Agent + ``` + You specialize in handling queries related to invoices. + ``` +- Policy Agent + ``` + You specialize in handling queries related to policies and customer communications. + + Always reply with exactly this text: + + Policy: Short Shipment Dispute Handling Policy V2.1 + + Summary: "For short shipments reported by customers, first verify internal shipment records + (SAP) and physical logistics scan data (BigQuery). If discrepancy is confirmed and logistics data + shows fewer items packed than invoiced, issue a credit for the missing items. Document the + resolution in SAP CRM and notify the customer via email within 2 business days, referencing the + original invoice and the credit memo number. Use the 'Formal Credit Notification' email + template." + ``` +- Logistics Agent + ``` + You specialize in handling queries related to logistics. + + Always reply with exactly: + + Shipment number: SHPMT-SAP-001 + Item: TSHIRT-RED-L + Quantity: 900" + ``` + +```powershell +$env:FOUNDRY_PROJECT_ENDPOINT="https://ai-foundry-your-project.services.ai.azure.com/api/projects/ai-proj-ga-your-project" # Replace with your Foundry Project endpoint +``` + +Use the following commands to run each A2A server + +```bash +dotnet run --urls "http://localhost:5000;https://localhost:5010" --agentId "" --agentType "invoice" --no-build +``` + +```bash +dotnet run --urls "http://localhost:5001;https://localhost:5011" --agentId "" --agentType "policy" --no-build +``` + +```bash +dotnet run --urls "http://localhost:5002;https://localhost:5012" --agentId "" --agentType "logistics" --no-build +``` + +### Testing the Agents using the Rest Client + +This sample contains a [.http file](https://learn.microsoft.com/aspnet/core/test/http-files?view=aspnetcore-9.0) which can be used to test the agent. + +1. In Visual Studio open [./A2AServer/A2AServer.http](./A2AServer/A2AServer.http) +1. There are two sent requests for each agent, e.g., for the invoice agent: + 1. Query agent card for the invoice agent + `GET {{hostInvoice}}/.well-known/agent.json` + 1. Send a message to the invoice agent + ``` + POST {{hostInvoice}} + Content-Type: application/json + + { + "id": "1", + "jsonrpc": "2.0", + "method": "message/send", + "params": { + "id": "12345", + "message": { + "role": "user", + "messageId": "msg_1", + "parts": [ + { + "kind": "text", + "text": "Show me all invoices for Contoso?" + } + ] + } + } + } + ``` + +Sample output from the request to display the agent card: + +Agent Card + +Sample output from the request to send a message to the agent via A2A protocol: + +Send Message + +### Testing the Agents using the A2A Inspector + +The A2A Inspector is a web-based tool designed to help developers inspect, debug, and validate servers that implement the Google A2A (Agent-to-Agent) protocol. It provides a user-friendly interface to interact with an A2A agent, view communication, and ensure specification compliance. + +For more information go [here](https://github.com/a2aproject/a2a-inspector). + +Running the [inspector with Docker](https://github.com/a2aproject/a2a-inspector?tab=readme-ov-file#option-two-run-with-docker) is the easiest way to get started. + +1. Navigate to the A2A Inspector in your browser: [http://127.0.0.1:8080/](http://127.0.0.1:8080/) +1. Enter the URL of the Agent you are running e.g., [http://host.docker.internal:5000](http://host.docker.internal:5000) +1. Connect to the agent and the agent card will be displayed and validated. +1. Type a message and send it to the agent using A2A protocol. + 1. The response will be validated automatically and then displayed in the UI. + 1. You can select the response to view the raw json. + +Agent card after connecting to an agent using the A2A protocol: + +Agent Card + +Sample response after sending a message to the agent via A2A protocol: + +Send Message + +Raw JSON response from an A2A agent: + +Response Raw JSON + +### Configuring Agents for the A2A Client + +The A2A client will connect to remote agents using the A2A protocol. + +By default the client will connect to the invoice, policy and logistics agents provided by the sample A2A Server. + +These are available at the following URL's: + +- Invoice Agent: http://localhost:5000/ +- Policy Agent: http://localhost:5001/ +- Logistics Agent: http://localhost:5002/ + +If you want to change which agents are using then set the agents url as a space delimited string as follows: + +```powershell +$env:A2A_AGENT_URLS="http://localhost:5000/;http://localhost:5001/;http://localhost:5002/" +``` + +## Run the Sample + +To run the sample, follow these steps: + +1. Run the A2A server's using the commands shown earlier +2. Run the A2A client: + ```bash + cd A2AClient + dotnet run + ``` +3. Enter your request e.g. "Customer is disputing transaction TICKET-XYZ987 as they claim the received fewer t-shirts than ordered." +4. The host client agent will call the remote agents, these calls will be displayed as console output. The final answer will use information from the remote agents. The sample below includes all three agents but in your case you may only see the policy and invoice agent. + +Sample output from the A2A client: + +``` +A2AClient> dotnet run +info: HostClientAgent[0] + Initializing Agent Framework agent with model: gpt-4o-mini + +User (:q or quit to exit): Customer is disputing transaction TICKET-XYZ987 as they claim the received fewer t-shirts than ordered. + +Agent: + +Agent: + +Agent: The transaction details for **TICKET-XYZ987** are as follows: + +- **Invoice ID:** INV789 +- **Company Name:** Contoso +- **Invoice Date:** September 4, 2025 +- **Products:** + - **T-Shirts:** 150 units at $10.00 each + - **Hats:** 200 units at $15.00 each + - **Glasses:** 300 units at $5.00 each + +To proceed with the dispute regarding the quantity of t-shirts delivered, please specify the exact quantity issue – how many t-shirts were actually received compared to the ordered amount. + +### Customer Service Policy for Handling Disputes +**Short Shipment Dispute Handling Policy V2.1** +- **Summary:** For short shipments reported by customers, first verify internal shipment records and physical logistics scan data. If a discrepancy is confirmed and the logistics data shows fewer items were packed than invoiced, a credit for the missing items will be issued. +- **Follow-up Actions:** Document the resolution in the SAP CRM and notify the customer via email within 2 business days, referencing the original invoice and the credit memo number, using the 'Formal Credit Notification' email template. + +Please provide me with the information regarding the specific quantity issue so I can assist you further. +``` diff --git a/dotnet/samples/A2AClientServer/a2a-inspector-agent-card.png b/dotnet/samples/A2AClientServer/a2a-inspector-agent-card.png new file mode 100644 index 0000000000000000000000000000000000000000..8385a2b68a453235aed56438c6bfb33dbcecd655 GIT binary patch literal 130424 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°LyurOJH+U_}= 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 z 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 Ns!E!Q_tzVp{2w?Q#@_${ literal 0 HcmV?d00001 diff --git a/dotnet/samples/A2AClientServer/rest-client-agent-card.png b/dotnet/samples/A2AClientServer/rest-client-agent-card.png new file mode 100644 index 0000000000000000000000000000000000000000..44651487a8ec7b96c64113a8857b11cbbb3877f4 GIT binary patch literal 484666 zcmaI7bwC_hvpx)g;7)J|?h-t>1a}J_+%32b8XSVVy95s&+yVr5cZb1UhJoK?_uakk z?!Djl$ISF}_o-8-&grMBo>LvJtSE(wM2G|h1%)ajEv^a$g-iwo1viBN2RUOi>5vNf zfp%7v`T$imMtlhQ18X6wAPNOl6NCI@^cwOX(Lq|<848O0{qGMnhgJ>%3hJduMqKox zhyL-Zr}vv1@cqSnHm{pAX%H0^>??8D-EYLQHIn68u)5+RlK?|Cn@L|mBptAIL;b6p zgZZrsIrTPP@d`4W7~>)>=!RTkr_i9FAgWmBx{OuZ+q;2dn%0C>)Ax9=>*E6^Z(~gN zIO;fZ#=V6&2HWlg(^pnLX*xS|L`6j*kOyIW_~Xk185voueD)RDi!esMa~nv-$48Jm zLqV@lb9QFtn+4+EH~w*Vka$W`(v4TfXvpxc>O1T|MIey>yvv+E{AtJv^_P^m)z2lR zr7{pHgLsI4b@gYblj^cSoPSFFA8jB<2m!EU$p7Eh18AUs{nhP11^;dnmMa5Q`=62h zL+YRBC@iu3nf`aHzwSgx|3}x3Az!1ScDvz8skpe*+ok`y>rYYn^Z)G0;`_pa)?43u zBgtsu!CrJ>CeeSX$#X&lrTsrL!Z>C;_p=68-Pc#KT!@K?hTr);$c^!z3An2KbMRsM zl>ZoUW*&`vs))Hc&ARs$DirAMti+(xS3ZMVLGR=LNdJA5KQH|cTUgMA_I7W_2#<9K z!b8(BGH%xlye+7zQvBcTy!~KeOIXaV_C}eq%Jj(swyHXDuAFD&#L1q=c>S*UVmbUI zejSC?1zq$1N7wr`?Hc3P5M}Pt+@J0pFR!kA_WreWu8U(~Vg`HL%`+q8!S!J;6 zPe(lL)VB)GzA>#YvhV^zwToPhf8rf6nAA~Pm-@2AH1Q;ocnQ2xyLO$b`fJiV&~3Si zpyJ}S(Bsq!B0)L1_M{)P--c+lVsbd$f*L& z7DvvI7O8Efj8=7*KDA)D^fLpr6um?^-Su2}clGO*x)MDxLKr~a`oD(#_neR*vOw`# z2r*t-#5KAiCeXg?GN3yTd7XerBl&|?q21%v_oVTUAWX~W!xXVOs2hPzliuM9mM@5S zHdL@+{%+;lZbtAl4W!$a>hS;J(>HRIW`=0d6YsJnl@t!`L?(z}T^p)9F%{O+JKFKN%g1srn8bY#~1L% znRB1j-J(T~>oV0p5oL0k4?$#Xv-azhf2FruvN<7%LoX8dy0ZA%&Y*m@K-!>ZIla*< zWU#hgm41D%&O%vj9LklkaEVW_r@C2kh(Hw`%t7j!Yzj5EX*4 z{VU~Cy|d0zUI*}M3Iqtkw-l@)IIaskH649SW(lHZ-d};}rr$FMwVVza#Y3H;Yqn+v zNruF84`Nw-gz_>^>NXIFzcD$fwT)@J4EW2;;_Iw-OPmmV@O;=!fl(9&EsoEW2Ukd) zW{Ev5H+ubm$$)u^5{U(!PEOx#=gskBMvX9}G8kNOOIxVRz~j&5n31^qUO!z)`o_O#b)<6L*l+bz_(ZYAE6(|I z-^^VNOhMlX)mOSpDDNYYXT#`KzHiFCtCYxfTkQ%ML|z(mK5Kf}ZbLiMT1Q2usWUCt zs3hWAKyHbJYgpt!Eu_CME>{Oej+CbLgY_YRz#p1*KK~ z(DSB~4zqbz;8&kYr_@g4y30PDalF^yr!0x@AjNkpF`+tVpk&9@oxz<-7IHfcUzJ~e!f8yzZ6$va^6D0(;0z6$UmzXYRcL7_Q8e`s{Q=zDH{I(LnTpQl+ z{jTnOE_tcD{43`Cjc>#hNQv~#UBf#=^8{&sfY4x6pCuR;S8{-4_-+Lo}&@e!{z{6pw>E%;!t*8~UU@ zS6kT)aLu@I$xqthYTAW5E;8YIlS62=6b6E3S&am|jXV#Bec5mOBJ;pe${#*gST3zC zBrHgWJV_(g9emUMPCha9)%sRO#8z@o2;Z zUOXf~Tt@DWvkv#s{705-44(2^BkD4ovxR=2;VP5Mc$1N#JJe6;oSnPS_MxBvO=0-dD(L#}0Q(R!8(PUTt_e#|jdcHT&_d4N9 z&&Yu0@i-^5nXl?IuPgl;^3CEbD-g8Ym{9yjoWZOeL92A@qbp0+ed49G9^+oM)^4Z8 znl_klrKQLIB{<`KooC+@clSh(rqWFTm(5%~zt7#OJJcIo+(zxEER4hL#H)sIHr{mq%(vzkt;9xBQZg09t}?sK1Kt?kmD>LWvc{*!Gx$rFqz`Z zl9%P7?>E87?fpgDX?L8tsvu+PATXoACQgv^C zN4#-U6xueM`0cBkl zpuSs-XLAqFB>Xch7FRkVgIdfAFDXaw0rYm5wbjgy+sMI;o?#B0%P7|yeNhTIsMmh5 zJ-rF_D3fo<)|`19lw4+;y?AhdFNbFoF}F3l9OK^g{nmDast#8sa%BXptdmB*2I%_T z(i3=1v%@u+#w-E#Jq@~Rl3Ruc2{$^?0)&!J8(asm8sqybodiwDg2NC|SBo;)9U!1zAFZKtbs7wd&E6LzBZ_ zaJcfPRb)G3nJ`(EO=FhTddr?g7aJ(vS3b=#(+OGH5{SvSZY|3^AD{cv$LcHpoUJ_I zgV%oaOXJC_@s!6FL5${-vw>FSv+S({jvI<8Z~e{IFXN3pL0|2uDJ!{p ztMe^lsCkIAV)vipMV_ai(CY&yo1Jr=Z*W_3ml_n59nr4r9Xe&3gK2G+n3jA?dPtZ~ z3QCrL(Dp;!aP!YM{^@#xic28S@UYxSHZ%HMl?+97YN{qdEV~5bZ1}@dn7M^&n-gpE+hLp?w)Tj)lVOq!nyJZBzN9(?hR_x z)>>$n1qA-jb$M;qv23`s}cFCfWl9b|CvIXq4#z5T(>*3`Ypq`laj!miR@GXH$fMQv#qdi@95ds*aV)=dznRl)zsAd zJ}NAFc%b~V;a_HAfpInlBRX1Z1`ZSks-~Y2Ori2)I`l$26O_0+iCeA z7w<2gjd9$J>*bYKX%b)N#YTOYT0gUUmdkky+gzDnzI)7+;sJ2)?gj&&n}i4SbsgUQ0&R=*oM9i>pMfZAiJ<;Qd@ zj|1(kaOKCY4&5`}+wXPr!Ope>c6Z3X84Gy1`3mrTgWII7jD z{PBt9rdEg+GTWCHBqoD9uZ6TjU`FwLMA5D^cbq5pJ-QTGE1HG!kghRmU0H zm#Tip;+wFIENSv7CQyK$v<|;NOvXdi7Qot>XIsau?pSV~Gy zAA+z4^0u~2+!;{=q<)!T&|Uf0*OkS^l7e!7PvswSD5yj&L{5=!afo4P9BLRqHn|V^ z=nfX0f*QFumkc^O+UUB8U=tvWqLzW7pun*qh1KXj0)Z zlNdosNDi427!&+YN72%&odivw!x5BRp`}{G)%6C%-RP5JAp=@E(^pJA@#;^7tBn#$4!j zRdew1PcNzn|NqzSKb901fe=FC|Dtjr#vS9A@&dz$HqobVM++`_$GZ0%|EKE!&l^V!@^;%F{%YF@L2RK&O zF4|80Yo;g7eiMW;djFNqilZk$nf|k{OlD4#foMTy=wCQEI6&0+n&xcBbQa=*Tt-I2dkOeIfBlXjepX zKnx9YGWpe%Oqk20M{cU%yLqyA9BBFjHx^{{_NB~xOl()8B>IQwAPw`}9*ifjO*w_TDLdgq1 zSq1F%?HgxQ@2xS-B!HJlcGho7g=MJd1q03<+*5rM0@ZY*YyAo$x%Id?)2{3lXyR!U zeLYfPa`-CKw970b21$_2G;~X^C_SF#?D{V=`Q~`)kaqPxJakIIz%VwNq zbDc%&aZD&&6@^tpsr~)f zq$Oi9D;(fXGjA*Fof*xO>kHiWR=;*7sge3;Q~79e?ok;qH2g`VzE1{yW)O0IIH!L> zeFU~w#tiiv8I{7_eoQNWgJb4)5Zim{BebBaR-Vs z)5;|*4m9i-&ME?vm=mAWhgMgkW@k{iJZ4z z;0n*HOxxVYAMw=l>IO=q&@b~I_K!6(rcvSI$efz$&c~#$y+OyKZbDvR?MIBIw<(*m zrsiEUrXzsdLCM`lljBk0Ut<>NmnXV?iKkCOrog1D>fk|Xc{ir;MRN*yVDSG&4DmCN zzVN`ACqSN{;C@oQu6bAUDoxl==*qq=2*s7n5qBd0y)_hVw6zj0IVxWumM75qGACG_ zs7eBvhZ#*^Q*MbhYl6Cx;u(sL3t!i6&uo@Vq&36hO9j^PjvqsQ zy-53%Hbbwt3zK2x=Dj|nAr(NG$4)G^S9(Z+hFcZJjVS4b-gKYz_Ph`5b$_N?lxYoB zFBwlmgMpo=uZhX_Q#=Tc_%%)@4`QmqPn7u0NBLcb(!GrY(Bg$3XyL(0LAg2wud?u2 zJZHOJ?fFu)LtphnaOZ{*DPO*nBzB%k75QuXMCG={fL$%hITLQ{XPZ6)x|+>4 z%PnK5be@N53bb}zvC%lmiUGnJZ(Sw5Bmn9^!Tt|O4_l+ev=gLu6qw)HbsWByiNsG0 zDz!_@=P9>j6K~-l(_LwfG`Yr)^smI!^`AFA8mT0RP5NO(BAI||*`t9&7SHn`W-tu?rg0K`VUF!*9Ru=&m!$n+-8ghCa5ECH9nUHd!qW}1q410u!e zLfc(7*{|h1PcLZB4tgXSU*!f*A5UbWZOc^Ibq7+z6g8`nF(lTWPg8#_`y<+k`7@$IAbQl)|J)@!a z+`Rgd693;m{IAuch&{ScW1=J{@P~_;TQuv6`LPMa{JB+l@sU^RC<e!V9bx zF|#X?v*FY{VVPLb4`q~sVz`6DhVL!=+Mz5{Pj0wW=}#|WiTwi@6BVC=uHw=qqPk{S z3x7EK`pNqKk#)W8BwFz_D0%zxxUNNnOe~(~Y|b}zMSaJKb5d(rRCu?Z-Fn+L1l?I$$;hV`tCgJmwx`YX@8 ziomKS34I}13t^)#8os6|uu9S>EX8Yi!IfolpnN))0fcH_0S*eSv}2`0SkdUh@IZ_5jgea83N zWlMtQIyMVe1ydt^3DcDlYmN?O3B8kH*i?pi?ctWIUUm3PZ#_?m^T3=A(+E!LRV{}1 z8kcl@OUHf@kVw+(w3~f+3?zGujhVQs-hHZ@b2yky!1s$+)M>%Y(jZ6xqG_r=`EoU= z+sy|n-^Ij&q)LK0h2TFIa93~<1D>@owEg)prk-8n=mUH?AhgSj_zzJS1vDVKro;8L z`Qv(FBsI{PZR2L`EVX2cU6Qx-`50l{2V(?bW(jU=Yx`KoEdmVyhadS#{^bOqQUF7vrgBSPL~pCd@WnE{-x=NRqzno%X&|pE@~EP%nfjX-Ff5Kl zHuwm#t5C!OfdWTzoQpvef$bL!Dl04A*xml9@n}`z_HBG|Y%;+`mwR#Xw8fok+SGNz zp)KTc^dwS>55`gWWZd6*G*#dP@I)o0Ig9B!Y&f+A*7M^E(u@7oMcRi=WG_8)tSQ6L#Dr9LnkY45*12amW^^_nmd+WgmNt@{%UP zMY|by#-!H^IY$1~xNr`H1x6zks?dMKnK3G9V#0by^%o5M2O7S}!0lukGaH?i5P5+6 z_8*pY&(0m=B=kOn)kXK`_Av?(MTshTeBh){VCQ|w{un>ihTE7-A@Dv+<%)wK*3KGH z2yP-I6fm|?&N2JtsdE$Wn$|fLD-$V~4L_XM9%_GuSnkJ*`iB%-imJ}HpSZ2j zxj#f@g;>*C_JqI1DrQ-wHrkiOB!T{@`;GaPR1Io5Oc)ntRvyc$HX$eAtZ^duE35I4 zG}%?Ut>oI9?UGZMpPw=Y&*iYg9O1R|CXMC8sIN-E;dkPwjd_%NOH5;(d};Tv?he%c zYyMV-?2E$Vh9drr5+$^Iv{t4!E}&a1u;*Z=mlRDJrAb)y1uA3!_%4=UNw;8zncD_O zYXd<=kqmr^Psw-e!A2f$*vz}k3an#hNxV@G>Mh%wie`<%BFIixo_AK*QbqBIbYZF$ zLs?F8;I4{FO-<>#2Yw6`raDja+>LR@gQG3-fFE8ERQCJ#*QRYi7VL43Ubul{uJEb( zGfkda42zoLw+_Xgc#0V>)$a8ri)+Lia^(8*H0-BC<_oVUQ&+kNpq73iYz?4ntmr0$ zv6aqU;J%ggu;xtTP?J$)&z<#%xd2Ot*)szPhNs{qA-#JHNhY|N;qr3G-=mjK8RKXE z+#v0v?;fD@VHeecrk7w%i ztTzZfPvB|BE@Oomyib|?{7*s%8(HTB- z1YhqhBfc1t_N-YL$M>VboXe^PrP(=l>P+{PBdHclW9C$qS%cQ!Y1G6Bs7KoLqni<*_GW1x_oHY@?Ss|TL@*j=S*yz zrZTZ4E})FauWlq z-#A+8y)J^>_L+i@Ch`E95ZF;=Pw-B6Nz@WyUESx8;+ zqREXyN&?%;a%(3P{$a0=-K@iluZr)5ucSekA)#bFprD>X0dTAqpv0RnaMr%-AE9bc@ zv5(@chdTKr7E{zS`HYIXry^NsBEPf3?70Xwp3UrgS^&r80>Tp~X;3&-ntC}T;U>bt zHoPzOeAkljI4?}bFE#`8PuEnlqkQ@76s$qPCed{&%>^}fhL#AR`LyRr?YyY*HM@@z z+qqg>+x0xrZ>~n;DWU;4rCZ}OC(FEfCk7T)ta@qv8=0(}oFn$7X#`}_w+PyA6xzCzp8x%WWv4qh~O+?-}Bq^;U%ZFOo3S2nqq?cRM+utoT zK)|S&v$jM)r{o*-pTk#7?TEf?S7`TpZn)M)g{uo`v6Jw= zYr=~5)d{9%iNAk_t1o7)=RR{(6Wd9235kprLFP=-a4_|umoS(!ib11g(2tobEOQol z&k#;79QgsAGbtzERQEMCX^L8{IzjbtM@8|YDdkNl@zBDSRf$sDDdWWoC1AFfc9$e3 zOVq00w=MdzItg6LW8SB;-(`kIN%29+!;)^)}^}3d&Y&4CPArD^`Tsk4|O;T%= zZq-e;O$hEMmdFc*#CGRIuzhy!abn1U5ltk3@^Mc`(R&^i|Ipn8&MI8zyEff}6gfzr zvFN}+edf?Lb&3K(i4(J`wCM7CNs3i?R<@#K%iB*1GbiP_rZ%q!gh~r%d`4N8^+JnA zLW=SmghN+4K7^ey7fgm}5CMezoyP@JNT1I`4{iXQgs9-hn_lA@Wj(!0mZiMnVw%jc zE_b|tMH1Zz;P6n4{7M8tNtlTl`2^6(du@Z4~oMk)_07Lcvs16ms>AJG8?>#8UF?VQZ>#i8$mn>Bec!mMZNRL zNipR8Bv#pq4k79L^|yH8h`4ez9lc%Iy!4rime9mg!}#Yn8a})`8hSqDTyP^Uai~KL zg>m31eJP9#u&>~R>A$u##-3#i&i@Lg6wiwpZ!dSheHXdV9Vgp$08zd2Tc5S#mpXzY zKSc+RjMth?g(BRsi7CB5`zR9+vt6(#psuMtu~oP}Z(+)R%IuwxOLS(15SmZ9X3wm( z@bo5`M#_99-g5-q=$FR2Acx5CpQ4-o3Bd6f$9qy-LXx2ed+<0Snzw2&NfIU zi|tei!@|okB11=mk)ZMBPu&6fQsn^cC`Tjc+TB}R)Uh7+=nD7woLA}|U8tH5MxQe54VHZPNGw?nw z4JJEQTr8`-@%|LJI(}}3Z%#vl0BtdiAZj=fhzHyCU~}Yl$VJlLHkhk>F9Q z!&6gox{g_z(2zeg8Dn}OXV#_m(N{W%&mRx#6&)3hcD_G?OP|CPoH=$qnKojjxAr?EVlpNctGsy+BfLT zg(JZ)5jrmYZo;K^hY<3+Ck!7$@+TCG0yQ_a2M+TTzXiFXzywjj~q7c^@7=iB+f| zuBiC-CSrx)T2|F6t9`RBiHEZu0-KFr)>kJyQsNTMNGNg{m#V%p?p1r5HCn!Po#pwy zeAsb&!o`w_<{qo(<`=S-D81!rg#Se1_8zIq2m1J?fRX5Wu0)dXNGy?1d{`;Uk(a$ z2ll56Y46_g606n2IT#7fO%#vWtqT$Ni%g41H-so64{5blamHSW} zuT$1GaCd2qCeV`c?sUEP)O84lkx4L~wM$!Hs^L*b1RJ$THbhs<-_Uru1^{e7T0Q)E znmy_E323J2BVE+_>j1LaN_Av7}_3VzqYWI=dzw8p!9 zNbPVW34ij=&gW+_m4BuJ6bKetGx-=k3AEeVb?wd6${KjPWB#9J7qIGqlta0&q}&i* znci^9{Iz^jB-_folEKS#WVY^ftrLS62r^U z)|*QY_LM>CphFU5Vd9T*KDKeiKoLh_!_0%$j6x;KhH7JtoRefi75PnRFGne($A_GU z4`yDqI^(}|_Trxm+WPFXhQuIh;=yrRu1s<$%Gt#Fl$)m?14TH`Zf7yQE-=*R#vx!U z5cK7cZimlg z!ZK!m^7vF|flV`={17QjGzj<=?1hzOp-OI5kD6YShH#v&dbuHpunNpClo!q4D{>#EdPQ3o2r z)NK0B-nfv$3#nK6M&-QSE>C^{@w$jF5)8x{KbNNmE98QH>vM3VdU=>Fx^W%FG9jVva5cq=gRo!n#uHqNk>K^s zNYHxs&PuAtC+lUKTY_~+7$!u|%bPY?Ak{=Q`zy^0B@{l zZeBs&QV2||k1N2B`>gTq?~O-sj2(h+7u&sqHa#+gsWm&Y@$lB>EoZ9R2~`2Vi~1zAwV>W zu(4MPUJMVX7cj@qQUpa(ObfQ7xs&-ZY;pI2kR4MmGMKR=og=p@pH)RJW%Vs67jd(U zME{B~1Y`IhXcnf|u3BvwGI)x>YB&yC+c%qVs4P+mni<@i$H{c7EC+dDj@$S7QiCf% zTWq^|Ucpu2vFnTG`CFxG(dAhdHyUTI>6u(1zqVpRqA1@0Ab-(dt8hbBg*uw>eDfKt zX5Bo&alP8bl2M26Go944uEeMe{FwO!K1wP()~_~mgo4js)!A=v`2-Q2>+qioct+xu zxb0SUszsx+4%G-<9;;T>Tq1yg1@khypQlNci|VSqakO`JS0`AqXfq}?JdSyy7Jt%F zBcwgCp9<{VwmDjSf-!tlTwFK{q+-mwpsME+U|B6E$oeA*0tcxkoj)L8#*YXjw>?py z3%4YetZVhPr+h<()ChbgSh?Sk&3@iiee3&~`2pE!_Vv%Z#kPH|_ESoYYOiRb+2Ox7 z6zV4+DBHNa#rHu)MI|rW)s2WN>mh+X*Bt#w|0R*)#%YokQewiH{n7puYA5b=V;e1t z!D4>O37B9)@W|+X?igyD*ybIU4+9r}3D+7J!#{U^W7OffT$W8z|DWvwBEBvAwH+U&A3yhrO% zQ~3>)(RGgSudLvD5*9}4YLdJI{#l2zX>1PWsAESJ{00`nm_(=ZQMH)|WGEtxabC|; zrlb>I?XlC6p^KC@4mvg!ln#yr)4H!Af&&YkwYV9Y7)1|Dwvy4P_JRi8Bv93v5S01N zbtA3o+?3Jg+z6Z;<$qo_x^1&TW{CYRcd}gD7M?)*_VXaqQk~AYNhjz{ES#Py{$zoP zOgn4bfrF)xN`F-eqm?oqsz%9DQ<9M}17&qI?TAiq-hPZD16Ocd9d@{7qw=*eH1%uW zAwIjz1+AZxe#`CbFrlind5L!R0vEH3zo~*1SLMxwD1Eu(mF`Rqb`)#Ihtos$ve{Om zooClZzo4qR?K&x&7k6TM^Ayd!)zisYFtX_I{YK$q9i{i(7oc=oztN3n-kgfTTA~J9h&n!vih!@ zg~2&3V;p8v5k`?szO@`?sIX<%WG(WSrMw1T+g7CeTZOM@x>ywQrmI;Jvi0`hhHKK! z?QiouNAARE6Ex8Ek%!3)Xac%DgK9vm?&25zz5{F92y+V=C~z3CV2PhCg05mbkbzI+=$w$H`qrS_Ph9?rXBMdL=m`> zqeRO{oE!PFS8Wv9A{|3CP~YZA1+#!wbI}k5sc;QbbfnfP1&MMs47z1z`yKJ11mO+D zpLe2bp!e-OW3ubhJyUKmUOV$T1~>2s_(!Q@j9AwsPT-0l0J&eH-}LRy}B<; zvkRy}Ng3g51QOyBpqv@b3m$4-v~Mw#CnuJ>!;N;FCRwT=i(8!h2%6C@Ldc3(L$mt+CAvYtREw4OfBN!BFp z{_C@gz~+;076a299#3i;@e)j(Uc`Cc;Np$;VeR{PKvhci0*xV|r=UCd5g?i2emR1Q zUL&C=lKHma%wn;idB_#Nt0iIH;+$ki3g(ZM zkXZb6xrd=@ru{lPnVZww&X!wM$-)662LllEuGix2ou&sEoOSQTG+mW^afoqLvfh0J z-2ID45MhTD1#t}>P2F9tR!Mfr+*rV?S1aBDR$FMl6}P3s#GIOg%4BW*>+X5@SF1n1 zoP~1ncB08*H57 zXwh9)!ZZd^GjABl2OgmZO+S?lQMtEJOJOAC!W<`d-etAgwHL#qt2tpoaz;U_5es!Y zYfhLK3xr-;aibYdmAVtUR(&K4Z9Imqbg6;kE0a%1`q$9eU>YR8v3ErR>{T3@-M#!g_tnRdYNR zJpZ?904@Je3kxK?F(e{&(yYR#AMrY>HWO|qJu9WRcDoC@QVQ)@oVe~)algoEC^$Em zDmN1~A)GjQ_c!l{{NiM%oDKa6Q%OVMMH4QNl?MLvX7++G*fSi3N9yosuE2Gsfq$m4 z)31GAn2%q`>&N-WY;yX5U&`5CuZzk-cmYmCklHG=D>8RCuXmgKsccQ_5@YfMOfw&0 zTBKEd-%J-Qs#n4v)wGCYyio$i0JhUtr}qtLla{~FFzlDn^M~M{fgzO`??);=`2mT( z32!!xkcMI(HF3T1(>Qv@O2*auk;C|9*x3hCYk@U);##f5&VasOfvfoW4owJ7-Y6CO zDarz^q9pN1(?jx&O4;os>Q@SyA@{fb?JFT9WDF5MHp{RZsjqzRECrO6+DTaZVgaS4 za(AysxuRxlt8%Zy_`fNYh|Nz{!7X=uK*(b^A4!c3jDi0B8Y0B|6=eI&MUZ%L+zCAp z4$phmaqHdykGZ-qDJ41Tiz_6sUz@vaH;xX&8PV(X&B*VOP?)5Mzi^D^HRpN z6z{8|!=%TBV?=S1+Uz>xJ$~0lSF*b3;EmbL?Nxg9Ty=7-jVy24ETIU>1hHKnum?-l z*Y@UtXj%&LPnfy(6mTr4u!y;P46h(eNI zt(Fgwg|e?Fmgaol4qf|s0u>(U=mdlBIu*;x&^*C3ADjl;SGepWu1Q*wtGCq zt9N{Id#HCh6WS$o!ui>F6#%Az*{hZ^uw8$2*5HX!mGJ|qy}65Z6F77mG@2-STAh4( zU490-y;DC+cv(^X+zBAA0lL28RhDeHyK4k0<~`zlLr6=z==6C zZ+hW8M{ShM-#YjAmF_=sxvV)0c&*DzS~S9u!lcHBCS)`vmiWD=E_{dkSNespcK8sJ zhhc$30+*nONK^0md|2K4XHxZ4@^KQfg#kT!yO#`)-a_}AFPuFP&d^PS4urO}E*@0t zDjcwct%%a{VgKn0lZCtO&r0!1QPRxMVarlEn!;O|DV#uu)4$)8YLB)QrMXMmq+M?C zp3p>O{t`Xc+`36!Z5h@Qh3XS`YFrcb^jSby@;IvjhFqsyLD=yYOu`r|O7u@+XdXf< zy4!@(u|?^V3rT8e7D}vf7RhI-gqkakgPcN`(BLig(EC%pulB4l=p@A>2q^w_OChsL zG=lRg$gZybESU$H(fN8yMpivW@CcNjy-_e}qNZ5D3fG-hP^FV-gjur7MDdAku;0CP z%T4vTCD{zkh24p4Cd|YW*f`SYI-7Odm)^4{i(au0w*3?rkF+Q!L;?3A+&yNpl-H~3 zmVR`(KX(ZkjiUVg4dhK?O>@YZC1*%=0i=2~c)Q#p4AaA!g{Sax^q7#H7bMQbuQL#+ zcxVc0{8r;on~OPt%|#dT^9!bb!V=6)S;hRs$9ZvV5qZwRgx*Bo%4sYihU{+Vc!YAz z0ADUiK}od?C14x22Ji?PVz$H&OX6m2zbytF-wF>`iE!P2wle7n&&eJ;Pa90V8R`u) zAyQ8h5)4@BTstDoBFPa<|Hf#o3}k2ca=fhC)_;Jo#S%`q2XTcCSbo(_eOAX}))v zVRM5 z&=Z&(j!=ujU&+mtj~b{volG6#e_>K1a5^lEfBYn~OI?9s?k#i9X){{yLvn@oBEfeIg zJ6VE*r2X#RWPMbX>ciyE?GvC0+-#!8%2@?OTg$`s=$={#Lbkfo#DTkxRGD~dfOY;E z`Ocgo_VX2oqSWc|>7$VL-vkH-gMs(O>v18FZAo#ZZd2KNcMU?mGu~{U9Y8<2714rm zm?F8&iu`B4&(y=X^5{YcGMUtwoHYDhZzZp9Kf<-e$?Q3q4S5KEk><~}D+CUdSYEp` z5*QU-4ybcli@H;MnR&l@)XC?c=(A}FZ2{m-+J9&+b~jr!6xS+kD~>D^-$lpMQ{cSF zDT;q~3vu$cFG;u^hs`ff%pkEa#xa}!XneBsBAV{ZKtHTPaTN(tm0sDe?s?=M&UmO; zQ5A1!$i5`8zv#K*pOp}wIR1$D*#cbW9{V2bWZqoTCbe$tPVFC8QGT{xn_+?P}Em82({O}5U&)2BK=r>lL% zpD8_e&=6VtRbjh1gS}oJDVhv;BkFn7Mcjk1R?_+HF(2%of_W+VA1Sa5J%i`hmOjWN zk;pwL&OwT2-O7ixcSy=J9pF-t}%)7w>9QbXQNe2K(~I9gu7+oI`xc?8AvR7X9 z-J3tdpD>O{^W%k@)%&3L9)DCKTQsRmrwP-;Sl@m>y-}i9eTU~0FuePrKn4ChXwvVQ z46gXSN+XcD{+ zC%#qlwObVyzF9skUlW^rL^|KYHaiBi#KXB_cCoT0#c2*;?={3QSL2?Am5l1>tDNSa z3WqMXD-Oo(%Sk zpA?;H!!bxew0N7r*ua%IpDwL9@0E0S@WW@eTKUWx1lcFu%hWyK;MK?}C!LqN9|33;YOvn84(B$Jmd&#tu8H|1;wfZ1NjZE1~nKON@vYX;KnB zC3PwDbCKL1uy~&k9=XgRBWFDgT1|UKcix!J%_hE|-322{@DCU->q@%HBGF%zUG&+k z9EPnsmYRW;fkXePiwoq3caeZ8)2Ulvu&*X|8h7FQSh+1r&%;|QvgJ!lksLxl5S zw3{DTWB#C88KrCH-QL}e9opB#PbKZUj9Pj&1`tLne9>028r_BDlmzm+M}NQD8OxaM z_Nm39n0~dKyM~I>4wYM1YHA*mblHAahWq(M8GE(o+h&3DesE;;9%qLRe+>1JIYU~E zP1Dt%DTsK`H^kxKs33c;yeqtYBDHad@A+9A{HvN>dTG}ybRyL&vUbG$n=Bdl%zVbZ z{gyOYIeis2+ER5btQYjIv#Mg5yfSdfCR)u5F8xLvTq1k{4_;|)=CaUm-jd>4Xb|xs zEo%UEfsTvyMAiojgUazR-~S?pW$YK^E%fkbP@UVugoYpKD(F zlQ~?Cq(5K?J$Z7k&!mq?`~I5m=F{Xd`<2$^j(lyTa{CS2<@1{*X^V6a-lYZlfZ_V^ zP{wm;gB@WD$@W{CmpqxDs{P3l!ux3_z*kulo80aFZWe;CNtsR5dc60dIEJ;>sm;xw zJgJ>S8ua&>cO??%7rHHJnmd7VGP@ZbPmO)Y=jqe{tzp6RtpCT=SBEwEw(p}Lh?EFY z5(?4{(j^8cozfePNH=4YAYIZOf`D{)cY}y@j2<~+z<{ywo1b`p@B95@e{9D%j)&*j zeck7Ep67Mm0SJcms+&x^fh}WDy|zIN^Fm~pm%F4?;DC&s=GPX0VefZy#{<8hR!DpP z_2}9Xx*IsCtUVq!U~fR{{q+h%@c6i0SU0I=oc+{?#qq!X8@El$XN=!I$(lYZ@QjO# z(`TJPRtn*c4wb<-lnW8x;&+Jk31h{Xv8($~**8>er^RA6Of#R{Ktbb6X?Nx`8*uX8 z6TC1f(*js6%YC8GyMy_rM1S|!)$ue#I|RtW*umwdFhR;j)p758pryxixtg-ry$XVP zWPyZY4v@z-o6ZUWBw(AvB>vhcx5ORHQoY?IO7?G9DZroX2b!F?ythfcms*Vg}8 zz>kCaE|IREgfB=R-)pq}O;33CaPIx8(HfOgkSQ@NRw0{+OZ%z!pFU+OR7RChgWo>Bca)&H@2hR38TER4=CGk+WbzBA zAtYZXw?xRtVBBLQM&0?hLY_yGtd5{E51pLfog%5R$;h7d&Na1rWz84^VS|RtDh@K6 zbI0_U-{>Rc!#K)K3v6-VN5AbwyGUz+6-6$ro} zFNW-8E{bq~X!p0<`sD%3bON8+>ng90&8>dlDs@FKPSv9iiX7~+S@6!0F)llqUTNYm zMk|)W%%yUy&uN*WX)U0P!!>xt zREGO7Pp(Y!Qq93HCIKhaUXv)0|_5Y>@cB2qto|jU!{f& zb5HO-WaLgdWNSoQOf)-J_#SoZ8ORTTnKMLpPmzEim6{i7r`@vcdL9A80@}+>Y1W?{ zza2rPel>smRB7fAUo;mqweapR_Ca*3(;Vw%3Qt1?hCf$RYW6l%Jg&+!ri)qTOK&-9 z3k*W6bS;Y*^_3bhm>ymT(2jc%mwK_iJgyQ2@A9KqUmo+njKYVrZ^_%lO)z}SsnZb2 zq5k=>uEh?E-~VJpcZ*0jSe$F6;_K0Hv#g_`ZaEG^USDk)YSj$O&V&n9pPJxTm|F6K zilH0$0j4zW&izp_UKM=I-wD&9H~rR<-Js&2{_~U$El^*l=Np2QZkHqx5C!8ffDTd;(Qrq;{k= z91t|Zm$npbfE42hFz9xKxx0iJ3S>?WVQJqW?A$Qd3IJRh7oHw)fkd0~rbqsr6mv~9 ze+her?dyuI6SH&493uptIk_&3BWbEQ&c37PsuU=!5m@rA35>1q1~FwlNo^=<_pCO7 zCm<4x8=r&$9uMZv6_%}uf+-Jol#uRmEQS&NlS*)EnvZS;N5CVosR8K9k_?eP(~Ctg zI{wR!3uZFm#RlQY-3tCso%E(Jt5gChk|QTDR@EfTbu>S@Jz^@bxHC78`lT&-k=6XA z+g!k`>av|7hdJHwKG!0p%Sf(rans6t{Bo%~mzxE-cT4QKLz#(u{;U9R8Iqwj0KsFm zrU($^(d#{J88ErarrT2r*#vXD+6*mF?og2zoUxBKf*zO!o5$1I_vWbV9Mx`!Rs~tv z14o6Y8G${iHJ+q`!RcMjp(NSuOpeAY)|QzX??8SzFH%RN0Cna^clym=T*9sR}NM~cz=K#JNRcEY($zkR!=N)<6VkRPRJ-VQ=e zat-i#-dFG`*h-)HtJu{jsTT$OV!;39ywKya{h6ALRBtZ9)06~z+=B!MKz}!DcuM^x zsU=|ycBe*bX5B5~;Xb0I0ms;~R~s~XwfnsK(J0jt>3;NrKzPUU;QmB=AFN*8)t4v} zNlBj~MoV^1?p>P_G?AT3S7C7=(HN|UxTdbT`z{?pt5`7(GrZVHsuEBuSXbHE6lPAh z7CCOepo0u9H^>BaWZ)b3n->qP9~qvk1T!2O@$Nbjs@wP}@keEhN4Z!DNORtchiaHt z6t+6DrUO9Xbu9tlf!N(@q~2+SVH{J=^}aW~kE^o3*RILm{Nr=YzWTQ{Ac4X5wTYj= zj{aOu4b0EBCjh)w?j|_&KrNdI29640Ep+|5eCb6|)|C29jt%68>jA~nX^6Y???hRE zOLIKs{b%izhA$8b$0<7kRxY7kMKs-h2`@Dy6_U0$q8@pBvk)}-NK9Br)C7Vj&QSYO zR@awH=}2hYs@b!^D^W%vOBs z)spm=i~1B}L_#B04ubJuh|Q*q2gy`F5i9q&Du%Bc54|JNw|Dia2A(3kM3-POyd6Fd z+IwV0n2eNJ7mskrS}*Vw-df4$AXJSWt9PYR<=7W9HE{UaTx~CJ_?qV9&%0kpk#3b3 z8i|I{U7mA3#qInBp%$->O}`f?^^v!&Nc;^tmDbx^oI0${xj^Dl_eu)nOpieG#8|^H zg-P>T)Y4F~bSKO%*m%XI z6o4VvW~BzA{H-_7myomCtyeJl;Txe6Pp>Ye;QCTy%bSVh)3YZ>Ewk{1rYzFWjE>5A zjhJ+<%29Bp2Lw8%1KeDhr{4Qj7_;)u#zB+dp|>|KwK#&83%TL#90I<_fK4^_oFuh3 zL#CZ#)2EkhdrUnb=8owAU?k)HoaC(@QF2f<72;aNotzt9z*|{LpWdL%oZ$IhqSX?#7U{tZU+ZNlWoll0nxIe=tHyTIadYHc%l03(~iM+Qjy*?%aFq^D! z#!q1>sW@}{+ePyx`{|uQsm-~L732A83p$lJlkbdoG#1HdOVDPS9eBcf+nwV#e{Z zTtnljiG4;!fCn)^zHF~ia^QncaSv9G^lO`*ZE34+PVd)2bo>fxmE{XB!ACMvLX9?K zzu&}z0Xj2>p;AM|H);&;-$Z#?JwdR;hn!8j}>!>YR8FUd?L`F#2< zts2m*B!B^DA^O>#O#vb~n|2Q6GrJiV(&TV)JmcdrqY3yyA2Ujr=_BJ2WuvzrnJn=R zAE%ynabD%ABc76`=jv?JbF;$Dyh6_*2V9Ut(|1-j8ErZThe)>ER+S<{O>fpfn~<%l z&8?7Bex5*GCQ(T{3k8!M41aK0K@z|C>&K$rRX(^>k)ba^Y2v2dap&c) zhzI-xU>@s(s{oFgeM#sm(>}@R#(aF0> zx*9qI#yh{7>6|ofr%YA)@8l1e+n*&DG)pP}1{ma;3-PG1z&B>j(^=>XpAXzQuX_Gi zuZRjW{vfw5e8Jkx8nDu0p{oX5szUe5IHN*-@|%MIL_6RZ40Owu;*D~$N&a@&n97-E*2 zg=yqW*A&6iZ&l(&t66{wu>7eZy3Bwsy}o)Y=*cGQA%kj()6)4mBMWn&#ejf4$UL+8 zrNrpyC0t>fQ1I1>IIP`FPsBSc*JjaH%O;S5xWj}nLxU@Nk;7!_J*D^Q^Bc;Pxqi!$8&gDQC56MO_WWHbUzk`7Od#4wq~`5 zb9-%_D~8Q9&cx}#1)AU$<<^DHF^$%EWt-%xi}+j>yf4TzJDJ&$oYaEjP8UQYl2^;Y zFT91)PU|pPM+$qo1+bQi*>c=aQ5)g%0OJ*13#Mq4Lo8zPB5+wbd3hoZ$fozTq(QoG z*-yjI(%~bWBEd>aMvy^)3zA!qT88WG&`%gTPO}SIaoh_}|M8)McsQM!-YaZzpl=O> z)bcuppje(!Md<#WRLNG37*bN$gwGH-wIa!#$8dn#gNL9zTL0Sc@fLMTwdp1!8D7}h^|O#YY~}&U(^ao zo+Djut!I}4^;&m(`MqG+H=i!pOh50HMGPV1p{+0ipTDyr?oZCX!~GLA@XV%OtkQs1 zcjOb>vc}-}00HymzauJdCKlUMtUest5z$We#k%Rylib*Gk5EjoinQ6ytO3mChZf#O zTp^qgM?*cSzg1>hrr2keXgamq_Y-+AV#|TW7D_Nh2t$+_d065yh**8lj5WinO=%B5 ze;|$PxK4;bOWkJ)k%(~mQ>G&#c}N;--$;21BC2`ThYh{QDg0Te%oK8 zN!;D!!OelD1j4)@iQeJ-knAncp~$#%C+xfwg%YSwXt)s4yk;v{K4%;O`ov;lhL~mw zy?F(c*H$_(+LXkDnd3L&^rm=Ok7_YjY>sgt-I!x|cMHD^RoL;&s#&YR>2}9KCVS)bPmp4&2oLc=$lPTH z>t!R;Ny;k}K*dmjwR&VA)^=fJ-(#gqait5Elv!$Y=8`pMi0f}MN4^abwg*MfAroDM zK%1jURgiS+anWF!J0k$}a#ynQ0^gYV%43XU#^`FaW0uUDP^~Xogz~r-`Bv0_j(;k3 zN(1Wi?LWnv=1*qGeNr6Gw~8L{KGbmSAp&`IO@f@iN;aeQ=^#$;m>v30e1_EOpGS9t z2Q(RQs_>V|Ox;|YYC<*DZOVvs9PSr@#{k3$UIFh$dxjeKJ32$*Y#cdEMK9J6AbaaTW+#JM<7U66tNp5gBJ7D1G z^3BBGBgn!0GC=MZe$}I@{@d>E3IX&)V!)wlJ>P0oO!eC7U<2 zc75}P(sbzAna~+rG%ZffWtYC++p?;!t!jF%*&nS<5-VbaADQL0|GIP;5RCoQ=@H!5 zukBaIvQjeldV5I?Kjy(uQe?yeO*!Uo$oH{Zt<47D>7vGX?@V@rs9{a#rAwRWZo@sc za%-~ljINBT;dG8-kJ7c*=j+5N+nWO2IBLB~H$uMH6C|4sxt+vRHPlXtR`v|L`~L_6 z()lFlLhXM=R15+<@v)pgr-*_0J_f0ln#h0Cxe$KpKIjVbIpH0p-WF^z==&q}#@&+bsTqASPCqVDNQF^ zr&pnOLQ>P$b`Jxda=DrYJXgrMznhv>HQtMEL@R|Os+eFa$X zj4RcAwYj6Q-oDi_H~tjXSW@^Y3{Dt0>}`!y7wC}BXky1iA?YHZ;`Nwt#z+D@;ic6`R@5`R^Q&31#e;AG1!?sO zO0F#YCj;9cv28Sy$T*qPq8 zoxv_ULKQdC$L1l{@Z8zz1n6e$Pz!9SZEv-eE8%I~BG5=3yd^LI4-Pc}NqV=$wKaRg z<0a`83eLtw^~j$dw@6tW+{(v#S#plhJWxnv*w7fL8^1LsYT>-2L3LtQaph^%!()aR zc;-8DcaUzAUXj3~x>~L6cOGE`(N@G$Y`G@rd44<3-}q{Wa}yZ$uQvwsQHCWZxOQ3G z2dL#R4n2{;a65FWRlZ<#8@V@Aj+QqtlV%yGoYW;GU}IkXbdWAn*7l3U&fsgS-qrKr z6Tum@H$C|5aeYxh!z6W>GQ;=|C){&&D@Mgdc$Y`OpePL#dCG-1$Mk;gtK6@K&ouHe z(l0wx(xx^u-Q#)67fGdp#St;;#A#ZJ65E}mMK|Oh@^pA@Eux;DY=}mV`8S1EkCeT@ zNM$_3EalAooaxppK*QtI8Rx!vfiDK(b0`z(m}Ork-oqbwql9f2^p>uC1>aHLw0c+< zZ3nv4rhMy(np8AIYJISnyXL9H+N!oSiU)HNG%mL12_?{?)QvN-D*HL`BAh#Y_C2L4aGy@$78ZMEgqA9)#~y1 z`Ie1?HodYyEIq;BJ{@lQzSPyk^t05=7LVva21EO6wz zXtF?zzh5pf7B5q8NW{@@v;zrHuO9q1!>8ILP>WginLHcgYR^u za2>HtRSN!UYhCbX{ocU2@7}9RT7F|}-$|1PSonMo=*nW8&vmKG59WuBt&VwKCY&RH;pMIM zYvlm3DeA*$h_LyXxq3R*0;Wy3FOX1M8Qp1FhNF2W+GJIOf$w11`&m#`+#z*^PW$tr zw!NU`VH1Si23^UL>}*(GigTD1Ow`$B9ZLdFnnY*>EV5JcFRXJai2I)esor@LKWPTo zDUP3KhjX(;N~+VEqerE>6doXTjJsjE-%K(2M5EoxE1Y{k#SiQBJDPn@@3UU`+yy{d zRoeLpyaI!x8dkap7-(~uVZ3gVQ_V7ot`oaDin|H%bAdYM>t&;%^b`c=`}Dlzs5v<{Csx^`rNRRM6e&KpMmv(tl%kQMlJHu`}x8H z1I=xG6(w7l9Q`l3p{L@i$a!|pqi6JBb6Mn6oVR!^6?{}n_lRCIl1e|mvp!KQp0tR~ zt=2_(Uz9u{HFDe?j~cc(r|gC)$CB~Wnx|o>mY?++qR}uiy7Oh>BZ!eIYm0W`XsHaZoy)4;_@{ow`>PNseT(D#vLEBKGuY6 zrLeALk9nK$-!x}mvh`G`*g_jo{kOM8JNYCzG}f+hYhOCd4Q`C*5rwK>Tkiohw0s(=aK?cu`6Ld$A8c! zG`Z@q(MpbX$cN<#fUGXQq|&CVkvHhOgT&zV{#%z9agP3u1Vo>6Q@ZV5^xpYG3i{!; zhoMCPVMt7*x68?&?Q!3CREs?8xu1X6-y(u7v9OLe=nchNP6OOhx z)9R68Vffm4W%YEasUluABcRyK*D` zHzIEm&v2rLw|x*>MD-b`dYh67jaW9PoL7Lup%%kp5y#qOS{cb`WD0*aDIG4>VihO8 z?KAPfZ*~FB8;$F+U}^?Chatzg_O^tEUOub%7Gp0(;XZdC{WnceXrsDP`kJp_)8bDM z0^XyQt@lh&IVIN|ik*|S_X@IQM`Sg=UN>AasUtbi>a@4j_PizOlb{-d{4dI`;HBp2 zG(Mdi9R6&Ge@CSO^4_%W9gbYSSx@dAj2v_y$w(TH-6?yXAW$rb@_ALV{j89U&O!Ym zkK^+fhu0v&9Om^r^7XwtX;qOjvUXo(Cvts~5)DLG-v`i&M1yV{LaDhow4sC;Mn0(n zi7_i0jl=;n%cEyW1)D@vHSR`mV@$PKUZ9et1H!G4jgUEV)77oZ$y9}}HRlgoWVCq? zGqUC^foAg>U!02sj0v4|4^^08UeOj2M_laCDyF47kptxD3uQ+^!GCS|hkPba$yn zVFmK)Gu^u}i3y}oBr*w6Ip!cY|cge=RFeX2&p&(_0hs-b4h> z{Ze|29I!lJiA|Sm#h^SN*>URS>se0y!xSOESK8af3G!=+JCyJupWVDFIr^E&{%;kY zM0~^y#gu%P-Ld$;seRrRRFeZyT|a9HY>W!g2Fz~^`ipLM%UV(&R^(ekYnI$6Br>#; zt$PN4%e3_^ldVtA9Bj@n!qzgQb^gf;&ycgMp>6|IWD#LbrO2tZ%hc4}kum*kSu;5| z_akdrBxM_Z7QS27B{btTJTg)_cPBFCI{z!}q00OFYAu;o_MZ3Mc*IGj4m!TdczD`a z>SD~;zZi>4hb#IqxmRTdp4%6Fwl0)S>yve(PY#KxYCot~+C0G5@A zsIKFO++}jwW$%^qoX)dg!i+;*q0xPcsrYG8I_YI6p(l=RR`KD-2$_$K~W zZH;EAPwbhw=4vyPtg$zG&v7M;9Ziw4%#X!{)6%1QQF$_xdwvdoEMjLR`L+s=qhPwG zudQjTz)bkOQ+aiPBM0Bvn?7Hw3;phIWZK#{dk2z9;zNiXmRYYmDLB(&R@TDeWG!_n zW2qKEGrA88!y%)jVh10R{kkYMr1#wLm6OM&HAS6Ccp4e?E@~rcHXmU+0syWqgojr$IW3&Ox za7I>6U$ChI9Ja{XyCzD-7W(419qx=X`cjNFK=?@P12A*hR{}g-3l~Epwk+u{ZG`)d zI=Zh(NYoG6Geo1sJRl>Hu})n^1YT+|CYvUoQsni1?Zyx8yvAQX=i%?3w)xQpCvBd( zKUirA4B`b^BJD`|OqQqlsxdD3}6s+8~2kGAX=cbd4CNo=+p;ND>_}g{Sh^GNDpTFoCN;493E7KxP#r$OOBb$@A zo`|r!!KBW2uMy%g-UT9{AIpJpaMiyW|InK8Msf&Y7pY~OV9gLsobt?9LddGw{xfOA z>N$tw#1IQ@>3szFt=3*-ZvnVISy?}W^!2A;)&3R|$iffvOYG24CYvSlKZf?Ejf7RR z;`$o*-De(O(_-h&)FV6o*xV(DZ8*!^C2uJVhT8%%UF~9qzGjtqwYf`i0u5)I8T{GB z@+`91A>9%TS)cF|1bZz6ud8wc~^P&F>Urf5BN1dtF!G3k^Qji*6drX_43ju zkz|wKTjP~#TTJLsB|jQ#znH-$*qiH9HYl?(F;l|itg-0m+f_`?3Ddh9QDQSe4Eb;} zrBIp~x+`sVrgzYO$ACQ*(-EX#?ofwqY0eJog~vuyYLpNaRCFf=25Tw4VS z&PanB3FRglLqf7$tX={CXq07JyO6Cq9EN1?#*~4(7;-hQ=DmBi*;ddW)>4UaIkT>< z+12Xd=WfBK^}EbbWhR69O7L_^@!xhA7ILP71N?`x6R{nbF}V(;v_m^)rv?JP<2)BH znR8F8xMzI=nD2IzBXk1zh%aL}$KnPkyz!3;>>*>5DTzr;n%EFF`!Y*ms=Puy64h&2 zx15F8^dMv(wOR|jIw(I&vpQ?Pu!>Kz?KgfwHd|ps{R_Mp%}P<@?k8o;vOf&=^a=uV zd4fmz5_>B%R${!f?3#gBglVh;FtjOmq^&LxklPn$-vTL$R74f~TQDZWs6ALNDl z`8nQ?8{prF)#gT-UAZ7Ie5Z%2p~MHfJX*edj&sLVxDMu4Y{c_JMni#hzT0u2tVidz z`e$tWBnycf^iuJQ%Ns}OHcJ~v3>Y{edbCgLllvcLlKxLxarzG44R2t@;`mzx{bpQH z$mL{POX@M)?23a zpKATZ8mlaIZZvxp-tlCz40qi3ONaEWjbHFQu z1qcb(^F*3TFD<4OJxs72WL}&-9&ID3VozWb8j;ItI+V-OYsm~a^`hOAfgQNhx`P*Q zWe#-irqn!`o`(uR%KkYu`Hmj7Zx&L={O;fmtE$-dVQ z9gPp~p8B3VtnjPWq#pT=n*KDPZZiN~B=K$QvpItGt!q^mQnogX`h8fUM+kEQon!Kf z{XOLEsPAF^@oqYoM&vyAA>IkGvMfG+d7=34mtB)(vhLcf3&_2&0UGHWjUl!@(M#2t z24yu!7-pGD%vYfvZq>o|1I9yT@rZ36GfT{zbZ5dn1yXEeNxV=b9>?A326INb!F?0w ziu1Ekt>_)wQ;IU1u>&ubg^u6>`^>S8EZ;n7+CsXJI(msLUgCSz@yQ?TbC;HniW_*K zK~rAE%||7GMT~iTR#ptNp<=A;WFi8$VNr)M*qIuAAu;1N$fgUgVMO^xs_~Jr$uwkMpXSQPwu!d-;XG znOl2hhC`n~S^A+dBm9Vu4yxyyy`{6hIFJ8}dMaP+ql zQ-b|^c~gWwK!-9?9pkDx`K=Ur4%S7FNPyb3jJjjg5!K!&S^`n$2YP5l7~!Y)hRIjlN1@gC=yLjJapPZHRI%NjRE&KH2D9Z9BEWnp`{R>J_@qT=drWDu!0fvM)a- z|F#iA%1{6};nGBLg?v~Vz0g^n!jt%j^^t4ySONft_bDgICd`GB}Ci?i~S9(gE zM37#Du;WX;Fh!VG3Uxat|JLj*ABSvA|8!?;OtSM-f?>g^es3^#ohW~t`;d|3K}+1< zcgL!d&My*8CsT7x#A z6W%l7K4dVlE&6Dr`}|}V8ZTi(5ih94874jS!jfjll4zVPeXh+*x{zbo$)vEzR4nL- zXT!6MKzUT`IW%L7XvHwP%$p!d0HL+$PtZC2Wg}tsCOpse{4l04IkTX+Om}}1KWhh< z%x@?W6B1>-H3%)4%lF+X0Fqs8B|?p7{LcrWr4Q_xPozXD{m60sw=L5G#4R4BxJ za(jsad_Wy-*h*UBftk~@yuXTc6ctbtIrZ>DX)50M;JcKYCR9HH>!5gJ93+G3BofoJ zHoHVRzBN<7aO!DV@?b8fNen$zFS&#ah~#?zX36qCR&H1SV0yVV`?F2#&c7zjKR7a% zl?d98>A&x?{VlcqbFbb&3aBOAOBJPRM6|TDepqF`4R*e29igVVGd4p;dwJyEKBpa; z&mOr+8h>@NY0_)`t;toQqNBzLz`7FhXTG22D7gpN>;DAsTw<&%EXKEg#o^I=OqgRZ zVU&DKV46eLTFMD!Bo+NzRP^oINQ^Mn!~b7wZjcOB*)3m3OW#C#VImi}r}1vbyGZDJ zer=ws(_a(38%8lI7AkVFg zaq0sns$Aqbuw5#ivOl|U%U#&fnIe`SGeDp2(n!KWJc zi(ya_OLqLkuIE9G*ABlvR@>*rK$a!Zbjo)iLV*%=)tKHURxqF?s4{qV2b#?CNG~cM*ct-Kjo??3X~Vp_l1H-A#OndZ8P}{U&EUHy zX$tMSk}-ITdb@k9D;pwaxjC+69%DvtLX@p(dRlMW;yL9D$e=KWsOi3g>r0EQL5LE_ z--|CQbpX2oI6QdgeM*n(N5@I?vVxmIKM-P920l&-fnO9e`$1s@xyu`Bf>A{0zZ2*- zN6shUvxw;03=-deYd&38_P1=m6$}_V>F@vy4W5ZIy3%~K|K99L$1|5F(0U=9eZc057bmRIjx>{=cYB0o*Fla89;gm) zXXQ!W@x<0u6m>h}izQ>Q8m#m@qj_oe)N(VbS5uLU<^_!(36;>BXv5CcXYZ-bhlg_b z-;jyi$c<-Lg}!qmiE0d!6=@p}oeboY_Y4TF+sXU&4!OqY%O`j&&NLA7j9o04?+8<< z%kH$5>kWPIrm^nfeZC)F^8S6~%dmOPXiij|MTVe>_r5e`hsxVj?4P8GVq`TFAFu>M z=_J28c51u$4>JL}1~Q8}RDL~|=|2Xx4~16sgob^3PW>;Ox2vRX>Pu|Vr z*|{e_u1WXUA2)0yId{==~MruDS*eBrG{8R~dN*x(2j9%{)q%{JvG^_I||JD&DMgH{@m} zFxCmtK3FU>vDJM*o~z$aNpu}5sxi@)fLUjcNQ`K=YE&9!_l1b9@gpKxKtFX0(A#1w zmvACq>B5LtlDF3rz5zPPqZWpit&hA3bBLIPH}DLRgAh;gRq+PLp&zo}-^gP+ZtRae zkvnON-di~~;x1_GsOQj?VIu5Z@_10p(XQ#8?fv1w=t;NXA!2FNt z?=Q4smGhpY%;Nly3D&eo;&M)<3ml{H_pbcq;d<$Bj>Aa>U(oT+j@q$R|r`CmodmBg)=?N?78`1l0*!*& zqR&Sw#5{k86A+nT^Rimxc)Zh9dGWUwsR9=Wrycw(Qu(+P!V3z2L z4PEF>7x(w3*(!QuTu-+2*lfkhsQbj(cHY5q{iBkWR=dT=-tEuRRg=x(eI3M{sj_yM zy+OnRu>V59@nS=T?|anqNyiMcTdxFOAnLJ%h~MkX<$G!O3jR zdIehsR-vZBb9GxCT6*bsqzXPyg~h;2$yIOiJvflFT4Q0n=uS#iOZaX!uQ@Wrpvd0! zn_}zNZ^|3^Q{8Z=G^N71AZVA^P&5`zM`b4o#SY zn~8K|j<1T1dHoawZ$_Tnpt2Xmv`l(}VtcD<8ll%{Kz@ttS11D(%3;0hiCmu6TV}9G zduJd*auA4ak9TgV9uKvlTsL?e-*~tOU)tAlIgW@b)x9tt;pZ4=%BXu1vApGRy2-7% z1l^LdRa4R5LvUG@H#4X>uo^g7jdu(_Q_A$fmdO+dQx}6ettHqrp2u#LUM;pMoko+X zsm7YsSWOOon{ zltDR5s%LyJaATHhRt-Pfq)LdX#(+5G88{Fo%xjK7WP(*DwL&AmvRU#OV7J~Frj`0=8F9}dd8 z!ZL@IW8lC1RDR-o%)RDce=$J5SI2sJMnW(M0YuYP9)LsSDeP9?G3A7r9rqHPh~G2F z^bUkzXp@RtpL?n+Aj&H=#BnLfiDRn&>~&1UaOP1Z=G zB~6ZB@rl$rJRTGH)g(*<4>lv*CLAZ0IWNc8?=n)oaAJ9FptC-c9ra8dPnQI~5|Hfz z%?51s@Oo6cOg%_i@sWa~ub-s@u83s(R_TTfW@!MtL&>^XOJ0cTU2}=bxg8Mw!wD~b7RT$XZ zeH6;iel==y(cX1LZ#WE@@tt&z9Cj)XEMuUsQbahr9ZD&s6@mAjH}mMd|9_k~`E8zrc= z#^6B#y~id!seJo9zJmJlP9P!-UOKbWG1a+x0|PtqP!cipALG|F?u+D2K2FZ_PQbNG zPyFwFW_`5EZr9dQx#N!^(+Z}Hp=j_PdH-|by(qYjl9JtP@ay+CTOn!s9Lr$@Zz1GK zDL>5ZaDMJge)R9>D4kXiRuqk_n^9P`EYIKji3{1f(9cluzt3n^qYq`xQmw`BR8a0x zph+{JG%->EnGj(u>~Q6-8&C>K9K66z3FbHNhYa0xMWy5_mE9XBufr45-YgQqlDKFV zPs8fo2n)T?*FuXw9w_twyfd2mrk*R5l?BX4`qE`fM2r4>0l>OnA;_qqfsgl5RB1k% zB=H(yY4rX?h5^&%2u;-ZMLVm|-RPvd^aH%^M_`A(>;4@l)I?GwIw+&H>$U%cCb5}+ zAfEbZmt%z(=!+jrJ7|r;JG&yn7OVG+LALnl7sW+Dj2?OvGUwQfys-n`?w6aJXHcYt zUwztQ33zX)c)Q=|Y)-46#)kgHoF1Uh{tjvt|A<&kb*(;%+*9Z6?gO-&+7Pl#m1^)Z z>Hai)=);Fx%UijXrRj@H#oP8TZz~RU1oV(5O0?aeJURYPzuDx7q=vf$m?>Mtq~q?HaCYn1?P>m5gvdr?`^R;g_#Ti<8 ze*k`035Bfa-1Y{5ef!*WPzg^?w`*2O{8rId@D#xfS!p&`E2K13qZ|=ka*;b_jd6w) zOp#vei;Kh6|E5a*%M$sgxAl0B^29qT^iPRezpy2j`&O2(+MzTAm zg^LXtl8y;-FSA{xPqGg3O=Ot(rDz;AfM*_it&DyW27ccpNHj|yCA{BYFRk0#Lf0K+ zKg&EwCw3ZoHSId}Vx7pp>q}P~Xfi4-^F{r8Y~O@m;vT^|QRPo~Q^=aOPQRtR_=}*{ z`dsx{sddXan1k~Kvz0EV)p={V-&HU@6jwQ5)8?*o`O&*9(;Da|ZxO#b-oNblNrts; z=3{mkKO8-}zc0NRIDH$_A$d-&D)r}8lhHbUG+v!sXY)Q3ATxGsJlNE4H z!kUKVa~IoyX>>XA&2zcxt-jf-kE-n2TX=J|&SDVpsrS*=R}WO4^oPD-Y-kzYMsfE-3evsws#PcpEB}ubsd!*E_rgSyxgm$c*_vhjahk%oNHZ z`ZU9y?oc}qkqipl9kVzhKhEb|H|p-xtHha{4> z^M5Y00jv$K^!)z4;Mn|Pr`t4?`bC(FW!uCKr~Rv=It8GyOl!~{ZfDA?o3AHWJJr5Q zhUBY~LqpgM9_4;T`xOmM8KN{a9|>PffA}?*17OF=gIQelE88xRkDjrRM5#HvbWL>H zd(nS36}^H+ z=xfSs7}h>kJLhJ?BNLTY*6thgC<#R{PL{XSEi;+cgcc@{WHrV&&5o{J$dniMroL3#SKz+0!fL~%P>&4NO^ERdkbnYu zG8S(KuOG4E^RKblToHWzznSiTt+&EJoTrG~xk;I|brZ`@*%OQ_yG+68)tOLK;#S#w zl&al;snchk*iE>-`du$3`)AfKBPYZ&EhN;p(WQ=z$V|lTe9O^gLGf+OM#Xn(Wdwcc zt6l%<2NopMjEZl_-{O!zbjrOCJ8|2<8c==f#3VqDz`Aqq4QYbWhxvq8-}1q%c2nC3 z`GvTp0=owmepxZ{^dw&6QbTR5(3LM2nX2S%;)W>H|`|#)5yg60*gfC&r6bW9h^4e6&>Mb#E!O|1nhFM0hB% z!ssMqu$H*H@V#oct=#V)S9D81#qLK9D=lotG`h^G1p__kX0?~3SKL0Z0sh@Z^f{?lwYXqZ>E?l!DZAk_&-!Rn=2aILC0Sb_W5|)*>3C^RS zpEpTRPY0OcHX5=)C}C88Ra@}6(FQA=eUziNUOOmm`F?n4=eCQ`hs@mj?)TW9au!vi z&jE3zfsO>>Pjj!I=9UJs4Js5Dr?rW&kLa5$a)*ZQ1@4ePjy(TX{lkmxKc3b9s>x2W z@3F30e?D9|m$ToSCe#@odav6T&OL3axUOL?Y|4Y3>EYsfBfCB6I_%Vyw;X@w>B!m! z%M6ycw>UoMvN(8J@vzh1Ve|Qt-2|ZLc|domEcCSX&1sG8HEkso+2A6IZQvrS5|$o8 zOo_+*wS+^m1$XT+P3b{B6s#-Vo>Gv%# zB?up8l+qgLNB4<`{uDlRbSA$HadC@ew51i#C^x~yI{kmFy=7EfTh^$Jy9IX%?he5{ zgy8P(?(R;|KyY_=C%C(X;O-FI3oE|L>2tdK^xNbTmudkei`J&ECY*_#*6&_q^#x_v?5| zHBXrm_jVEyD7gW*A<7Fw6WzjH)ng|Z2CxeYIRUwDIoj$cYQL#OGKfs^7 zJPxz;`Jn;1QK^P_Bfhl*tJAwDy4{U^Y5a{oA`Jrb?93dZnHR!WB<98bU8ng=-itsS zD^e`@I89G59iE>bR=w#F{{t{*I0K5GzzK+DtG|C8t)GXj2qzz;6f0870_O1}Tr}%m za+K13eu=&fHq+6%?;D86P&Fh(e;(2YIY2}QIDBM=CE5u(nWrTG8m;3#$bihN5$djvW5GC7y0o}ENz+Bo%iho&ca)u z+Vl!@pP(vu44vlg`vL&g+<)1_*vQQ$QB@IOGKk>yq#@@6vI19KZN6INc$9kCTjG=ZskBB0ss7odC>1| z3^~N@%ixU$f4dy!&WaW0bcX|%0Bo_*UUJ&{J1FOS zVpZE%4Bc_-eODQhjafjW?1b-`dO{|^CFp$RIhD5-59Y@b#{;LItC>RR&Lt4ZZWrWp zio0x%Q82~HM~=vQE3I2wQl5^Dk*qYF&BOpa;EIL|6KTD{eRprRJDIoSU?=X3Z|=fZ z!R2W*;<~9onHdpWVQKVMUi;}4w1R5*DFqG!i8A#LJVb?Ge(}Lh)Nent-PL5N$HIpL zW({)e(^96eS6y-d3OC#9WBbv8YKZ1V*n&&sdPd9FRBau{s#--txwl^1%f;3moQ>p< zf3q&0oNCJBbwBg6WcVuz)UQT>q)Ul2Io5Kho(sM}u%x|BRLw`| zA+gF-fz5-q^Im~z`FwjDj=XZG6E8&gJc&ia=x%4E&VrYjir&nmo*JPyZPmv`IwI8! z!&~GBaa#-V5j?n z8@h^qC5}lA%s8Ss=2hbcX=9w^^v2psvOJ<}<)tYftuJ9UQD$W6Pw!|;9_Ibw5Coky zJeQ}&2OKDii;Js2uG~A|^>Bs*MJbPsF(tjJBm8Z*+A%7MjEbsAKI0+zF*lban?mIK z?Hq|{?F$tQXCjV+$*YpzW_+M#TVAV)1_Smp7~2_qr|G+@^plZQ-eB8`WW(9U!IE_i z{zl1>wR>7BTO=5JA%@UZ)&p(jy6SZ`^u-!V-g+smAED%uw&y=CpjS4yVujUdqWx24 z>HP)Q%~OQ|OVsd)4hq${h=X6GBi_#OBIyut#H>Z_fME;UO{!R&-*DdO_|&G{Yq^52 zh+Z(xj39~BxEOi9M^s5?H^AW9-nXkQpDaIJK~-uVjyl)5&-$mRsjl+ zm8swSO-}M99Rcscy{2;}f%6h|Z{@k>Ux+>F$+}j`eH+w2FNA@>2)ox~DpzjNj~$XG zi6r>p!O?{^M8oGXd`Uz&Wg5iU5BQ5=$E2pnsIlI7t3hLRsMEVm*8MZ_ed(}7s@f>~ z$=n;eQ*wSNi~7CUo@=i?K~}03V)mZF59g;LIlCeg?a$!Yg$CXJCHLR9&_!edoW6#> ztRs3@JmFCAK0W)ym1x}d35#+H#|$fyInm{Gc#|{UnZ?$4r1F`a_08(8M49}ezHl;@ z7C6|Y_W;Cd%Bjg#qwxmvkZj6Dd*4Bu%4rS#l(KiqttY$1-tf4P|jiUuImJ zz)pN{tjHp0*|cfWe@W zNto9%aj-s`b?MF@QH4ly_$8Fak}Bg)Uu zKVw1onuFQb*Ow@3PDx8EwG0(7YLef&bidpEANuj#Ve4Mx2kR;rxt-Yftg7wZe2NAC zhOe6xK0bIWQU`$rn7cvh;pP_ah>#A}_iX$l_hfU7ID!XLg%y7Zsc!E!7-de!Kep6) zKfgnWHeDFYO=kOiBj;eW&TdnOI1PcRWJZ^}q)}e_-YT!MH6ZAlH_!JSu4X8e=q!Y=(-oW;H(KWkPr6ppz`)Gc&)GwHlFBWu?Yoa}dXzhUsi5XPSzTiT7tJVY-8kO39ZaA;X%Dv|lRAZ~CKIpK-Oj zd;@9c`-Z_NZM+`z?7S}_)lNUinTfD+$zxzQ#P{tu>6DAsz@|O-82hv-KH^FOPJ$&p zpGfA8dpSQF0(YnDJWk*d9X%FhXHWjiEzA!LfZ^ z(BEgZ{gC+CZ{E|e*Jk2Ck7j=cCsds_W`;4Xp3 z+oA1zw~?MsV!667%+FT~-h0UR_$Rq*pU(5{GS_Yd9z8$KxcBrsqvEwMs-bGblQGq< z(C=~4tT%Pkd+@HmsuaH7ym#k+R-Y7kq~opjHD#S^f6n{-9FBA0k=*@@*u`?rbGw3` zqFuN;^^dyjTvK3u{dW{X6wa~jE5e9qX4Q2Q#oIOS7g{O?E65E+NK4;-o$qphplah{ zjM?Hh=xTI^kk*;^7x+?=LLl94`Tk5uE+j5+q#bv87M0U1p~ZJxNDle?K+>MnKO_48 zl4bwUh9aZ`R6q?Nagcrli59296t($g3E_(~anaahC>qYvV@1H} zMj>)MxE4z`g`qz_! zsO#SfD3Jowgmc_i0*T%~@v=THlMSTJA!m)YR4mk*O@iPv<$(HC)^!ClzxGH@>xIiX zZSL(on!nZVpG|HhRdIB5#5Uc1{F6HO#pom!{PZ@AI(xCb?$Z70jo)H3x-6N@ zjAUDZYTxz2{)Exu3EIwu^#E8)dt*7#Zr$v(b0tgglSxsisoyh`e) zwc3HQOIDZNYQw54l|WiYNa;)h3Z{>S1)k@fZ4+6~Tyfd`65i{lIhqY-vWzW*FEv?C zm|Gk1e0o{KEz;`sUX%C>mB1|1P5<6XZ?CPgwdP1Zq6?2C&rK%N{a>7wnhoijDZbM< z4d;21F~KfSF5Ow?8;?OG$u-UAbIi`Io$*e5|7|8XmL-7ZE3z<#Kn_X)^-N_N32zLi zQN@o|Be*l+pCgpwu2waKPErn6c3=EGFhwS{^3~NCT+{JmdI>Hr{TlJ!_^R4n`P$K( z7MxH0sDp&`TQ-`9%;{SkdZOczM&44fAjl*Euik zeq@duS>$Vnz58{Vi}-nX=3N%tYyc&7;AXeGj;)Low+mEOW$-7!t2dqGnU6KP7z~m@ zPXH*5>m`@=4HhJ`1mC97kFR6K_&uvBgCfh9cA_^U7WoC*=x%k4D>Jhb=?}cS-$S=A ziktMpRq>I!CXe`^JP&|1-N9&1GzI39yLXQ3a5wH^)0gn||MOjiBV$rR7Zyyp6=^(kW^7p#Olv>S82eeKoaVK;E9KG^Nj0hCC`jKe-S*tg8+PnV#4tK#<@EiGXj0T5bh?_5hP^Z(@K3zD* zICZ_H-{9Fv3r{Owd_Fp2sQ}#hE7fFrwqttRcB1{>p1bXJrGCr6 z{julAE4H#z0^@w`?{mCHN(M%=((-k+LiAU2OI=6fxSKRl@Hv6R zFc>nO^t&(q6J|SK!IAJ@(WO!Ml){ofz7lV>DS|f#&mez~QzU|%DP_Q<1oobO5yNFM z;b3MI5Vpg!Ah_ENJp5?LJ+=yq8jMIcMmxnL)z^iQYV-W}UFsjxZNO9nV&pJFumiQg zo`mvI#p_AA=XEHQt&ptG&h?=P=H@vziw^_Ggxo zY4YP?p3I4c+-o~og$lE=ycAl*gARWhnW|RGPWkJ4>!p`nEhms`HZ?c(WU3m$NsLy$ zKe0I!y`f$#;_1X~kwwe9yeDxajFOL5*>3AoOFQnu3I-MxIyZx1iE{8j5@Yno?1ea% zq?8O=bvM2pq4X18m>mer)7P-K&hMGVlnMz~)b;|M5r9l5g-0}Y3rxcjSk|ZSu~1sD zSCemgrfsfh{#|z*Q9I5wlA-bwq#v`0q1xbW06<^jE>~PJd&23T`(=qME`I`xf15`U zz=8Jq7L>8X}!U`b0b(Zyge5;euq8K zo-hMxwHgeiD$TRYdK^S=wC`f1dG4N(DNf-?O$NNhKR)K=C9V-&IO%YQHnV19+W1FI zb~8ivG_8K#d1?}uSe3og_cpk|)#wz7LHJs!L~rLP?dnQ8R2+ zS4rbIEODR%LdF7HLeM2GIb?Alyo5e~ic0qI^v<1Y&qM)BNl|jLBI4`ri1KOot%!=h zZQ5PxbS$}(?8dS1Ui|=}+wcz!GHKO4f3`{=eSIP+p&)AM`yNk5dFnYXZ->12;s+mJ ztY2(XwLH!fes@N#T#2h z@bgsVi_Z!m;~^VGRR!}~Z+t90>e<`~nFZbx~iKJ81L#Hl`~^f$W` z?+q8nIc;)Q!Cn2#uTQ0&_++x1pW<s*dv>w=dMNp0YOZ`hR1K)=9?qUp={$+w(?2YguF;nSQFj6?V`e>vky zUr+7uHNK5rc2txCUkrUDCOR5sDr!%hii6HxN&ljG7SwZ>j$Qb3yro#P!0aPk6$4h| z2`O^N)wm9UXK$2M#$#*G*I_)DtLoPLL>y(6uhPHr7#XsjnS_?@btYXK{LQ}KX5Js|C+%+>l3bhrPK@5(Ud+<896s_mXM5 zD0Eq5@0p(>^y@{}$LoOuy9DC9V#nS3>wuT=RI_{P%gFRn3_bvTQ*aj1-&L?(urtFl zO4-(1zTJ*XNItHA?tuRf2T*H*r-w)G><6G+Wi899yjxahVIhSn1{l@1FFQL%rVW3S zs>sAUKSA#~=LZmt`|oY~K0ltEFl#0G#h&is;@zo&U$baJLaSu`zTKuuehFw8+_&VTD@dlC||=*ahmc) z*@!M$n=OFa<@3PO4ZEf4nZ`#cbRyQ;{B#vf?dn1`NEFf!gbZyA_!vffK) zE7{^t77Aqo5vOda$z#Y??|98{&t85S$eE2N%>Pz1W>}G|55w` z9vg<{7$>Ij1&gmee@)0v`|WrTyXdA~9iwmHZbpruF|O?5$g3)l>HU(t#iWoA+AKf;n(80 z+ct1P!QYYUOZ#d6FxcutiIx!B4&JBnp2#7$ZGbjxd|KXd_K+Eh`8ER7D(=E=}-av2UuCDkSj z=JIitTKGbh5KGtITX;^&tR+HD2b|TtK6*iE?-FI^B9s?jps(}ct8;36uK%?91br1n zReubUUs4_Vv>df|rG;;8kHD`5$v9bI5$V>n*P?6m9K(NsTw5Lx*wLf(qXJR+%6yyY z17E=*aknO_nt#>b?JaiDQf3I5{z6`4F&MGXdlG>E2Cb6KibRN`{qDp1sP?xo6@H)? zgKWK8G2GvQO9-a$INO!o8hF|W<$(j=It-q;VcF{ zu4@m#O~RIQQr5-w;SsQX+zK(NG+cVN>*AM2EqbHXlc52TS&!15#MVGczAWitw~P-F z7AK@!49l8g5hjP8i#q4lX1=^x{jPg!N3PN zvHo=h!@LiSfvWmlQ@iYDm)F$e%kf5i5EhWDY#yH?CR@8xpKp;shdY7t^2y)$Bl;W7 z7Y7$+FytbWo*&>vTbnd^LOR&JYKCqAF?abM4YMMl)O-L)?AbsezexFhUM$9h0^s&wxsFnPBb+1z`sT_qyx`K_ECrZqq9qMX+s{dV+ z3ccXByF+ZA=+RwCwnBG?s?`URky1X7`Dt*2au6VW8ngX+T0D$zFc>yZu?{A(LPk=W}@+VANXBeBO8IibI% zqwDuP@*~WAaA}y;ZvE4>59V@|R)5azerEg7N}mlnZ+mC=Z9iptb3cjt{GeI=jbaeVgQMW22S$#RuM^mZJIhXZ zqH=0FiZPdZHwm-z_8RJLE@SQ}3cgTw6nLhxbr=&4Lz*HGm3>gAnREFXHfCJ;VXDT@ z&^>-K6-MFh&b&7>r7tgp@#XQ53LSlphOQxCB{*4(WP5*=aKhUVrd_H1>QZ_4hzLVg zrX?foNr8}wdTxZaS8G|fz-N$-!dRVdo!BqZvC}vr_IPMgndJS{K859iNU07-Q`gjg z_2`br?MY0L$M{2aO2+1EV-T5Ywrk!E6Vq9dMM)?jOiTjDb-1x~{4S6SsdoQT}y=5~VcS8b&JJ@bQvenN~c8hM6aQGyYlzys%%|us7 zwdc}Ozx+*#RHq1fXzS7M6`{6vLd|#YxqJ`Dk6-BpyE1?7iSEOsq6$pRPWmsavbRvY ziJPh6ZN=*ERV|DaV6A0HY8;YXu;5A7+jub%;e{s~>+f;rqGU?N?pFO(=n* z#1i$-TR*A@*&#eIeiYaFnkS*7zX5{Wg*3+uSBo?%6o!zj!*lQ_MzuBSdG*@@#qx~9 zW~X=~7^T&A*JutKeW+ zT$NnTRi=O>Y%NJ>BJlXLh?p2kF0SNKW7tou5FT4N3$^rpk9UjNa3>;)4mRYV;wGW= zYXm}E>0T^mC#fo}IW*}rc4eR8?XMQTVc@NCEuHfVa%bxLKLf6lB-0!(G{WKthjo=n zc8gN74Swyht^t196IPYpuRs0LuA5ZNDejdhvZ=^8kon6_`5&5EGSt`UrY#FERKiil z7J0+5DzS(=#;vUdET5Z6Ep$p>9|@Y#Nz1RfZPpH-B$k%NXUno8H_&C$(h|X1pA0W~ z*Of0Xya@#i2ZHc*9klyu{9>yg2A$`{ejAu@LOATD`iI7REckB`zsr@j#-z!a$QZj= z^<;{Tg_%oLO{_n;_D!av7IK(tOQ`P6F)WAEuqg^e`*tmz=J5FExcX%&c11V7QnNrl z6$is3l%F1j+LH_Y3}E$yZd*tg%@0gUNh?=RmgAM`!V?btP8OWFPQ^yE0{Mp`tO#Xn z{Uhc}aQ>CTpO&e;!h3O;qpCNC4}uoh!d`-X>6eUb9rfcyOU>{=F zSf8c+^x87*1--&sHoT&K|G6Koi&4w(4;^Ygf0C0QbSsyaU|T3t2q{$e?a4=`X4Mov zn+;jAwmZCX?>2h87zg-m6K3Zu1(V`r@bO-J9{JE31$Zqf*ZuaHt9#U(K#3I+zZ-k$ zpd5VWJNi*`_B3{-iTh_ROV~G)(GHI>eCTau<36n@>s*2pA?=>Aby|}WqP=O#n+RrW zafQj?zm4LbcH99|Igm*rohk94Djn^pXc)Rj;Oof!`te)=mf)D}Amy-M@DyhG0nrMGk7R6NB4)d^eS)p*!&T95I%HkcxX+JWV1rY6D~m+h`>2OpFbt&aK1y zQ9H(pW0ufn58d8%I^H!&U|j~Mi-Cw8rMc{(NWapQF!5d(CrICf+uC^MlMiH*Fd5G< zu#`BVs^p&ipw0+n=lkHS{UWqNdm)@jNhBue@?IP=`L@cZiBt;odi`G7LA&lk4ffVr zX_z@JGheXtY?vJyv)?`M?oRqvnX00mj<}(r;o#(_W6kx)N0Ypc2{&_P!140cn$KH? z&y702q|yxyQ$y|#+)G=`e$|%li5Ji5nKx0`O6%GSi)7B!(``x9YF_#lF!q4nlmox7 z^*ge}gxry=?%fm2qox4wcCF0ECZk5#M&joWsx*Ysv9`sX)Eh=d(ciXkMp zLsL<@Wq#BIFLSjpo@@~GE|>~A?IglSeU5T(Z?{+z=Q)ru{nkhUGE>%cNYvTMak~Ne zlcH}$pJf}G6-^p}$|rO1>d$&AUGG}kAW~Tlh}CbiVY!}5P>*6{t2^vGbG~|Upw4U) z_)_axIoQq;%M&ShBu?}XCz6XpDz>YH(b_JBP!h=O5$SC9{^4Qq8~^@(A0}4ZY50!l zQaPB%lYWzzyOh|?iKQUFv00LZC<)8KHJ;e z5yzshVAqdEOceB#IhxXIQ%OD zQgN{EKTb9*J@(XK!l#vnP_7Z`)C!_AL)y=lty9GnQaO)wvQs|!-7#blMX?5Utj9Uo zT)>}nwH-M)DnPWA_cd6f>E-o~ox{{-y&zeO$}JwzK@eyL$E)=M1{>?cHpZ?;gzDys zr@=c(V3msLU@I*x98;t zKvv+?&y|VXulW=Z!N}8}@h1ko>mkqF;1*vEtrHu*CGR8Zkx|Ucx>y*ZedoR25b=oKnZ(!<^^ zqB#S8=r*c{7fX}EuCl<-UJ3?w(Qoi{nmb+zAy?$^z#u+#yq3uoRn%x@_oaMr^iDm1 zDHnQncDC`vkMp&BQj@}03tBrTC5Yp`SWBCI@r-I2Zc9F6yZ>}C?oAeiB}L)06rJwW zjFpY(4BMTMzN{jdr@oTf;jh8ypOCt#{%>k?%EuUdff|q@G%^zZ`>)-i0*1*fD&36j z7!>0bv-=X77Bn38q%o=>E#gr3RVEMopO}<>AD=|nnHasdcMpQJDnct`2xVRRg(l*z ze{S+PdI*7~Q?<;NwvjNUj;!CnFY}aKpo{qj@iq?YLKblu)9?gQ0;qx@bRDm(zQ`9e zCt~j9qGZc7-!kzP*jT1 z^WRy~jFVd*9`1p1_aX#Cs#9`OIg9{6+1o zCD^!Jk%_Xr0zMi+qT*_Z&ju~^hSjGD)89gL>O88oUPQjD4(8x?~u?=FRFY=d|?5afNyjx*jQSITsr zW0>7~1#mrOF$I|v99*C`-?GeBrQ+fj(M-=w1|R&5R}qhA%xGS*rGQEjLw`C{$FNWTuogrr-P zb{h%_#(b4w-yOglX9(@h>&Noy((`hCC3+X!|pIydpOAo9wH7C1W_359I~0cbcc2r`@nUcg+yu&G@PrD+ zAeS=ooW6KrZ=P52^EmPRT8S5NjB#*9E>;Z(V{^CLkC;sX&Y7_l$r~eCA@cmm(GeCQ zMo>_dND6{SSGA>$is+hvGRtTTdB!Y&?hR8h;2ib|}~>_LV(Fe}{QQ znRF^((5McTbS`^F8+*3AEw-bIeW}?B4_j8-XC-D(%sXVlHpl44kiq4BzfvoaaaYqy zjde@Xz@7D%Set}om+cY7jP_gaz~+`1@)<0IT@tgou^jmaJSsH7d#OGM!bJKZQO6zh zoo?^2{PF?Yzqq)h_kVG5!y`S@|PL&yt}q z;PsAjtUi@RjE)s{rDHB~m$C zI10t8s}MBpFIXcxc4jEvMPr>&f|5&TDur}1d!3oH*e^KrxqN*(R=^=2b~kf-`ZpVbl0?W=?;T;@3;)2btF&T7!#b zZ$Iv-b0Z_j6nH>{S$620r^Uv*@cGDgfwr$H)s`l{)mFT*2=Nd86MO~l9XLt;@b!KqF!HPd9HWH#VZ$^l zu68We!SY&eHY71DlwWgMNVJC_F*Z$v%o~7K`eMm10t}fzw42z>>@TU7IaddFj5DjY zmNmC~IUQ~n(l2Q>r)9?Hi0FWw>@pSYUw2ymO(Yl^G;+Xinh)JJ-Trw6X?Igu1JD+P zDo)Il#wViL9!B6?%7(s`nime{IsyHhis^Mpq4NB_5E&x>pd03T!y&Ea_easMK?h9sFIHiw|vMAiMXcQ#PmysFwVXOx$GCa5^ABRq4;(slXzp3L*N7`2 zC3)a{{=#{a=r3Zf+uZp#F=rM{2Z@s6)%ly4t2#8W+Bm-HGa5?ujRtIP82`$O1Ua0X zFYBb@8Z3;_HC1a!7Fe~ryhF=C;GYJ`Nu3mb*T~gNC)>Q>Utz0%pO@8yO5l%#mMixB zi!D`TrcercJ@KwWH>=e)rg5zisx<*frO&f2r?BE23nFdtYhounZHM01H_^El0pW+l zY;HAG#P=BLNUC$FUx)lvJeBw(&A;dE`JV7Ha$bRoAJ(2=bCOVaXHH zX216HdQ=j~#Q&AC8&CQ_VeAG?rOuXNFCy|cx4) zIYMsKK*!cn*}jLL31X!Td@afJ7F^G0B>TY|5%OB%V3+uhaoces|1Uft2m-oO@CM;! zEG}ba7V#jDx$WiJCs(;djdX?d1I*I2E zjK67%@zZw4(<|LVrDlJe{YJpIg8%G^e>5+J7W6nr@K!f%sTvTe_I5QH!mWNEc_X?W z^O3u%__?s=*z-Y2^d*r%#{lOUsB2?HvK@tGt;k-DE~ZTJA1!3vFNFiVXjPNo10(!@N_~OOQA; z$CRhgG|loNid+hoB+34gE=kk!!TK&;sOYBFKVJ#a&espc`EY8#D!gV$a#=SRyG3w1 zv&}i}d7)qD41|JJ4GSPy?#CjKhcEieil#Y1N_sg=e^qO(=<)Cn(C6d+q!X>RHg@BD zz~+9g&F_PPbB1gWR1z4lb=MB*&uo??bnoJdvrnluxl0F;aj$&iT{*)i?yf}#(xtGD z?p;G2_9^vFcOalnbMj)m%Y5U+eYUAsgxGNnc>2unzD)ineeyB~fWX9b?Yve`xq7PHB(N9Y+e{<05JmbU-lSrQKR) z1c;9#?}Kx7n}IGe2Smt0S@$Gl^^B1ufQGh^L@-EhL4>06C2&mf%3j7~S}mC!UqeX4RC*ng`EqUr%-{|~CPAj=ly2*VcB{L{X}tx+nJH+K z{7mHmUhw-(s@QEs2o=CK4N4*(?7p#0w~2^?DWqOf?*4(iIZnvbt}9K?lodqWE#}r^ zI_^Czw)tLyg>A5XGljjO5tIA|KIdUq*nLKND^|irw?GmzCOR9x>G^u!i-hZi$euz( zQG?_n3&V2O7N1ExpVDr3{IOIowCF>O!1$rV+iw?j58V0N7D+-BWA1%k|J}pvu)%}E zzgqooS0E4$M|jR}H;vboPPyj7r0SBI$?`m_ntVh|V!4d-6lg}Ld2Fdc)0-(6S4eW` zz&0H;*3KzXl};NTiCD#tth|E#d15R!YGAO-e<$I`ZZ=t?lX~D4%SpFfu1s4Xxxl&M zCwg~KgjWa98a)%AqS}TL?rH|ifR4tNAm4Aa&3?kbt`h$ir>64-xjpt` zYutP2s^^qsDMK3uv-Y9g-1D(TJQ7^h0MRQ{Rp76zUjJqoBnxp6#=^ldSLDh@Jw84j zD;|$6p8IruybSd@?(%TByRO8jIOCpR`p(vO3jnk6=1G9F2ajexU{sf@v^+HAJ?rvP zBF@3I|MPWI|mU7o`V-mxk3M)*|%hc z9h$SEV^QG?NqEaybY^>07q67L?T2fn&vI49hbivy_AU0VtO;FigzentjliP^2wQss z$y{qLDe3PHd!D_@PXba*vxI~j_d=3FE?HvmXmQ3YyZhvn|c@Bu=+Q}R9U})xo)}iJ*|s7 z?1JgsF_qRTsAWp_emNu5=Zm?$6&-|gN3zu~vCN1Cz<3sLx9&JS<+2bce*hZm#}c=Y zsFM6%{H^=bRfUKpmDJc>*JMs3^A{vf$$hR?SkghUQFk{CbR^@28;n=%t+{l}REGdA zKc6#C>{I3{!X9${j-gsJxl`RV_sZ{xCe(RCpL?@M{J77>Ig5HYKZrUz zsV?yprk?sTVTF*ms#0JMsEG0`&0sFSMebmG#8X@Ra0IjQvJcMdB|P@4EV_Z&{^YCV zS{6UME6rItz`#LK1cc}fyI((>+h2|f9dk|ugI0OL-gWhs0?PncFB~k530!A6N0SrO z4$fyubov}4u#YUyhn54LY7FPsMv(|kQar6``a&Kl`lsC02Vva)+u$c6^Id``oj@B; zH1|Vx8KRX~D#3*leNRV}VRzt^xxG)H&~dC;UvTTHIm>zZLP$7Z%Wh^ht9wvFQj>F6 zZiC;T+uoIS>6OP_p!I8{s`d2oo%_>T`5n;aNV0p<{lTRt%ci-DGfgt(VCfDu`;jQ_ z{!%QfJ1VYN##jnJmt=Bvb6pJ9ePrVa`x}XFb`EC}om{F?X%=I*k z`*A-o*5hm6ud*?^$v;*a~oq!%C+X`w7f`a9hojZZwp5e#P&-+KH(R6Do0w2dmgkA8hy=02+ z`nV6cc-H`Ic4O>6SpqNN5iM3(2~ovN;nZS=8dzhVJ7f+stcd5TwW%S3J7!nI%)x(Z zf!<^s7C4gVi!D_bydN20Le_xTF;5F$@;VH^JjJi?dH#qlT#T?<#|>Wa>YLp0%bU$)4AR72!ALzJF9?;LutlSW<6X)$#c9h zSX;B{AJ$k*lndVPBl@YS$Vt?{S5a_T!!P;M%cswatxV?)g zk67yjSjOQ2CBf?O1lu7%7M*@yJKQ8-d0Ze;bWc$}ZlFcOT`F<3MAmqt82tDl1Ec{xF+Ejd%bsXCd$O3Vxg6*8I9o;y;MDS+;X6;`H{fn!7~&Q<~+QPl%=y z88@D`HtKzJfs`b9-eFYqSl6YrVY>*dmY3hTs9>UgjsST#li?M2YUyO6?3M&WltnN z6t8;^Qe_!oKQ*De&$armtGWdZNd3!%&pl)`KEj0agdYbu$r`D~sR(zus8$55*%0dv z-S!Gt6RisBhFlarz@s|#W`2+DtPtwob4QI=cO4kljhijTzc~KfVwxJ0mZ|mjL9)4~ zKt?EahD{KmOU-kP8|y|Xi*jmf@<0qC>#rKJ!b6&LsP_E) zkO_aPI_KA+a$T2`hUC_4*%^Z&4<9~i)K{^oI--(_C@Zb41J}&TkfwRMShcf~W0q%Z zBq~bjy1**-%7Dr*kIAjuM2^ZLno}YPzzpe+#oT&C=lP}?3XOCu)NdmT5^+jU0Vvbp z)<;W}P&gBq6h2aY<=iw3_r0C3+TyzNc+8zJP3cxxUz0ju?5UU*S{4xc4{d>j4rXsE zvO*MYx57Rg2DGE^eHWQC*IJU%Q&0fSB(fS6Lq|W&5FY%-V)Zw2aY^@Vd80c~gcuX| zDIJ|Ig!U+afpz6z$>6#Qh@bEMWwwiyFvp9>D%wB_Y*mz}=R(UV=cD6Y^fEtNEuL3W zXyQD3t8ek-jX9CdMP${Inn!clSDk|LS9WD3rH|+5B&h-?h|Pt^mcDbw{5)+qvbS^$ z%i5k7)sm02%AT-W{;TaitaXg%bS&p@k?j|+Atw7DQSP>mC72ytON;e(z4$vLaH>%x zjMt{sLFaf-H=&eELhsE;O+1?Lvx59uQq+cb?hH@6P$Ite2Mc*;@IT6OSB!t*@j8>S zdjWQXLU$UWfO8EyQL@LGL|PcF#yT15A977Ro*u+_^^qcE!-sMaZmeEO`e$=q`M*-f_!;hg4sy>&M&YVFVF6tA_502mP zG`_f+b+qNt!Rd2OV`0VmBczY8Ke+UB+w01=U!hTh{cj7 zRU;9dH3dPZPbS>cwu6#Q23arr!ue9YqQ=P8oR-(`KRW@3|)M6hLa zw~#OokER?I>ma3M(R`kgh$WEqvyb=I_Ax{G@i@l04NKZWlo)iE02F%;)aVFHdCZ@h&2te5^ zm0H3En{RC^0H@&}i7#q#vYC6{D*}oc?lXFVAEWd&97?kLXiIDtL&${l-0t2{oB=}n z_Mi(H+P`#qJJ*4w&tyY!l@U5)`56Ybf*oLtOKIFAgbtz8$QQ8q@jtg@7j`tm*OWy0 z0wKQN_DDh|QaJCe0#7yX*p@-qU+S&kX!pJ8Zk*7!)&sj{m9RK)o!1#_@W@YZ7fdKs zzOm618OEPqgHN+k6aX5uclKt2&z#=Qx-HFwL(LudAd*&d)bW3oRd{Y+ z$uA|SA^e+^nq>c>(+fcJo1WWJEbiR))HO39OV8@`1_<;xv#le|FdB;g%E1 ze3un8Ye44v?C!Jwlk~6=cR9;}8%sgBZ!wP5_P*tXpgS;UGi~aY(wnv(EG+r9gQw4I zhA8^Md*X>Z%fGg^e218O%e!$YQ@@TLN092agHY@Z^1b_^+-A z0FO8BX^#WCYxht3h3TN}&V>lq$?G|6>-NdIcjSB|U}5MDYR7s2*w&3nJ`3p``^0a|{8pf~iepb%)4Kgc=n1TlDRjT;a-7h9~T zVg}ws{jQ(b_}~NVjo#Ox>PBMz>pcyxL2-<^SL|ZhlK^Qae4ZjOIu$I{3tO1@X8IW7 zIR4QkJy!8ym&0VsxudNFnRHh)x}@fXMZ?>gCkc`Ulrw)*w^S$#*s+P`Nm7kMHJ)H!P}=8>I~5y0eU`qap@(GI zIxxPWd;ll9bb@6`$N+Fk?3c8&Op!Cv;(#@&Bl5q>xhJJ0E1+x<%l?0yePvXYUDqy% zf|4rIsURH!(o!NI-CfeireV_?R74s?x~03jr5hwSjndt{iL=n>dEf6l=ij-1uyZcM@ ztu|V-3{n8^0piu%l`)aOT+zNl2ljj4P~yH$?lKoElm5vmpAfRw{Gi5|RZm+rr}Al= z_~!+51>1-lHR?&9Q)T>{{^n;$Sl7(8%AB|*k3QGlSt_+TSKrEUx}?86yXa+_&@V{) zl=P$Pq^+alF$syZ@q&nwEE4&dFXvW(t>;zyV*>#?^pNY$0P4Efcl+~b1#qEYA?DiY zeE(;-&zF$|9b=BTYy{apidcP;Yr;?D7$|IN=u18)WQ-CO6i}(}=k&2jnQ)lPn)x1J|1^qukpN_(u(`rzps@B8;5?N$zI>UAfht4^I+H}=yBoHLgjr$_Mz&kV>AsZgl0U_>=0&!H9p zzsEOwjfOVtWR-2f$SP0ghICSz&)H}-C;w1#EM{im;}0wL?jN*be5VU6$22Lb{uZ&c zq5x6rl`L$2iigaBE0*QN;3oaT#8l~1MQuUzrlWjw)2`jo=SLV8j?GpNnl5;2=LiN> zajY^%`t=_U*FR^7cjqg*N(<}7|mAeghc`8n(Tw;K3tJsl zNjfsvy;d$^rSB4YRk@>4eJZkqr<9)>ON- zZ?&nQ_aQNin&G9Ztl6QVq|GQ8EbUzs;Wbah9sLUVINr0=(Ykp)RU;x7kde_ea4~qP zY^kgk%PjcAAGANK#MroFc(*>)KAvjhTKWz8&Zj#F`MD4OMXqTYA(*d^@ zcg@g2VpW%I9nJZ%7iGZ|M1Q|Svk$?I#qoXL=A%_1F;c-t;nsPN(qA}_l}aV~i{pw` zwlni39}05P}mEJq&gOAq@+O{q{di-E{Ja zUo2Bwavq*mT~o0}N5u8c2S_Jm$S8Ue2ycEISP|{yvq?Twh#mftK^mwFT`H2)nn=89 zM0}!p;M?8Lp=hf3JN2}Kzn;55f;D6D*8_9QFFGDf8ce2}LY%0htA{;M&A#Medi(LC z)%n3B)7=%T^sHqii^}h}r(pwVEPs$V#_!#0#QVL%frscK$k!9fi1OSVkOEW{WR!{F1qcc3(la3X3!cgU85#*~T=4$uI z$Hxbcn<^u^4^rl1Uk8tS;^|>Z$}JBIw7Cgb?aJ{8=w-@&iRwX8m9qOe*5mwKSqGYe zGR`>>uCi}HgyY5FFcw?m6N8K?Sia{IF>eaudb*C8Lv(5RsNcI8MJ}yzgST~hrYt_7 zPEP}k&3%JMJh%o-0!m8no5z5)LwwRcR7ECLD$gxmb(8HwOTJc_Jz#AdF2-y6=P+kW z)ZA?RiKzABh1sgZ_96vC-C&IV$XmA{_XzEI9VuT3Ekj*9p73^RhN6tR_@xawEUTVU zmCfhj0y-t!(~f?)w)W8&0yH|fq!Jfwdgwe)xlHpXQeAlLAZX;F{b5}e1s>@%QhKYfBZhI%y4tF``kP<>7CcY_DA(&{Z z>X_$2W$SS7g+ZTywN-wuZGN-Z>U$w1;Z+}LZ}pq{vsHPDpYa#h7CEmV4{{*lW$t>U zH}$iDUQKTC-Vf*_v{cMLx&2J-6kdKfgrq_&Fcy_9t5MqgX0{u;4O1{$Ig2WqR47ri zAZSm7S9IRelG&1nJj<8z0g94tE6V9_|rC?4JpM;NwJ_S0vWKXX7Ot=c0*UnwBH%eA2t8}mP zy}R;>jYYxW?bu`vcNr27ou2MV1WM7Xb0S2fN&G(ov%oa@#k;fV6iwGAR{UKcSG74~ zBmOF8wr=^$!0p!W?`<3q2rv7^K>Z=pU(D4ApYNrf(3tAiIlAR=hO3VrO3ehH+K=?v)L0D=7sg$ZrW7pv+}K1!%l?5Hc5D`F)!|5#4{h{6 z*xQ$eFz~l8w^_F7pz`gmtetuHrL9LSev+u8UElvD=Qhia6*|-NYfrOhLPYQB2juxK z$?EB(1|VWSn%)D;uKr8>U0Rc+|4)}bkNg8uqnd{G#IctyN@O$Ru)+gO2^hLTnRsDWR|VjyW+-irA@j!hUj0&&Ufh z_vEExNEbb-twChy>>tm79{dz-{Q|(dqDBhA1zBq8iRJ+B{bfBxaIq&Pq2Kg7~ww&lo^>p zd{mVu#WrKF0XJ+s*v~p;uve~hnes#iiJW=eK{Q>Lkl%4j^-V2uOt;MCuH)EL(;}~y zIWp!OT?8R+ORiXT-UFQD3bkbU`S$2}>M32{8$a@CBh69cN4R7JRb_P$PH>A;Uyq-Bd>W=x(8s0O}${Nhg++)geS^fn5 z3Z9zlgP-?a(t`ZQi9q4+Tnh($v1GO8ah%?ztnja*WQM=Vp*LTD&1KfugSbL?dkrV|uYvovGMufL zwi+3{Dypa$R=R#LRL?n^`srs{OP!{Aty4u)1$ypV0axw}2;6)|+1dW3x+^U>ovJRD z{{Pz;&?-=y_yTt{MPP@Ru7XIk`w>$!_oF9k0fFk$;MOwIDbb*N#&KAQZ;iZ|8KkrQ{1WUGunDAVE+YkZJwSQ5V%D*W?$X<=#EAMv90v z+P4}Z<|YszH)#D?Q?00#E3r6)DSFs_h9;sor3m?6V9^vv|0?N&V`F#4eNx9<7Rk8o_npU7=ss9G$-yub|8l2;X`IdCZJ(DaR=&gB zGq@GnMhk8ozc!WL<_LcH`jzVlWM2JR1h%BPr$d!0Ow8wPUyWJ64B3xnlN`eYd^8OI4i-THn81P#=r?JgLnkQCUseIc;k{M;mdujYZrp_fqUpvZ57o9Vfg zv>f=)MqPPS=-B>gsTEz+%=?SqGeS_xe$20x>nM6x$1zYL3y>eYQYJDdYeOr#?kTB7 zQ>Du^X=*OthyU83kegP(n-t@_Gj(J(Izh6YGu_n7i_Q1#7eGjO^1k+my0D@ikTv7;#aJuAIxy6{h> z!-m*aXThm6ae{j=&zkeQ4<|RZUe;cCuwV4KpmJrt;1BLI;=J)x(V5xMnt%C&m*VkQ zm^hnmWWQ7CHyk_1&Ey`_A*9Q=HxI{4YzjWsm%&2XeP{t3e9rE2{>Ztt!Q~8oiAC0a zuZC2oDMD$)=ekGguP^_nXwyC{$(rJ(Ho?V_ti7!;rDuhBsdP2^-leO|Bnyi;exTTX za{9^Vxl%v1e&~`_VsoWxZ?XB*!iY5!`4pA0HR4G)7piv^MO4c+;bFneNBjTP(x`+v zV*8)09pjS@p}x#WE-$|qGL#-$;h*pET$n`oEVMmu^E$;8!}D=Y;d}}PsBG?8=*!u( z%IIC@ZE5h%iwLv*r)*Q|{eQ|fkp@khHVwUWDuljdtUW}F3i@TN)xF+9yxRPU?LBM9 zFT%@+LuYY>Gksq&b`^`X`A8izIbY|^$E=sJqJapz`qcZ`jfeJp`eg45UTb-m1|4r{ zz4~a0CNdGI^gr@+Djogh1zDuMw>h~N5L)Hi28Lv<@eVX2L+~LE)gL>9y9>=eJq+b3 zL9Pzi;rthT9-bIZUIV%~EahtZ6_ZV)R}ajo4ENNrOP5Lzz3*j`lb4FM>_W2$_FKkM z%)hTJW3``~1ViT8~Ag z#HQ=-8Vyfp-MCQjt;nv(?D(~=7rrMynvFT}3U#oG;EdUPyWeb6v$|75jr?YIP{L;Z zZFg_!_PuFv76NdsrU-{I-Yy{AhN{<`Ced$&Dcvfww zF2?^Pg<0;Kjf>~x_~kF6m1yL8c4K_-)xTe;=V|WfR(CR$AT!Z+z@4d9$7=UJ8v+|a zTCL}RM~dFCm~D67D02m({sd&ph0KM1!Rqm(VCOhb@+uUsyRY=cDlcxY&f+awhuZwQ;(aXGSiVht_(L(k zt~sxjqudW0_0ZbBQb_C|Q=ToZ*-+*?-Eng6-o7T=z-rg!^hCqe{d*nkkYycE=IKhy zQ?f%;IpGtEj84yir(nsXsxn!jpr#|-+}%K`YezCo2GW6YVGZ`w)9N?U;%dklDsbH`16$-`mtLQnZ$K(pGxqIi1jX4D5xv!PfbUGW!zU>hZeAEKX9ht87)HN>^xII+gUmjjkC+V)f z`KBY3piUt4hFc%+J(%>IZU+C|mMtAOH%(wA!gbHy?(YMxDkclU{QUhDxln#>>pF;) zbA-jm=ULu)cYd%;D&$#7G10I$Affv9=|BWWBzDFf`_}ADEn9juVW&*ucW+fBP~{SR z^0JCbdqY&9P32=1jqg7=%*Ad7s0a%>r{6h<6afLW)W7W`Zwqxb{mN7uqn7mbXxZ97 zi&_2Px1cPriIIjs(EkbTb9^>+Wb=Wcs)c^T`m@u{=Q?z{?sQ7Amd*t!+;&|mqkUEH z^E3R$xnDX#JR5~!ZCYq0%bQryurtE%JnAi-e}kUO=|&j8bQF`Fi*}8@iT7N@slKbU zbhywC`qQ=ymg65zCq)RNz>Jdv8Wt@wa?j=-Q(#`BDgR6Aii3Re9njx0n#C9IYhJaH z`PHjn!`fd23%+VtF^Aogr}Sty2@*N>4t6Uq``PZk%sIR3L-EBFW(&n=XRJpN`G~5b zK_}(nUC`wDS(8`I594>es_@Bo$?5xdUMe`+>l8qj1pvOhZEP!X?G@G?&P@sI;yiXu3JZ}Dm1 z*T<>lBL^!M`pi+4x7Iz1A?-y*H?{h7ERH%EwNG#9B zG+hgw6kO@?rt6KeXgzb+m(FYC{)Ib58a#&;DJA9_OQh_{MT^T-hB$2p(Y9*NDCmeD z9D|(pz+w^`oJg+Wh<*~DZ1*Wmd%W0e3@+z&TurspDDoM)A(1wo_~1SycwiQ{AdiG0 z@|ar&Uz#@~^I1?$EcNbnD1d!$#`nE@>g)o5G_jtbcX`TE`8Qfn%T76U_R|(yY$HX) z1<!{sEQy#77QzKIEpr_XP3|f5j5Anxbw%^|a>STK?i`GKlbkCHYk)Ln(9w#z5@4-Z&V>SEs(j6!lbH{Pk}{vu4W}sglw{&n z)^ne8W9seYr8#D5D{bvn&LfZ7Cxur!9}Xk=^bpdrVoT}N_Gj~W)cNN-0d9%B$-Aus zW4tu~R8|*~-j>diTi{Rz9%i0<)XoiKIGgYzEk`71v?lHDK3$cfTV!CtT4nyXziD-h zBvQ@qgir-FT_6-wSmY9$$Z58x7&-9Wpq}2obh5eraf;P%r8aq`b>aheH5j}LtOb#e z>NcOh)`mJ&+w`30K;*2N2DCGja2<~O9ZA{lsmoI=XF4%O*+1qkGM%lD&b_DF!grF< zSuL@*hf@J@P9Ifi8>bL59;DFcCo8=lVlLnJL)&~Nlf=#0#`jPGH?aNniGS6kVE z9O+w?%J7*n36po@G101K=4pZnSg(AcThh_-6hHW>`_|7H`uM~yhh4X{5#A$%!37uj zv`-)@yAcEyiHKg_4_kdOO(y(G4{l$wrhav&NxQ=b-;Y1U`>zS!b#8iBhoivL8j;*u zh=(y*bJ|@i>r;nS?UWB3V#LB`2R6#`+EcDU(AK`O@VEkK zQixuakiY&&7azsEA^qxFl#piAFID2I^|@cj#$e@YgW{xm$whyzfkIsD$mmFW!$F#H zuXEyivGhGV76~=i3G0Y3DKmT0$b^QIMAm^mxKH(oI$!cCB;%s-gygVPgM0TmFa~*t zQySBdCJuOg+<1C{174v35ESAIQU_D(L=(gf_TQjqzs&Qq0-%*t!7-dbpnyHkPuIAx zSxGvZFrNSS(1o8vmSi1ykmnUz?aV7YoEwEKlsl25EX1!<4T&zg5Jyc2ldMk!$0<;# z(T|3r;cRHM+pe}Q&_+zRHLKcCSX<7OTF&Ta=Y}uMBl9`vAw)LaZdoBYiA)lVSo7G-C1}tPeT%qcZF@uF!3ve9Nmb1ryeP3x-Py9 z%gPbAAN@hDi|TxO+(d&vI*zd^lph&xc#K?OdL7`-cz`46NI>GnMAULiCqv@pk&;A$ z0bT~Cci}fhs%7Qk4#6jRa|aU#yx;-6BCXd(0D3Fo>?m+y&{hz+V5I5ky&gGfNWkV$ z+OL0eO4f}v$VyF}n^roVf&nW~&lz(~K(t zuU}}l!mQQZ&QhB_&Zv%i@p9GOhev*4{@rbJPQn+$x&I!ZXnS7RTI`RRaPeiNd2%XS z_htE(;Acqk8DZ#$vZf{}T?wJLw#NCT=lM0}P|T!jY;fzj>M!n4@~}q%rV{1*;<57D z3(MCHpIB7-)a#xuLJG9By2r{qRbN0-orZ=1As-E$7^9+$0G}DNR|p^F_fVJ2P#Q|t z#X*&Iuj@d{{U#wy=S_Yg^ofdp6j7s8XKv2zLMj-nMORly__1F*ZHNSHv{b-BH=mR6 zq@~2}isG)r&?Dg2Y>m-SXArrq44s*mr0n4*jPcuRGxZi&*eIXhI0NHeN03l*^HsDTP-A3m(f%`iKfQ6kTh#?*lj~Iu-+`h!?z_e~w4I5#=v) zF8PZk|0Xc)eAZtcSOX=>8ZVL(!fgz0A-dR001ad|1w72jxxde)V4IcKTtvB{`QHjk z3*T`X2>sr38($KsYGyq17`EQ4=Exm)%4BFQ1i5d9Iu=)Q6)Xz@6I^`xe%P`*F7e<7w?J&|Y66PvwH zTCIgPe|pQCV-UAHDssUC+m+<}OOPBSOG6!49##Ig05@{<)NzEODNZc=9ZX-Z5w)Gl;kIz0nm>YDVJt4+^lLteygAS1vo z77U;o9SEFaj!OC3tFQ0S26uB%_G1nlBKlQT81Sz9Q$cap%+;rTOv_YDCpxfsCdzn4 zzcM+KdY6|?(zj;8Dnrd3`U$n1D3pxW3Y3;B;IYR`E4ac{Y&c`C$rZfn@Qbh6A#Iq| z=Lrq8*`dWDFvhv$Q9YkY!x9AxhV(WKg;l7?=7#tT)@hQe&*()Xg(5I#q-;d$^7#U>{QM6SUR%)TYe3ed1 zqo8G3zL^KWZNc2wuFN?v>C_%c?|e+prF+pr8Gr4;GVJPXVMmWC#*K5o$x|*T=Phqv z2uL6U0b+<})wC)k5;e~o0K3rc<9Z7=JnT+YnPT`puqeV-=D&1Xc&KC?HU`~N}_%+7G&VzsxP!MKOJ3Uin+Yk5m}+ujvcTjTo$5E z9}jU9EIiH<^$01llgh|^J&cR{R-hQnLLNoMI;?TKcnHW+bKO}0e89;%+EeS5x-9G4 zyFWi@^t}C4r$P+{NX$@{9Y(5~ZkDB>*`2tmj;0WXVjv5T)mRPokDhwiIkPs_7f~e7 z4n=|+FalWc?POJC>G0#_Vre~5Jp}9Mxf@r}RpzGzK9rx)ssBr6vKJN>Si1O6j*c8n z!XFX{01aF9+WBvzXVU$>J>fLF%g*cK;uj_(nvzmdN?geA*kkEf3VAK&5xR~ICdAvQp+$$MuWNE7FF9`TluoQFAn@ASq5TW`HcUd@Re0KJCi!BO z=BYwVV&>K}%6vRg3K&I0T@_GmWl!t}c})g4^FoR-iC=)yzIBuUb}K0YHa$^wg2=T> zOjLG|WaE$WlmnPl^(h1J4_kYZj)a~PiJj`*LstOwJCYneEI@?jauT9^b$iDzt)G}` zS;)Q+JNN+8MgkyT%PRwx&IR}w(FOW0`&U8dSK5%Tkktd$$)o-{CoAB39!{jnjt#mw z%Rh4u&E_mlijM00eWOMf&qhEp*|cNU*G`jq5mbz!eZd`=scA`3w;c%pA=Tl8rwNDa z`))vdmv5MXa)>ViCr{^|_HR{|ne+^rjwN)0{nwrJ0e)7LBFWp?ZxNy%TN81t-`MjJ z+C*2kCb0ir>$pK=Qb$(p;OX_bAq~(;)d~=5YB|=#kuQ=%8T_=SU13jo9I&I~0=oD- ze_A&X*I(KmKFEh!P4&pi8lJ~1(|5g9bW+9T_jsHrF{^LapwUto8MuLIUJF=4myS9h z-&6Ar4HBM{u%lTJlUclEtx6Ob~oMjwNoQz2Hu&6L_!gf89 zq6EH-pApAbn_%V5d&x{K6y`jUif_;o1fYacYaF<-V*X- z&xw<~tblpGNgnJkaCdyVUIZmnG;qR=at$WZYiC|B*hPJ->#chRglq0TC%1qdPOn~F z-PHN7zKXsmj$H|@*~8c+D$SZBW$T}2kTR^)6afS47LJvBGN&(WAN@0+;kz*HU$IX^gTi!+gn{qf~#m@X|5mGXA$OU`gk7vrjP<5PYo!4@s4^= ze}%d)$FkXEa8CX9nUx>_c74xj9;jNegx$LKmspy!SF^TsJfXGF3U+_~l_s4F6!{0Q8oD);W!Vz+xg$34ze3XorFuNdOi-JHd>kNB2u zugY(LSw2rpv%-om;@`V%J-nkcqrZ}HI`|b)c;Q-k3mmfCBF}>HFy?;N? zQz!{J*!B=xflFOds6J0?LTR+G=sk7tw4*ZD^zD*W-dy^Z5-|*{Co%sxsgQg6P0<}TSgche!kd#R1RRDwhbBS=0Z!fU zC>Vs?9B^l;xwFD}8&CB8P7PKsNTuaxj85kq26y!pl~mqU1uID^BD;IuRx0^h) zHY+6m;8qVJ8MzvqrQ%|~l7P?O_YEsy|90`d~bz_a$S@cVLIc61AnfY@u)f*{Um z4IuOA)C5dmbW9Tw%K9}~Az{UC{-rfN!0svN#AI0((yT5G5$|?_kow0(1{AeIL>01T z(LX~gIa4Y=sgqSM$`84U+pYaH7<~trF2K)P!RL=+7Lem08JL62l69uhc@=ChLt9RK4HEsTx`7yn6IY$;oT%@ROG7BAH_wnk=-J?l zgM174Y>^9(0sU5?cAc5Jv6q6?n+w}(RRhTzEDXJ(W1VbifT75*K?ak-WJT%mxQ906 z*L*~>Xs$PooGPe|!+qje+u{Y|TtYk|D8RmX(Yg6cN-Y_3K}g;W4J=r#k)O(n)&!Kp)W? zW?F!xN;mV#=J#=cqMA5F<}dn^diIwq9qeFL%*l5QH4r**fUVXMXn6`Kf8w|b!6+~G| zs5IVae>0+gjM`dH%=1Fh7*8sA8S@P@Vf2vE^Mn2DC_g zjIxm~Nl6v1>3tgz0=5)sKH=k*H{lSgA=0q8pXPeT-7?GFRoKs1O;4Pdr>mWjnh?Z? z;?bhFSUGFaK(k_&Kn+Z`i36u=RtRH!uDN637(;GSa9Pbb!dGljt<(3L%=zTjRoRXW zQJ0jUirMp(DX_r><5GQ@^?k&7S->+2s2P1%&QSWTE%B?C<`^bq$ig*NO`)k5+)9lOKZf5{2x3qfnOhY6ebC}+)LD)$Q@Pb+9%tL>^CdOFaNx?)cOY#*Te)u zFsMI)Py{60)dx`MdgQXIqlDWhZBMaM?WT7eYdz6XvIZ792$~iuc_Gz{BRADn0~WQj z2M<}a674nc8tT*w#!y)ARj7Wr_aZu`>#Yp1Ys&D$?Nn$x|LFB)4r7;^o?b`E8MH!8 zKYvN)>#=?G-|S##=wR^j*}xZpT|lSatuz;{YhDBqA-(vkL*B)?^F)7&g4GcDo8_ra zp%#wfFz{VKfXw$z%>QDs za!ZqepsS_8=DMRI)a%V*HFCDD#m(1=K7CpYFZ9t_M*kD` zx79rRPv?t2=F;{@KP^&Xe01p~h-QA~d=<`U*?Jj(0l|Ui>Xt**H1hq~xDOLPvoqsM z7km^ki;RrxlI6{{tTZZ#;KzGy_ho!6*vY%o?yBrC_H4+_8ny{0e!TLPquR}ModGoy z;;GK3ueZM|F2LcV~+$XI{b?l`aGdh;k zBi+Bqg{>{Vm97UYkPQa`9xi1O8m^c#urnwSssBL8d}=z#%LcE?YmWdf#Gh-Z-m`sv22 z4|kiVd6ccfU{aP?D`;%Q$bB56{89e7Sz#973G({O(zI{t^qns13q!%CNdjE+DPP5; zOcZ2@K&dBSdho0IHN~3fSpgrAIkQy?rTG)!64zgCUUmck!j)j`(TBzf-Rx5X`;fnr zq8!?5ZX*A(G=HPrHazj&-@$VYCrI{o27(=zmLL~@Ff2zGzXh`DuTU(Cl+?o)T693F zq-TXO1cVgP@tN!=+sn6P;SYSP0m&a5N=rK=>6U!7qbw{SB+K=58d@J(vnb?2SFm9E z{sIvJw7SAaqv#27Vv-#9OuuS)GL+9R^T~J#YnuLzKiHHE5C+`xshFBc!CSb#^j-P9 zcQc^S)+SArvBAb*Z9s1tR$&PPfDMpSxWlkrmtuy}qbGKJ3Cp*Hm`x|e4&8quPdyN+ z0+vT;QCp6T;(ktWyv;AyyrB2)%q7sqqGQf&i42hR0D%U;HIc*^#R<*6?vALTe5V;H z*_^dnt=~9vb`g(2AceTO$eF`SsVLleb41!9TjbFTG~}(Y2IDeKbjHP>=r9)w%{xrU z--UST;>EB!5p2L>5{&+)`*d;;UepUg?IAH>^ijoZVb(|uf-LDTtoZW^_56QwxQUI- z?%UR&JZO5>S8Mx|lrDmGk7;@NT?t)$`@JPlhxt=t<)`Pw-SWbno*l-dafL&;CpUFP zAgVgfZf-KPaL4=zwazM{@A-46r@+}V3->Xhem`MHRyg0`d7^*@$EhTGnFWsNZ6M@C z@w#&}i>}ThLn+66WflW-X?u6DIWfge23T-z@x znGVXDMp33Am@2NQT;jXdy1o^#ZJ4#xVo}gUW4Tg!az&?U z;B?KojbDW|d||A!7zMGmBOzDHoGM#yuM|B?Umn}hX3%0C*C_Gmk=0|x<6Qu+0n6-k z^_Rc8BWybCDHU|j=_+yt=Dmk*jSltLzRD5hFV^UZ>|c2H9DdOo-mWyDpDZ}IGf$Ab z@6HZtWUEPbGe)}VB6XvK9AMi7>MZ~8sckis$V-QmtOi65D}8phh*+xw1lGUQ6p)=l zIw;J_?KU4OQdp|V^YkJ&3|Ooz%SvTEH7U0R^4s`EXwLofZc=0H;Lh3~#wq!{NJ&jA zmGl{l(p<9vXfmNBB989mb@HsEp2;Md3~-d4kDM1CJUeom#F(P~!&VA&(0W9iSmPIma~3pTD7 z&$P9pmKw-H+cb@7KSv4^ek?ZYoy+?fwa@tTjum~mqavLGdU?6R3AssxKnu-hYHDLv zTDycc_vupOf_aKkGK7}XtJ3rOB#A-XWQRgW=uU$~z}1j?KURn7B&wOm<1`%p3b?Sz za&1Pv%LtCk)k)ylN_&&?i8m8Q`5z4oLYZT0t-8_%AYo9tkCEmq7Y39U^Bgx*Zs zHT|~rds}@PK#J{r2fG#SbK0>J!p*f^!2oAAY0kTO_GGKU(BQQ=3nJN*lSX;h>zc@Y zRI~cqa8N&)M=-SSy0ib4bF06{ep|A}^-$nwME-h(g`L++xSE1`?cBsnGg0!J*W%># zu2lQ!rbKo7B zR%M?|_p6c}wFyw|5oh{M>(zt0HRHab4L1#ghGmPA-7mRLyLRN(j$Mz^$KWzQ=M61) zQgdBQs$5CCw!INHYsam3_@%^#9$%kk^}3v^n-!nrRXsS13l?=-4w5&#WLqVPKjoQ* zY|pF0H$z;uovRUoPR`|dbG}6D#JGWtliLZF7GUU!~cyNHcBiL{oHh1H!eZ_%J9?*qLv8Tv?GD*{MiMa7kfJ*gjj+(05lT*q+CA^k zbKjJLkLYL{+^#-192dJI>Cj2dw`{uh#aC^k<&_Y`Oe6#*A6YpaD+7kS`Vz&vN5pa4 z3nMRm7?ds^v$5Q1u`C(ZP@S@3LAXzoH*Ys*vofxP``RpbCzChFdtEbGZvMJxQ8(J_pSp`!t_WS7?tPYw?yf7;c@c6y&9j z)dfHNHY<4|s2tqSW)hrq(11ZH@yr0fai0Q6eMccnz7txwTy=G4QI2z#@eyIiy**Bq zGLv@hjun=f?%0WiR~;*!^!jx%YBp&lL{kH;qnAU*K{0;a#v8)~*!SqQ8;Yg1foN(q z$m&i_xhj=*5xFBdsh?h9`F@IEx*ncjg8kG{{#ey`+Ebl0nm|6-`f&=ZzP?_(vhU7F z7J9+J8FlGJ^&C;fsQM{!F{;AEYpIQkQ&V*0>F z?4&yy9g>^9e9QJETKoi`(p%pd-{7h$@2spgWQczn~# z@^Kivnf3Vl6oSBiBt+3`uK}!NcSWNxG zu;T%#;NCp*t={~(y09w^e$%=@7=rqBfz6KbY?hr!U@=lBP?kG?G>Om2t8&cut#D2NQy;VeOKr`5u(|g@uTa7x>?qw#sLRJ0f zMPM4-qmhuTcRdCjEIN|!W0<*&9MF7eQ{bEQ&zFV5SfQB{0ubSF1_8om_B(%`0H3;O3PDhKJh z2X8E>e!f!x%X6lUMhX(+j+X@g85CT4#8rfuDiS1iTjeqp_B6yOt<^y9;1sWgchmw)K}^M z^2s7!)|YQ;iQo0?gC~1+#TUKmPqSgo;KGavClv4`o%LFNe*30BSDklet`ZkBVrpVW zcBQL3;jCA}P))U*S67CAmq12vf5GZYFj%c(yWeJDAH8dtH-lt#)0aXG|9A;7h3Q2g zJ*|FY|KbS2b*9^8e}j5u8Sku&z_yf*5eTe`%ZS5VUh&c zGVF%Ssw+Nccjd~JI`U*+4#5X?{BsjB=Tp0WZ(2G`djj!svF`=+Syu<+pv0g51~>t3ttqj+G9hw0@VE?}XFWQ!4U-;w z+J;6#@0cKA-y0D%%G|rE_y?o-a0vQq!sq(C)@cdr+#+}lpLNck$4bA!Nk6!$1^^|HZ8o_a zTK%f^5W%VS23#kYQ3(kN^TZ_1s6|z)^2mti9#LZ|ZRRf#B2HqCh7wpw<6(6T0%Z3 z4Zvi;Ia+G6g_e#52KA#1tEfsfH~O9ubAevSK<$WJ_xxpeE_A-^I8#$)Q4BU_=`?!^ zT|S7VqA_IG4vL2kO%1mtnB7Q;w$m69l&dn6x$ZsC*p@R=_g$H?SA(6@mbk`Oa%TNj z@CyzUOmkA+ZO$9=P5Q+Dd}Lzg>65)O0LS*n)rsnA3A?VSm!Ev&Hz3X=FXRXu6W=*| z?=oEa{+R3c5p|fL$E%S+1TSs~FHU^g>f$uZ_#*MWE9RgWC(FJ)3A3FDnT~$GKm!C> z`<{1*$-HvmyB0eFg-UyoK19`2BBs#Znw~c$O<8QvZ(Ks5O-u9>fgo@jE!SE4qSs4A z`PU0xWQ!ZU0p0OD4RF}YSQ%>L6*eH4-Q)uI+!K7?@_c5bFIeO4+hMrVY!Fxq4i|_B#8w!q*m*kdBQt!KRYe5ai_Oj9np7*rd1-+}c2UlO2|@IAkeeU@)-WW1S@}Xl_3&$^ITNgKUGew+LzS>gX~=(J6cQMCi_Qny=%B zH~W7z_Eu>L@+Qlp+IL?Om6c|ObsM+r1kl1JG)l!~o5Q1mCk;8UH)%+*OD@x*C+aN= zOQbKdYhCaPrn4=~D)=W>i${3&qj4NIY?4jKIlWA}s=Y59gDw~zKTxhP@G0S?DQg#( zn(GR2$f&Zl6Lo#iU(uk>Vrk~w9?s_RjHs1%AxOT(a-DeULFI+UQIo`jvr1jMHp4Ow z;~5bdp&N0ztL+h8YhT@K?GxThG-}h8PE41M(xZl~ea>;Bcq4Z0d>5rMu*5zQ^+=C^kwC-tm`Bwg~zrj%UP~7`26XZAcWuYooIwk(wH+)P^{=0e^S5bOaEM^u4{P=GGtkk8Z`#c*M12Z)iL${*L;5QO$0;` zxIELgelupwk{T)_sdwYo&vcO1Vc!K!WVevBYP%*Vu=dTz8)OvY-4h0Aq9fOjX3HJ!>2i|Og+TA> zlCCA;Yaa)Giii$k^sqNAFLGVi3h3NUQUzcS8Vb(qBGd%#^WF(sbG`2U6*_p*F4AoH z%&yZ`H|o%hlifQ#Reo5w_$Dk+P4GGA9RakD19aDH872l?j^#|Zl|YEIQy!k_TSjIM z5sfb;^|>cA=p_*|Z4i`zBFV%@GFPWbceJqoAz=}Hjg$GRN z2p6{-KDq6s^Ye8o%&8;u8+?z&_-$@`__L#4tMjcYZ*~{Q85*_0NAeYq2ircO4aPq# zAcVH=w%C*santXORN7D@j3l%S@UXmDcAKyOD#a~l14eTos>I~`U=)I(T`%!Te*cWY z?}zmjGnRz~wb#!?;lEAh(9i(}tgyh_L&1ic{hGN3i}%Fn2sPd-A)aS9L8k&@ayGe4dteFsnCyoQv-72zmm2eG+vS_J@AV}CSNTQ7pBGRc!-K=+K;XM;Tr zYc}Pa&d8DgysQkPzGokx$rK0XG2*k;b)>At4M@&qjSK6}LQ4bK>q?9qeS5gzSQl6c z4LI7&;i9CL>gP%tr$)LY`E7v4jy_rO_Dr7%1#}dEKEHfbQw^VX_w6z75UhYQZ>+X6 zA`XVTrFnK;06->G>-qrmyK_!;6cUhx!iWgL#MBH2lzTC4|T^}=M#3t`lz7J7@*$U=MMA%chK-Gxz%UY$@QQ( za@ivhp#PEc_T{3ri~AALE@yhJPRq)>PajS!e-XXVPt)KR8`G1$+q9z7{{o0C03hC_ zZI2p{i4*C9KH3sfX`LICx8Iq2dkzlbe|i;@%0%CHadW8A7bEr$*{{fHjXQdWI|MS7 zvfITw=PVCb@_j2(XR3ROQ0MT1Lu4jN)g?$U*hSRENC!9}*A85!5j^p0E?+`e2z5YV=dKB)LH+3w40IN=| zc8;0-xYNLPO_G9f>UMkcRp%G4rY7GT97a>m?{Bm;5biv7XmNh|zPe%#tdWJKkZ^E$ zE?a)qm4R&jZgy~Sgv!|p|D0l-o>;d3J0PCsdDA(5dwkR%r4@i~c?kp-dI1J~bi!e$ zumBjurfk)v(Q)+tlfjlT5KefLZWR0L4U0C6x?Lrxm$%Iv;iBEs)@uD3CMWujtuc|y z1kNFK-U}g&r(-%5<}JbXZDS{ej}Sm4@@z~?+1tiLU;-gdHb9sPUiZIVBs}kpnS7^Rs$?Q|DFWjnYH~J0!4CklHKcd8}l#4 zuCOH?H$>@4o@IiaCosklQB|x8CO5SPQbUBnqkB-q_%A2-t-g64xzKsvBMi+ka9B4l z=V2c9+~kZ?c#L@WQji7wvNhKuU?3aeKAN!aM#H{I19och(cTx5a({;?@$mm`2!UxjF+ zlr#VXZV+@LKs4=URX_j6`#W^~*oDgVfDA zv8#;wJj2_)D|9f!U6Shr9C}4T_;&6MP6GHZf0UK!4~M&XUSDBMC--^B)VJkrMi~O; z-ylwn;F%dW5N#PrDmxJYa2gV07XJv`KiKm7o{RmG_?IOA*Cm-v?fX|Sd5EQ!}$fUpNvYMU$mWs8aiT8iwh7gs5l= zJha9Frhq?&S-3SY;{sR)d5w+p^Dw^SJ3S}Ycz-VzXO9q_GY=q;HwI)YRyD_YU9`lM z&ZG^J|C;mlk|Xb!XhN09FG{>1aoO{cq`S{2^Tg-an{)bu+|~C}I&8or^~sUs z;>#RaW-gH?lQ?#um7~r;2X!7YACCCP=T}}C!2j=bu5KnI+TgcfFRWcOXQ~|Y9kjKH0H?-B;KA1l8GqErw*0g{TOIaRHf{o{<@mk zJcpIuAT)a|L^RoXimfbZDY!S@XKszt`t^?Syi?CWxP@U4uqO_^8RE_(dGe=ywfk{& z8rn!am^o|IIN#fy|YUkTlCt zdBpRT%Q9~8*t3Pi4VTrlx21f_KyhS_Evz}W{beR-^iSbExdOlVlbYTiAk=b++);@8 zU*hrN*e~0jvp!n7Rfn!`Ei=}s>KV)p_M|W^N7=0LV0HY+11nc`gL)?4__$%6At%y3 zT5v}NjNz+=3hKet zq25Dq2kYS^)PZ0ObJ6Djwv6th4qhrb<)UMDvA>5cDwh{}CEDRVoaP`=%r+7lUI z7-Ix7=Y%AIayr`6!NAgBYr{iujzELfbxvc3{<c{>0TxIjyVO=+mk{uw@_%$gmNNN$=11a6eWLb2-eu zizJGZ3m*>ZfkHSN?4@I%^&n>DNQMo4WsV||j$Sr@=sO-$!T;7$_iVlrVr`abVyP0g z1&jo9HggqA9$ll~0NKUVRZse*{u3=0v1$kV0H3XLJc=yI%1gG{_diu}l<;~)eQk|c zHL@0alZxR}4s!`o0*4!$LJuLF`EZ;eJS{8=SMV=35UP0*g3Okeq;)HX_Qc_p>eox= zZm9t7RJYZJ#0*PhTK^|pm8Is(XEw_yk|!o6g7Vv96TgOJcG+PlRzbY#*7?{K9IsD9 zGc>QLEN0X;-WYcXB8`KmoSL0)+z92P#7_t<8!jZA{#3a+_A-s9ophOt7>g*KNNX50 z!!cR$W-PS3T?zk+;q7`$^5W#+W8$0fQ;U}ar$QJ0q)DyQlV#{N7rUO6BeI>iroV9k zQrw7XZxTt9;C{(0Kz^M2sy)?KUZ&tjtRZ@WmFkEPlh8%Jy0ppM`#PR^Gotl@4m&Op z#YGH*KeBo+-;f*EIq_+Kos|+5& zlcc@_wR1}cjklh2uN=vdf*q52Z-wR|=#gUH;VQ z`E~hzlh?zhZ$7+g>$tio9Cm(8?U3KG`iNZe^fBUbzU%@Ep?0$JTLv=Y%!U=J{MAMV z@^AdB(gLDc##CFqNY&S8Kcp)4IUu`;+lwh_c9biPqV)(R@>$O&WtXgyP03&NuKvig z7NgHQVP9u`@s4+Ffur3${7FezPWUA(RenN}agQvR*|p^}{V-1^+A$Z4Pps6~TTc0O z1b@i1aGHfvyOhHSciuU;!i_h9h4+pGv?~i&Na+siIQI50aGLDq%Y?WBD@vp3iIfzp zN)Nc#xD8-1@_Bf@QOZ|G;(ge4c_|~l!cnfvcts7D$D=cGzEJ6e$CD*dFdfm z`qX&z#Ys(Mwx*oK?7%xndf!a z0BJvN?uaEj!X*X=M=NvFe0y)$bb)hpCI)wY?R(}~x11;Kjw5hJhS;$xCqn})#zbAK zwGn2zpy`()XAgYpvT?9BKcGnsCC**>MEUkgQ*MdFj4o_BUiTvrwLsP3H{Hn_ICQ+m z#>PvBIKB0}dGuIm@&A89TCr~{G=&^@R?cE~|JSrc&+|zGzK26vPvDd78g)`@1`GT~ zTy`yx>Sim`xU4BV8XI{OF!T70_Bm&Fel@YKASTMp_;=Fg8eE8-<~{su0cb!QKW9}# zf91MteS*6@636NTj32PiEfeca%j( zUtJo~ABa$MY%{2E1t@t|vnIGBxj-{lehCB=!UNd6gbS%o`Y$%T)rF3~*8?XZF8H%j zC3atgu%1hLRAAneg>hs|vEsP7+OuuSfVtWE($P%F9Z?sKWe25R<1dYH#*>lY(O%0N5b-dTQfNt_`In>d(NvW^nGg{>XX{E`3Yq@v< zop<@mc6BByEB~3YX7HzO=l7$aqS|Qyfik-eE^*VZ7-p;R?H&wSN?(<0fLa*GjGaWj zb4gzbWzrIdaIjTw^}OULdxRKiQ2g%ce;ROZ>!Bq1Umr^!RB})#_9Ju$ge;xU&7^xl;2xL zka-mb%u*cOUagE;K2-WQZ^E!Rl)7TSW_~`=c&j6D>M*JpD|F?Gtqe8orL=r?^TL@| z>YnmPwb-dmae=!QyyCKkVk!jBSos!yWS z>B_L7c*(xQ)?LPCU+=(J>ZBZspr*<`HF%}j4=S_V>Q>#XWCZsFvp8XP@>>5l<^`&qNdng8t*!}C-(`7QFiK}{W5^Vds;0DNu1QP6>giP~b$N2;jxI;LFZzMrl?e_~~8kLbz zeX5Wv_2~%THG z5aF_4=CZ`nbmLH0nVVXAvz+VXhMX=?`h{ha*eVbMQ7<&6r`k;B=dG8jg03ml;2>6M zIsA$dGSa3|tHJ2ugDU-HX`2syiT0{Hq19bw@qxo(ZLsQ+vVRGO%ZNwWp#1rJDy$Za zkh-|=cV&#wbGJ=a`?U;s6$@o_jRpC!@s^-*6e_8ukAiW@%_#)I+t zJ?H-{ADP!oG8LQJ zuK)7!8)iCz>DiQ`2|kh0Z)?z2BX+=l+3JOV?I&cGcW|l+->z4|0`Hf@S0ywq*SbFI z)y8sh`*C5w$PB4oh$3Gm#_eWUTIz0m)F`xk5x*|AP_VN#l$7pkHbKeZc}(@%{}e=N zKK#u=FRDQgR#;}gur$(lP-@7~W$tA;7S*6g&Z^D{tyyl-(Hj)a!1qS9^c3Fe(hqEm z)?d~~?Pr%7{MO@N=~C$B@~Y?6$}Kgz@+p6*i;H4R`cTI#o9jiWUH*jxzodJccxN1D zbQP;+{g!8c?$%6ObNG(4*c1tp5^t=O;Zn*47Ys>lXq@i%%1NixWREQI6aCeM;-v7= zy%Al92mis&=gQ3$#p05BYiu)=9~S8sNlv4md{jq4609Q%lBc;KXp8zIR~EhdUGtZ#P;;W^-NnI_l(8A`uX67aqDsAT;_}gnE{T$B_I;FU zMIk)+_C9*J(T7J+Eoin~-oidimz{~vv4kLdwAuw^7M2HJW^T9LKEABCj4ltpGK9N| z_6ukgaHb(j&t}ZO*)UF%@tpQi1>FT{cSA71jZMpwC9_Y?zns@3k2+}$cP!sr5grvj znXNSY7BQ_iQRR1;SL$9Ho;Ks1hCNrc7AN<41|ze=4uQFt=6c+>d{vawENE<(LnrC| zPiNLA2!gqpeN+oG*!5r#29Z`V+`EGsz%Mymu|L4pN8H1!0rb^Bm*F)w*%>jor|__J zcXQT8S83)gir^!Q_gxMm*Fk9w*Ma}=?I4AqA?Q@QTc=v0s&O>MFP3v2?S_SyzD-Bh z&X`V__!aSYLRTl%%AdH}YzB4dzOTyUj%IL1RS(+)Cap)Jsd`WXP1WZj=Tv=*Knf04 zY_GOQ_R(>Uhvq~tNqq{6Qy^)c+v2%@d1!4)u8_js`*RJiUf{bSkmous3arWrqx)tz zZ_$J6gmVY2dQx|Y8OGmfD!;KYVvDwxsdjn!4s2_}%>=!+XuKbRe96!0QB)CHrxK7U zD2P1lsZ|^%z7Jhn52YF=17?ThlqjdgeX_9rq94tO+kIHNAhU3A>1sfA-=z-{$h8`~ zu!P~f|Ar~Gf#2K5z%)zJ*yOi3cA@j3e}9$UXl!&?^)48dC8H5TRwytD-KK&2y3F)j zQNiP)6wh`=Ug~0BnO3xo?FZ;OCkV<6wJ`>g;c|brQuF=uhXOMzqvj$Nx<6w1kOSKW}$hLiI{8#TOYDABR81>v0R z!@O&TP-C@U+Dzs#BP&Ww$gZ8%a$v=UCL8C+ItE-@W~$z`WA@w*D**4n*NlLRV761o zNArFOoqz}++UR#~_`lqBLze`Nj#%cc6TST>2I+qU0k|7B)D+}nxj2}SQ`FArTKeiy z;Sb&*(Rx>4R|uvFRW;V6I*#n&XRSKPEU?z8Afq${^FGO%hS`+(Y9934yg?OeMb_|` z*}Q_Zro93-%fbvkgHWw(0vA)>^AIAmfT3W{tOz~NnGX@gvIC}~StsVflrzyiVI=VU z5I86qb`V(cD0pUt`#irwT85_O6nFUiZFDNyf-8y!IUfS^ZOn^oV3!bbPa5jv&6fnM zHL<>Yjay6%a1|hsfMWRf9~e4!v_vLlBVLU%4f}ddx^qX4%P-`_%BN%=}@cxad6$ z{W7wz5G#wjD9*@gxUgmm^X8UIHHV3Fa6-P#TT!)8Nl2&&B8zDo>yiw%5NK(0oRdp? zKOK?mY6%WhD=^U1X&)x`XF-(?9mt1wNArRjd?FPrgav1^Ei@tIc)B%uc#g1!zqOTz z^vXG`!O&_CnkAca*dO7C!=%ry^Cat)4F%2B;<0MC2;-0UIuAP&5^g`&LfovcXv}+j zxx*$l`q2&|T8zejaFND59={oH?DkU9!h;uN3EgQ}Ng8kiSsX8j(4Q zSa1r43&-9JY>>@-8mRoGnO@h3#o&ng^$g)>O5sWkwSGT@d#GBDYsZU?GB8R^O;g+u zvY+YJcQos7A6R)2dNfrbo|BBzz$G70QxLADU}gEZ19D*y^KCUOm|t^U~lLgBvmh75L!FiSlj9B8W%_aUwi(74zcgM5T&&+X~r2V4iSBHDH>) zv>#42ggk*~5(b9>eTrU)_OhgPg62y=+%AblJX{2|zsrI+)=ItqZD!W+P+!%)o=w>o zv-)%cx)I~jt2Yw*1>>t@ufF7!2{RH>9bfI2w^hgTkp+qJ23nM;U0iQ@UbM)MKR21b z$fUKI`>-}2*thT~xyEE*Mhnqm;h5IJ1!sp7Y#o8Dq7k$xB%*t|bAN1E(JHVw&Gzx* zcFBX)CFQj@v|}Wo<~hnk94pLFudZMqxDMlv6=fYl@?hKk_UqGwpua&y3RH}s?`C@EU1JkOd z#-GwRoJaZDntHYMeGn?V)nfOzU(0zr&+}fJACkDz8e?^(^)okTh0%t&FGS+A_F%?p zSwSfzJX`fz*Di;{@q(xm;&;XIMUQV*t8HwdjYeLGP8#1nuwRFYqAv$~MPmAwQ~Cnm9fh6j^;vzey2GeVn#AhooGm%8e5+?AJm1niw@b3z@_f-_Ki#?6MXH zot9Rp)lN|So^rvC3-V!l%&K#ad(F)5Rkj||GaB-dk_4PO%eoW|(@4)N>@b|Fq2uF+ zTe2l>l9_PcxvuD&8rR`8N_A3Se!r!Sgl~G++G=)`qSw6*<48_0FOT)Cq=5~zbsnD_(;(wiaA3A+}c!YfR9 zFDimez$YEf4Z^wWvT7>ORC6YfsM4rEA@+#aE))ZPqOv+}S0aOF??yK)vA60>Yh0rt zy|&ww|Hf7XaTGK2?!}kqImY*svR(wbc5zOw`m6Og@W0s&4sGXT3KC(z5OOYK4@+*R-FZ2FvT&@*5~{r4#v6VAH`+qVTuf1Xj)s zu<+|({1#9l`(Q9Ed_(ZwJk3ftEd2DH0UY#()x4z;lWe7zFF1S|rS`@Q$IzCJMsm4T zJxy3hAD4`AoSc0%5`n3pWf0hXrPY#E6l$8TG$K03p>tIH?t>c=e|Hq`xYRmYY9tR$ z6B&egwZ^w`NM9yohXYf1sB-d`jI>{Vz;oJ#9r>8D>kT{qkc|;|a-LYvGfzxrqFk4t zN?;y}fCBdl<{FI$MA5CG>8&n{E{)x1n^Fb~^i^)fcp6!zuYO{kSL(aGirQ7F#Mm{*Z9KKv4 z9EJOHj`v?FoIWLqRlK}E)uddBm09^f$mfYSTi*no0G3}r zcEQQ?JWHBjmAi?+_>S5pS3KQq+j5TUmx=H zEVQa54_6EtRsV)r3zkIoXYmXKQ>$EizwyRB`I+YW)NN%5-c+h>*2CSh{`ndobewzM zH`8AiG3WZ_U#osnu!4TILu%)aE=4V5!OC7$d^77&q)lYiux-SAKFOvi&TXq8tG~O| zWJm!OSg14moxgQS;jzOz=fHmJ+_>A<>*f_mD^coBw10N)Wjd@$7D05XiTPo0m1taCniR=vVE=cXqCc+wybxmuCw$g+-sOP%u0$LE=Y>W(g zGl)?Wl~@qQ=-ynPneSVgy{fx9sxuZNWvdkzan=7$mO;pJp$;2Q$5A(Wy#0u$+7L$Z zT;7Aq6+K|k%^cQS{4q?=GA5Y3W;{_u^4PT%BGCq`>MBBa&JJ3i;&NSB{0L)XP*lC> zrt#%)ExsaHM3yi{$b+1f>=O$q^3o|+0h-Rk7H`jn;jEz^c?Sa8FPI8 ztdQ9-oJej~Xob@AG_B~P&eULXmcv$jmXGe?UxI%Jo5KWX!9VdSGd}y1fo}J8no<=# zC`Ay#F7y4{5)o$Ho?K)+i;@1n(}o6~zMV&u&;77SQYX&2r}T_D473rYpR;ysgm|K5 z81HAKD*E4|f4S?qEWIsc$4x(U95Gmw@2_pq9&_7p10T4V0p>`(K@&lF4CKNrzH_zH zvj_OZO0xhWMT)N>whAL>CIZqQkF#czsIE6M>Dqn;sus&ie<8=|hG+Jt{Cxv`@Z-tJ zC8BSi{GHn^65d8H`UcPOW!fQY**NdEI*nY`xFlpwLW+}aQ+t4+UM6J zU^3CUp#NvGl$VCnrBYQYB~hviGR#%e82*JRO|fB51U?DqDps^bxzS}ndQmxZG#+t8)aJ*ULA;%= zBNh>I*~+h}=L*sbpEmVHcfRQFbf)!5cgO8*9w&x?wb0E!~Wv(<34^kbFnQ*GE28Q&GOk zHjT*&s1=lt1>N?QdbT`DrrRE7gTD8Cl$5U0<9Ii}Q=q4YbD(p9<|DO3yhRK>;7h$A z1`5O3jI`gVP36fv(2nddNQ@BUtgbd_6)9=%!}$1=cJU ztJ`d3z;*&TolQFXmQp$GVysV{z8yu2r$S!q$Z_m zo;1+&F$GZu|8@@5M3UwPNXcxTiMxlrk>;cvVZcDMakbcW4q?`-7io0r7mN?JH2LjD zQoV=xr)uMuNMB(sw@azlC-VTUcCRbE+LDHXvDCpbeZfGI}t+|cteDzT+c6YU5-nBFj?05~BXmJD*f5lQP1^*~60rZClB5bVK-P%2_&l(#;k=tAt*)wPjNs75%Laa6F zb8B_j=&-YTnK2p~pzQbJP>QD-ihY65YLR4n%o|m?;ivNl0oPRw^!QzIHrD0 zf{udm^TnwW=Lc>d44D(H(ePzT&n2yS>@RiPLNaRnc{;CacE|f(HiU&9zNi)W-*rBP zP}Pzho<6ky6aWqA7D4?}M)b?EJfsIG{3pr(9pC+@i2jeX?y&-N`S-Cu2D#%v-OYlO zEEdKr7p-yuX2OEu;U9%G|Gr^?ECt9t<1%v`BmGJ~Q^qZ8|0GFb+sP&L zdnWoo-cPkm*?<)S78Vwi|Bjl0frA6`$HndG{iO%|zjOHAMJaaol@Zb~p-)uU8Y&fo zbuc4E>O9nNY0uh{0ngZ?cGs2bO@3Z}&|TE!r?S#-hhUasWu=M5dGl6Ypk(HL9sA}m zsi;@Qd+2hCVMA^M;`iIbr2g017M+SEt=38@GiISOW_ijulWjId52|@7g_Gq^)-{19 zU`@0$W}8r>gJRg-$LYrJVkc4p|-yp;Wi>;KPi;8+|J5X-L(F9(zh5=*7j4jmQ+ z43E--W^h`*eNadnN_*=L#L7-e^Xz1Ds#*{aDrx7R{aN2+Wy@yFz{C+h-Pv={KQJO@ z`(!LG=fzHJb~tR0qfk~E(K|f3V##pgzztlP^vAJ6#(;$zjj8Kqv^RX;fq@;)C6>nq z1qJ%_u?{B9IL@0))dhwI^}Wq}2?K&> zrsqqP!b+>B6LKME(@G)d)0iP==MD~UuY(b$fj-L_^QHHO*RzeE6)LiadaxC?&eFX> z{n?lkZz6T&(>iOjpTZ8aSa1KdNq>*46Z)jN!X*V&GnMVH#z{<`Ga!`&IHU(;hraBkLz+W!!X8XOB2Zn1sRUJbE)z^aO^q-tT0= z`YbL-;3QzRT?U&Amysxj_fWh*s!KFNi9nEpsjegu<8y* ztIA7o*d{pS^dg!>&s3E~6JAl5#^xVRju8+Sshs*g=(97DO(3g3s=4ymk1MWu1|6K& zQkl{XgUeBMcbR^Bm!qUbG^Z}KlG3Sj!8CBbz)Ka4BSFABO64k>myC`X}_{OMEUA!2!Ns?`U811}I zNsk=14z{J0(@3g0?BJ}q(b&K>?Dsl7Nj(eL+stjOe3hGLkFzT9^Yt<9Q}<@6d(xv7 zmJWS*^w`rNT!yRe%l5kQ3lP!qxAqP1#p{Nkcuw!$mS?T1OOm@b1ziGX*pK4^L3&M6 zjhgaw1O=)ook=lnJ1U%tD`r0K*dEz;!(L~Ip3cLc)@cQ|?#zVq>`zp?ctU63A>IGsRE}jRbp(SDu?R1%g5IB@-jGlaM9tM+7 zodw>ZdIVlDr23cGQD^hlB>A((Pzm-$Q=@mPw1m~2toA*)?4X+xt+p#%DPk=*Eg#GL z1*GjhK+&eM#UUyq_G`;kRBbHm{wLJX|ESgc4Y+q=03N-dCSmmJ_3woYiWyc(WIZWz zqw{nlsY^HKy<1YVjt`+SZ~!-3Ldz5`O-jOGNPVo>8E{bOBDgaT4Dc=#EVACA7sd|l;Ji2=!ZzLLAVuid6Ac)-!&^ULCrQ!@ zR1kffgt_Y2#YeXa@WDbd&3+oA?r4t>%c%)=&1X?xX5kQ-a=d8s>UqPnWiZz-fx!NKX5>3GW%geGQ!0%BU@R4=Qd<@fSU6G zMqxyy0p_IwZQUTO~USy{#oN@L6_(L~VMxqii z>Q4m@pdFnt;w?1j*+8voC54|g3?}rVHF)#W&-0eK^=?SmJb-dt9KXN4CK#79pwaVueeSd`J9dG!?j>&FSwiKo))dh9@+404=CVK#t&jtL}G2I;}YnA5+X9NBatIA{6OdW)sp)8J! z>sni@?Ilvy?I5>jQ#DRy0?)bXH=G%2+1A}p=gXA-?87Pd4IkYO*NgbB4tP}?S7dtA zx^^+V<&8bor`=yC$n%80riGzx-|k6A*(fSO*(%D;8Z$9k*d%vZ$@bgd5nzt*?#UOu zE;46!cz*nt)nreDsc18RkNx& z^#tJoqvJ&I#H1V3v)~-f8*@(vCokKLe^%{wMdY?t4DEU&(SHF-A2tKd4TyXsxhKpp zZlyilj9)xQvst!bk%cz5Z5(WyCmIAV9M4?VR;vdM4W)^eYo~>qU zlrK@K4bM@j68Lg0hGx)CI?;&8l$K7>byhE_wkrxrUP0E-rla*Qjo@6jcXjp4vV5|W zBQ@#xK%Uasvd=AWcl~jiqgJ>wy!iR?Gh;ggm0>uQ!Oz+5jlb`d zNCpOR!q`sGem+9N?k0K6iL>J7CSCaZn^MTiaiUgYt1_+wQ39r-Qx; z!YNnnPpB-+L-lUOax+ggiml3~2 zhct|P$CN4u*nYBB?>_h@1!F1u)Q0y}Fy=eUuvLN?ly6*DYpyArOlg4P%6kYV@JLFj z7I-m;R>x#b$N22$(RGcj88y8gQ%f+@)uE5=gHgec!#AlC!mR|RU)?Vy*j$>Dm2stu zPw}Ao2s{%%VJqQkw480`uE9JQ4ZkhfT~z5P_k@Fq{n~VcQSa!^a{0_3QNWNj)WfMM zL#-<>Pc?=zU1qeQFXG;~&byB*qU*BpQ#5^DVtb%J zNmJsLt-=k0$@7MReA|{D3g3o4%f}803MYCjSxRDbO8ggZ!gztw)elYU7(KUJ1^KmU z#6LnAduQ`dP3dxqd%<;O)@bbPkezbfv2THA>) z(>VV>Xa>L-MD+l1pt?w)ITem6VVxcLF!NLSGnW7(CY;cISB9zUn4tTEjqF&pBaTgdEqzcI)`Ql^MX($|4|Qsv zeHsBz6Ew`Icd}P7lM;u+y%^>TXS(MZ*lF=hwU zc|~Jzfo9Y?{h*l={S^)y2E9r#bva7fs_enBIaNi1vPiJH%tG4nIH$XYgjbH9NCIZW z%=cNHB9WZLXyg~Q3SIir+KrKO72L7?HaFBKkJlo~74$hpnD~1e^C$5wIGGb>58D0j zjscRqD=74 zsU10fm&!qp&IzaUWoc8|?3XG#%o!zZ(v+%HX|>zVrq=6%2FHk;;$DgWY)tW~PvqDC#OJeQ?ZK7al%9y`9*^*`roi!`G!*!yr!U^(n!OPQ>eW1vlE1^%|*B=IEuNb;%aztslB>^c2zEbg^nU9k=KmDL}Qj(3EOjiIxC6_nc@7r0kZehHLC1N#l z?_xk8uGyoQt*{{1w~zB#(i&;rd|7QjxroKb8=X-^Uf?W^fbAtGrUgTX6UlePaXtw) zXknN{S&3oWnf-dyy2U>6!>J2RR$xyEU%3*6JK%9FMc%v=z3Fk8DjwMAoT(^y!$6Kd z5{(rUX9_1=2Gg8p8BthfO#!#MuTUoABgvUBpv|8_;iS$FBB4})$TK&)4hRr8u!GMT|pBtuDwRo-Ln zhYY+yAz0R|p7>#+^EZ3qSp4mwi%!AerDWdyQk+?mNd%!VXC9A%5H<8D1*q_Isk`iLnb76Bh+#85? zHbld^=!^`}gQ9!`SyP@$4sgU_s;3?9k@HxnUNxwB))iU>L}uG=+A!oWwI8&>0fiPd4t%ghR@XIK4l5m7;zN5U z0;9UmyvN4&%B5_qKxGa)KL_aQsA%z`0z150Y?rdcA_8YfOmG;~! z&=fpkSz$xjjrq{va6Ya;Toa-=8n^94=enzi#b9fzQiI)Pg|jhviv@MmNUQy@hv{j~T1aXf$C(#ZvLe z^Vha@aVHcp{Q)z$_a8g|iBUyA61~bE4_RG}vC;gu<`9siFcM|_52AM@Kak%yfB9UD zsU+I1b@70e9+VNrjqiB&&a2oH>AT{L@PdmGo24~l|DseM)oZVojPqh~Pe@jj2E1f& zRL_#M!wgSShcoSoz!Ep`y!4R|<8~r1q25_B3$9mJy>zt|$#yJ;iEuLVT~Yjch|fsK z31bcjuh-CJEu3|#b{|>5=Lyd<*w(T`ErV;>TJzWx=+UusS+j4kYg(_ostspv?Nw>e z8i6~Z*OgIX^^3MOW9!5Z!wYYjI=xe2T#qUcH27GT96K02wdEVET4<%nw|Lj!x!`zQhP;|shvtvOpO zxpkChMy8**NAVa|y#Sru5BKI22DD!X2`~ja-23KJg@fc zp)ybY+A!!pw3#8}mzO{X=c^JZ7GL(Up-Qq4=Cuq2@=avE(SwrfsJ zD#Q7IqHO=%e}DTGA^!{tW}G*CJwRKt%hmp!2Y&QjiJLcY+;9pceJ`eg>gAz%#{X70CW;VhhP-FW?$mc*Lbn*{ z3(UnZzX}a-k>>+3Bm*-t;LEDcXA-BeW`v(FhDoc_E6JlMoHoM_9ky8U1?Vwo^@>$m zdy|YeB>xZWO!$SJE#&*E|3c?q4W&(pE&cM_xw7XdV`NscxM$pF^Xrx%y}$rHPbYz5 zW#uZjW8?Y}FnU=Q{t88M?oaY-Z2bF!n!$(?y@gfdvl-G2-xDH-ZrgnM5zyN5K*Oqa zuLb@o|0maF6MXJLE6W$%wMm}6_dd<3lUN47gRh>eKto`ua!D1AufIQ(!PJ*OI!C+> zewASUb4hjpVZ}*QF9c%f6go=2RKj=(ps0r>@rLAVUXz2)88M|(XQ!a3Gz>%^^{Jvb zzq~Ozw!;Q9!HDW3gxcK~2MF%N>vhBzXG)kgdk6hD$88E{K~Wliy?x+1?vEB!pq#k@ zNYmsJ-QfxykKmg0C2sFy30*0>A-F<=*8b*Hbuz%8KVJ)HgET*pM5vsX=_!%-Ww^1|~6@A?$SlhNytffUuad#-i-3b(0plA)JK(XTPkRZh!id$Q( zxV5-Ta40UpEw}|IAt&AMyZ7$*{r@@Z%vu9kfy_Mf&}#z&TU(5FC~Woe=sq(1FV*n3@sJ)7AJHe0pM~U&NsA91v}SsI zwrXczQ2s6Ty{TProl~cIXySU6aRe4>s>^yAM`3LN>`On5C$tU@-`s@5zk=c+4Jz&q zwLxOXmq(5}^f{{$q3>ZD-XHv#~EkxC^%3=wQL~+pE)Z9{RiNx&ZnNhmAy>A}o z#$qy#QL)UKINUn6QU=HSeI990jAk9f>R21~x+;@q^p#4=$o9>dtFffE*Eks@0$1s) zwd^P_9n}uoX7AeN2Bf>=j{5Rt%+(mJA5N@Aw~R9;Mq0)&E@1_@<(#fyx0H_yJejzf zn&3T|^&>p)P6TZZMIEh&CjLuEf4$ncRsB2Bu{CL`41m8C2-#Y3hUAihf@I+58AUf2 z;saYixi4jT#p^4#l%NeI+mPGs^7P@+OhskHW>oW;>uX5y4|^43#2}a7E>W$9bS~x4 z6$Fze)rR@)38}-~sq^$Cb+2oR-zIjZxXpuFP1Z={wfqt?Z$K=M+aLZ!5GTv8_a3VMuEtu!aVU?n4brw_?w3+W>H>IC0S=7>g%5WRLZYdj zzkiSJZhCq7s=tZE!I}}{6Se4TDUioQddwfMWk9UL=xEsGLtN|rsn0lgD zn^SUUz_!fh3x6x9P~5r>LLy{P3hs3uwM4{NuA-n6phlko7@CHBs4@sw@K-VR(AWS8@>V)VH{7rjEjIDL-64SfKR7?d?D8mpE~MK`$I2#;Kqu^6F-6k*je} zp|usEu!VpUy%Z679FBE7W5+V9h29{Ks4Ml}AorbFKX4xq!4R&{gn#~e1X_MkB6TCG z3I8zDYVOc7jvOeH#vvk5#Lb5+$cG8KUot$#Ax5w4SyvXiI9#+|zPiT?-*V2jZfZdq zVxqOWIh-!5A2>)Mf}JL$6<2Jw?zY6}5a(X-+up(&M-NWJ1LbxrRR1x-{pc2HB`_s< zH1CVfj_qp*{SMT5a#leVYwl_)w;AEr4;naRgnh7x3GY?#KhFVlhvIxXGIpt7@2sgm zLzZuS!v&2*k|N&o|1ZW=xcG>}aOC{X)}W#AZU1Nc%wEBbV~*}Lv?J?jz`1Ee8yw9@ z<8arR;kSO26?n{`Gxc*+ z`wawnY!*xQMwdvfTKuu|4UKI1dm}o0CCT-04FS|nJagmxu5&`VX;=3GB;-Z+aQgK` zQA=@kPyJ3dtESvRaj?)q$npYpPGVpe)~DU??^~y5Y4x`prPPTZeRG1*-018#bw%aS z3_nSE*lcqJZ(P`z4JYN+wbm+AD1nx*=#1BkuC)Jt&gf}3_FaL zJafn;mk*md#gzWt46{%HwJZs@lsvYloSe~WKJopTzUPb^6YaZmN7a?%Vm(g#f-p0- ze9U)|o~zfqvl*7A4v)hXP;id40R6(dq=)QLy>i4!M|bqkmu(LPTG?ZQjx6Ev%vDps zp7C1;OIf1@8Mqc(wkU0i1OD^a_wwdRPVEoLIj;v+wri!?>RenN>2%S%& z72_Gp4UZ(M$;jr*4FfW$l?_#K#Czec}nACsNzm_sIxw}ZF2}R%CbDyx7!OEO4!#I z^94V7Z9t~Fszp`n+Y1i`4%Knig(OSHsC>j-h95qW(O=r63;3#fhftvr2IITIybwqp zGd0-s(%r~Eni}umt=|X?(3(J(E%QTq`YU-(=P_JpEsIQ5;lT~n_qk*4Wtkh7Epdyq9vltp&Ul>^EX%&?yN3M4C^5iTG*Y6oAG<*~eC9 z=j(u#Xwh4DY0MR|B*~nn5JbY4wa8_sow6MMdW;`$8g}{tE~@285xl_R=XE0;pkEc9 zHy62mRU(z!9V!iXNmMk@rC!byjdoIw)kS*4?ySAqC3*Or_Hjl@jD@Yb08hz7CiL{D zGgT|Yy`C#`4&O!SR@^O@1~UNJpCX5S9eeJ<8#JyD1RE}g=>_}mWdP~}ac^p0E&m@f z-hD^rtQLAbY(lBSM18gFmE!C`*)C-7wdHPMq5nM{CTXAMAtw6X@sWHYZR*3-3LcX~ zmAaE#a+NT*r>v^C9CddaPr#cjt9+P@fGO7H(?fZWlWkEqeau$cTP-mAj8XSNB!>QF zp*{NL`!n3TneCNvY@>BJKLHcO47X!#M0 z8i)jliJV?|k;7)j%OTI9M@nEb*5@-RO8(%1pF<$Tt3wbF-7RaG%5BJm)CK5)3nOTY z$0eY8S>3fDa^&K*Q@afNpnR)tpVee%t<#4fU+`c3OzDqXkIYh`n6X30@=nD>dtBf& zhi~b0ylrB}(&!{E;djBi(c5%k$AjocymehF06^(6(*P*>KbQ1Z=JPB=I|}E28)_at zy-$2Tb6yQn7YdfTOx&c1!;p824S(cg%tBwI8U>#|`%)A*FfdSFm#jMeCYQNbr0n&T zr$1_tWF_PPyk!>=8M${7>xVF9w-Z9+MBE+?UlEwfBB&lCG{H z9Z`tK&fGu6>WgPw3#t0#;U+s6;WAz}B;k!$#DhClRGApp1W<&Z>RwRW?@{Oq_@I-b zw*Y)pc$$c1332KCG?k_V zmQi%vr(fRsE%_AIhL{$!RSgbXPqWdO+#f*=f zd8I;}!r30!n;lLV;R*QSRenyG*Fsx+j2njCr@`Kjv%+MeB!#vPOQ^yWnyRWY*_&>F zE)NoeWHJz^Z@!^yk3@V8@JE8CGxx(5D~a;5u1m+XDC&f$dtVwqJD7ehuVv5fj@G7o zVN#uY&|JHsL_6?Rd#N|e(&9%}qW-3lGe85e>UuG3hYP~cp~)l20(*ggpWED*wjkV- z_s|Vk2qLqS?pcM8A)MIb_ONDCGPgT&tK2cOc%D2(te=gYm#-9X*y(#VA*SJ2RIulk|B?|owRmbB%ln#=kZ zwpnepgrbk6k!ylr$L`p>_7+jqgFCtVX!|VLuFTqWS`_%B<1$B~&YON7&K6OA3uT)| zod^v?0L((w?BfgH8C1y})It!u>0Qa;%=JvqdlArYRn^k4Pj6U)v|qm%!sT^5q6Mu4 z=AJrHAN?nx{~yJ5Kd*2rs5E^Nc}B`%&quM=%+~c@ZFyi^D=nTeeJcTQ-m(#cae?#PkR~(T9XsUB-H^v*pZOWQOdAM7R~*gkcd|r=khhgEJ*rCdnVf>JeVHZtB|Y*fQtabL*N#%%OAc5g`2-vA zOu^ajx$n&^{Z-F--Dx{^iPZ!20v0xU5aV^0OWHMHGg+{g$N>Ys$pu_LT5v^_NgYoM z6bFsj6&QHpLVxA}?=hOYO+p%8G<%Z&T`M-w1F;Hzzp^1`->29 zV4pC1tX)Rvu?|*y4pSI3BR=^YLvRh*_ z=40g`PM&MggiTsKM@QE)&yAoOC_db{?IlYYimSqQ9RC@6JUfA0sg_TS{tG1Rzvg9L zPHkFmLV!+3A#Za#E^l*>sD6|m9eBh1IeTmAr%ebe2@G+zQ<#&GlG599xdbVEbc$lz z`t=w|lzth?0W8l-qxH`kj9tB}|Cv@6FVC6$*=g}v&&cPE!;YM8KT5xAL)BgPrleBe z^_48ngHF+gt(`jY+kJ#G!PJ|^E48IN*i@-t^5K5zab4#1nW({ai3&vqRe{OGhgRU# zOAAoL!I!6#mGF3#YZUs5iJq0NqhJoGAsEfr*)k!!#mx-9s+gGb5kqhGcuTnXA{KkD zevNrr$`73v(3A>(u+nEa$)8n%QHXLIF&&0Wa#j!87TVz+OJ99^v}x;x=>dHMn|^aM zwEb9{Ij1+Hye4;r^lw_XDO>L*47tiso#qBRiOSm)lA%922OgqbWO77Nj9mj?_tU0C z;)cEDHoYd-&zWi8a$pD%Slz%>s>8U89zqO`!7uNcbZ!^5KaV2FjSweK=zLZ{T%IR5 z*;nhBKgi3YbKEfQDYG4u3tncsT%At5!wS$`gmT>s#$G$S;ikUs%K-ZBqp@h7Z^U{Y zBsF|~dmTpY_PB82nsNqk#_Jr7DXD*{ztnt5nOs`ZaYQvNr!74=#p^qQnr496go++L zP82MCRI~yi&n_f%x|QK5v17v0p|!Vd{}si;h0jXzF2s=)pPk52azE9-VE*wuE(0Op z);Od|b;YoUQxutr4Omirbgb>$FF&&gZt5vB9{4bHa(kf}?2aw5O_uEi5~;Dg+`PbQ zqxjH_iP`%E=(^dlp%QY)Tcl5zT+r6eK_#B(eW|>Nk7VC#e$KVP(3$O zy&acTJ-^Gm?%@5)<2CB>h$O4} zIL1WgcVa}>dUUzqId{Eq=_YK#U~8qskIXdq}t zrn_0f<#I;I_{U>A2~qy5oaOm#L9r2h+E+mz6=s z`)62NwNn*H`i$HrD$*xki#Q}iojBy1u=)l7>GDN{m6mTDw?RLpPhekSvn9SoF1u}TYEP4Uqg zc--LA#r=Md9){k4)D=5F`EY;C91QM_Vn#&}qg}jiA`e%`1N8J>QuH5`jz7mvkai_du+?I@zfmDhrHI@!rZ#f9o|pY7E{;F@t^Cp9 z=qF}BsYG`Au4@1ilTaGrF+xWj8~(s8^8HpTp-(DuJgIphU!XE4A-!O`At!F&Se<+d zy{ozsLx|+$T9VXoBrO_wo$dxiqfTQcuqvT|bL;PZv#jLwB5_Jn?$*T=DmPGj$Ctg~ zOTM3?Qho1B_Nmvws}uE`Dv;r(%iJr|Fi|;U#Eg_kMV)uE9ZgzPwoqDsBDjpyNl-~sfmsB+R~ovZDI~=xf9;JB}*s@Eorg(4~23` zcBwZ+Q^)Yt{!#a@WYqIgV}M|TGDGo4oDC)GQ;^h04XG2X-zSO`*VZC=VC@vS+{-IIp{rgtAlubCZWVWG7uik9>Of7Z_h>g zraT5o%@J#C)Mp%;>Ii92>7USh?gUmXT*3f?4lVQ^hYAFZ`#Cf+c8>hhWBJGFD2UUR%^Vi6=4SE`-_r*!`UW6x;`HA(ng#hy!Cu7Usk#uC% z;HOSAu6IVTJMK{DQOZ#MTgmlybqj-mp;f2UhD?vypF4)7*HK`- z%#&K=&0AeXKE_DI_*$jDs(&o_biUS9D#@g1ELK8HF{^80u^4A`E17af2dz+&n@V9h zt@REQN-Le1CV1=I6xP&8Tj>eTMkpROK-qYtffBi|b4kA-dXA2Jjarm~`H$uuCY~r& zy+G<}56pm5tdKY_ZN6FEm6@mm+?GE6nmknPkdO$%auLMOn11z+o{8g@Va?FJl&u{i ztBd&J;atMZ3qAhWV4~%B`gUfeNUrloj`MPj!|5Gl^kmfMXf@mC5Ovb6^uxg5<`~=L z+Ap>$#O7nHUn6hGKvA;D>1&thmbettOpvI$N(WxOloda{2P*b>n`0^WoRf%)g=D|i zDP^$}VC0)~x#Szpj&#R`s;UXRji>AT3wV&}Aib?2NKiv&-cd3#gU&ZCUtce}E5Hm@ zG-h3U-oyE~I7R(_4DlC86wsstZ#`e{T^)~p0&4awgqjilQm7qgz#*dEY23tUp4KBXSkns2f%B*c%^O;5Pq3qcZ*5k;piP zScs~*0;B9x`Agam>IX@@=EDgASpnc{$Q+X#{nFMJox}N*{`UJf;!kDRqs53dVca>$ z1KlC4FyJ;ONcE;vjtpmw#RMxpdYK#Ma6XFK)in)M11C2YjN6TgW|euAVE8=w(JN!MVq>mNAVJ9umRdL z{S@cYD^OEMsQ0HlibXX=rj4@+a8Wa;diCQp_V$>4$@W}(_}*)ilua$Y`+UYol4wwSLjuisqvo{JujNa7Y9EAoj~ zr+VI0%n3W~1e74p1Z#ybW4N3NXR+5bOs*<_cU=qXK=wb_(@73FhO$bIsFGe>c5cjfwVCn;V zjh;j=iy6m}l@>OV@}*Tp(Z48e32%(XzZ%>{#V?f8(nBWj6w-@y7w;mf{m-Yy0L7uT zzJ2(Gncd3lJ*`=HHSDt^@jF>xP??>a$7OL+dnAV$`f3z><{InODlXWhsnO~3PHKEN zqj|OjyI01_)V&ndZ@#^Pg6HvRXL>W&r~>`_ZB4A@aHhd}B0{%wuV+-d6Tt!f1VgfU zMG@~HoNkh<)-lv5wTZR|mFe)1w%Av6nF>!-u9p3BrgA8a)QOR-nrJv)c>1B6uU1Q} zRzfN26^#u42$5-P-H1`;hBVOI`4U!+a0jEp6)tAp4&&_!GjuD{Ifess?DV}4q3}-t zzzySINsw`b>Fk)cqm}pVY(Umuv5IkWl(+ozIscNpdAyH*)xSds6clcrp>Je8v3pCm zgSP7YQtJt@xkdVFC8+syfOBJW(`ni7(y5o$!?+G|jW`00TITp!(6ky}r2wDXy}>$; z18%FCnH|J+AMl8Lo^%x@V;KJtT#eEXvun83j?YEEx%a#@z5MdFjJpSS<*D(;7`B^k zpMhe|Ga!>}SV+QX9g(9=zWKT&*X^ib={${EtloZA=d@g>U6R(i05~z}Y~DSf7g&vr>zN zxa{Z8IA1&6;~%MWAHLFOmc`J;-bgUB`eF+tCD9(EaE%v|z^xZ{x$eavL#y591pQ<%>ZwPUDoN0(V){rNo`4}3*Q&PUdcE&$3lPn z!exW7rs^c(A8=O!yk zmze$tjSI!2tnNb9Lr%7JCp%0l1X-59!fRS##37IgsYl@;>kjqnWYuP4>ij3db?!+W z&8;P1TbQoo9fIqhoPI7uiGm@J;7SxjbHM+Z1YYW}f^YbVOe@NVHq@Zz<^WnE;QWdL zr|?2C#6oy?1{(5(RvW7FUNXzC!Y?j}_gdKWGDm$Yb=$6wNYt^9_sG~8c$ONB(rw4u z%vxL%eftMuMv>pZ!zs$aRn7#S_Y+1aD$~c9<{2{<)$aRf&EPvWejV=F; zkDm9!bSTAV{8G3zx0d9lZm1UvWDVu0hYzYwNtKsxZa!pcHs0~NMCn^$o z%uBGH+O$KGc}!i91-hr3)p|NDdaFB~9QqBCdBX=?xUzY%pEyG_`P2MRW$XKxm`^|M zbSY;cD~3*j60Kw>DxG%nVYitG1&pleP`Ex+*i|CGl^Q9kr z4gN;_lAO?aIbV8efhV_?gNiI#Jw=8u?SGVH@4XYVbPG;B`W zh;v!=d7$roqmCC^m9lSJ4W$wC5=gYn8l0+I82DKwO4WSY>*a2*E7X_9mo`x$=?~n} zXwp4Dl@hr^H;9tnqdP>*73~AA9|0Y^5DR|0VcQ5F?UQSlEV9~h@AOVI`-C++p=tYW z+|qf?=PH?@foCjSvW7b|5O$$mu|#6F{10rM$=4bu78M?PFAZJJqz1B2->7q*8Dr3- zq4uDT@Fejeo3$TNdqfi-xyCK-G^ow&ohg~Z^q!|71T5p0cG z-(j@**odwksSflVK*zk(ERej^;i=!GmpVSn2)P9Y_&78KrZv#5vq_7k>F4Yd6&vrP~F zqFzXu+4;GHykyk98u4@zTp@>op4mmr#2y;__t;cc#iADkuZP=G5euSkvC;K$f(0wq zhL)E{ISSQTyG#O=1oxI2gT1nlVLE(?tGDzWKZr-xuQKRX1D6#YpayP~*lNSn@+W}&CSgp@U0GI4KSuGF1piuI4@Wg@>#4+Ke9UQ_i)mJAdHT06~(+? zW1x+65?{*|UL=kJ@+O$^VvZsSpA<#^JZ4kv*-ZZ#4jxWi2`9?Lo}4H+=lBsp$}R({1a_C;EF zLws-AaZ8IHKH_iclUb2cM|AnXOAqdlM;(O?E6HtqMk{qRnmxQfSL1~^ zxvht4h&n!d35}NE@J?!=)(bUzJT$-5PXEiZW-PBt0 z-F+&#nvlM99OdPOVha+QeAth!jTHAlq&z<-YFD`UF<38DSI0bqO^xiJe{b*t#bagy zCmS}`gVJ>_=MUGFeqzK+kjY$QXYUx$`Kt2^%Q2X+n1<+Bl^fEQ3A9#)fHmouUx3l0 zC+*MN=RdBn!IvLK#iD$CX_uTUo}1M$x?$*WaE8@A+c|8PVP=QpvHiJb*n*(}@S{vC zD}8mT2;PR{>`~4}bSxbdI_Hmkes?kNOvP**F}VKAhJH0V)&e%-Qg_nj#27JC8w`ww zS2zwXFPR&fKHz$Y2iSjXe+`ZF{pEhwaxv?-04v-}otm%$pwUa85^z%GLj4OJC7|$S zqu=|3Iy36eCfCJ2_(DPhqrilW`O+EtFBxon{MfMk5wl4DAhA>ATobBZbr%u4UNIDt z=krWu@I6N06EV$-`r~5H=L@(!k+>~0!QQ;P?;5l>8_Q?TW6u|@q_$$FeF@@CR5*0M4IrR@D<7jZ?kVPdH1k){zpny0_$EJVZ!I79Tv1n z2h+V`X~p0?gbe&x9(c8iZF-HyQLxsPp?k9H>I|HF;r5gLJExhn+tg-#ZJGH@<5Buf zsSnGOyn#)CMOIb}yA`3KWl+PQ!JVeTTL~*}w;bEh;+~lkaL~k+lMDaU&pYnJ61O+W zb%Cs-QJa7o?N1@=3+8XN8^2#rF}bwrEs#F9HM-@Xzq_vEy89NaRojF7FqU()RkJxq zqFw(`ighJ-`hb~>k<}I6p;hItHb>eN+fqy;0<0ErRgDvIX!F}Lnb}vlnnA?E&La%E zYGDA&q$YwIMrMP{K@zeBAnOsRKF}^WX%cJn?a|kH@&G{|&lpX9L=&+M=J&7G+Ai^@ z7I|4puNk7Zi~3Hml``A}$O%WX02&{eXm@2 zPHQZX_MKz#r8<6l>;{<+f0}BlfAP2)h2WjAk#tw|3_Kor>q+X>lQcPb&#tYJs(6UV z)w+tqiLOHR2G>RVuzjk2(a*EQkOWnIHfu^j7k4?pawp)yDrwLpxqj0;RUhFIzdWvb zD?i)>hpnsKJ@VAy9cf+n_)%|N_W5?+&Oq9W4zQ8*ns~NF#)0&?e(TooZ3XnO4u-!7 z!hPZ~g~_H`@HE231u@fhbo!)7&_n>e{)z7L>8mO4LYI>&E1iJTDWv(<^eW!8 zH4PUKL&Xm^$M?6#TBKDc>xEbAL4ew;dDOiY7htC!rVom7GV5fTNg-Ev!@a3ZgUj#I zA|6LI?52+*NAR_m`#Ez;P4vS{oT#6zT-7Kfv;w+X8!DV2b{fZb15z&y@b{FArMVSq z&&+as+eP)Ce$S0L{keSHM#h8W-bvgz^WeXjBKD2Aez#KWy6Q|V`5d*5q$-=iSl_D+ zPkYbPTQ(!(kGChk2RJa5@ox+i_SNX$Hb5Ksl6;aXt`2=xf;!=uCH^TDM{m&USYwM&N)RBw#1q(5zWy zp-%13%S8Q&F=Uq^i3*aTMuGO9(QJB8;FBGf9yehKLfdMI4wdy3xMTs=cIr zPEqAVHt@1|{*g8QdOGMX!$~n%esxC5_aNuG`RBojWE3Bd*NvS$4NY*-nNN1Z1>Mrb z=4k-<7uwvrws@3W&5-qTCoPw8GAy*@_jFZ(@-n9U_5Y}SXRwM&`VDpz@-3Nn=Yk+5XI2J zM4F!=m;%+rpz@eEhct!b^DVg4}+joy9OH;i{C`E_Jk z-{hg|-eSgkOvPWFIlXWo&|hQ8NM4sam6pTo>`<)^z4sUE2B8tttpbZ4nabkV!u zx~q3e|Jr^_+6Pr`Ux9k}l0g{tZ9pwZd%1J5iEIMr7aY87u8Hmx zeZ)KQHVB~v3;8@1}N#)E%NgDs+EC6i1g2xwc%yOysp5pWC zrZ@+qu9W|bT%ieB+jbw&vG?}n;ic&6&5mxqtdkCn6+e(WwCC~NpI4fW>Q}8lmMU+a z1T~qRg|$CkM0TKLB7u?vE()sfH$@Uc^ka~cfs zLVj4it|J#;IEXQK+wLQ2%cc6*zv0JIn>>O(Cc9&s1OI(X_HQ#3H<^5K4IE&rfSOu>Dyt%x*FzBPb+2J$SZKN;QfWAXOGB z1rwB_`aL0? zs30BUse}u|;yTxzBnPHgz7Z3D7Pi1gP3_0~Y{v&HH7IcxA4AtosPa`~xr4^T8t|C; z@y>i9p`RdeWd@oNqY*$R1^`-EJ3{|WAD=X_HxPMg1bt;aBqpDjiZeWg%^gX(T zi1PmM+y-P(I3B-QM9ZFq;A(c-r;)9V>b6l6ReJf$jfBmidK-aA9Qi?fz5RiOJWPQV zir&v4L%E}s!sAl&ky(HM;5&(J`(g!bg`-F=DT~W*n<`tM=N zH{9gVJ1)L50aqItitmw04bkTqbr8`RS_PL}L$PPZ%-&3r)>x0^s-~Z;R~S>Hmfv^x z4eb`HpbQB4)7Uxkg zPcu2qqA_Fp9GN7d4%#>vnR74qXbtywmVfC3!&rpuw{N5nc-{Q2-hjv8xmT%StAEkV z`|jbkh~pD`e8`t44%~x))Q*i3-c7>)lC(INldB|?4``wY;0>(jl?vPt$5#x!;>M+&khnwApU9 zEgY?^VoZ90;z(Lle^PCk-uFM-HiT6EgE?gNVcHJL=e(ixB8H}a$1WfH zeS6mt3fYV5lC0ESa349eb373yZa6w}IGq|Xnh^P2{Jdkl=Iwm3SmU`hO%$TugQgy0 zSwLk?R}Iqn`2D9D>!1j=Bo{>ake?|}fNAlHCa#UY#LZ;6r$MM+tZpO7tCRydsObz}hB7bN?CE@ygR7VP>PI01G;}I69=kD(AuKb<%&2vAw_GMvZ zr5ya5%zWOydlx)s3%34A#f~1AmS)BEQ$<}J7OqQ6}M>VtCg(e(s?D(-^vW&yd8 zrJ46J+Pd?8KUTJmSh9Ubeq67pv-3G%%LRChcdaYs+_hdxu%OoDB{7;wJefcDGl4Qi z@&;`Y$1QV7|tB?`Fuo`u$FuE z*;gA+`+KtY*Nrkq=Hvpl$MX}@_}s1!1ONF-!@>gQbpGA^9Z#*-9NwyU0C0c4T5^jyC_>ZW7;k5l3TFDrW_BoE1s2d!GH}7R|kp zR&ZX^M)|(HK-7sb;Wf2Z$lK|orwRTu#t zz+J6POicRD-e^cCb$2T)`CTr?HU4*l;5TOnkh+uQ;Nal;Mf*}Mo4(ZhX=!QT*G(H6 z8x$&Ns5h;xt)n)X2d~JzZUeI!S!Ygr8|+_dDV2qr5DM6xK9KjE>Dr})p7aHN8Nc_& z%LQ(1E5|C;z;y$|AJQQ=umxMa@jQqz5eu{_Pa3NCeH$iao0=H@W(-tU z3ZC1)^Bm)2hWR6I9fVOLa`c53HY!O(Hnbl4>nJlwIw<~fp;yWDSTxm?+Nv>uqDtTD zzJEYy`4s*;2s6x8HC&of-2Ulkn>V}sS`;xOGGlH?lniD3MNY2?zqRXUZrDM-H-l4B zhQvA1$n(@EvKNTLXf_AakmJtsM2^E7T`0V}r?vK`ULS8rc&MpvWMcxf9g%}?C?|R; zUpr)M4qb}~8Ds(ncJi?;SMXIn^d_n&=r=5>&f__U`}kA}s;YP@ z$z1&E*5o;(ifupy!|_B>aPZdyM|J6Q)JP~|uggUb`Q86+=jq-=ox?I1L{3Jw=H67L zSH@^cWoBVPrE-vvm}ts{zT)0gR8+*D9Lu$Cv~hsz9WuQu<%v%lY)5qQD=*K{V)fO*>#2izSzxgoU&$!Ao@ z@APqXfz(hbDJ%FPuw!(_;A*xeG2aJ=9<$y8Haj@TAv{eziy2G*uOpCjyWy95*@ZNaX`bJy2kCgI*059)l-gB+m|w zi%Uw1ii-u3|9*-MM@_@dn@f6=PCJd7A@j_SKawx$s79G9c-h#0ggh)jx6Nbo}GQWYUjy}6Y_*@z0?^?ztvZq-uF15 z>K_UZ4XbPXWM`97^AT*uX5h_{Sje4!PPz0_Ycyl1TZMPCijuxs!jiq*S{{28X3ct7CgA-x>& z%awCz_=90$FNevwqAvK`zfxN%QdK^b;G8lNTV75splb4K=1Dim^Z1jeBccx%x&&Uh z`4ZkmXTFqJn1E;NeJN^i8M_i*4Zx!k3QJ9;NaoW0kU7aClM{jP`ZZB|lF*s-OMeNPSb=T!^q$I3B$NFkFj}Kdj%es=b%=?;a~$ zDA2|c3TX(lg6B_t2MCn+_5b-JaF|9rCQrlhiemO+T`KtBson~CY9z8UvSi55io&w0 zydzh3+w3A(jgHb4sXXZGkjHn8p6glnMYhWb( zo3c@m#4WGTu4TPoba(Mdp^;cIZ9eEoFBs|X9Qu;yCG*`;=i#gk3J>)5Rl(L%|AqLx zf`Y@#9MylWFvlAbZR+X$vA@Y)9A@}c_{4S)}r1R!SnKOELD`70DoQ6{Rc#-IK{w>0CH7%NY66!Pp#+u`_oCD zQgIU*I!8JItw5Z^V)id1~&AsWe^`@g%!GA=J0s)-^Yk4~~5pB}Tanr%9 z2i}|JI7OdUQ!McDPo{$i4}%AQe4$68GiZ;5Juxbz#nQ6_nw=D&uT*`lo>~^9`Fp$H z!O=NMWe@q>4;Pfx)nopZ7|zFBewpE>qiYFc0gzy*(;|M|@$3*6M%&;YS=e!&-;1A8 z1vxYB8J-)*tCuQif0Z+$=iVt zkaJfmB=xR`cc07c4MelzQuAG966uljka>!9H^}g7O@BPMlvHM4pOWeStt~yTi|?)*q-QAfN{POIOo$X!!q<)b=C^prI| z4!(mVfb>+k88{U~NqmGY{FV(F9iz)wCasYe& zM|XXEOt&r9zhd#vS3eZ~AeH~D=RZHKgeQJ|mG=X$b!cj88gqs3iw51tXJTUuVO2?{ zVE-983T5Wt=-06q@;o;F@ZrN195x#LcaQakc4=`jPR(4@l(n!r#{bV}L7{E4C893q z`tKImWd0*;oz)B9{EwCHJQ@NAhV3_v0+0rO+R*%wLw61T_tp6^G z$SUY2-PyFr@V5*58{t=+CBbyZ&qd-Pk>)vq4d`1qFZL}<^;1rqQp##oH(SV9oc?!@ zq(oSSf%_I!ug4>7-=;%LGRgiKk6cAU>ehkBB5IHqa;;9k;=u}6J61GqFSy)uLIa=A z*}_p!;3b=th3lw{tOUUyzA6e3I~RrgAdweDFDpXqV@`O3|Ks(|wFU6*%l*oDp|9A| zP4Z*k)Jl;*tTe#_EJ<=HY##<8BgN3jqWhidd!6kyrp7LGB?U?28ybN`1dWSh(UV~~ zwW@CA0?QKMaP8q+FiNmXuk!dcj`17ptJCO_9d(P>PyGxGT^I>JP26csem z7TIM|(tEzhQ(I9g7<1(&`9)3_m!I5n+~XsQ#d_W@!t0QprnV#8Ra}H7az^bAgu}rp z81ukcwr&so(U0bp{1t?nt%05mjoT+HPc>MmxzwJE?zy!%-8!GRtHjxPgDDNF_t;0k z@nd}z-p`+tpWnTLzaf&WHkcIh8w_i3A+gF$RDOxeK4gJmB>i*q9SJZ;V~PEpXU5!O zLA_D}C`0KbA$yDxk~4I4FW*{GJb0O`3I<)TR(g3%%>a5Hg-S(x~gx@`&@<`+$tm&sXPrvji{@L8)trkYU7KE zWN5YI3O8h~45cijTa74wvUsTR=;kRWhyF6$raeACIYKST(^s}7v|4Ryxa~#%*NLwc zqqOj=8oh@qVFeRbi(=j%1o&fC!r3}wG|bz%)ABr`k5SH(szoWk`fEjZ%U|bJpY#Kv z=rjb42Fpo@n7P!bVi~p!e8m?SY2@c0P&V1OXW4&tlA$D7-noRHgxtm>{@)YgB%1T_ zxwlnyFc^u9>|oL0;Z=(v*{neod8uIUL3mwZzj6yIhu*T+(!R<$U0n=1p7@Qs+Rm(|@5&ekxwIQq}(#i;bVRyHUEMqYNC+%qz# z6t9`7+Halmg>By#DJ}jsf_*`kwc}(j)&$D!`UEQ_kzh^hfaYHf+5$a|g^iG2+?-2& zrnm0AGODS^ugI00wf#O*>tu&OENVAymb0`f2KZG6%@y6(6aO6Qg)$>5KBH9KTZ)|^ zP}e=}D9cW6;xsWQCSr$%_!Dh-%tVOm*Pj4qE=3H%+CEuqGwm-aPEn*8w2m_?_78BV zbuybI8E##=P^2oT8QGK<%;NNk)JGnB!y43|ymFLopkFK5*L+mkO%TgIfb*85X6I)X z$=38b<}R**ZBA0}#WVWUeHMaP)^V|>Fd8)X%zk&Y1?otS`RV-`;tj{w+Od`y*hleM zi9*M(v{yO|3)VFVf@56Z*VcRA=k(;=x>A%{pP0b_qBTxg0DPKcN_ks>JBAbGlWs+f zpFFj#s5|5|Ph!ew;|Dv~vr0CR@exZu$*xZgv8*IcA`7AXzL=mf;=^Oze7Be2MfXC}BR(1b7Y+@pCaL7yUu19`9o3^G&WigusT4QZ3 zlhj{z)+(O$>A|}HR{eiu1w<+RS5Wq%{)|)o?yVa9N}VBVL5u&@Sv_~68GC)}a(A&u zafa&$0sv=F0CR4YH|PJu*IP!#v29($5P}5@!Gb3cAb4Hv|K7jtyP5D}~J zI5$U!C1sbKIGKY85tAJ|ok`Yg7;U9Lv3d`7MJ1X>AQCer_#J)wx;K4K7)R}i1xj(E zBU!oF2WQ%8Rb|BsAUb=8uBep;k(Z4$`RGZm!2;#%ja@ay1WLbYISmSl#$o;So+P@K9?QEN0Z%K7Um%M`>F ztxuuf(`vz6u+Pev>ChxjkF?IC<*t=v5U|>l;4(-cEb;5X#-=s(QjM)Vj$qT@OTQKi zpu?Pg&u#ZblPT3_QDZqE9-D5+^kFXWc%C*yaISC-DJ_TJhMsL(Iqq4jD^e7vF+#t% z*O0waySD(cBx&c!o4iXAo%9VTtw;K3gSa&Jk{B}*>1%J^cvFL)Z7Q;Po02t+-cTXf zvDsO!@1Ea$8PO@uyFEEmVEzG@-6fLD%8_|s9~0_p?nQi(o^J`s^Lb>;?(@Ig$Ur4( zegK!Sdk~gBR}w8P6gv`=__0hL2^84BT3~JowpSe=wi>^;FrgO>W3PUm>Xb!k3N)&1 z;&r~UpP5@3?c9xv^WNkA~&Qdt-dAa6Ut+!sr;p|3Qx@n+1+Bm@1sWd2~hOIy9@c+l0#7k!D|v z%T6TaX|BK?4<57~dn19Pvng8Hoady}@-tU9Wp>t!cb zM`kOPOO^S3%wReFaW)CT)BG-pmRv-9>H^%|YE)U{wfqo~r0-*QE2r@qDK@9hy1Cbf zhsnXgYmVI22pz8x28M@O6BeNs*?np+I**+d6{oIM5&w}yMe7~-c>3g}IkHn3`NM(Z zDj?x5x*o~9nScz{)#vb;J1AZuvLi3cSt6k%u{AD<-nR~Owl;XlN;vFbQrTsHSmVnXDQ{A*##*om}b_uXU=v=NT?xyt#^uq8yVa~7+n6hA)?w`C{M7~{)t0=cty?ra4RMz5mpx5~I#t z6(QczFf$2n!A~{!m8CF#pkYWPvOgYdnM#+U;l90~Tx9dS;92Ie3_FV;2&2=e_D#lX znS)^I$o5(m2{U>tpwHQ-E*`O1u6mXM33iAB`-va+8v>zIbVPg(r`!hxS-|aE$hGtJ zzVuivIagKch*)~dovYEO!z`o7W{TLW3g6%E>O!u&FPn8|{B#PJp8C{K%9z-@>%Qnyijuu*HA0~B9A|^=0cmZCd|eP$Bqw}K^=FGg6pMh zmj2zJ$a1B9Fm7aBYTs`(TjJp`!0<#eprIUQGlM`f3kjHVgG{r0I+uT=aeu8G*8%ws zyQ2UmT#Uey5*ou|IEl@9#lRQ>STIkA;j;(r$FqCk&rTJd^<@s2a~+7t?6k7F@B)^5 zdn;KO?QwC~T|nT#$OxOPdBYldzmw+RLOW+>t5cl_S_s*)a{pMAjmc)v-PX2H*K5T3 z4Nn!mw2>V~ZC4mA>rnnNDjPd|CojX0i0c}M6L*!i>af-xoTR7_D(vb% zGR035%49LDNNs}Vp0-u_nl0#Zje7z7XK=Ve^KcM;hS4kIyAWvyd zIoWInQ^L6T8gK`W@9v^1<-r9lwVJ~4s7=@aXfLN`SlFvE9VrOMBL>dH3SU&yIS5V$ zszS&_6KTsbjfYchw}Z>xnmTq3w%)^)GY=%LU3fXea6uTE@3rWycZ@46Ic5vBdXJuv zZ#PFw#XB+RtE zHq`st!I*2C!g?8u+hu5_Mou0qc_EM8>M6?L98iWetw}Eus9Zx)^%N$p-|d3Z_r?UM zsv~I^iP6VpcC|+s7~-x~+E8;1x#)i``ay;#f)NhHH|e#sb}ws?+T>D_0~f^nFf2q4 ziXASU3XXbdzJl#t8zBRQC}@hw=~e`jMud#;2s6OOjWmz7uCutY4V;P1{B)D@(8x#8Qinji?MI^h#SnxdUhoJqyp!cc_gnDz(^ z?9__28_Mlk5|fU)ozVIWFsOz#1if1-7#|p1S1!I+4D~uVbD%_KpLh2qS91c9Z<^-s z_StLja1@kI$jSKLw9E(@SrX?DX}NmBI8FC{iJ7KHl}f_6>1C!F@*`pHmDpv$vYpR% ze`2^$afQ<*tA6bvfw0oD@K{Ne>Rf#%z%&2szy=P977QzRwgHfvFuH(AkjvnEns6gU*{y{;C{&f`r(efcj2-I> z(6sk#Hrp+v5|QfXX{CL!FT*#t_^7VjSq}~4r=P*;AoimmNi8nxWn)1P7t+X%-fa{V za^_{J^9w6W4YkkDks3$)=wwGGM|LJ90ER-E@qra*p;v8@E(UDARHlfBCbu_=g5LD? zI>;X=)R~gA+w;;Movnd^+ZOxPmTiXO$ArFh`owlWnCU`orHJD#!csb}=b-r)f5sjX zmQz6fR^|B%eqJ4x2{A6@+Qr}#QZXSLmet3DajRCbmP&;OFj!hd1lq};G%|7n7aS46 zmav##RyM5mBCEbWp_zM=Hu}Gt)?o1^RtO%3S>Xo*C^|R@YEF2wxwYtYL~{k1!X*~V zv1=xBBP*M?`fSBl=c`*(PoW0gyMDz$>xd&645y#mIQ@3;>ZPPX+5FLGC$yRT-g9r< zLNpZl(_)O5s&w-`WN4QeJTJ~xds}SRvb!hRlD-cAHh2WgV7YtE;(HiG2;tzPcFM@XK0NmGR)M<(8wKgH(Xrx!5uhzzdF&|b zqb)uBSyO8x+)}H1ZCFl^=$7o*&MpPc^ox2~1raJlf!OHyd232%cq>Q=DQ%H0oElPC z^$Y#(>)`HiK&p*rX_|Q=#=$}HvpHUBLep8@ZrnE`ax)=aSyxXVh_fjaHiA)kGkQ|n z@_Y*;>)OiLCb#4zvVnB9{<)$}x?Ss)3Nil{g<|MZJL#N3iWr}2@MH#$;#=t~#-$^uu>nG;(>=?};&kA`gv zJZd-Q?|Tj{Y@d_{6ZYMX=ztw@iuWX!SqUQz;)cZO77 zBfpCM({&G;mzs6>fvLc{^py>hYOjoICr;=hi0pBL@LA+lm6zV$G6!j1dwv&9s#b%o z#Iq4zV;lZ%9JMaWqSAs~1XNsjJ9mK17X8-EL@zh3-4 zdR_iRGE`;urBlz<*H6cI?d>*fLmAY0ud*rvYoJzdzEunpB7KM>nd|SkakBy^>tU=##ffy~Wh-3KlOfEa@bDSJSiTCp6AZRIH!md;bQ{m@Dwaf2ri-kd1ZmV#|~H zN8L^IeaZvc%MGYIdx$tMD{Mp6l{f%qNLluI&KiKkl2jU+jxtzeP90{a1`POJxL^t)MH+!|oJuHFp*7XI7%$KBN(zt=yR^_jCc7w?8|uFV*<qOzN~m0pYC6-Nu~Co!?Lc|&o)>Gl4jl^T=>oL}l!x$V80W)8W9 z#?AF+Lp?OJRP&s6d$KS%T?kp7qm1GFh^pyO+SZ|@s!-2=C8fUV$>>{2iJ38bwuSb9 zZ0_7>3*St}QKYFVsGgpX-RzI$Ao-^4!7ATZu)Eq5n1 zz0BJLixBX!a1l)Q;){{be(BB!S}^hbjje>8?f?i-{bUA=qH z`S?++HL&6rIOD#zDxQL79JVPhkO1fklVkaOCIg+L(cp+wj?kb?G_+0FDm{mn&6F*K znaC|r50w+wa@Es{%Sz$K^s~2{(>^bG^w-~hAAfkq6@G>UgHm01&Fttz+=(1@lVj1` z(-Aq!r&Up6UbLuBM{rl9r6ZJ#Xe$!ycyz*FjOj%zqTzG#O48e`@1B(z(_4u7>3KS8 z9pg&Bzr5|Orv)PdXg27%k9E#z)}MMjv}&}Dr@MXW#K$=JsqP7#mYrw?F4WM#CXjD2GrIx4{;s?Y-(B=;X-l(v=*n$w5`7ixQ7Jbsj?4`<8+f{|1Jg}B z`1$ZbcP0^zOc0R}EqYY$E;nl+qG8?N&^K>^O*utWGfVHT{qY zH;Rt<;t}TTYjgbz3_S_wH|e+^#YA2w;v?MJ8mq3o;^qZTcD;CLZkj_T&3GgLxL6p@ zH;r5$KU!(=Yhr8Lu$>gbrrBDU8dyCuS}iO-mD6iyV(@dc@Gpv*#bXS!=RD+39){`= zJ%|G$TRI5adVANxc|@LE2`l%XpDcKJidJ>!uHPh(wVF?=>k`@Kpz zbe}z$;Ydk;hsTFp4xflTWG}AubN;I%YxuGcX)9E(4Gtu}gd!tR9p864=(e6B)7vP# z(=%o^w4%nIc^%g)W))i>`B1QW+_P!EuOh6x@9mmlg@bK*e$Nwh?YJhuaxCH!*8O4{ zsta$v_oZ!2nsao_XnfDeAeTPr;P4EgJd9%;ev&h)XSV%pMsUG9h)FR@R(d^yyPm!QAY|s=DMVVyfDm!TW8rzNS+4aqyx)TTK9bM705m zXJN^x_MEghf9GMo$xIixQ7 z&)R6pO}#oERzGz8S_lArqofYE>l03%pd%|dLjG0K6*UWLZG}L>s)C;A@u8Ok(cK@^ zVK9sjOH!N#EKA=~6^rpyCwVHB4;c3d3tVcCH$Pq|Iy_amxQw$djf`j|ji?=u9EJF` zKcOQq&iJCYaRTjr(!4c(f3oFja3rt$wL5Tkw&rm?3k)a?cHSK%WOe3b*GIN#E<|E6 zISGD#4GS3R2$m{DngT{O^yr-BD21cBxD4Q#p9#D7W*tn%CKi zWpo9hnV(cchY?2?yT(|9o%Z>HWhis8pHV#^uH{MpezdT!u37WR8*6%f-jZYL!v>+9 zUd8_Eh}+}q*M7}`SwO@5@II2!I zXaS&m5Q=^S*3PHD3@iVGRhgw1aJ^b!G;i?i1qi5*jG6>a)X~?wu3~CU4_3>6Aox&tmW%1GI~DQvhxhV+Q4h-Ao;6gjOG>*6fqcX zPCYM1o&F@ZR#%ZRd)K+Khc6t?lw^QRWhal#p4DCyQ=0^?%D65NvYo3a1_I{Z6M3Xpw_HJ%-7p9ou z?m2tL+-uoSuZ{${V$_$8!ZrR*W`Ha->yjVp{ zNjxxsTn3qnU>duQP0g!6{wU72lwt$>(DMblZk`9l->z44O8X3n?l;f2MKSTv1?g%2 zqkagT!^OpNJNQUH=O6MLu7=~e8P96%jHVBj?K7S)wn+8wFQi&W!|1$nv0@sf6eB`m z59%Q;uzP^p)%$f)1yk49zy^vKC9nX#nD#g@OQRhOg+EJ}W+g=puhFlCtpiW9quQ!f zFZmu2lq}&BU6$rwExn;R^hpwNU-=;<6o`+dCu=|M=82%q{ktsXDq0L6`UT5DC}HZD zeQ1VGq}>(5MIW$g+RcXWS;jdGKnzn<=&4S0Q&;Tc zScfh&X=I`A=DtmES|1ot{x`XEwkv{+JxRfk;6EAo_+_KiN6wY=@tT zDcqd~y|_PhDl!L)i{OqF9Pv|y44_v=NryjS6fQ$qvQSAw^!->>O~x%7P#AP0S2z}L z*9}G|&w(bqZRsD?6B>sD6RWse zm7mYoR}A5n(52CNn4qTHm77P?O81Ws?|TFDo67V`XXI;&L)|-5GK8Xra-JUM4JbFd zvE5N!^vt!N4}H#`m1HIGeFElVo5vj-u}3?b=64@O$fSJ=zkfen=~c&|8T*3AfZ>xK zcgeixJbU7G#LJMdwb_;8Ol}n`Z0k)VvKnt37YBzK-JSMU%93}@PGMOK4^%@FTJz*& zk*MUZohj~l`iaQTVBcMsyO;^aDXyH_hCs(Uw?4Du`OyBC1|Ha_O^iTnB}Hg5t7@eZ zhaF{I`)Ho{co~ZGH$2=g-@+q`5v^3pzBiNS#9Sf%puHM!(njE%ge>J~+c_h<0Waam zP3wGf;fM@m`j{e{=t9N^B#p1QIq?>7Je&S{F9lEHlLSS9lRrzxKpK$?|Kp*L2 zz^jeiiy;HalBYi1UgPwA*8V{JPh4H%t;~KTUPqt#a4e)gTHOZ3Z$Sapir>`KdncsB(S1Bff_L@j z1p?gB`#4H8qx_ZsrK<)F7=^zIm@qSNX)c(9!l9Hks?%_oM$!j@8m*~TmF$j8=s0$= zlyGtpT`e@JnX8Tph@6Zk1D)CHN?skscMQGS8~YsDEY&jskB`H$lBIta+O5v12V$WJNy<}MKX@prf8-Hfv_ZyCC zJF_*H`K2DouEvwAlcnLoTiz{zM)0=(=m%E+PJ)}%04R91&+~KtI=40a(H?PNe>UqJ zZL$6dX(4cgDD&@%qXsW}xnIn_iv;uN&Z0%&(UO;`WLWl6e7Z!44fS^ovHjiDyE{0D z#)9n8aOdSFvc-;T`JFK#&k?ob@oH74b$wfkFxbX@ytdjzN$O7~NHXJVP9mdaXqxpk zmM_5LSXNR|Q6j>|?cq4K_o*gm`&CV!*Vg6NpPZ>^nXZr316f|H&_q=(brHy9!)txx zsJ6&OATXUP_ZWq5qv)BfvIa+QUeH4fVM-*Xw!~yu z&cETuxErFiMVEWWEN`yW(EZkZWip^U5P77y++b;mYwLO17UpE>eC~|DWWmYGwZn)v zfzCOm^7Km-i3~`6+J>>^c%9%Wz6Zq`FJI1 zFb<}E9_^t>U=z$p5Yx!n*jT}yV^H2a4X-6!C#xKc?egFQOgV+JR1c-qXz(! zSZJfLsxduSP~vT71FmkP6XZGWksXhM$1kB@Sz=%jx85yv<|`|0s%|@d-%J%TG-*yy zuCiLktx>I?maLAfxodh%Qx;KoOBX=>T_Bbhx^B6P^LWfPA8Pey*#8Cct`pe4ytL3< zsLxMH>8$|yg0Ix8PJ=Q_Z>(qyFIfoHAI#oUh-R$xz4P5IE&<|UmtA<*`LSKPC}?e2 zX+hjw(DFgMRo5euKz0kN#IW*8YyHa<$>r6~SolbA#Hq^HrODsf0btN{B#djzsY@(1D&ke&MaZ-O`+}5+jZe3#?)^MNBOV%^0mKc6kVj6}gJLaB38&5b>95xg#+U1c zk9}4r4utA+TZ>8qqh%wH*l+l)GwU-tptQ)3u3lII?RDwIO@S6D{0AjoYPp)PSQ`?c zsBt^Ls%rGjB5uu`GMkR(L1{@p#FCcFLW9$%73`|5(~34XgwXD8PFqT?*%9W%Xo46J zg;4`#oUqgik44;bFT3590ZO|;#Q0|kZUhi7+q3&pEx~vp4MaBMo*29=Ng_xKg7SeMA*K`EQ2ELXUNLHo>9Rc z+<e<^k;@&Y4a?0`*ThDSC%}#ds!}>bS!_$ zbv6biLM21T#gN4?JEJ-BCoDmW>_gG5IaVoN{o8XWHPVdjA8i>>mSm(5lqQ|UU#K3}kwyfqpqKHKJKgg`Y@OWAg&SmM(c z;vYARLu*P1Iw|7*#UyhFyzGxH-_7UVH#r^=rUnFiqvw1rpI1!#a1~6(y!^6VLy~{Y zWF?3VkvgrJiqbOuh?TXgChyByaP8{^OyRFL;zuS~Wj``Amn6*2#>&Ei`(XMTbzOmWeM)2pZT0l+&4Rcis=}-UVL@RI?JN**q(`f2d1-^m-<-z z)+N8ufs3zgwc*544r+I5kzH0~WI?dnnLNy-PBS{Z3>Xzw6kA?ZAvkhYs<;31xbpB1 zeCUW{xc_G4^{+#p&p!&cQ0^n*|G>9@B_U>P{~}5LHwyA*{?%Xbp$7C<>i|@mKRgzu zsI1KBa%ZIXLg&ttUmEHv^*4jJM)m)X8~Ps_JoMwuu$-WLPe&%>w2QIPbW4Pv{jYszH1PX>Zu~bYBrXPReIX+g<5YBg zi5VR&@^$)UglYjRg0yCiI?MBS_LuxHDYmKFh1!7-_ zFjcT_&5Nm*L{~%a%92=OV}RRpZdKgq})^fL~IZF@=;+Ybyyznl%Wfb)w~?` zZ;S1mPTZ5Lk}|Dhp|tX~KQ{2q+I4x-{|SoOU0 zVJMIDs)g!PmIp7U38Qr)O2eGAnk50BY)b@)k3IHz#cwL@;&9SWXAa`W1n>@FVZ_%@ zb2X}(4+)mbeXu#%ilH){YzT`q{l_Ev^E6ubcYog^MMC)HXigYVM0?$yzv)KFWX1he zEoItZYeTzDVQjU!Un{&m63T=Mt6o(b#q(UzwNE;~lQu>2InIh8Q zFYV8V`DLRh8O3#@8{LH@n??Se+G8s1neNC7Vr|3ywNYdYldtOHw*UgMV>-y%cn*tB zsH6a{m8s3(pQ+ia!rRw%Wyt_ifhWCI%6YrAtB+JsT+yTF(Q44-Ne@k+l^3PYpXzOz zMsJGg*j8!$^F+G!bJAsC-{vZ3P=MmItor6AE1eCSCf;)f#xA7wfqJo3TJM{B^+5i|t1f+CVWNk0h?j#nM|9{7HT*_Zzi=L4rlqqq^nGg6-#|2qm z2_i9esc%QYSAB}l270{NMFkp^6xP=SAA?5p1`bcpJ8=Z|5kMLmGtELN=E9pqoVZ&bB^j`{z0>HENYC$-|edcG1QO?7U zZ_=F4loXV;qiV;#$@0^}Nsae=ZRXc6ipH0|vfO;(k6*`<%sQ7)?8wj#IpU6E^18 zQi7m(^f|KS+TOtOSQq8mEIDKIy8x&^i@#We>pPUYY%**@iU!&%x=~WxzOX4w(E!w~ zD&iEeEtj0TexKS2YSG>*8~BB$w%HuJ>^kq1Jx9#Kbs3&39{$RLm%n`U1G6_v2+Bz_ zzG-bG`-Ef1S%Xop&(e06wgm-mJ0To{M}op!nvg@Gpaq2vQ!Gfw6;(n(KgpuBxZ=V< zhGa0LqvOY3l?mRL|1L&k{{x_Q7)efK?^(Oi&t;R=ql6fz33U0b&N#O z$?%%#HO|3Xkt5#FE$GTrV?V}SZMFMY8E(;-K77;xN$=|1(`NZ{xl>+vwo{IEs~<+_ z6N(dsV2;+nGT;sn24WFmE{o<*Ps3d!b;0$y-f{tqZ?n{f+iB{JpW>Su&^O~jkc5EK z%KV(s@LGr{k;B)V^X$%KSs^gh<4px-tKA{>aPzig`)zTY`%zwoEmoWGC8!+FgGDSe z&UZygZhJAg)!`CpwGnLe>~y;MXt4ifS9}xo-%IN6zb0{^EdxJQ?|3{fZ#vFb#(G@6 z^dgT$#%fPDH%xKuCl|yBn;Uy4@8(xUQnSogG(DNEa4EJ)is z>APe#DsX8&y9fwWGUkzBu$7W-_svkisHN|Vy!;}Hb|mNdc5SgU#bvb{=UbI#$FZ@B zXFTv35q5P%~Y|@$1Pg9MhCUA zCKBPPVLkkq{n2vbMVTH;_k3=%Ea>dpr&~Gl{sEvnVcVd+*etEal=?&;W#Q~OP_R8z z^lBkVxiRcjF$RN^NKa3rg|96Y<~j_uBVRKQCS}p>IR(F)7yotz_t}ZTTr1(NyC^ld zfHNkFVEJcmb#c-NhSTV?NIIJpktsMfs8wr`C|{K*dZ#BkYpaR`K8*;byyr5 zYcfMx-$jo_F+1vCjaL6ykfZCN3L-`VMT}~lp;V(=5n|5+p))*16^IT-1W$|}(V)l< zpR?X*?cm57xEbtn$V=7!T-T2v4e^Ng^2`0&pC<}l7}g=1?uvBzrlecUCzEoQTV7Vc zz5a}#I%;Z(p^;p3m&k0fEU(ja=|)IE_xGGZO}nb472CJDfmW-pDwAT?HxaF!)&H zvl&HQjBymPIGm!qwcRn)K2@|Hp3oxruAF8=lw<^@qdZ#+$LgmzH zar$$U4IaQ?Sx!TsFH&>3w;yQik$=m1ds}S6AZ}(im!DOtNsM2Y{^5P$5{(m^=ut7X zol@eJ#bZr7rtQ zGrK<0W0$QTkQXe;m*iW(+5xX5J$^k6< z>wMqdw94)aIhFEvYlsS_RQ*LG#hl;cWl8uRRCz(~sBjvim&wC9jg6C!MZCDox5 z6*=Xb1IkOK zP^U(ctbW;Tu6BPAVRw5^J3P?031=|1Z}d&lO5!kl3RmMF*0q1#dvrb1PQ6GTJvV4L zSYnK2?KT$~I)FT;|3Z1IvJr2=q zKqE<;FllK7jY@fbBSJ8K_qd(v1`=-q*D+;aT9TXii~g-P;p?4Oo(1B}qjx-}Ivl>! zqanU1KcL$b;Seb#No1S*z{jtoYBBpx>%xu_=E28bVq7PyKLnnVEM}RjRtvbMyAk+} zk;*3Fl$qf<9Q9DejBAB0*E3*Mi`;O9yaYa1Q>ICd#YU0!qEEY>u}dsi=j0U;kggHg z5>p{0pmh=*V~mrW&{g+MfG1U(&M-`44G?wjG-kcd$oXN+`|nI}@}2agNC)Qg0_aNV9<9`=&lK2?%^8?TP zS8@CY7`SG32a!sRUllZ@{x^DSbq$zPRdgYS_|9JdWT?P@?2Z4*E%L1X*zcP5qu2a) ztCrZR+Wp-U`ThRz+B4~rDqXCE_-y8@qOvftUcKmRYYwaf7ALUzMoSgqzPKa zTW3aPpW6ywHQ-rdVk%YYN*M3F=AOE$*05Sr`T}y~RY{Q*3+l<_X4m>pl%0BVv z=VmM(2(h>GMN(uY#Mpz0zC;XnTEEC_AZX|dqTv00uid0XVe-ZX^lLC^baN^263PsP z9$W8h|A49ihn#~$we=YB=OPl}Ch||_ zYkb8~do6%*u}=F7YORMsaoKl%vN?5UwjxWjT<0rWWp?U^=^RN@w2r8qS|iTd?oRZ zDAs!MGAHHfz?TU(Ss>k}wNZr+OVEwn9}$7h|3CO)PQFe)+2ngSZpYURZXLZ&&p7Ax z?>(j8rpplswW57o=&caibo{@|mKoY_FfC8Gw)>hYd(Q2u?Of)Mnd*T0{*Ji5C7k@9 z!S%-{i{!EW8)mbgzr6EIlBP6NM=!YYcnjVg+Iyog{O4x97q)|cXO9Gxcsiu2M9cg3 zeIm`Kk8bvfi|4O>F`jVaERwNVx!XOPo9f&}&OdDdwmR*9$GVCkzR&QmG_(cV-rhc( z0%qBogN=>tzbazT1W7!#2(Bi$AG4y}6AQ$kJ@_>sDpgrm5xVtE+elM>WWBN8;m`Z0 zrpsSRmAD7=b>f7-U;O8BMEgI3*)`eZu-ow#DpaBzfA2UL{ksGF&tn14xrMm4>5lY6 z*#8o>o4F0uBOOD?@mbDI82F0`*S8Pn<4&0(IlH*T;#PQdil@uv>5bKpz|M zx$LzAQ{dS<7U~}Up~&{f2Sfuypvd4)-~Yga{q0{7jm%n*d8=*dRtQd^@=3pSzFdPh zUllxB5ValqKKzzX+|^8~V{jdhw(4K0bbwG7N8Csy3XBjd@^|AfmlzV-_Z=<+7Arjn ziBx{8W4NFKCu}=BxyH2FlJm23MbLxi6)9~mdkR^y6p8A4EdUgyyW#86Zh^lhecytx zlxjATnRu_w`rrLI2GM>ccM1Oxdb7W6aqT{Vwj#Ar0mn#*lsG3N^tL)zerfv5|H|d$ z$(yuGdph%6)wxwzV^3@eTn8ce0r-|ai)c({4v}Pz{cP!D{=o9P@7ZM|7`eG)EDe?O zRF=fkj=^Tsq7m1tdkNy5Iakop|FrofdN!$50DI%Dy&%g_!x{N%8c*gfge$%@2!Gd% zqK~uSEy-obd9NV7IMKjNtnStw$0~+)0f&z1;ISco%(t|^)RpoKp?&ot=vAM{m;+-c zPeo!Tu{6CVs-a(jCQwM;)roLp52vybq&lBSSh534Vt+MY=ivt%c`I$mlkA6lkY1X8 zZ^&QSEaF6kq%fWqM!u$Mpi-n|Qyjt4W5S71TlVyO&R)9 zr+7E5%RR@6=cSIO$Lr``1^ey`#7V4JvT!W)2oem3bwwN7-!F4NAhXT{V(!Pg@ru6U+8yioAWyv4QnhcQXJ2*172@4T zb-0l5#ENe^E>z~QAWjObgbZ;0@pz1Mz0Qzo+xvTzcRMtj4Ja$e*wDFAxtQPDUgIo- z&m1!V77Q-wCPd@!67eBPBlj_#+H+rO3IxW?fklz;j#JjGXGvxOT(MT;j)ZYGMhb{4 z2xMy?tkypEN~a96OzpB9%MoAJuFva`b3t~frQM)m#?SVPv#EI4E=_=r_3Cg$cff;D zdLufB^85mkkQ%KO{V0|n%XOm?>ut<(=u2x68la7L@&krO*6e@}GUG+p2)Q`FJSnZI z>{`vHFwaQCm(z2_CV(Y?$m79sK4UO=$&{$u$~pMwecfM5_}rmL>BhIQM&uJA^g>9MwqrTA}3L$f?V^){5_BXAJ zoL)A*CS2kE(aa~>6@UY_-nKJ*yzpZ~BHOd=itL|dp)`qvl^DaA16RbCtQ}nXa+qsu z*gJ`e+x7~_<%kZPA;J|q%bH?Sp;5c3@t)hsS7Gde$G8d752R4bqM+=}lhhtDb#)_r z>q^kv3a4Yk=855~n9S~g`xF25r#XJ^Xhai$w2BHY#QVZU1fNPOciN78f0xzQ1uL_o zDv4$jX2C}G@iKMTe3uV5 z5RfBDs}m70$mqvW)$tu<`p|^=P6KkUZGT7K`Y4lcCxZAXd?<2g-fONdfx-r%-1LmS z@c`GvvES<53v$YiQ5Ha3?9dlfO8SM6a`I;!i^ni&j|2V+e$%&YQ=3(FJEjY=hfu}6 zr-QoTJKzTsdX0et9NGc*eO!`MeBLbjwD2Z<))gU!ZjyTX^j<}%=vB<2&HDBo?ZC}T zxhTW_TSP9NMn|735`p-KvBMDh#=XUX`~1r!aGrn>Np_tp$^4lRFRB${7B_mPm|>WBmaIqTVEw|8pYvCO`gpy__{5N@;c z>4$YhXvkRg`_u^@DjXiX#7A@#_v(F$$J22SNz7Q1q?jD0W1OuQ6k3KS= zxWJ9XeWt)5>DN~VlIfqLO8X}A#i+UnH3gDB1@*j{+5F(HbIvTL?Mi(OTpNxIw!0U= zrUMCGy9D$OjD&@dcOl6CsyOBEv`X{#N0!?E^5t~Z@o*wyPkeJ)HVbdZ%jl0puxF z8#+@I+mLpo)P9LH+Uk!EC%OYXPN&p2f%r#fOxcE6VjvbX()RBHbni7IBRTvkeMmtM zW0(_O9su#Z$%s|2`|(0C1nT$44(NF{ca~sub5EbjI2n+qo0>M$%B7^=5{~{L1v7I^=#Qr#7;^%b!AjW?4Eu zZwyF#GlWg9=^rzPFI5r=mQ}ZMBxAkBBwD;KGoHx3OrcA(*e7poAiKqS$I}+1jp^9- z7c$9JLyW*A8XcMKK7#6<<}J5&{jtOIoW94Hq*Gcg3N-OFydJR^`ROV zME5F;Pmftxo9+2Io(mh|Vn5Ej0*sn6t*S+ZGF4UO0%cRd*gZXha&xln&D;?C`CQvC zgoX8473i4&%B~wzo*i<*lQ7T6J+xu>ceWmvVl*%%9G74gL2w5I9E@DVt_-Y5#ydaI zo@2V41y$*-LPe*A=^}&00Y{+SQnWNN54@A`Gk|*MZf^T1r;|-g)5Wki6vk%Xr8V(9 zvj_nUuD_4BBY#+j&gz$Mbe=_g&)|+r`zqEa4>I%d%8K)awDkqH~P z%omM`W)(h^SWC9_DGMH|CXBxCef0ZX=;`KjAQh-+l!f5kqqT>>G8wT?zm>2sB(DEZ z#pG^PCpJRmjP&(tj*i~yd=Xc!-cOjhbHc>?Cc&GoFnv0c;e5hhJ!@4{X1@abcy{;3 zo0=&y0hs=~JZ>J033v5w|A1AyF>^ zj{ zfW%|RS_CWyY;&ewy*?41`@?rn| zRKVj8Q=)1_a-Gc^0Fmr)`!MZ8L42QXku_?@1-j`~M4t6h(C{-b-3lc*^ZvC1Wu|0J zerTpH=~y|-66=lr*4GR#$QJ}rp;a*s+mX%G2)God{n}H9TmYqY2c^AAXPTI4e8osr)$|n)&WPmbA86k z9Zl`cQKo-9>Hbr1wuR?)0^@E|DP=8oFfn)JxoWu4XA_#pz`k+@{iSSIY09(iu4+e% z){f|#FQBe6_9!O%80X&qN7Gq`HT}NtpAb-L2+}c?Qt6O}sR)RqLC5HcbT>l~P$?0S z4y8mI6c{zS8|lI5ksB}?w*BY#^ZP&8lWoVlj_tnh>%6b?b-oc*4B!j7C}iZkiRckjM-`sw)mQi*a)?+Xp+WKHLJpn_sx^LqYh2_SUb7T=Y~!*K#M%ON@(sfm z)bVmLOEQVMO)Bz(Z7;>N2Y$9gbqWdJpA854?Znr2#6&HB%Dwv z`yl~yBSVgk^Quw_e+219CcXvC)mTI!da!^51oux7@bG|{kS}In|B!V=Q8x< zs;1u0VgG}bZGb!a8l^OT&5~azo4vy}Cn|nUadjtTbd@jO?#jhTyvdESif&}Jm=6Tu z*Mbm|k z`@T?2q{8Luwt~Jec*NEp$*5-s-U|7$N{gWry5EQF-KH499=>619{s&;qaaTTET_Y~uo(=3feNkm4BwJ^1?&A#q}m?-+YQhTKF%&|JH2pzV2HUN z9PDo3Jp0&y+v+2)qdstTZDY?|HLZlck|8NnQlfin=wrj@q{85~GOsE3)22TN`Ag37 zCJfJ)j#zT?_Gqso3xfSssR81ln~2|t*$}W0+6kM!qK9Y+`klE`A}$12iK*IkO=IHV zrIVBAt|1pD3KUTSmROZKio!U~g)H%~UX|EjnK#)1POzZG(^%N=D&EhxV)|!H^kpMfM(%{7d&pv70c+?nIV4D_=Xi@aO4bdU5#MQ9 z$$Ui@^u27pqNl_BFv??L`oChR(xdY6nR|T8D#%=FHM&)yY`t6_w10`(@R|$Ih%PsZ zh+4m#PI$L&)&V5^c^`vGf2!{J446qyIt_Pl0V&gwvV(+S%vPyw4S^9W8hrnFS= zY+Bhu6?pqCo2+hDU(+dQB}tf&sz!s_A6GS!5dI#~Y%h0wXCrtQ++&29S*Td+q;kM| z7_ykS=`DXl+8r;6z#UW3q%t%^U& z03V3+=%j@DMKT3go%Gn*+aGL@M_l!It{x6Twqf`Ld`AT|5l~azd$DG~!BS;0zEF-6yU4go`eLZwwNWNU=~zp4bBuyi^1NS}m|0aT>RC4y;}KG4x$U8Latz;)Ml zt?{D)*JHTN%qg3r>1-cuepQcux0-s`ZH@+le)U~krZj_id-Sa5N?yssn}IR?_&+u> z1Jw}h4Bb{eN*XBN62B3QK$fvMB`-^1t+$yDb1pBnfD;dITGC%p^n zmq1O;Ne&#e^|5wu)5IjHQ?jNvSh0nK13_;5Kp8?voy^C7QW?}oK{n2D!)L%%P{xZ5 zg;dFMBs3*0iK|ro&RC?#+lCb8RE)CIcjk%%P&nU+z|>zA>*LO;H-z z9H7Fq%s@|NGJO3>X*5H67!T#|lPqjX$VEJBHToTYjHHNUJhi{veT`NrYzISgqf0gO2AJWU)2X(>zfL zaY;6vHbiuzA?iqe}V$0}Y7mn{P=y6~GI=Uc2WgwCvy3N_qIoX{z@TES{pA~M$${V;+cE|VAr zf4(z)Rt^J?&;mMGu5`y3)$O#Yo1<{?niVZ=Ut_*JsffN-+u_9|pQlHi_-?1Vk43FN z7wLgq!{bk14c(Xv^|<-C2mG2ErzH$qW z7wnd)iH-iFT@DRpc~#EVmOE(o<1Xh^@w-Uzj5O2L7~PC`xIVYjC6Oc9TcN7gU|(CU zCE(j&vCnXtGuCk6v05aF|N3_{=et|GCPoXeXImS+dZrk#K~yZqVUe;kL1Sfpl<|gm zBy!40*iTR9S~k6O&plT{4>6UXw$5d-Rrm12M-fWPj0wo0s=2b=NEW|ybGPT^LPzZS zMwZk!(vOXpZ{?{yKbE#!KFgrhcsOL`_6Y+a?x}8YSIsD|&Sk_xl@IXuX|VX)p@u}c z@Djl|8AH;|sS@ZLe|J4b4RO3%*#f$w9+ypAo_jo$s=p{M4g#MjyJva;^@!thvl({Q zY{jY^Kk>De;^#Wafc;bq&1}}=+KIjJ`$`_zdky?680V1nTiH3c{_>|W8a64Vod>yU zHe$GVH(7$iQ_yzGc+mw}WxPVg(B+3ZR!UdwB4iF!Tr}y95>Kdc^naxb{!*1=2(ty2ZR0d}`}yF{GxhDxa{WkwDB}3?04*V{Sw|5-E?< zI$LTqR|=D_VkHlHFjZ9{+OT<7J&wa(7?ap#vrLRkx9|O|91>d%HOUBSO3MMWsiXCilP{z$4xWQBbJtE_bNxGj~jE;{TDa}bn5dORY{>86-JMz$)g zXQWi^Eq&(CmvZ_Y?-+NVcWb8IiMYcu#k^ZGQ7uwOoQxH0g22xtEl zZ0AXKiE_JCLSYPlf&rGt;e@=dY6)j>D=5Uub98Sl?l2a6zMfyEHLY2_?xBe5pa*tkT|)+2?Xfi}gEJS)p4?U$(#jQ(0YBu$c6Z z@fxSr`VARzInDiFgV1YjtSSzw0o+!1eS2Z%6Z~h#ib-aKUOSfs; zQuMmxEv{j;y0}8HmEu|m9^lFrqQDCck_W&WqZ;z!3p8qqG#<#}?pNa8`X1os0iNe+ zyqqYZ^2o|Tq$f(5JYz4IX92>ng^mk8*}jE_VINzb`IF;yZVr6*s`zgGD)@MlIaA(~ zk5?n!?yyL)nEW15i4fhN_C<=Wp$TlVXDtCCEB88VN98>b~m_^wyj&t~XdrtE$1Njq+Pet3HLJ+{tW#6*e*wYWn*A9}=wKAU$| zl~wz|E%R+ak`pb0Iu<0GU;3U_N`Ef;hQ9>tmbw9NZo2Is=w#=9%*(`{$khfrunJzd+ePv+)}x$oScI>RUD zIaJ=!xEnBzzy2+9vU*PPcqc0QiW%!40NB}Jll)Y2daFfb#J=mQC}8cIMBJroyMgi;j2dcw*^ljwj}aA)Zu6$Ua4g6X%YZZd+*Apa#bXPQK(xMMAlOlxjI$ zf)YWu*PEzy+`Of}kTMSvU;}y*%tqbKbygC)VBaSWRcI}G1PiNvlwqZ7EavPMq}2_87fvo8L=Flmu2zN}V=^SR4= z$r<`0_q!H)!7J@#f2NSsAYzd?FNePAVC7JlW`S&m3z4L@1!f8*_nuyoZ)WCe8{Mz ztZ%++kJDx4Wj$_uY5Eb-M+>2{2ohbdf(`Tm=Ddk{LonRb(sTxGH@K-W=^;s^Qve3W zSzmte$BowS>fCWdc;Q(~kREXGKP+``i#`dAnOgR$HGQ{Q#=G|Sc%xTtorkqdiI=La za1`EXTu7nn?m4V`{AIf)595s`gvS0q%Q$rpMt>Ar%HB@UGZx znA-z~+!kk{@+XzuPtVEw&^8DWeu)^_SF4oQ1&P?*qK=1PT;ws>HNTKYT!V3epmc#e zl+gfRk{sNRe#`*-SJ`)ax;7CME_$Y4WuG!3wHp)l;BOt(NaL-~{sPZBXhu9QM&-7( zu*P+79uPv&$ln`zxhv}lm2dEp2p@f;i#y=qi%bj4)-}FD$S5YY!18fLHeTQ8_yTr? z`VOkTXsnny*#e&-BPNW&X!7=FoC>1(LhetAgIjdQC-B(?X1Z#v%%~T4F68?VVc68S zod9>8tmb(paAxQo@Z; zw)O=1_*+yfmQZ+Q8p! zbAP_Qa77{0Bjf`K*RrqnBQ;Ie2{F6h_UEzfV(WDpJ+|FF@#}8l6$M(qgIIS$&HMV3 zg-l?c_c1Mp0G=%A?2;Ta^%u$v6A?Fys02TctiNBx@4R=~;}TG=4jW2VamTD+SzTAx zG5zAQtPXo#{2h&vq{QxfScImSn7vUJ;T!~Amf^9?)DRI+I3;l7GBj^z1j{!;=i`(P z)me}g=KYNcxOa7i1}n&0Su+!ba=UzLtKy`$s#XZ-<+dVA`{g>i%iEQ}#Va6Z1XS1x z7!SANe*161Z~0m;jD&=6r1|vm>)wheS*UysT0Y^%sq#<|*g5~VHBk<>chg;2K9!A% zavi^{Ng))xTZpFv%+la{iFr)zS875g%{KfctV0{cjosn_@0KOXm!3$`ciN(* z1Z@T4WxOAWSRW69JQwJoYsE0&1u@l*1=B43@#D0f4ZdMzsV#I;(L@59U><-21_V52 zA*Lg)KCcatO48uTX8rq^Sko;QQN)=r&rs@wzgQA^OPrAQLU*S;u0TPj+&l(pbskrW zXMmRV_ zZ#@ls_Uw9oou)vV<_6AIpl`5`FaJd|X!_xFmr~JS8cGSJE$5YkG#^!Zns&Y{Er67c zlgEhN@P|&oaHkVzd~Z};jugBkS^szs<-Qw=O12OVx-vzFx{_*fPMsq}v_p7#&y;5= z#gD>UqZq~oBm>tm}cA6|5 z@C9#neu^^rvIS65s4-E1s8f}w`HdYg^g47r=ijkxiYf*SI%u{A1s!!bclhhs?*}}l zQ#tOk3zdu5sc1>mK#4k7=)S72@y6O%UUX<1Qbt~K9r0A5=2-%PTQq0#vSKR*iXrcg z`T#9`3WVdIW4zsX=k!_5CIezD)m++s?%Nn0)MtMA$-iBO1_vIYOLQx>?ZaznR z=qocq0+o_8MqEu36uoYGZT!YIcgmYu5>)Iv-eS4E+ECkFgF#EC0-(C4EjPYk)Z|zS zrTOk~`apHuJH;#{{J*6i$|nu z%|B$TBwF6<7eGU|4^xN*>l2||WPIyQ<`L#4CL7%4{-90r4vDQ;01sy+yfq{|4=)b<+Hos1F7;`>u6K>ujsT6GJ>md;&_& z>H_d;Xba-d(X(!*+_h&jb-C$alC*$P?(u1I-YB|Axt5%jgh|YSbfPfvshuOf$Qr_oT`-~UgcQ>u`ExXWo#&Qf5~iR;S7leGtMxKFB(Il^L5tp z!*WZ+HnF#*SfUnXGD}hkMO%YX(j$#1Qyv#b_OQ}fVH$MsmmrJ;eu0836C_ai>2AGj zOJD44NxDG}1@bZji=uF*?jNOJ+SLE9A8}OyIXD6g5mGjSgA%_V=Ebco7Qdq#icNKY z`N#+TTt+mFGcahr{q4N1c!fQcCP6;sa2$v;@TsUzL@Aht{E3OD;ziBL23)Le+!@uW zVXcz*$?MX2?8tLycF`M$P!4k3RH3rE0W77)7r^SjDzna2&NkX)fle;iofBN zQrHs)Pjmn~XT5DIiAnLaXCn<>#N4mnaz{Zkaxwgmt$Rpmo28c=1?x+k6me&hH3`dw zD3li=Ut;J%1Xa!0&RQ|C>9NLKMfh)IdNW@)@PfE0{7RqVnD#kcqnPX8WNi*+1Z7wV z*C{?Ac;eEl+y>J_Szb!teNBbY=LOLu`tBfQ!l((j8-uzk~t zr9>rgFz&Oz>MJCOP^T}t<_SnD><7Ki>{6O;M`{g^Kx&*Jk5mUAsbkI;Y5uQKSc3?2 z5L)?gLZ$T9sbt^gdr$9#Jf_Jb)7xK3U$bl;c=RED7OVH@`LhzE3I{X-R%+RtzEj>< z-o@bg{5kcsg&+57cbx6PO4rp6nD8S%|2%SO_>+<+qsGEnKH-3s9u&Y=m1rFny-gQ0 zbAU4>+2$ZCTRwOnO-)A1cf_kimix^Vf*t>Q+{pUnbeOI*_`+4DY)0LujJ5rTrrd$d zMLc^o;mO`SWFP%d^=#fE;b8T|l&d+~3lS&gv3>9zda?lF1}%C?>sf@b1BGXGUA)~W zbB+Z?Qzf$vE4*r7*}(aNAO6Vt>LMMgWumI-dgq}uV8>zPlB&eFexFhOb;UB5;xcJ@ zz=5D^#;NkO(h(qDg&29Flvr-nG1BtsS1pgm7`y^5zhQH68bic0BhG-n6z^jd=b(Fj z(wYlb{Js`lx9Sf3q&e%vl;W6x154!;Q9b0Vo30e?dr$v(TQm^}9%x{NoiWmXFcOK~ zI!a0l*eVt_vCWhi9g9sr5ig3}*2*mS;DarPOugq#yF?ITr6}bhHR0@!@^Ac-fevM4 z#Cafff8H<`9%!G>X9S;Z zK?8QWAgS_R0#cA+yD29lalXGd<~TM$K*JYRLY=IB_LD8tc*-~&>%;duEKzJws6xCy z6N9~=#NQpB=Zitv=R(v!kHh6R#N(AAj1%DCXg$#RXJTMS)K&^mCv#b~h4)`Z+0(Vr zIvU74w12STy$?q0I{VT=p96lxEZns8KKYX<@ATa`A7AYXtU3iaLfCxxiX~hnD;dnP zLLd)X@QHesy0t&;(A+4pVq5d7oTTj_Pi-Oh3B>AT(e6+i9vCS9R=LbXdzde zkd3W>`$UlEbns#Mp^?M&X(dm9?F5;x!#Rse09w5q2o*?)110UarS%DBkay>VTxV6@ zzcx+l`gU%oUn?W9Fdh$hIrJL&OEaY+08fCwsp1BK zW-7I$D(7JJ{eb%t$z$(Ekd}~*)JWPa0VWdtdM1+ zD!@-&Td`KN&iyn>r(H=1IkaRi;G&^I(H0XLKA=Gv-11*Wcdw<-{`v;JB=b6K6mx4> z0q;()=uDy!r<|5orf<3o_>ijo9U5;wXQy{(O@DcHp%DicsY?l1NS5;}z3Vh)Q5u3W zFvpK37yU-CUXwJbnz5zTI#=Vjsv+~=f6dwPRHtdOkK%>68flbiPj?EBwS_L7)g6WP>nB>e{l*8iXCmJap^_CU zvI#jxuKe&pwXEs){F2|kcIescF2vMt*J|OG5fK`*b)d=;>gyP_In55z4|2=50_DOQ zf~!+RT?{GebYgLJ6#>finYzDB1HaySv8XhGnc$79f5xQW-Yr{L>8onZd66r2@G&tp zY}{aEzw3v6Jz0rrG(+feK)zsFTVo86aLi&6q&}xpTtn~9ki(+$_NDN~p^{;6mRe$C z@erBZKy|v&uYyPI(Y*(EF}W@Gz#1-P(26|NJ3cv(WdEMNL~SI*+lvwJ`~385;eKp6agXMjuS7-rv~o zhp@o*+h1he;WTOkvLFI;Xk`Bq7gKcl}=gay*<%rV4YSef~bdX?A$zK?EOtU0iA zX8aZk4mshcrK|s_c-jNYLI}E5d%Nrtj>;TY>$f5!n=TG(5c1!XA8obGK(i`kYBHv1{qilz?c_nmqX{+)M#CSY|B5P%A&e_t(?A_Y$lMJ@1`O1*xA_PO9z z!d^UaQ1%6}W@U4{=2jft#4CqCa5Ib6*~n7MsXg;QZd&>^6(nSbcY5e>(e4nkxO;G+ z$`;dek}jR>@lNyb%X-t%rCBOAz;_k$RSF{Eg>L$m5=zfDP)i!yc+k(%qcQ!aqQPS+U+A*4(`+C`QHZ$0~SyD z3cbe2-LS)O%Z$2GgYOi;8YE?3Aw( zlT|5Oon^eRL$Xq?2)U_pivjEi2yWETV;}@viDqBr01ogpzyDLQsJi%zuI*jURorVs zvQCBtzo)%bHy4WlHyv`zV}Bv`h+V?|11v8pJ&MF;xNgR{fPzORw6(czve>R>^D(eb zq9f<>-OWf_uIFUUv~^e0E_a?j=i@@^)hDxX3cS)hm`*rc??7Sa^78`FekuFIB{4IV zC6pC?>iq|B)C_Oz0N0Y|qJxt!GlTB1UbWmE<#g6}OG0uozNe^q$i#7xFX7 zT`?qOy+T}zOIcU^TW!M9J`bMy4{^WP@G=8F@S}bh$~!LM$d@{Bf4H z*5K`l;mhCOOUQaVQWen8xCGqYF5f}RuxyYw(*FGcT+9$kKOQZJWmkR82n`zzcJ@~A zr+vp#cW3^)GEW;6Wxnh)Km%k>as)HFWE0#}&>THTi>65%n~TwxEgMKBD8a=XdhS20 zf}wT|-DPg+z1JP_u{RWQc*)uDaCrXv!T;K1KGy&mzbNKFGyGVZhgJcLZR@Bw$nMWO zN8G8;&GUx{PIvd#$_oadVoq+QH4(eDON}#`{3YSpb!p03F@?>8yHIHVZc)xsVM({w z)K(swv^z=jii`2-z`|PCB+IREXD=kHAtmle$zvfxNR{J{Pjk@P(qWZq`V*@^?7~4J za)a}s$-(=*v?{X*rA{Y3p7zZW^itY1a^t|PN9u1Xi_+#rg;&F_|Ml0FgFIsLdoPr1 zT|>W_xP|nWh$*#|C5bMVV!Oq?mq-Hn6)V+!*IZw_vp73`m#oS0BGm&`|2!wK57B># zj$J-918+=1n_G2I1+p~hrXAtxx|e%dT5aQ^Tjs%6VPRg6L^4!DV)^e->F{f1n|2n4 zO~m@_8;iObO8ANWTxkC!Yf3-wdLh6o_l6Uh^%xJOPd~brr2f#L(+47IqYL?!9bF%A zPkuk(x=aUBVED@t_|azS-NO`)`8l_Bh)~3Z=y~tKV}q3u;6pyEV7r>b4&qgy?N7LPgios05`7aVb~-`7873o^(FS7cwcMpSj{imMuQ^ z2p~24M(8z_?Ns9=zdO#Z=)1dgGVfMYU!QvaRiXQ=eab-!{3`OFRPo1Y+H*wtRNum- zDZC-)wQzxO1*5n%#*d}yMbwsc-j=T&xb#uTm_WZL?SyJI1B)vSyTc;*2ViHpM0C3> z_b5f*{>GEdP`)Z(u-rH-$YdZ{UeRVifjWyM@kn9A^b5Jz89$EnifJv0ldvj3uG-%*W{!1CYXoID$#Q~1vZ9gZI@a7_%o)l{ z$%@kWe;udvmJtHYt-H~Jk_sY6dQOccP97VWAHM@AUnw~4+wnlxZ-@euo}2#!MG-$q z!Q6$f-QEs{lwsy{$^Be*9uJj&kx3kj$iR!OEY^QLw>W4YHW-9qJsp~Azakh+ga;3f zltUhzf(X}8oES082izny`tGG1I(Xg1RXON-U-wtBM=m1hZAMS58NF3)zBCk2Fhh!( zGeLxONNZ?tJ~sAk=noLnh)MXH|Ey*Q$NKH^YQm7FU!OkuY{ zA(Jn;`MVWmkzXxyMoDF4@7_A|rr^#k#2aDO92T}pN++b2|GlTl3w|si{pV2w?IB{< zGx228Bz6Xr@NYwpgMY5dJgRu#`b*-^R~FyLAL>d+z(wt`)IV-Tx~;zMDJ!L96~4Bjz>XLG|RA=Rd!$s+Qng z^>InTNrM)s+ANdAi~er6MbyyrqQilnqQ`2h!*P*n|G>=HbCPFsu08SGSH~4l`{V6N zg!hC_X7FL3uw_%K22a3RE0KlyS!=8Rq1;Z`%uU+*3&OKa=7by9cVyyujF4(MXqJ72 z`<7Vx;ixbh!XXVTS9Q(=ZGy zPq%c*GeI?eLAf1K_(d7t!H zYx2lGY^!a%iQRp;c9tqUH!hcr#FzVw*aG@JBjcivmEF)i+|4xzK3v~1NT z;=�D~`+aDl4hQtl|}8d6Z)($RoX$1Ge~x6@e3lTMO7O^SeQx?BLog$YLncNOv_7 zuEWo@7Af;&i(JI zEg-k~^~)cUuJ?>a>XTwLijuNJDUEcqhH-V-)u=~)(Yq^?vMDu<^tZp=Oj+!%+8fM~ zsqr8MxtwNQ+f@}srKLgF+HEJ;y$&n}124!Z-;>MD?oXOY9|!W-C$}&)DT`8Ssoj{J zZ0+8=1-n}X{I+)a1TLL`Y2cIQXnXG!fAcOR0ZcfLwoA)y%*8^M?=H3bZ zMJhFJ9By5BHl==0yg0qW0#Ch;XWIB=u5s9mvwc(HTk=uB^sLX{E2Db0k-sIjKZ@!0 zawa~Jt*uO-<1%d(w?{OnIW*#^4g$k|qN%@FoE>bcj!D8o5dC0GW7fRuo4M?xBMI}X z#;w6j$@E)NVHu{7*19%s4(LCF5LI!TpZg9)n*oQG6pIw5*pWEYq?ya$wae4pd8Uza zDTCz~cM>1<#TOEKR%uj|DOE5Zh?S5`r3PE5Wk^Dl`@~+#sBCU~Rik(WzZZ{fcNSB< z^A+(3%Sz=97p5E0KU*6?zqjCljyKwvEwZ{wX@Xl){?keOh&rhSgLJdk1Cu%{$~v&$ z%{q`|20wda@bn#W{z9*zJ-^=^e9E6MD@y(l{5Rjy4->F<>Qqx=_eLDn+tUW?G5;wI z7M?stI;pLDNDOsqvCG??C)W<2c$C{LGs#MxZ*v9&4zGNE+){rt3Fgz!VC%(;8j*s% zZ4YpPBBZA4KheWB+=e3Z_X4k6sy&ibx*2v!UAI(O&xd=;l*_Yq^~Vqr3pW}HAPJLA zLd6eR46ggUdJb`!V>XvqcuTW5jC(5&d+fN0K2lKLhmFYv%HmR)^|glq??x2+qg()O z5OucUi9g?6ZxJP*I(Aaq5E50fQz{=Zwyve_5xl9Sk;wI`V3d6**UHsszwP#`AuUi! zsB+AftYzn#BIK~`kU{p<(?ZFUgi|kk_J#I;u86sU0=`7==REf7EzmALzKN^Qxz`Qn zVlrQofh3w^s=eEjC22%etI-eRKpZ%g2v^wM#f4cpyXlg7@t8djrgin9^;ResajNUu zykwPn+wYRk%@TJ5Fno;pjO&={ws-FP_+GIgDK(TQ8jO=wUu;ZWMS}-7LCu|&QW{ou zBo7sPFVQ+D-G0t{_L3PP%xO+SKaYTbAUhe`Ly`9-xg`m4N>ex-{}l6IQkE3}D9Pir zX^{DqsO_^;cWr+USL0Szx6&KN4);RF6HuEqp##N94xU3`VuK^PF=1jvF3M8Ep;fgF zKv?Plld(8`KA;scWPL%EdN)X7t!W-M2#?pQfQP@Z)jf&)P5RPpITn64O_)xBZT!j$ zULNQT<$?x1y39d4N%+sXu4Ag~wd5_iXyrWQssw)a;nGCzREYcK6XG3?n1t$`9;;8R z){fz~>p#Y(@{HnFv~<8tFwj~F9NzKGM%Ge$h7kbijMNG78_IEA|LP@XbLN2~w=9V& zWyKGU$1)-o#ecj^Sp5xZYS(ciUd4rm_SH^Ita2l`RZ^Y*wNU_xc$%J-fn%L;R3pf@oD+K_yb6!r^Rzto>&c*iPdbz@C%=BiezVcGZq^-oeAfC4a}Xs z55Ug-5c5ACwrlO0hh75Uul?I@Y397b_YTCgSoLYR9kh0-?%TN@M~B9lH_75mRAvny zb352jX72-*UpC#r}pUF`&hb&TTjjID+j!fhCGHK{W%<+VXuXfE;Xi04IWSbC}6 zd5_S$7c23xAmSP)F=ApAI}s7OqC`k?)NX+Y0E3%94GvqXf6%njgGWj^SiRM!Q1jFD z-LW3{rz!P|(9<(^17&Kv2u4xTHcF~NluAfgDwSJ(T*@N2yZ)Z zDLzqHCt{nMWH$kmeBSkVVpo&=#0%_Ob=AN009p1NY0HctAE)mKLaahk$md(m96Z)N zJ;~ulAGB@tolpf6FC*~kxlO4E;u`cVb6O*L(2zRP+Lk<*8OTCh;I znez8b9~pg<8y^dh7rvCBk>)7b?0~w+Q9m9|9(e32i>R%JHUC4>0#Gv#>tZj@ zQ;H#Y1#{v#*6;y`Y)LvI2k8uZ;?Q_mvAP|4E<}JGKTx@{wfGysny`MX;FOYn!;==zjZbAsS3P`I?L_pR-wm$>pvreIcUr~dnH z>x$E_M7ed`IQ0|TR;Fbxe+|@j7y(0iY9#@S4b(ECg!*-wO^WrFesuw+!gNN@>R?Tj zfeC-TjsUn9{_7T06s9>;H+Y5ngHXAYn%cOvT6$v_uJHC(`-knx!>-v(sP1Z;pJ5{E zNLm!-W3(zfH{=SwzrW#$@B2-Kv^L1DDV8js*7;NOVWBZ*{(wR+28ppf=QVt2d2&_PGgnl9K7y&dTlBTC?v>iuhF-vS zEtT>7%P{#JW_0=qpk<~$;{ieUxIw7*{M?5p)UOVTFw+0x;%`(=IOvR9^X^!vI&CRYSu%iTpL)=F{;;{TPb>bw`%EbU78 zWb+~`SC_m4|Lj3r$nqtP*@y4- z=Gz&pJU2?I*{#Cw(C#f961dGK=X9Ebey8fZ0}-7Ds$%u|$G7uC?&eWZO`VtVLF}=g zK3NQf5_#lQ&c^5jFB5jTj>RSOu68OOyT0L8bt;dzQ%9f@ZkkykM9KTlMAWEIr4X_9 zf86`GDgsMY>!~|Slj(%C>t2naBRwt*`-SZhX`8r+w|?n#XDnVE1LLV{Y^a?`_>736 z?v*H})uC~|F8)?Y)eLqt^Gn=pr)PfgYqC!0sD1itO7LWVfRf9P?|=vQ_r8dijWH4v zMQDp_LoGQxc3X2D(M`19NGda@d2+gAspd|M8NEf{BSy%JM%KuomsC!**W&0d6IbPg zmI87mAca)SruQsr?$uNKM9Z^zn+}AK<)wOGh2^rKPS(G>d3z48XB?B_7M!l5=EbZ# z!wOeNV2<~5q=&|2M1pHMM>^ zgm>PgroJLwAkU@hXs1f$rw0^I4>l zJOL0J8)*Sv7&r!q7G)8^XbPe>WKzg9?FMq+P!CQfpyf`<+`^5U9tZD}ll3R#;dgn< zGrHhJ+_zk0@F{d!61Ov2=gQT8S72~0t8YtAyWr~R=%MzdTd&(!qRzL*u;DIOX)N6B zKr%jV`dr$F2+-M-{yeb$qi|#C3fT7$difR0IUqSzy|p)xwphxDWGFwMXUas%%CW%? z(_;LO2Eqy_cvdFoHP}PH1=9c#el%DaN*OZR%8yp+0;1~LgqGQ$!fV#aPW9RG zfW0*zJ?s|=7RR$}Pz;Y326TH7%wDl`)O$>tovnR$95iv-RRu@34iGSmrO zwO8s6XFcHCw%{r+i8`co&3eYc=`W#xH*HTTDQJC&Rae@@(#q0md-ia?2G2@q?nt)b z#iF}zK_D|$K--D7F3QeaptA+@Glu8#^FZ`9=b%1M%LaP|t#t0IJMkf?2OH)RWw^G# zwQN3sn!*G+wxDF^bj7SKnh3W?Z$q75SEIXPQpetHAG&}-({nVQr;MzfM2GjNV_gO| zl0b;w%u#6J-n#=VPwx9^I{|w$XLo_euw=VlWw_q;Q!(zN z0tmlZ?!G5SSO&lxj3-h>aMqMbK*ufT|67;rO|Da@1C*8JPBL$3r;Z-U6{fg7AIz;X zpPC63%?qTw_Daa;;P`Ow^|Y!n;3?b1xI&YV{HXZ&Sb6*`$)zh^LD4y?@{R$2r5{?6YTP&8%6o=2_2bTE79&=}4O3`wB;nS-tdt zN6LISYz1Qr*MW8Q#kt&pPtLO4lF*|kI|u1gSMrtHx)@@31p|hihKWX8Z`!(wAOMwP zCPrIVON2U|j%vYkZnfLX4cQljeY`A?2|Mf^25&c9zFRZRY=A+&Xyn{4Jc#|Pi7_$> zUQ2=~ct>0NGCrQU#`snak0Q;Ka<|!Hdlv1{>t`Zv#IM6X2u@pUYH3snO^?GTN@+5d z-nfqoU0!u8L0Y=)!Z^MxjAJnBfO0*PG2T*(u6@X){Or^A{vO4uit1gR%EUBruromS z)B2U06yAEG{UqUT>Q2L6nNv_SgW0}Ab+GwN&8YeKDJGK4r*BT`HgajboR_Z&Qh6hP z-kWyWJn6C-@F`|NAgng$xbiPhJT^hGIZ)Ea<|IjGRN-k&o`jQKMBT(KSBpZw%| z7;^ipHpJJFf#AN24}7N-m2f3tGLNw5e^!f;x`Q6e?yxmSLf&gz4==bOZZ2NKl!q*@(&>d_w@vZU8=Q|iS>M~_LVN6_P1>N= zUeJ3(%MoR#m(jduJ>g^Y9a|M;+&-{68Gh_Q)%NSZj<(%^W1+@@!t884t8}KFUU$K> zFDd%9PS--<5HaJe9@JXUOvlo;+c?RDA)NM6d3b}5n(Sih73B@MCmvyc=?KLAlg526 zuVBfJI&xd%^i&ZX2HY2DGk4m8AA+3J6i4gsAbyAyLf9!?+yK5q?P9do;U+`=bPIXG zdv4ZqXKQJFS<@G$pmRmT`@D{-r8O-`mf@!Krgr0VDI(9Vgr(~tixF-Z(#1w+|MvUK z8V?M{&FCfSZkr!4U0~Hk8!mF^SqYGHxzF-yYzOnohD*Pzwdra+p6i6czQ;X)!NQuu zUEIu^fa&PNEH6C*L*sgcOIpo1UA?`^s5DGZPTdQRZ+>@)D{r=VH1`l5S6i(+2R)^L z?Rrcu_3fUiTpI+SLF_3n#mXbO^_Wl^&zLS`@kAFpyu^c^V7Z+jXrR61<&Dof`yKC$ zN23Vs5`JyHGmgn%AFguVtJ=Kf(A1B-KX|y%WjzxlD89GRf&q87Zz|zrr8hou(DMfJ zrutAlIepkBR>k*pLpsrB&wS1+Y+I6Rz2G0%>s>M%0dv!6 z99d9_&9aaCGLLxS3?^D;e*qj}z2qulv)hNlXe4`(>4@;Q+TOS$0)x5{Zp-1MkK4{M zueA1q&bYX~@L23b&yr-#!%#FfPEzfR&qt=kRPemr=eMQ;IUX}X)YUD4CRTO_@&2J^ zk}N$GZXcb>E#`ej2nqC$N&R`bTW-Ri4;_P*q?Nv@7ZprnBy$kdT{m*nZ1r4e@H)#v zPrO#X*Jurlgzl0Kq^_|jUfR*0tD5*)0!erdJuZ55ww}6aJ!-Ru!%l0ZGNYK{;>mr_ z$uQvOJmfZRa>0Ca?w?muXkDbIsmm$&gm+NgJ)AhN8SXht%DEn_!N(k(X3D`(tK+TI z4LFda!?w+fsV2%V~QRv+LO3H=4WFOnJ{UKtFlT%cSeLlj~MSC!>NwV7sOx8Sy8=sQm zZ!#{9ceyaf1ba)M;vv0>0v?_XKWi zG|pZJ+Ku7s2(tjsg7rbxQhv7D`HqJ>-+`K6xZ#1#0mZ>;^YLB&#nGMmgE=to()@QS z%dd{cZvZfGz?C5iK>PazQd#h^@{1C%RCvmztP_IVxZDw~ms6c>H>8XPlY06Sob1~D zR?LzqLxERN4s{yW;SS_7uAm^ldtD182#iexY7le#rw{p92iJyF?~ z%RgbU*=LVaeK=vRT{-Nuw4^M}K>d?|XfsXRQB@1d&}-?NddhfCrRyy;T0+!~4tq9O z0q!Zf*hXjRtwZxT+@UK{7?^Jyiww=c5>=!mc|XZqJCDV?@&14hYra|ZJ-aPtV$-Z0 zR*~W3sQO5bpqP&Lrl4%Txsj@-r`AT% z^#fB|aV)_Xu~jqJHxqHp&O3KY64e8WR?E?%MiFXONyAYa<8~L-(sq`*y}^<_R(qKWbnCyl;?U-!7G0zd|hwa8|%n9I^ zf4D7|^E$Ok#X`0l zdPM0h>BgKBOWeZ|`o#UW>-T`WIzv*a406E6w4N(m&_~sZ|I*$r?m%y}E1dDlAKT)^ zly8L%H1j<}C`YPsgf_vUsJbR?CP%(1lN(O-14Fdop4tO|;LZlDyUN*``yG@1D|hr- z@=E?YF-_XRTiSwKXAI_|Cci7_=p?@Wwev)R)#$I&F>r8Wrf++OR6FdN6I(`84>)|s z4W7$B=;EBjS(;3+?MIn2abYpE)y8l*0e*IP<8lK-*35B#z7{hNk7h_{S3LLf$CP{L z#i#gtDxrqf`jw*}Sv|O_%>?DNdBLLqT$_os!|nWHyDrntW#9iK{(5XBR`44B(}TB9 zIu8uYoS*uo5dcXH+qwkYZLD-UVA}NXfRTdC{^o<@MvPOf>wd%y_l6{C4)U7N<NIfHG~==h@y1z4@RY0w|m$g%yTnVa1-HAOOp`ARO zYg=E7eVWRZbfvCZd&o$!$PkLc3P(5gVl%N|4$FPP$x36ejn&G{jc=Cuaw**`!F{n{ zIF-YpvZl`|vfh^JB(*+rw>S~pmd41ED4GgHzkHoeAL3fC@78Dvx=3IXn*_(l>wNbEepZ$1-uZNS0wP89C zzl~7q5cA`x*6vDQ<3TQv8XQmntWSGAh@q!EH)oBYNFVR8QJ++wzSN{4VIPb8&B}sA zI|AkfwtkA=p2?N$7u~jEIK1xcl*#r{|5OpE9b55DJ)2D^Nqhw};MA-bpp^f4C(0G6Ir$;ISIn;hX z?eVoNF3G`?(oy@8=I1k4h zAzBnrzhJK}Sy90N5=CM}v4inrXOI!M}b4O%IcANh}k6fEzX4&OKdtR*k<4~ zE`h~R@qMLd_k%9oNf`{z@8$^!JThJXbFD$?$^tgH_=WX@&xQ3sykdbjs$^{WVW+mb zaId*mT`pCJ$4&4w7+c7o8G&17NdFr5D;Rt2{p!|W;Pc0u0K+{;5wejbzTXeaeKA(N zSN>PZD^7v`=*B0>%s|3UDdV8thcAKMS}O|;fuaxGiPGTbe+iQKF)yBlZBya5!u;CZ}rX(a_Ma5{2se50~46LPOURt(q8RcF>9^gL@2^SlKw&V-&=sq}CcQ z7MNxJB4v%#<_Ku_?$l#seG(BHYh=C?12Ayuqe9We1wZCeEB{x3fnxPJFn{yL|M4+U ztVjT|+5ER_#OMW4_18`r)9c(Mu4Pxt6wa@?3fZ;;Cb{t;C$ObDveKJlc9hn415Jim zT6Q?Oh4f_q?PqLT~b z_q%lzfV)XZf0Xz^JFvv8S&-Ftvx`0hl1*M=e9s~a54HFxhU_>#KCY;wgq`>smG;lL z`Bgy12AaX|UEI=iiU%CKmj21poc( zz`My~n#lj`ir;@_4E(>U^xy6fnvwc{JmjMLGY@f4Mn;yX@AOlzo}Fi(WLqqstM;b6 ztdNrT#g4Wj#)IB_?v2pHZv}(~-JXuhf{p&D=ikLhL4Ol~jaN&5wdvOa!ukGec?>|* zeo88x&j~pT*4ped*LFJRI5VZ2IuVE|)b7~THY(S4Y$G!Y zkB#|#P{8!hUJz;ovOfPS?kk65R}WY&?z1eg+2+~J(zjZeZrc_KRo>tnd`U)^-H>RZ zkFj`NY4HBGxB7w{J0oJNim#rp>u_22vqo8Ab$9b2>aEQ;ib=2zS~D=cI!e^aSY!-* z<%D9;LKUD`_VICgWk`;@wCCh9D;$guxc$*2kuxn+76+r91~)d62B%575q_lQ)skkU`#jb?VUChu|2VZh!5G9+vGiJDF~^<0)y$Uy|lo;s>MLobXUra z#kb*Nf@Xvtd{{c^&LC9MuexW{BAJ}uR61b9_JqLH(GBtBMs=ki-ursS6({aXl@8pT zVdWUk20YQsm+=?QJ4a)evk(k())uAU{B8zNz$LW}0SB*N$3AO6lJN@#tXNg9psrKh zsXY%42>aXXURlq=wiQxwte2uIx{*@?3$!I5{H@tZDq*FVefuX%g#{Vr3R`xN)=t6B z2?^82_a3x{>iL2oxbAyY%T60ryr3s~)*ZXDpMc>Kd6&MhTdzv}hxjZM)Q!I{n3?b4 zNXS&r^f?~ts7l;eA~ zl}t&!$KM~}1SLK%A7>M2NEsHjRu#h1eV;rkE$B{1GY8>f*lqOHpyoy_hqbP^QMsHg z(2XDPnhdU9Ugshu{}53jH3@}mQ_7LP{|H-;jJl^im*p0FQRA`z7;#TfAfvGK$H4LjvQyqKrEyx| zp4>uk9LS*ny>_6jYj7<3Xi6cZoB(LA$kkgSuDi+*eOgruuY5QaLAj|G$frCnNFP#S z>2y03N>LO>R7WI+ITCtAzK8ULWm8^HN?8_>opJo=_rR(rHB)DcnrKFotFSwL8ff&r zx_c+Hm+O`0js=Il$l}3)5DSO1v5kop-VED1t(_O9Hea{SH-S%7+@hvCj)VzSG8t1`jnjoJKyy|UPAZtCoyhI z!HEiZoBW>Vpjzv7-X4XDZ}3z2VR3yNTAz>xqcm zHCS)IfnDSmxhZvmCXfhulcuIro31u+S2JXeTpQ!1q~Y~0O+2t1m1!o@YCp;)4b4y< z=Sqd?4-YhMW0)inqhZz-n-jZ5f6eH*!yYcS_*$OtxzM}gbho{R{K-`tsn{y%2f8xu z^VRxMx4`5#_1LI#+Dg*f{sOjqZ?ntD4g*bGwOMe>x-SBjtWwudYz$Y~Kd*PBhCG=xA)OY|LTiMLS+j@+2LKA?St* z&Pr-lM$b)FN7u7B=a!`WwCAG@yBvG-9imJFEjY4!E_Xlb$h6=DOY^h!9-$PJtR`$g zYyFxzm#-03eX4m62edbNuD>AolTv;9G?N&Aw!}k}vqaSI>bh=DyjAvU&HaO4z26P; zOkVpernOeyHISKQ-3E`96GK!HYix3PMoC~0>@QY~i^u*ViTbq^t7|+tIu|5s=9qSV@k5Ib0ZW&Dh}xtwV7g zVsMRzD=HO&9bKI=vtoP9J1>0Y^=|Q6V1M^Kv>9xpsMZhWAfE!CrKvo;F~>NpGoyMi zWSd>2{gJmkY+*ylvbhl3J~N#;hDG_Ps-Ic|OFPU1DLT>rGL_&LwQ2XS8Mx0hK{ks2 zD)P0-me(7dI2fFjKR*C2cDV6cZ1<~0mxkLFK9|+Tp>D^0sst61KljR0|! zx595(($_D!X}5sV{SZlpHy=Q1B}Ot~sS2j2>E%CIJSXGm*+FUh#?ZyS#kkJ^cMG1V zp*w6Il=j$m%4o-wfh|$BLEP{0D*ryCnAtGUuj>n2#LyqY!Wnf*>SldOd$;ZMMR%67 zXN1%{<*vYaw?QYEXgee26nDD?awZTD+ZEY!ibdeO!;0!=q8(O8l;CT5e~907UqTq$ z?`5X2%{(_ndT&U#&mZpTNb&=wz3cLN!Kf|%bp@jAMz{Ki7%qmSl({wFV>MtwHR9%R z%y{@X)qH+GkL_%NI1$TR%gK0u9@MfIZ0d~KWV7I5&gZ}QQq-xF*85++eH#tqvJ{6ykLMpb!Mz0Oeq&;OOv?3Uu z0a7>h@=k~1eB_<~Z5D1RIrjJYrU%B6uo$F7h5_%x_RoP4+ z{z3pLh2wnF$yfmsClaf<6qsxB^o(6s*N%Ffro?@+{ z8Kk7dtqU#DryVrj^GPtjnGmP-8WsKtpuya=MCO-mIy!5n&(0|(nwk=mhTcBo;`ELe zd(^8sce9-_=+I*dXLiIKshbK0p=8@7)~1t2KyopSGQOegcf9^FDHwTx#+4n~GjI}V z|FoAT&$i%MN=6pX*wqzbn}l!Xo48}n=0FUtki{yE%Nl7;Mdzk_5xcLm+E;7-@I8Wr zQYdpeD6n!h&`G{zVy{@t1kB6i?R&}4ANGu-LB`P|L2`pq811WD5Cq?EK#zjDC~U+~ z#_?)gawy5He%{mPV)6!>(Z-E#p7^z=YEPF8&#((axo^+~ zP>(-AAqnXe)iK}qn(%dHWOWa*OQ)pH6fJu7Pv*?4XsC&-q7htYgMyGKv%-VEq`u8H z+4{nJbjn5PEyg?+7Z-h@4_%vjK(Sd5RACF0%B9I(J+Wd5X|k55+Y`3cz;ZTyqo)3T zzxv7mq_(D=6@N?nmAzb}rWb~{aAqD7KR|FV{bdT$O5imG9OPl~a9yQR;LbbXK7v_0MK@V&(? z_aPq+FWaG9Y1#6vl}=dNWQU@wJ;DZ&ZTT0U)@9E;Ltti^oy5lm#Ws~@AO^OWm%c)OvTk-VRrQI@1y^%;KCGRmm8>w5vg7vAaqoj500JcTRB?X+-LxE3&#uPRHX&3*m+n(~?G=mvp} zv!P*nv6>4v!OjkHQ?my5!tmW+&eUHvu!Rnju8Fc{nVtQ*7BZGrd12?_5j6eC=YdB% zUGe5y!8}bow37)fQu7)C4We1h^u@`k4Cv+CiO=J3ij#7hrb&xkkP{=fx{~PU%_5dB7J7|tsy;S&QT57Pj9%NGYQr&Iu6!a9P!bBOjGm&0$@y_Qi*J6{ zEgp)Slb?NEKW~2B;QNv2xa8A!!TXrT;JGU`1CQ;F+IHdTO~7}pw*Iya7hOiE+W)F8 zXqQkJYiYVPnAYm6!rm!s)8K@mOoOIcZCO#u%p6Xh)0kW%a9cGw#GNwW?4kEZ1x8>6 zD##!Qc{>e{9kTg_Bzb*ikA>nqPbik`5;xS=$XBc%aTIXxYV8m|iy+nicm( z4GgAOKJ*&(Xm{?E{;9&%b6pY>C5?3O^MN-`Nq!x`b|9^yn^x}`?Wzl--Kyf3NFfcf zpvM?tzuwu~Z{~q45>n@)ob`-vA63V70s-|_>`?pt+^kLVVnh7HTHV28FJ|_BM*^>3af=i8S2MU4l$zBunh-ajz$Z82xCAvX#d2k!VwE=1M z6F=`dG03PsdUfdOAiCNuQ*%t21q=IROhj!?#23yM1M=@~TdBg2N2rl^U9**wLVtU4 z0F%R0=^X5E{xS%0dKQkt;pDEtS&ai}n#j8@u*Qek9Wj1S*B9DBDFyugqpEN1*%KP3 z-`gU#r#$Tg@#;uJ{<@_JUYw?HaX+W%##^Yqy2UkW_*w z*Q4;j;j+FXe;x3|TIEAsQI;C4w39N6k|hySQnygMkXM;RFDpFVdZ*^j`I)f+dnA%y zS`=wXS3DFw&`mi^Xco42^5TYD9Zr-943yJ>5L6k1RO_~UQ)^`y)j+8^ZGINqFaumU z(REhi@}{&JIO8DtL(HE%L~FZ6T`G`Xi508sVJml<(p)#JV&`1DoAYps_b70habhcn z)&&-WTGgcwoOK{BuKRO<0IOZ_DoaAc`=D<-!#WYct0Z0w(gQtu{$w&tg4*Z0ks!R7 zn_+?-SGvBzXVnVyY4-}2s5{W~VEGG4CWLh--5{+X5~md$rx5`t>s325J4Ig3{7#&XSEm=K@a>Lt0aZxLU04 zu5JFCJ~m{!I-T2bxuym5G0X)fw#v$PeDwd*Gb+YhxH*uqtLD#?Q;ZL_S%eQ^$nx^+ zK##~z00@q7`q-6W!ieH%; zRBGtuj6yd`s02Cg8Qb+GWLFBj#=lfDqq7cBNKv>GLtf~wgkVs^I(;4&Y0ESTx!ipz zGS3lsO7%oMl_}zt(|M-KXPXTV8jhquF(_u6Y((>g&2Xf(k_DqhESslE`^=KvTFU$u zjm32iz0WsB+Ok?%vi>_fYb zKOVkr680zdjr753dsNxzEGYK~LC0UL9KX!Azh3b0V!EfYX%M;hu&!skt~CvAD>Bv4 z1)du%-jm1nYJxYOhyZ(2_KnhXJP#n3ealQSo9IoutZ1?hC1N1X^dhGJx7d&;L>yuK zD2VHqT&|NI&ocu;hH{v(=^p?8o^{|SrUODWpDX)dglk%zwWWNFdbqL{1 zm@G1@0(&})uO=YE7q9J}@>uN;Ek*tmQk8a|Pw~1~NiR~{{&|c<$?1nJr#&9o8~azp;a1zpKEcbQqYIhK-~y@nO$xiztCuwk6y{^7Bc6jHL0HKDTLpOh@w@NrL-jGv7Qj9A#%qq4xs~-nT?{ONT>Q=qX4#St}m4LQG zo0knG|IVyrt{#2xeI|$=YidX)X4!!x%zBAoR`+`~BUHa3+j7?AzUb#rc=9|ss(R0Hu}viZkytOp?Wc^$XwSac&oY z4M!$3DsU{P57{;iRQ*;uJ>UheVyOd@9|QpLv2%(Gtq_gA{h$TRS8=5D9ii72ou;td zXU;fACF&*9UOPB>T-fGjOEnpUqKgZOW6b#MuJL!2FHko!p1m&K5Te=c*f_l$l?7m`_2Hk%EBiEtOwfTSmD2?&G>%ny`6ugmk0I zZ}H89^{AxPj&h?Da6^?clBME>O(Vv)2a3t2J*>L(xWX1g31pGdY+*fCFD-l zbfkfbbzdFn%>SBaFoe`ouAOOlDxhn4^$WqN%$e}f&b;($t^VC)>s|d2SUY-#Rwk(u zidBA4GV>ro`fi(@HEd14-GtlitI~&H zg;NfmB~^koqV0b6l~I`cj#-tifKi`%45FmVpQTOun5Y{Bj;nI;UMsXS=(Z){2=0*g2n95}GT{qpZZqvg@Ssc!ByPlZS8^Y{vX1b{ zSQJ7n$dqeU`2AIe1P5Cl3ogn42q9H(O{`E3xMrThbVGm?S)dxxrH< zJ}V^6&Kg-5QK@}#4F}?8?(*OfQyn=b(fJ4|MH^l-+&mhmFMb)g@C-WdcPsIEYU`&i zC5YDty+q1GGxFuP-GrVR>9ct^lbkBEdQ89Ut%`bA%lG73ji}>1Aax=@*SJ%!b2!|Q zE0-+z-DWT9i&DBk^hI&eysGIHg&v4E? z^!eUFCV?G#LyZs23YNW{Rh=--&9G1h6%W>F;!_A~yTskxtgR|neFYpe$D8&LZ*HZvrfA`F9FRcGkGsj4`vDs8IFENt_shHa z{i8`WBRzWQ)x*dS#0MLU=Wb>P2x*RH$h+E9o#@m$^Tc~oO>6!~=-;)~d1Pk!?m5YH zZq74IZq#S@l?UlV+X5TQ2nGFb@-Uw`m)WV^2#9Yb2F^5so>W=MBFslvHpzyeAk0&o>klsz!}(Ol-qIs55-ygT@Ub=(D}zm4u&>q~quc^! z!uJS4h5HxE;ZF$r4h_O;Xs|_d<|nM1_P-~30|RVrci`U6~m)F zuHuu(wyr{^dWl58aI+QoC7V-*L07Nm?KtbjUXasCPk_|gB1b~w*AD#gxP_}=U<&d` z(bb(r^Q_(fk+s5V|vSy_X9FdGvSdWYx99NrKNphv|-VPv&YjC0T)Em zrMAAvYQ>XMCgDgs4gN-lL^yiCw;ORD-s~gF^A-E6B3lz7iea;&KQdvt)kQH|b%@1~ zIq_(9&$0GK?5*UP=rsIriTkqsJk^QlKYjw(K0@qy%VTSIMzy{Ar;-mhnFS zi;90YIAy5_z7{S%F{~=)!p$Q)_!)L^d2{pjW5M@Zvq+D1QD00;qV*FK{^iw?A66as zyZKP|OUmzKp9BXn;Eql1-~9j%`TTn{Wj!$cUVtn(M?hJmQP_?aOpXe)R|i&xyBB+^ zszT@Vc34LFahxh#gF_o)@&{Y8#g=mA#KDzzZ|k;T?#N32H} z=3P`^@nV>89ql1m4%>SDf)E&JUjZ&FGjWY;vEXHWrS}+DUrW9PZCSQDW%#{fB!Wqt z7wJ!9uOnAygdR7mF5eH8;07BCuMruv&9sXM62G^*5Q7{UwJGR}?_m1TBEEUM7Psm6 zVdov)BW%Ie`UC7*n8KgvkjOy*EW%;n(%w(&A1=J6l3~RLCyOn19pjxLyFIjQeD-}Y zp6qpWVf>+dy*}pb{g1=a?sU@61e5RZPM%;p+E`tO?(r8xHyxH%=Y56kHE0er&qjlwR4^{fds!K*5PvW40H-eyesBBJlz#Q6)r$J<%1D%dWP<*H|& zFlu+}&yl(R!w>@g*v9)WkPqLU)i$#r2AMX+12C=;`6K!x`r|wIOKSk&cD0dc6%x(a zxqGI^lo0X{6w2+Fw)!0lSaL{R;9d~* zdAFMZ)>tNM6XBV%+o7*xU}z}P6>ydXR{KM3{_~^^Bq`un_gx3?Md;(XZge;F&|@R9 zC0PE(^Hq4aFSqEGVkJV0NlG%aI!Guh$Cf^a`h`>tExqzm*<-{_{O9)oKnFevtpfms zdBhJz`e=-;+o{zVA?3&RuOO}p{ZB~%t}VJOsItIr{z0@?;s%OnA0i`taVdYZq5e4l z0__?9uL!Qk7M$V$=P7@-01%Vvwamtp8C@pGlsggw)tP53W{<*nT>yxdk`xcM3A!V@ z7s>;W}kr>8z&DQSPIvnTFh2i*jjL@jg}$A7z-q{(SY(`lNd9q4V=#T<}pf zw?MeqD$Ktl^6%2ISvpjiST-VQuJJpHoi}O^6tgdIK7*BF+?l?j9MlHFhTr)mulsij zA(QOSFP5x-?NdEHr|#Wt8ChlbPySiShxA_{f$%8*IgM>wP97lwSzfWx?B}J~;s*pG z+hsQ{2c6+LDDg98WUgB0SY1ih>_Mfk;-$5A@+7*|&nOm~j;YsEywpZhSIcH)$f}&N zgSDsny?QWzvQfP#&n0kjK~7v~4j^or!n&=CeRr*Kt`f$hOLT;ct9wQ#!Rp!OB#DZB zQjZ!`xX^ZiBCDe*vm{{QKtaiU_eRw5gs*VEbx2l2WFwNE@w|XED7?%oJL)!d^SBuV z*Bn=(HOcHcf_}P-FB?OPxMP+(m(Y6ccTr;Gca)^l?zy7PvHcx&E8(RdHp)2cuG-NE zHzb;nve6BDH2E8-{!(mkwzP2whdAv*zbKJacHi77k~+!;qO); zbi!sHO&hL+lGGKhCY>+QaLt$GVEgH4-tScljPc#2& z+iys$;m*>Z3}RVD_e-BdHV*tdvmEw9Q`FSbAKF*{0^t!J=J6QmNB=wA11khGG9S-y z@*YRM8P$}UHzukL>A@14ik278=|v!Plvw>0%OEFZa!idsRQ_{2MVLm}__oXX0}9?T zP7*hU%RB_^2)uP|3Pdw0YEi8t#2=zvu>Dw~sp9mu*9oE{1G+vl)OmTkHi!g6;>KAc zSH-(?&Uwj-9QoFhPgvnU2pU@-U$qAz9`=RwV688UD~tbSUn{b4?(uVOT`dv6#%|yj9S1lv>8Wb-=&x zBO(&ILG}_OPnW1OXgkl7P|46*muUEs(UxTmE&0>?x<@6pqj&o1D*9e(6ml8lw*OJK{o{VGik6-gG3C2-ek z?CiE9-e7Zr?q1*ed}@o;8m}p2=b68@6LGF1YRbwPjoZ0^Rx*2(%7I(R*90YJ+8#vS z3p#n6*V(b796PtGVkKUb5AeLrH)tEu90@Mf8f}yHuEWf{)^416$+I2rualYDs-^wR zR9SB@7B26^?m0@AZC+dPUXl6T*E$Vh58*1vY=lxptCh~fzVGa9z6PXaT(Z_afJARC zXN~?3=vpDbEnIx&kT$A1}6lr;ovEHt_x7Rz!3 zf#&b9aQi*Bu?;mI?4}qgRwE4l(Ip z%&Qf0x$S#{4)6x6@-$J*nY)NN;ineqG#h$?@0eMdLaoO)+9e3@t_12x#?C-)0L?T{Bcg@ry6;~!V zNW0W{9o7&u?crDxd$>-eS+ZfbES5Q3G39;cEPtW$f%wc@I^g??o(2I? z8^6KR_b&qQ$HT=?6$?Bg>9y3wYRi%5>Zco*M4GhKBpRtw)<{PaRo=0pm+vixmz0lb z&nz}Zh8Eu@LQH`Ko>c7$A(9mXC9g7s5?Z3$!A@Ff1?k=P%P_H^6#hzG$Ny~?nGgc@ zPThNt)|&$NTSTqYBks48>mM{xbU!p=*k|=;A}z!r7DnevObMsvinTPOJ0Qu zm?ChoMVj?nwds57a&66h8R~g{ z>3@~R-=z^5AR5P>Saw6h({+b?p}`G~&*Dw1{ud&{F$cVbV%;zT_7xH@vFi{}^5Vc9 z$Ck}Fy6A{r%=m65FT3P4b@8Mi=9>L%Mp0Ja#Fy8i7uzE2@2gQch7MCeY!_-H^@%qiOv!phcN)=lB@zw~K4ednU#BID1O^ z)4S&KDSs-bmHED)Y|$0!QiF!SE4oaZj&HiXy(i9dsiuew6l0c?l+Gkhn$ud~+(EvI z8vDCR|GwX(0P!bo{J*5W&bP+yeP~dZO}LyIC2E+9kdy6t!CJ;hbIo~PL@bN z1Dd)9=kR7?fVjoLtsI85Z+V)T;M@TrFgt@tCvHOjJA>)}y}QWRee;dHteQPI=L`#% zO!Bw=V=g@Uw(^X&9+J`+@J(M4l5ihrwmER#~WuH<~2&rwKTSiA_f5M&) z-rvX%7+l{o<~4MpjsNa8T$pV$NU?EfxmN(^uER*^!Y7IpPKt<9&$N(^=S4_GRRG;T zwZp7-5>krd5l+8&n=?*5&AWWcy{YIb1eG~EU0T$^z!Sn?rZeaKvUL9YH;)K(qjiN{ z*cI2&cDa}tKu7k?Ei!WpZ{N%nr87=fh{JBL@pNVrSni^)TIF}0;qu>juo z??}y;lw(oV_ON~gC1j~BR2K~$!>A&t;}I{A4+PyG_*-{Pv%gegqfJ7UwqtV~QA6$> z{LbovUQuT3#X&J~97NR?!W2_|87og+3)!U{Z8ZM=@YH~`VN-0S9yY_b`u@FVE@poZ z;_cp(Q*qP4J&{P~6-F|VcLOtNrcR~w&dH8A+neDOb1<=5akl-Ll*6=&L38Jps5Tvl zypfjvclz6oFP_M7MycY;sRY-n^*X*K7an(p?i$|ZXl(|thm1{7d)|Y1PBa11&VGyc zM>O*0OSD5et6lj=Fmi$uC}vcJPs|QVZ#Nb;+1(X6BauANT(FMruJva}vqK#i0yO!; z6~bj(1!%cIm$$R-2{818iZy%osp*AIji($y zM1ULrHNEcGwqW49e!72W&fldy{l^e8dcmr2^j{%l$j(~Dn9)>eP5WN(9H1E&`)X({ z6c#0xF=*WhF%omYv$tX>d$fqT*g0Mk5qcTCVSDc4QyTEXu&$~M^0QAhdL5KYO}EQR6~8Ed@G%j>IqR$MWVJoWXpi&y@v%l9h)jCY3@SB zm)?q3rx{muGv-sMA zD(9;YrAr5dat9!So7rl1&@B;(GyuLYGG)`UW3QTdO>H6GA%a$ZKh1n;`4!~7mUuOw zP4SbS(|Ipt&S6aik-RVw#KYCRJi0wPCGA$dO%~<>F&U{3kYIk|oV##K_uz4BBZ2s} zxE=0+aMhA4Tq|-wo7{QPA;e>4)}6?f?YP1jNM z&uuv6qQYmNkSN!8*f>tu0$%;RrTxt8Q+&|uA1ZvmncdK@G#O6Ry9-uQlH{SFc@;U; zPjNUQci3*{em!)h$btyhCf>L%IEeN2rTb(^bv#7dbc-Rz{$5`ftv&{XDP$WM z`rV_1Kklcqz*Fum`R5g<4>o_StR}%?lF6mH3Id&k!Wo6f4_y=J*buscmUU#(IZMi3 zOG@qn4C7^1rh7ZS;0J8LfX>M@SQc7vgBVVj&k6hWtrnB*qA*ShzkG#S@tzAk;%q4K z@^b+Buxp;cyYvdvz8mrNFa{iAx(#j@8fB|ys1yl$q}QWO;+k% zT(5_xIG(2DD=!}os%O;LL11trMQJBsaGP!}*8jPy9j%}!oD4+hD6+>`8FYR>c!T0F zD|8?1&a#Wdx*l^gv3Dj{xvJjeqDx0^`4Gh1FCt_hG#XT_Icw0mmYWVny#m`_-|{hr6VC7i=N{rqnH1iAwdR8n76CjCjPM;tB?*~vr6+aMa^PQ|HIc?K()0!aiakW6t@z8F(^9NhafjkiG`I(f zwz#{r#ogWAwYU@9-AP`0d+-0=_kC}@v({Ohgp-q<*|TT%*l(}|c;2k96%@eCAGb>g z9{9K^O(YtI!Nx4`Y~_O7$r5qXKb#v?R_|p-R=WdJgc}SZhES?f?QbWm$tnyNvQTy=4E{ zcn|AfrJ1GuN6T$yWy3sIA*7STK=ECkQz1;~6G@tf7b^Zf!~K??i$J*J-)=0Y?-f(- z>#RGDZr>1!Q2G9K!1Po0aye{DmsJ1_=;`U%c)pUJHtVy}#tzKAc~CwPWj}5m+QZWt zzBeCRYf|ea^6~C8y61Fo!|E)m)T*FLWdx(Qy?e5SrmT{h^70^D{W`w=qDavY-255* zr+6c}W$0hS`2TP@{k=p;y--$$%v#5|gBP<*xKD;X{6L^g`5YkazY`Pf{rZ1?C>;L& z=|sD&z8qPt!c(C16}OryKdfhl_E1XQ$;J`#|H{2vj^5wr&G~B2yr~a%|F1i@pHdPw zKMQaF=LygLsS4Ks#%x9H&pT^>v?Rr=iU(zK#%qMd{)g1p>@c4AcSQeQML$XY{}f~X z`-H~0k-1m@s0f%3TUrTLQhi-%Po*Li>(Gtz`bxq2LZ*+DH(--m;g#w5C#vDz0xG?} z6bCK_sWh<7|x)>-IV7SB^RlcY z57(Dhz3wDrBfE6^+u1%CXbtqbPxur<*RLg4E%*9iF+; zMW%P?xRQl-L4WC;qd|QheYQ&qxQ6NfrC#NUgmxp((ytk#Y$~dwDJYcBb+X{HI+fl1 zua-{VE*O}3MhSN4z0 zWNMnoJE^2effzdqL}e6nFaP3+Xi0VJxuah7|1gNSKdg+hTKodF|4F8Co8kT@9NpsL z45nIMnk(I26dh_;q%@jssWNLKE9Tw|&`mKjz)%^UUDf=GJIq=O-kHC<2#R{C*`-W7 z4!#7w&dJ3##6W2NH5<1+tTlE_vHh{ynXc8pamxm6peDJb?9uPbJ8(e69&pNB*TsXi z*cq|dXWdBTp%8oZO3qY!H~@)GS_R912q{U&nY-kULr zEc*{iBANjVqrEnN&1$9bj@BXk2svb{&?k z>gjin>Qef&=?e>y{dziQ5JX!B%>9_MP;pv)5?-vsUq^Z#NlCl%x2HYiaY6O?HXspF zAB9mPW&~2$U9fKWcZc2&*RfP(J_hCFIY#?kyPS)V+M}?4ibv!)Xqo1-Ko~*7{jK9# zuXtJCEUik$jYC|KFMdivI2lh-C>kskY4nSonvka)E28n83%+V#u{{%V?HX!7%3P#C zY=9fn^ubP4#V)f;*=^V|Q-YEITc&7i)q<_Pt8)lUshh zEHydMaV_8KN}O*f#a29z?(UVqU3rpzqqeG4S=9;EZrOE#sCu|!AuCcgiFigCY>SRW z*tKP43n4igvHtkcnLnZF4zX7{U#_yg-&~cS zQinzkb}BdPsp1JA-yZP)aDAP7kLu9so~maflX5MP`7`oFJfI-s+i>Jul#)>zf^YuM zQj@tgw>PR42L~DhOQvby_VN1kV=9g|x720_Few}($Z%&Nrx?0QHRII~!tHK(NwSiD zx^LqpdZ74tLJfMDrBME;>=TtCxNl$T5$QaX5Pb##7eDTaEdr_e9)EC3f8|{-Vr~9MgHJxu|7UIHDSPv( zjIPIsX_|+sL5vYhr%ncGeU;lCQ8ES%HAoeyZh!CANtL=9t%3B_#X(M3tv%LJTn?`R z_(n(>fz;?(1VE`gJR3O1*{Z8>QI$BIF9i9l+Hxy`Rr6Y!$hj7{Qu-3c>q{Xx@ALQo z`T9^)ts!+44XpYLd&uK69sOJC0|JrWUMzz3QsmQ*8qFe~QfHJ~ILGSH_4kW7{qweL zZWIrQ0~ap}kU~Nz8%tKdn?c?wy%giS&VB*@C7$ZZOGDCTe8UQn@*5)HxNa;MMYUM1 zMTi8>`+vZgo*$xrlh30t2W`CRBe#X$ryqODm^t5&zsDsZ@I%RGT2|1{05<-+F#f*k z|1Jf$$rBP5c8(Zw@R@#KQQ9RMh)Incj722(;n_!x(d$f6aEpck49Y9f&3Quc&pO4Smj?;=U40J*J=a2o`v6BFnJ#1!y5L$XVbf88b~)>l*br_7 zESmeewA=Anx#YHtCVUh7zEn~BX<-l*{}SI%gKmq6#)~$&l)3~#0u%e$FV2r=TZ;TQ z3uwS9An^+J_yuKT1(pR6^j59Gb z&E6upGT$bL(pFa(XghKJ%+&1AL*ux8dTOIP>!!Os3KeyHXp%p%TFUuZ@r=b`cs2Rf z1@;Nep=sVsY`z?&OB%EW)4!e(QKiU!7}58R0&ZG zMy;0HRj)cjYJ(9_I9A)LbjB2do4pydJqKW1dIc^%`WI17AZM6a6@TK zoPa0!WmuSKbeP??$o3mc4d30P2<>eaC7sDW$-gQLZo{=-XG}9~&6Ex&_TnFCq547~ zFdmn<@!7Rt#i+1oq3R>ynfdbQZWrFy>f6ajLs*g{DQ9#j)PZ6-%x^B>fd3#lkso~9 z+AWoG#_Gpi$}1HHO0puic25T8g}*BCL|4MW7`h9bFHb)3p8j@29SVM}(7LXBPbXo- zGjZD*q|Xz2rjgB?ehBgiE^)VQmz!Kq9B&?VGPuvn!$oup<*D4u^9}Hlx-at|mKt7* z0S&K*;UPzplB|OiKUKcRUc9iER7mSnt26j8u7q9h?NS7KfMm7^Miv#0Lir#@O{lt2 z5=yGI*u1K$gXP0XllW5{Hr2#6q*VhBz16*G1CRW*$tuZHMy93(;d|Lyr3LY_+sZ8Y zpT6a;efjbRzEZb#mEs0I5uqNiG5K)kmez;RHYB{0@vxPD(|WSQw|00e`|(Dq0W`T7 z;IW{XuK?%HkQW}lqvm!^@7^ou{%(8it*liFNuII#1! zm}u8Vd>o#L?R^_x&(-43GSCfow&V|ObQb0@vyC>b)x#{*4} zMZv7EYw#aUKVn+BpIR@V+a2#lp@01}J6)V|Pk6DaC%rV_b&ytzW&e;VFh2qZeTy|U z!$WsbS7NQll^)0{)Vnl!w3Vn;bqI=s6lP{jCDu_`j2&QA?@ZbGP*c1z7hM&#v0+-J z(rW$$3iO_q?8J5TZbvyhcTgASMEnr~9jC&z`Y)-(RAkQo0__MZ<84U|B1-TZsl? z%ZWjY-nTV2LDV0;w&~3k{T->XtX}c6hBxoWBw4maXCjYB1?l^kj@*gLZ@1_AZxSFx z*t*r8$Ue{!;GXrP-Ey_Sf9d}YH5zZWdEBDtvOH0d+pbu)zB^@oJXLX4e38Y(tFYLS zIlh^Seo(U~x8Zon2O zZf{Xwq0{0WzvrxVYnE|K9I3R~>}9LhLl3;d;X}l&V({5ht?D;8WJi^hI7H|5&0Ip| zH)05&FSdK1p=?}-9tiTwelb&|ccaJL`RjK@D3K2kxkWPtF_AuXXgEKfRF&fsR@y>c z*jyx}dx@R3%KgcDJCbOST<5KOitvZUnGC+Dv5D!i(~&;3{(fXsh3M*CrB+xTzb@%1 zW>c`3YZs<0*}Z5&%N4&TW}YF?PW&bhGPH|GQr}Vq%xWRCPQO>2u@RK>$RMF~l{dMi zV@Id(Oa*5Bk>HP|*+3qG_JD@KW553t7tWF+Mjb>~*)e%sQb?=H;z?L~YBia$BKE#@ z=n%Qv#1nZqY^ym+3DSdi>PzKxkl7pkP9og={3(Y@m znuyAF6D@Ck8CcBp!}Zg6pHMl3#jQy3X&1QmMKYGB+vafyWt>9#^_ghQPtwt8L@#m&)efAM|9%q>h{HqU43<9j9OpUY**%VIy^wD)iY}jXuZY3h zp{~YFrS+_QPH(W-)(J$S&RGmfL(x0XU(g&Gt+F)r6~X50EA4kkJ)B7o-oq6VYB<8= zcGcbyC^pS*R^7wvj!tH&zDC*kjuY4h%mvtA6k+#@l*Jf*2*YyGr-^%^clr8{EN#f` zE^zMb7FdVgKks|jN@syi_C(W_2mT=kDk9Dg)R;0>wx?NJ(XLD^fi)htHL^9MG zQdv<;25sxR+v0#1@krVvZd2`DxAZ(BvlCgAzKBEfgVX+@ip;c@>zVU+y^f@FXCw^? zxb~OrJKK3EdlTfy;DCDv6I3Y4LI%;8!qyHo{xGv3&ehz*dsjOXQcH7IQN`WC197B^ z%Li&rA??ZS(Kg3bl^Y`t%#y&~S?&&P>iak5*C)J}+p#{6Tz9IN2ehM4zIsv@^c^^L z`04eAr%-mMZB`*+S59$*d*9MoWc@&bNwUXA;b`6VO^UY`Atm#UufET^10>Q`+5%!- zD8VTiZK1RAw5=G$qT4yq3D$>Y+Vf}J&o{OUfI<&Bh7WB`7TfS#_I)LdieBP$j$=u| zdxRU|Q+p%D8FR6`P{NU$rf`HK=bS|t7EF!KbN$hR!*szkSES&JJI1fd=mWL(qK`~7KQJ>?f z(;RM`BRTr=U1`}VQ0)V1CBLT=0m`b-P)1u>_M$0QUY!A}*!%SOKOfe^mnZ*r`j z!i%rfV}@wf_HZ8NumGfN2L{hFT#NjIZ{%@+`BNRtVM zj*qZRYb*gXwZ~bzw;qg?)S2GbM4|Y<@#Mb%!M*EXusEzq;@#pZL^76E-VOS6!|Hy^ zXTBqAo$R17Uwbu;Jjdw?FVo~xLTDtnqUgfoOGi$rS6HmR^y-ywkb>9*kwXLpyNiah z!#0WXb!ItIDhW@gs$Ssi{pOy8xVbU-?sje8Wg?cPe{WT}AE7;6(q@6sgJf;_2RjD9 zVQN5{(q@w}a|x4Ht~-=!?W< z>Bt*?O7oiI1r{>B08v*N`%BsC+2cpOFtY6IZ5`01wan1u{_u{Hi&qcBq5B2xisxof zVO{ji#f2f*nwo%^r{R`kbo3ZbLl66p_juo+qU!5qf-sYfR7+!mjZ6kv*wiLte2b?@ zS%gOt+|zNcL1vTtu7N0F@ISkC*k9dvm_la_G?x*q%`}+t8ED1`9zY;|_TJvRsBj}s z8SkX^f#o-uVN@dGG-Gq#SCym1V{N^qBH^)S!))`a^x2eC=2`Q7aCV*S9~nP!qi$WN ztOxWIAoDp6j~mdKlaeGAqH`Lskas}p`Qd1^Ob!?A4okJG?1>u$;b_u?YvT7&r)T;* zTGB+&);@$D1h4Oy%Yj`p2mS$)^*`3LTBVdUG>ZY``s+g z7}%<9jjqngB8V+u=rU&(4Ro=lO*h}YxSgl}(}+c8{S z`&0$yau`-@tRq*CX;XMCEn36`>(_`)W_|Pk&T7;GR#%J>W^lI5&znI2zZJ|VP*~T2 z3t{lg?c?r4dG*tV>Q=w)=w<>y!L4?8G*B<&#^K%xlg2kzgAD@d z_4zX^KXC!1XczQAr|n`z9R=i7wJpN`Ojk5hyts+Urs>^#YMQNgcwaR#MsGwUBE^tL zyM4NvDR5cusVR5LSI`@(FwDNmJOXJ~VW2EhQ5OvdrA^Wb3TAx~j9=nvJ}N7J@%Q(B z`Qu{>x_A0GwuXaCYxi-E=)zTcC>FVEi)1PZynSh9z7nPR&8R6f22s-*&P3$~I$q&w zHHwoN@c5Hd@0_&nCXdL|+5`J^_G|U&D9u%q`PWsae!i?x-J2ydq^0ralOVH!sYv_N zV&MCi9>ZpvQ@oQQ=tirpsqqIzrG86pa~$N2Hv+`=De0_Nn@xHy>A!c%Z4l{C;?m1p zDf3fptS1*lqv@=XKaFDx2f+#U*R#;dQ>Z>|O@4I=07u!Rz+YD7P%+qxf!F3nBmY*;yxLllb*KbGOw zZyxSEfL6vLwAFq8ip@k@CC*#tb}t2Z*R{z0j1`??V~SQWLqd5(g_!FG?v%cRo2qYpl?FTTv%1q=u7Lm7@%=WU{HIc97 zuTMH!@V2jGlHDi8A7D;j9G{$RY1X#%mke+^?(x-G{Ded=(la2AN@0Lnq+U{ZaSB}i zKy5WmZ-npDRgjI2ie__=HTTYEpE80OlrYz`1%rtHQXlyGFa#|3nHCYzg^VWfg;^%8 z@HL6iDur{zDe!4fI949c*IZV+TEN`8aVgo!0SzEzvGm<)*&u7u?BZyyPqXTdZLdLM zh7~LBMNA?v%zIU6YzcK;%eQJXQ&?8P)^9x&iRQ(&S#%0v-?d`!xqCe9TVMNl+hB*{ z+imUcS)a=qm}h0PMdE6JmKejz>BDu6XW!vAbTl!<#p1zu{ z9&gsKFDWTYugrUWBf~f-{KP4$*cjVD@n&l_fNOMnJ_5(r2-V@6wN$2O{;~GVO8rnt`{=WB1AL`w(GwSW#$4hyExm~6H{-ss zk8w~EcDqP0tbhDo>fYJ5=X=CPF(4*`R7A91lH&P(-h4gF0H0TR7-hz0-DmRjon-7K?7g@gjL^ zqk}go5ND>(!WXv*+z6i40!5s%JH5&h_k5>aN1LC+?Uj~TA9S*!ajW?kH-DykWsJ-s zp|)x)^(U(QhsQtsfPthEz+#}R8L_ih0hjwycv$A&Mjt!i*{(OnE}j3M2@fDze*dRG=3o6 zaHYr@=lj!^wR=tXWfTI3%Q%lacj`L`eAfR>Cuco4Em%HhXx8so_Fu;@RcAPp6JwHk zksUhX7iMSPb5&UY2b%vE2KFxnJ>)2|aE$*^Tv@a^(k5%(FOZ9Vxbu5<67FS>`G3TG z!#S+P;Gdfvh~k1G(=Ryf=^1)AtaImJ_T=APWdD%o+fP8HZmfXwem8U7l9Ba4f!_wI zBnJg`REoT&+U)j$X}-DFO+K)mbRDgwv4Hcb5kkp24+Q-bs zE5qeDoZ?iDFLy1%fZb`=s1h6csje`*QX9YYA6h)_&kkrsyDt@Jjq7w-n47AGYZWv^ zUwpQyGH9yXt@#_A8Yg=HKLk)7BMoIXT?dnvecVV{k#|851+zsLnr?nHd8k;EEBuc4 z?VKZAem1-F3;d$uyrvO)dV=mZ4EZZYR*KN0zC%u}pi!@LM-2JddQU2Gc0u?ETc@wq zriTfQ8~E+r-x0KU_An)h9tvZOzdJc=SB4$DsJxKXsQ4dwO#d^Lzv^OQDo=sRi@`5N z5<>A}B{C|G>QsHqlhsj22{X?`9GG@0SR<>`Dgh1!H3RcO_<}C+7DcP~x|;cysMTH% zl$D`=8>B9_q85j(#XpV-HGLu|PNcJd?bF?PG)jj0N$Kg~!fLWZIg^D#(fSSY|15)S z)c+2m3YShr>y~eIsdJ9mq2rt*f<&8RK#`40@8H~_pSRXjm%+~(ZGNw}RyRh0aIw_p zYt!t6>v`XM#f-7JHtxYMYJuCe%l_0VO(V)L=FMAu7mN^GHGk-_kO3(P+bfBdr~sFV?d`}_NGjg7sfWn|cS zzWz#9NEevMSEA;zU9vbVjE{-IB_$<=m7Y0_uuasfk$7?3IKI03P}r*?nA=-o0gfGj zAPmIYLOpWWm!CK;dRIas!9*@C4}4=j&ZV9^jZ+*#gnhaEXA;0O-A3tWzqhe&+ z*}AyWQ@|EXwKHhSL-ygDJu|Fj1x#1#!F2WaecXC?clXU5jl!3nJwwb+96V2>H9vdu zs8LP>reyno_B>*C+RUw~y(SJ$y(ST5WuLv^k&%X9RY5i zkT0!Pk@%NlwXeeL4Z8sQQ}dN#NA2{x1x8Qr{x~g z$cPu~fPHIubRYQ(d-~jH`HgF|1u42O$&Hk&`NulIB6*J~qI`;{ z?2ji z^%6CEUZ#43qC!J!>t;q9dxHAo?EsO>h|f3kolGvw#{DE)x1i+M6RP~TDCRCB+Xd!3 zj$0y329V2{KB0vUxrqrip^LeLZuN(gjOQ7U-IvEGwg9b)VKTu8eyKvTzf_HbL^)E_ z)12#~U@r?mF3_9-=+vDS<#hP!xqQ7G>{vzwxR@ziccmiSE?>=+H8XgENeZ0`Ld0c>^xOSm7uJNqYJB5f-9$qQ zI*9$DB>kG#fJm|Mvl4YfRS6NY!=2647sr4|zf%i=Ti$j!`_t!C=i9WsIDc`aM*hAP zoMzfGgT0gR4q^1dSvIgKmyaNgYW?ScH4$4x$<%DLK43h~>?W9~m*8VmO8W#&X64h# zSe0ozUoCq2@Mz!zAKG|1nO|fb+wfVoR&eg-iB9{BMxA!iM$q-EIo4P(g^1$tSTZ0< zr>QW<=M9gx$1%cXQ3<2zK^V6Ny!m8f<<0)1-BKb>r_5Ygt~=o1?{6t>{H+_vR6o|cOV=WCCAwVJ*gZ??T_5^s&#CqlCldMEzTSX5WC zaGPjbhYFu|ERKnU(v*Hmal#r`1VJdv_jTYiq{J?34GT>1nNxU(M3kzv=HBvkTjZRx);X=oc3fgk}TV)dtQlNWwf!UKnSRc8ZUU?tJrB zZv}0R+S`#bY|hQ_zY(_kC=dh{f?#*`g&B7L1SdIe>TbpS>%~;Iy<_p*|ApsU7^jhW7D(m91~^CYo`sExLO0JsqHc)Zp@`w|{!6Tg@{* zg~2pN2&dbt49;XM1#hFdaa@d+ZEHf;OQ?05!yw^JQqLnDlVwv1HD|K zo#<8!tBw~~wEgHNaEVaTQR5t0dZQ_Ew~;d~ou1(H`P^2Mi@ z60t?#bzQafuh;@y$kJv*xunK)CYi4&$?UIl#MKNphM&Bu-^{B|%;ny46__8A{IlPG z&jKr`V-*9FAfMiXyBg}Gkl&@$8`wLm@032df4;Pkgyvw8#c{Pt*0ev&tlT#8iy3mz zwUUpNt$K~lS37<1-5Ac4dD@mSN^Bl$9m|W=>v_rD zMrRcjT{w%k2MsT843{NwKR%T{^QC%Y>bHKO({%OR(wE^)5~XPgIeKWhI&Q?WLqE&q zeqb507^armW*3~aG&M?l0~~p8Yv_jTf!U1fYOI1L6HaDg8?p!>!-i1Q^w|NU&2qsH zN#1}UTr(u)h)SN3(w5s-o{FC^^rcb`1wM5t9iXteel_!;V}&P!lfv_+vNN}rimj3+rG>^;^QHc8HN)fXC2u{h>gEO{^y>0Tby)G&?#eKYyEKp5DvYSqh#t5H8-(z8W zc70)%t}{Jty;G1igHo*egv};rJ#i*0fZ5{l)aVCo$wu!)>{+=t?ZoL!vaTp>K#Gbz zLkCDHLZ|F{5JT^9yT=_W0nBvYL=81HB|u`t?aG4)S*G4#c&s`OwZB2Mmg^qh5&@mJMt-a;9e~HN|>X%j&Gqpq>lSPYd;)CdYyyZR$5IizkxS)T0oaM4eL5&IA9UOo`ss?r*rQ6+WO?Y z+%eOELGEI`rUdSy1U6_3XndBhAeXB5Y~b7Zj%w=&ICH(X1L;_gP-M${n^}LfuJ)qR zY+4y|Gmj1df}Y1K3`nH6ojcaK9Fbh2RAaIQW0_~1X(kS@DUKAT>Ht$+Tj{n|0@qnV z!<{*+m1alUGo^YX=d7ZJ`vxB4*^>-qxA(QJ2mC8K<-c55iZAp@Cv(r;^S|xc-aFd_ zanfWT>UP67jqMLE4^@{jiqajpM9dDgpn*If&LU}bA$AFgBCLzG9{j7r9zi_4{Jx%v z>&=g6?-G2h^gOLJSa*gLbI%jWe*Zq9GW4nsumfmxt*p7Lrd`647VSJ2zxGcr zISHRd%R}>|=+KXk4<0*#M8GBa9d=30DSz+l)OaYS0Ne)G(T$hVo%LnTMPc%JjBok* z4JQkIagGhVnhTxV%+ z;=MR@GyvTrRb6;N1CtVAEkL%P{+%64-{EIZ|K;ERmDU#K!e}$&_MBSIf4n80%Hpy) zTnihZi^FfB8{f`o%J)#ba`Z}EQBVeBssQ+S4gwU#UiKX=kST(AvjWn(G19mbUITf+ z&SR@6HmhHdSY|Y)I_Al4qAV63?^d~Nt=;4&_onAEqW&!>&~M)~s#c9W3^p9;bHYI~2kk2_jI}d)Lc8T^GnOm!-F$@rrk> zp_)P=MJ`D%yA^~B;-tcq;?C1P;G$2OrgvtGGkT=hGE@w@F8+9x$FozW&qdN*J+E>% z75?_NUSNJElNzHLz5x;m?F*i6QK+50*`xEASeTvu&8v-~9PuD5v{KSJZj0+RJ*QKb zfKFp;gYt-TlFXTyBYq61xOu77%=0>=oUx(a3OL$6qcDjdDHtiehXtUG-d7a5vIe?+ zNN$MwV-(O6@s;9hett0UU`lABh`TjsXS@PV-*4I7WWSB|AmvYOLxWrTX&du%^DH)Z zWMG-R*^n>LR0n2i^rCQAL0*`{Nw4`XgEdxY9lctBmA}FQ%Qj-*=sE?0jF1}N!&Gv1 z&#|#hU_mzc`(}B}WINFpmVzM+ z2{H&Q%*`R5%$KKEkP&s<1+ijZ>^b>_%I4-jvq$^6hrzoD?-DGX>?x{~yVR4wpa|S8 zznV!z3~n>oiEFYxs8l1$5I0R#LawQeal(vf#U50pZHtG0TO-lJ|@?St*zksrz2Jqtq=B} zJt)6BY53=Dn9jSxY4Q|Cj=Ds+K9_WNvR^B+s}>Z|o<*o^lr7`S=_ z9{Q^`nD_;CtUud2-Uvm(n#$8)Jtlb~CyCrv`GP=x^uS)2dE}q|E%rIqpy@0Do?E>l ztH&~L$_)=WP1`e4p@k_sRnfvEXQ0u_=9ss~L{Xy6!&t;cuz|cQ+gY+83eeFv#w*5n zmSd*|Md)If$*cM^FWtol0G_~S(l0G=s8g|3z`G2&`pOKLT~Z(ywU(@3>8Ig&_HuH!`|8M*E+>zDz_9wqC)L_ zIDnh+h=91)<$D*iNvFeT!JVy`QuYhC`apRv@IPCD!mSjvSRK3z^Lh!Mnq~c;B@m7Vj zBJdL`My@FN(o`?nw^%JGtdvHVHo1i^wAgTqWI<&bBP3|)7b@o`evi1V-qFzNtt4kC zM30I{cRBUbbp%c9$LDUmdFFV==G^bZQ8-CRiZ)~u`zHgBVFlUVekL+WbGY~gH&$e_xp=L0^7D`REG<9}h9 zb3q46yLB1)XAf)O=TVXiz@Er5HO!nXIY*WLmJr`!$0nUq+fBe`OzoKjrgxL?A)aV# z!yY1L@scG*bPBu$DX;~|xCST?t$@Z7NYCGJHhf);X?CNfQ~m}(meV(^X|2h0ytTP? zq4&T=pZ?%6)t){rMCgL68Oe5Rzmf8GSJ%5Ld#V8 z6n=|k%gKw8hHuShnaoYy4R0ASlE<(f-sDlT7ssk312v75zX`lbI$s^$`7R4)xiA?} zp4I8L!=RCPKe2erm3f|FoIN|2UcAH&o-OH7e*PjSy2_dAjAVnM9quN}VyTGVuK@Za zsHm9U6?12IKHKIa_5~=$tffF}m0=EaxNBZW zYm;TU)o%=L1OrU^nAa%4uqKcr?AH4CJY!)GU+Sx9e3@pQLB4EY-S+RVzn;)S0->ob zK?X)VRhJ{?$t}X^M(rIx8P<1h0pm`tqK!O`K`}2}ShU-fz`ok2XHIoNk7M2EcNci> zPd{m_S-gKQ8v_nW~eZ@K^HNueVgW(txS2(ABLNj`ha>_;n zkh{47ZgJwe@i8gx{aA9gxJcj6mDR+m=6(!ut9AIcE7_#u83dCRPT3-}1G=dp0#6 z3Syp{JUFox_Q{(bwfrc(dROK>b3mHK?(Dq4=&O@k$eVJTeCaBvJszbaA@v>(cTbf)>L0s=P~V8q*iHOhr2 zv~^mQs_YL+1em0^>P~!eHj3TUtre@)v+aI`oesvbG$@p$Okl4K`>2mZ)~Ll-I69y} zeN4$8n4IT+Z9eLKPS>6&rWE5r(P`TXd6E&mLnOh}Mz|hr?Qkk$eC;ie5&x2rK!-0n zMV@lUQ-8$mv=q}#@ZEaF(Hl+IQjcB&R^GC9?c`>qXOYckcD+~!^7RI48YK>tS~Gb&O0tnGpsuJu1L2zpM|Oe;tyjJ`^thnX0R;Lt16bmw>eu zL$+wg)z_cThIB>uZMC@^zrW&~y@$36eB`^J#w29{=vsO$VmCfMnEt?DyykUzUSj*a zb9!svU8+D*+r@ky7I&_)@fR;1*|vQeT$qvH>!JrYYipJtu+8(~;4fo>zJ)9|2EPZt z@)Z|7Ur|4R&W{)_6N*BVX5@)FDnZxA_VvgwbV3X&!esgw6-c<7AMc7&CnM?YtUNUh znyxFB_e@0@%(2S6odXVy+BtniK*arN12Yw&eK&^o8U_b?TZeCM_XTUS5oiq>mu7?a zpw+_Ho8RRq$mPfES-l2VFrU9-9Db%5vaI(sn^o7HULI8N^rfK>*=pR~6?5(VnvHi7 zTn;qNbIH_dreaz#N?MTJ0 zMsyU4jiRHO*}-2Nm4`Kzw*E;AlYFiCm^2Q9$4{&$_*ZJ%|BK|7zCe9tK;_q38=|yL z6cZ-%uE8ws#iG9MENwjx&$T+;3k}xAr<20MG~F~YTMri9+H%{wGyaitmq;BY z+1tuI6J<;TOCnc?(3h^sC2;@RU3*?jwSbYzQ>*@&JJ5bPe{e@=e?s3)0D;z3x9~&G zEW!;-m-|r_O|dQn@k9~i&%5FVJCi!-sGHUdNv&0t1A3xFgQ(MnD}AE&);3jp45VpZ zsAY6V$7;<0>iqE(;_38^aH>CBd%`(P@kM<;EYmL;U8TXFD6FutF?aktD?T zqzVFUR>+RI9yW1dnSZM->9`QA%JXZ|ApAX+hGyFIdo_ejBwoh0^bHNhfah zy{^_FW3Lt=Xe;houOYW&Cr?JRK{dA>0YqrYXHPgW97ij<^emk*34^028W)qF7i|hV z-=Vl7I7C#^w0c?4^kG);lC_7PN*@UF4uJ$d6|tZvkfJ4!HhJ?47@nWy?X3C9otSx^ z5crasjb;qV8uY0NxF!svZ`3{YSKk3$z*crg=Mv4;gZ3;efdP?j(8X?nX3YQD?9;g` zKXux$m@TZC3Zjw|bPpCs2jYufF^}9MS&54-rA|-V&>g)X&?pShDMP&|-!#!^+RDxQ zrInM)H2b%4{U9~opTOs2wT$m-{)#ht*m!2Y-1$d#)ECB4=lO)_Z#^lBWg5AHw44Xi z!C$QndwaeoJ*HKq30o~X9ElRf73y;Clx2tfD4e?H{h7FbVv2!`9A-S^OOzdtS!ipp86#>=jtqGu)j^&+I+=i_xb71>L{0;T&mJs=cusBa zF-S+~+K2^N{f$%i1ow=8j5U;eGK+UaKjPsu*VzeQGCO35@5rvRi4=AwLkn9*5;U%3 zKP+i@uUI>h{K$Y{W@onjwa~vb=RtIb$D7kC9MA9Yl3FgMDWsKkKHS@svP+%9&|D2l z$vV1GM@*mwmb*N#QBwMl}wnLnn7 zh8VO)!Kv&x^!+;bzVbF@uu^;hr||zU_7z}Jt?#}d0wMwe(j_1uUDBzDAgLhT-3>#B zbPq_kNQrbaba!`mcQY`=UAVXVzt6en+~=<6ku^FDIBUN3esBE3*TGCI?kQNIZDv1- zKBbmVQ>(4Z0H&f-|5h1dK|f$)DFHSk$9{&B(~DDbLDLF}w{PI#INim7^>|dtQr($v z{Z-ck;~}$@M_qC6vFqyKg1g1}Y|kI}uKiF!Co5K1KFa)@B&19~l{!go%WjVI7Kmbd z_`Z2n^l*p?G|VDRRX5P!(aO^Me&nfRmvObex&-`1vPN7Lkmbu;hM|{%9U6SA!hHOy zs`NP_p_ks{vo0Sk``jp+$eIdP%kicKH4wUg? zhmE8{M#xX)JvLe+-65L`uSx&RTt48DHCEr{*h`AO&(HGP5RWJGVk5CuRpZ7hp z2D;o%yo=X*8L{>lEEh^y=nwJ9?&!^gkwLb}rlG$kc7ZBX`w@3ibBVo(qqN^`f-9fP zsyKNk=msq9p!B+XiJ`F&>V=o)o=lE#kiIA)d$b~!H|jQan-uDk_4| z#>k4&t4=j&+jFksJ2=q&-R56=I!PYQf*k>%A>8o7yZ+b~&iYkxr+g@>Kurrs3z>Ph+AGP) z^z75!bK_8o9);C4iIMQlN9fg+WFN%A7ct&h3f+8>`e-nYXDwCon@{bM`OaEuF@-Um zyKBwUn`Y>KWKI$KIhqjr=iO?+O^f=4$vgLp&0kk$hKwDF@Bonv^06H%HDiqlodR zjJ&d-hz0OTRirygh2g7R@hAQD>TulkMx>}1vx~5i_k%M1B_B2hn=g4St;_<^xM~B4 zybIXNYi4H&TRQryH~qDD?@Hykzc_y*QF2qAl~9~amu5xLkAo}JnLFKM z@g7X_FWkSP&+NK@>0DUJc1j+jOdR+m_aU&@X@Kv1r-gD1F-FQ__~86<#il?bg+;hO zkQUwtCL!n_8tvC9|0<06a>_^Jo+=7IU~=(rJ>=0HA0Qt{R?ZS* zGB=_hx=Ef+eiuGz%`~s@X=bhBGK`4&A_s>~CQ=RFR<^hkeH4;^6n_-#5T3pG0pujXtdC`uyjtTnj$ z#W+(H9WC!SvH9W_%K|3kZ-jbZis}#iCiUCiG`)QdU?nu#vw;rR%~^qVtM_6Urc?E! zgn*Ai_6N3Y2bil*8X!ai174E&VFw;T5tS2<^&*s{zRf=uiR~Z>IMS~D zzzv_>k%A8e-?MC7BPDm!A|+d+KFRzX^K~x!%*lfetZ^8CVr|9eb?@rHvJaGl?8Gz0 zvHGhozMFtI6RxMup(-@2OZS=v)~=4kUhDy!#g)H48tBY?C?|UpWJIdhJ zE)Z{edwaCqp=DIp^79V3fS5K!7~!RhN@yfk{`~<1y8Q}^z0q80>t7Er?GG=N7MxDs z&hAF{S|v5UTHvmtmU0{|GGkAsC3l{rXsCJrap7m9)XG(fo>Tswo9Of5da~BEq>j<5 z@?u2?raqcUCTx$jq<&&vwMW%hpu-mnZWr)1V)i+UT8m5l$g_F^IE^J&y}Ufwf7fQV z98(n`jN5QBP{$C%6{wgB7cf1`4%MC(LT1~kgzF2}{y5X_O8?8Hb%!uIX`rMa#K|4n zT30ipjIH{2LI?SE2|Ampv`dB zGK7u~Ci$3L8j`hgWKxXBeqs`FS-t^{1MaUV=;$%K@O)JSvU#o<$F>4u6kPi>x{FEg z6id#*1N$_?mG{!|c|B3F$yDb7c-&Au^{Z>+8+;v454c7Dh_X{E5cBP@<-6C?{tYt5 zf%KX8Z3|U7Uuw`^S~_ALbs4L^$u$jtWO`gO94Dd2RB*o`k~u=S939I^l&f(*Cca)P z%1o}jg`|RuCT1GMH{Ws%v1}@>2)QJVJzIs#V1f#9`hDM+kf?;aYV>GJ_0v)WY?6Ut znSC|KVCOl#q_CJ5C2aDL<4Q!8s%2Ie&xwd$`Tk6mhJS9`s&#LsetIy+0 z5WI-)S;2U&T%80N@t4EG2J?OMry!vuTmh7oSk=IQYr<%aHY^+LAJ^RcPh-JU-OFkQG0{hkLtmV(pA=7XVhxLHac$EG!&vhHmr z!+zfIDsX%HpNjac{=9K(Nxku^Lh>E?e)Kvo)j4!hFVBU9xv72>x?`nr{8N|g`9M54 zdziWUt9!;*ztu%qgEM-f*Nh*~hs=lG+_C?lS4azH(A^9-m`kQyS4g4+MtXVf#i_xp z&vLC_ZVJjitlJZqE@zSr6D-?A^zB=ROAa|NU*BJnpEuUmBh$3FF4ca?Li`kN5j5Us zlJWo`pFzuKH4QushS=F7TsKb{^>b2m4>$IuW>X~+brS#Vp6jOY58@Zr= z^EV>?=5G{TpHg0tyq*&Z(^@3#yz8$Om+k*iT+FYiDHkDmkn1FKSAz`$Up`&Uetvmu z9!{01OG}~^FJ?KygwA|p1~?sSbg8rt4{EM06^bPH4`fvDHA}u8VD6aCu<=`_qA{|A z`mnZHOnu(w`D~v4eF`UP+T#PG#u^YIqLH@nDAw zoy6-tndK&oL)L@Ci*!X-s^oY9zd=tP8PN1qFZFs8Mjh9|;kYjtUWRQmE$Zs<;>3Ak zRX}nkS3WcKQhRXlSk*G}{90a4+|q@j@#avAUz6NTo#^wk z(5FA(BkDaFPfZC0k-U?fmmYnRdmp*qOR_%jEyg{z41(xyW`g1eNyvP4uwLt!rkhFb z_RQLL*~woPnp}WlvNj_F`l!RS9n^T#=qaGd`j0nBBSyQ5Tg3q%sf#<4qtspe*2hYV z9`);nvP6{s;JLaELjl5%65O!JL55FU$^6!{lEuE&fVR^#83o~nvz^*Ur8k<@OE!a@ z61XltxDkK5I&SZbuOsi|UVD^K4O@2(tsh42%opgp?p4OUkvXuYd$=={m=%Nz zdH##!TrSDOUm*;0)`wSCg}ZwJl+Kxln!+HQ<<&(R)P%;UqrI$SS6)vY{)t9*!SGK` z=E(Q6Uq+8us~2zHc<$|mQAT*03aR1z7pl$g;yRZLV6IHQQV@=vv1`pTc#}#_UhRam ziZhe=K@-jTyRUbv7B=h(wuVErseF4!_fiS4jcM;niCr!@a`2zabSD;XXnN6@%UUpF zzj94VVzP4}ExgNFj%M;s1o```l>I}&W;;o_u(IF#OdrFA;I!#q=4kq=%MfHXb>Fzt+DT!{WW<9@BqAl%=NABmXW%-9Mth!2HF=!X<{_sP zmHB}~28*qZck42b{$UyXi3sBL08;OLri9-obt#(#6$TYred4LE=6k{B0olb@e1qJI z?{`Fig<~|BO0^I5@K6y5z!T$d(=xK8>gE3QXLJ1L%tZVDiIac$9D!rg0ElrQ5GNY=f8ON7 zxeHPLKkR~k{=W|onBoCZ=l|)jZGQ2;$Qq&orYPbhroh%7M#RO*`G1Rl0Bm9QhkQXY zH0+$5imH9;m7Ed4n0cS^A#AGJCZi-zQ9?q(+S;0hGY)b-p=L5y%?dDJ*+74DA-WEe zr_DR2r>Cc$34i(&QIe~Og@qL=4Y&3X{L`$=8&=kFc?ps{7A;BMwZ#`!Kk`GyJ?-@+ z(}CjuxK^3`e4W_6mzH3XtiU+wDMh`4B+Fg*4d@N9g0zw>NrYjIXH8`~@Cmy@RF)lr ztAV{gNu(ECqlXNcUL@Z{R@2}`0g4EVJ5K%M6`xzhShyd*^r^67T*-PPlpVe=7R?o5 z7nmSFS<=M4{c9{$MZO061Fo8&dY7+W);?%(QqzT)S$eTgRiq3^=5G?UnS&6c6j?^6 z+z1ds!5j7P>G9nraGUyM-Mtj+TvYHkmR7q($6KtVNy--Aprlya8)d$s@z zf2@_zdl2iVRJC`4t)d+39&Da0Mxcqcr^hmv_5WHP%hOioDrd; z)|LfVnELdYTBrpO_PyHXcenVi^nTC`8tqx?CClx^`Z;S2RQZo+X?60iUMd#8Qx$^4 zEgcQBfV@g!sr-F2b!Gj4JMpsx(G=UgxqL=C@N+nWK)~gV$o=y`ADiS9fX>`*JUjKP z3by4^i2Q&C{_Dtn74t}qXXZzp)Gh`A&x>i75J}JHx$H_8iG0=_xOF{)G~*MoF-Ckp zb(x&c<$>4aikU$4;Ui={wz(=!PAn86LyBL_hW+j#MN0Mn8eOGrQ_5s=a&n>4O8FgJ z=-R^ic2fS;7YS2V@s}bL<0(zQnbl2pRH5LL(ORvtj2h$!!rif4>@Y$O-PMk;jzU#h zTH5C3^y|rTwJJ;K^>wN3x`bZb-$CwXAfewdIS<0<@F!sWViHS?ixczkXehY~j#Jyx zH@{9X6AO_Q!&*Ygi3?v{GWS|2S9RkGd!Y1m6Kh!|BP@CMdcpgi?FU!Ig;Z3(`wWj* zy|nU}ShG9UXaawudSvOC+;@KLsiW*}^0&G#GWX@tzK$SyV|)t*XLM1BKG{^y@)bQ# zZB-L@b4LtPD-za=`QZcTuLQ^4=M)|G?c<<6^s2djht>>}y>)K{#5J;6ksH3#EE9-Q0Z`XYH1{tomuWZolbf z@A*4mjx*n0gF8T~()l%)zMC0H^mDfp%S^9^zTDkfKi)W#zA+k;%r8kP;vFr(*uB0M z`8%q8S6X~4wYLEuz=bWqSLkNmd%)mYCtz4x^5Nkovwos)r6#dx&~30UDE-JO-{=*< zv4d7m7fSl!j)Wf=9e=avV^`EECsDen%sSK1(di?z%5fxOe!$P52k~*TySHP46mg>2 zuGlv2j9mhpbvUPIPJ+LuV@LeOeu~$T$)y_*s|bbB0{wIw?+UH^uJYFL=qDTl@LdBh zL9?=M<>a|Ln|398gY%{OHNK8Udjb|u-tfJ(!88e|Jf7IJG5=Z4Oye^DqCYU{(a~jj z{Rd~2w)^tPstGDgGh*}!p2&H8kR<)1dZ~#Kfp$^-G#Tpt>A0q`{r7{6S|Z~G7~J>Q zJ6H>0CH1q>hn?Ru^k1oXR(QC+ozLDfu}j6{SK}%qladGvA-I5Y?(oih$VEVvAO@rt*6>(YOx4 z2LsoB9Cn2u@IIfI>c1v1x51pw0hj7rZJah1;sHOn;)h+n z-^e)t&L1uVvp~a*ksMDuJ1Y-H&@DFW5kY3n1(bFjj+8?s4=juI+9KkNL9u(ENXkCF>e`~f2w+JreNbndzc+hMkKN}weW^EY4e>=tj0Er zj&)YCjWmQaJhOdP&lp<=l}wMXGSD?Bp4feWZ(T!T03-ZIZ*=ox#8`iD2~vN(_Y-^; ze{WYRwS(wu zch+hT&xJKvR-LZg!pDcL`qY#`({2k^B@r65QOPFB?OY87%%;E@*wkro*U!k>@+~d> zQ7U;>^SC+7@RqC2m%DfOXLh`%YgsQBxdOL+X4P6dmBGU_`Fz;-WByG~Gv@I*&UC03 zaMVc)5sbIqP5}d1!8(EEi`I0v0r!2Bvl74c;)B+`O)HDM8`RH~?gs>IXU!z^2c2sl zv?ocW2u4!`72I?ubF6Ueh*OSGOa_;l=eG>-wd6OxAM_E}@d=3Z0Oc|@ndgs#+LU9) zeX+?{`hj%0-Y#Kc%=H0{tSx@lI}d|Q>w~+?{;x{Vy*VWb-7vah$##^@1c)(6hqVff}sYDA2E~VqP~OJT~)j?!XR1+fum_@ z5$WBWMG1Y@LH2U}HT_;|=f-F$cB`F7f0c-66PkR+Ivlm>NwK*p8l^YSFR{Ik;_VO8rXkrZFe@3^3I#r zR-}6F9NellXg~)rw8>q|m&K6?SG2Hc_q9-)YH7W~-8oE8$1cgcbK=!CIpdjfS~Mcg zSFEhVQwnCA|EbU9NTRxZGxTuM#t<|$HG#5($1Z#4^2bwDR9d=Uvrm!F(~GNjPO0$* z4EDHrL7V_xmZnjqtH`R4{CL!g=dRak%@@=(*@?)8ZfkySWnh;qb{iHx>H4mjHk|3& zOG^v-O|O?TN^UF2)yz}fz18~s%5O|-Z|8J@BfhdeaaG+qS%j=X`X*13yRxY!?U@m~ zW%i42p4@VRq;PorTEt??-%%raw-51(4`XC|!H3TWSyZyvAUo6MD&jH=p812eNusB+drM>5wpOQ8*(wj0?Mq9YKgv1D|^_1tqF(S{7?LZ3h7;M^k|Znh}OZV zD8%?IeonRSWNO9X`A^$}WTPtD3Kt6s92&1aCacOxXKhb7j_!059a)!>PBthBEg!*p zc`CP+*oXp_=37F$t8l`pcDn;BVrT(&4O$&bE#K$(x|k`?x_+J7%~j)z^rbD{QZS*T zvV4Dat|zm%1^KTsv5AxZZ26A2=bh|c0hCGFJ(4=Dv3k)(P+KnhZ3$$nVq&2tPmJhT zGs~9xNXu82*OB13o3`?p#-nA7+G+dDz=7p{leN7Ml7mtk2Nm=_a}=RsYOY=ZcplVU z3sngp$?`X{4l(4mNW>=%(O9k$tW_NYwInvV{n^~l&>r*nQ0ZWh4XE`u#@@CB?CJ&a z?7gdElD2MdDcLW{?`K$4K+J*(blN@U>bSqM{888Z?M3H}Y}{$p`s|hMsZKD_n=A`d z#~W3S&cisZX?-P}(>uZXt}hGX|FwG(8yxR$UaaBWVg(5}=5q3)iec0$ShLbH zH{7^Z_TbxpH>)UpWJ)YN*>cMu!BCVZWTbqq-4skhj3z zEbjGY?%CHmtoaFPie-Fb06=aXcBowJtFRO|$kofv5#lGtU*7i>t{T9g`@#&s8yu}^ z=UV&PMIHZ;2Ka?CS={T;q}^$&^HG05I}n}x5P@43t|W97?pZM1o%3*+ll|i22zsrK zZ$8bt)IQ1D9H>&jXPke@=RsuUUL3sH$S0ohy|Vli)H0dl8*lM3*_W_r^r?gvc4^Qp zdBcrke;2!#$5-dJ!ud9$MRVPqs@W@hcZS9kPVC_cWFE@MxtSk!sDmnXf4f0-83)xg)0=!)>VYAJI#C8<&He zlM-s_Q$Q9)t&8J`o>jzx^BS6OyNQpHsTbG$#Nwn9R?A1m%b&GpEXN|NnlMKPM|8(-gf~*$ck1DL#{w zTjM+;q~}vJh+S(?z9=5(0Q%FtaAsIa0iRMH_wM-m``60b6YK_kSE32>>^RjlEKVfYQoBqAV8YZq+Pg)mn1 zYW})ecAK6j6AC40=&a}+susuRRG}J<(G}ac`H(V-6^z4`ZjNra_{6K>QI@pD7p%3%;RSHuq_{bb7b9KwO|mh+|Qe88E7@C?jm}l$Xre@ zkc2QD0EPPWyG84wwHs_291WLJxe=Sei)q!^U;bn%NT{lqG2i)$kl)3GHiCnhW*=>W zipvTT^INXie3IGDy5B$E$89Kp$5(aN0`O6py}c!ouTPa7wO$6KpvJQ3I3k%S56=3} zClNrlp5QhK{UFikA?5dKj?@&!ED6Co8N1NOvQW7#-}|yMdfNM8B0LDw^GW-&SMHY_ zl%90+vY5vKmn)_Pjwa5k(l+!uIx)K>4SE+sOZ9pOdUe|``ky7$7v!%v?mI0X zqHB}W9y}+K5PC*mS$ulrLEWd>oH_C|!=W8>q>^d98RDuOX>^+sUt-wkP{4IT^~+uz zNOZ!8XZu8~iVRyWUV&l@_jOT;Kt-z=DHAX9J%3 zW_=)nefh~W+vsj5C44d90Q9fagm6;9*g87!AoiV*AWX-~=4bkyZk=5Q>t;-hd_e?u zjfU+gyA`ZEhMucyE(Sbx2_@`R#|osvm}Zj}6&kMmM^!=&wX1>sNXZCW3*ChzO$7oP zi3sf<&F{LIsK{nw7T(HyO7021RAgVZmT5Pe@D|ZW$mZN9_}ojexDjZ1c82gY>T0Ke zDzu_@f-0Ma)vg<>wE`y5X!uv>Bj!E)n#5ca|9auEhk{VtwjTVQ)8C#>V*M*$Dbn6;6cfjH>DI@bGj) z#s-_C^^tscssGz0?g+| zBs^mAS84tEKdnQ%UnyfCNO-p2`k=5OB#5bCTXBt1yJ}&fzkCo2EJ@ex z9^u7(7q9`>Z9e@)lxK1e`&A~wK!bg8V|21Ob@QWYMD`V{Dt78NG_u-*ny9{oC{eGV zqpvTs_iY*RxXb;*Q10%Zp5zaz%8#@#de{YT*`8N zpcVhQy@9TFq)6p=E7g|jGaQ7}sq*0Cq1un?1`&Gi+4s*KB(3ha-S8b9&N$Iy^3=-= z`=300%J!f3x4PilhU!PJ$X30}(h3rn(U+!U0^6;y6ijN9Iy!jZgqSxb{f^fV&>mT3 z-r7Y$Y?@EYuNqxh9UVBrk(%b_@Vw?SDGhL2P$`T+)p|Dh^Nd?F5vT$b%u&&akku0E zZwZ8)UiRKy8MpuHb{~|_D*O5K2KRh(EufJ`7#$@Dk252?#XY~o-cG+!0_XeSd2(gG zN_U+*&(hWhZLo3njostCw1dI3Ew~>{qQ)FT0b_LDIRN5KD=%`^9PN&D;(L-2Q3p|4 z?}G-_NQ%VyP?C#k<4wf3K3hF4jg!mfNz5iNjD`qo1|Sa3D1ZF|hXi9B&&_GPQfXSl z?z?Rz(>=}%h!-m7%TP_s@6;6AU{r3N$)sR$e(s?}W_Wx0IX0}pCpXgLv8J~y7Ne2f zuP3P;thPXLBdgjhr6ws26m3^;tXjvGeUt@CRy?Rdc{d#4y+3Yh(KjoWe{v*S9ANdI z$!JbY4iCRi>zXB)8`du{bUpfMGn2Kc*RdCPj*d#(A(OYanp3db+U|R<^(0ONGnTGU zU|i+Dvy=_N^STAb>U2Y+suNST)Oo>+i1|93U*Kq@Dz1M7Fc&1L_?&*EF&{4-+@!1i z6N6T<>MUO3;K)Zrwnh4sn1iokC_fTR+!ymYkL<*{2BPQhY&ibYyr*!;|7n{tXFC3) zOK%yEeVWIX8rehUCCnTiovFb|?ER?DO0WG*BN4ry4oRV(WA3SKE;W){5cjHjW6}T{aAdHqO^FqC1^^f*a^3T=16E zU`>q&F3sskpDY1v8U#x1YVpQ4Y=rx9u-lo8a*Kaxl>AB7^te-Qf?yl`P)c+b8t_7!iZfLQ6ys6WLN8RvfK|aMT zF(1b;U#N~X2-yIr!u6SeO1x(I_VaLx|BOj&yQ7f^>fL`mxvgH(FQn+cR-=Q!CGZ74 zU`WR;*~Qrh5XHl2_QwhKNUn+1?l?mbSaV)Ko})w>vK4MmhvcxsTbtNl?GR5yo3Wdr zG_}+*=uJ|Ag6E{*l++UmVdj=*rUkkwaUu;RvP|C|w#H6i4;SoFD%)glYA`)1Ma`#k z*363eCU8_hcVtpn`}Er}?F2_sy@g5E(;=M7X`7B*xXhHPL0?G77T>y_zdMwYFDf|y zZRgkd{a>CF8Q9ws&yRWN)YPg@c4}VCG(<0t2d1urzjRc+U8mo0=25_;;|C$_KK-cs zHfyzH5tnT_-iGZ{a=X&*1b|AF4e~I-WN#Fb0I|k2RK++ulFqbGUE0k@au-=a>10XN5ohWYHb~ zs-Dd+Sz3~OJ#-(}?Lt6oqJ(k5#gh+jk^1rLum8}-DB>jgKAW)&(&G;{1#1knre#|2 zO$LX-?Sied;f|iQ>>}xHit<*1%RTG6t%86VnXjM1)_{OM8Vz4nLjrXHy}ictT&kw=b4HcR+9G~NM&ysH*5`nd1y>>%EiOMfOgWVTQD=HaW zvp8&~p{!>vS}zmM%al%s%4K^+GDdNa$7FLAJe_}XGCB3f+zC2iI)rH9G%{=QyZf9I z;hQv)lxQjyVg(7cW2!yL9@4&EZ5FVo`_7$kE;NJH1d^Dnq|F!5qf2|-m%=+Tj{TZn zG(X+tq9)XUO1CT(|&Rdx~RwF7&rF{Ln z#(?38DdyiH6>r`9E{WWMc{{Y?7jhWCiTU1(Ke--eEs;9cdC21mrjbcrAc1&-a(MvAt|)+hTkI9LP;lfw`_2DdA-@` z!t$$~%3D39WY#p?lA)v1isE^uT_aI(YY?Zw-fmsJ60(M2@2LWaU*!}$|%AcY|aam2Y+{D)GwSB6I>#mN(#SksJ%H> zt8Cp|KQ1@hf242gwhr#dJ!kMnJ`8Nyd;F#$p;>mEv)`U4Gs;wwk~3sWidl_`V~E-7 zSOnNyH56j#3lpepX!6s`!{-wgx>3nq78{5ehV25A>?yhZimFSvl4;?na4CXupU>o^ zk7oo~*|^TW%jQ_t@zs#YtIo)t==H23IA4Z;x`tMNR;(La`;eBJ{s!T-)Vi<+j&H+j zfaR8i;52;7e49YJEgR=w$s~rY@$Ov$o6}3XgJRc$5p>M7M@y1L0equ5bg3iI$p%_ZMe5Az(2-4*QSlM~S_<1BLob2x{OQ2!k3omzA$*2@S_+GSGC~-?%f&C z#p)f+p|(`ue&UYt581W-K7!|=DB`mXAz)MI`9;V!Tu{3gPxR;218ujY6OJ9d3!^Xh zcT$I@LaS`*Wk-5j7R|q>?k$IVJ+~d?4(rPvbNVpr1!BPnOp!csuiw*=E<`)PDT#rV zb0Q!*`i0)8LdI#m>@elfSN4)cmcxMebZJc;@8?KGz3A7{pne>DGK9#_p9}cHc%9x3 zz$*f@-LE|u6ct0c@-&1TrfnTzL}3~gz!cZpN93F;&#L}hk&w%>?}unesK(z}%n6^- z1E!^rcZcAAgS3qMDX)2>5w@RW_$#0jZBkil>(Ifs)~vWVQKF*lU{Bjg3HzdkwE-W z93?Dn7VQp11zY~b5)}ecs|Qd>%Iam=5_iDD4F;`R-jkOIg@e-6D;>Ws61s;8qIen> zE;>s$?JR$fh4)5;AWPSCPQ3HVgrS^tOJ^e{#K{)*(*nu&bys!A)CD4;u-t+tsVM?6 zYDud1_NMcXNt1B(h`)F~aN<_?S#vs%N6z~5IW7?XJrkYyu3qGd#CqIeYDtooAX1s5 zw%)n-TTiih3f+rLzVm{zhcASYY-X`zAtWdRSrx$v$wV!#bfJjsG5~)gT9BsIDFbRi z#%`+y6l-ti6WHqn1jqfU!a5aQ`f$Wz=qWs=-+i#mqqA^~9B@APwY=3i#=6Pd5L`s- z1sUCE@Mt1D%WL}9Gb12+7JTN@niP~uqO`C%;jgml@?6mbE7M|D7F5OgSdrKA_dsJQ z!tTc1a87oc=ckMd@u;D>p(w|4+sd{?i#4_=l_s$=r!7|(?7u>+$3@}|DB&?djtxcVs4rDx#h zam1XEkO!$rgZ>ZM?S=|JFVu=^xi-jMk8iQf4th zWJlAJ)Iwc7FPAdLh!q$?k_Vwv+ac)Ux%bGA-^4gJR9FNp_Dw--+GZ-o^??Zq)6L9n zrI!^Bolx3!gW(msyBk?e^qpwz-(Vh=Uovx?kvxM7QrlWM(`q@#LyaCR&wlIULO>8+ zwZpwHO24kG{KsTS(dxVbAal0#EX!`y9lfzcMB}~&tdIQ_t(#l_RHLU*NfrC}3mX_Y&C9G?J;Wcv`;C=bFgErh# z2waV^|E^j1ARx_dY121&mG=8f%C8xchNbntj#!&6LxNm^%0N%XhO>yjPfaf3<3$6< zjp@|jy5hGuiJ3wA>8mxOaepwpt(I$8O5U^0qJb9?u$ecYzF7&?5ABni<}2vvQfca} zZf?_UN9{ZKr6FFUNy#G8b zR?3Q_{mVyQ#~!cT2^WL=Y^Jk<*SIK!ZbyGqXxhVdHh>HM&mh zu9uFXDP&^0l@@Lfv-``V;aW*2+T28pHCrGfYVB{!nLf{lHGp<@qYor<{UXsyKJYU6@>@p z-u7jjc5z&UbaCWK9)6W-Y|pcek&UF+P!ZjrSczlS-J1wj2=7O`b-ErFJTpCb++B+g z8vuc3&O_8sNu8qFJuL%JaXnI8$vo4nh=1E5+wRLDuDNUuqPZinJ6T_W+)>TIM!U24 zUnV>-<`d(P<;y(pzNTTF!in{LJZJKdoNZ_pR%=*oiPBi7hrqY2>FF7<7{9il`KIahR|1G2AX9*WFZk_|_mm z01@E!IAtqhn!nQ7_WuH-qHxHT&yjg!Iu;0kFbQi-vobRuBL|#FOn#_ZaG?kO&2adK zXb02Z8ShmJds06)A0JT?{O_jKxWv16-X$nrb#?qFrxrMeg%l9KU+-w+x{p}jpQC#B zk+kvig4OjJ1J%p^1|M2Kz&%z&=P8t!!~i7ZwpeskNd|@F2-Cu~HXpr2$m<~W-VY6} zBQO!#F8=Ox#*hR)GuqlgLge4wSasO{09Jf>Ta(@^`qX{OU7?5Ih-eeqMoD_%bici| zjNj{j;0sjQ14vdM3~30CmaOmYV*may97J&<))O=Tfj-)1eMVeMl>av@Hge{FMai1~ z8*}(iO#pBmACRqoBg+2kNu32TLUd>7`g~WV+&J<-;r-9Y3!K*y)Bj&DX2LW|+ufRi zu&3~827t>-l>YV7KM&V-0MmAHjnn$5pfaF)elMuokz{qU5ekNCpA>X_{=E$T+ZQD& zMBqY{yIxv8XmOhtBJ1!Uk7!+qL?92K1Ktkn))(fy-JP<39IF1?y8=9YaC0RwFEMde z{1E`JEMA|0>$5Da#;^Pwr%J2h|GG4P9!BPU1MtEQ-My%e*utDaYLk(sdw74I{MQ-M zL+j<1o;;7TN8B&q6Tto;VhG4n4hcP8(I!?ZK!R2@q;AzBogAgz_jP^FAONX#iT1_i zz5C2cMFq!H%>`6ze;BC$`l*9mQ2oiv{O7EL&m^yj=#Yh@p*D8EPMMcHUolMDjAmn>rcW=b&^dj=?_Vi{u%i?bl zUVwPQ_x}UR&R(nn0vLlU`8IOy+rM)`c=4I)h=2+G;k?6_ z)Sy#1hP`CIxRPUq&F_=po^S>W{d!^?X^D9N~@_H&n>w=ZC zsHe*4HlDYV?DK;EhFt+o0c4THu8o9&6yIxmiqqe{I^aEQAf*1U?j(Q;KlIPDiv;l1 ze_^L;R9ZVluQF-&X;{a(fA9`GUc44VnqO$f6=b3i5(v6wRhz0dL_wWs2U4AakG#UR zAj~CSC1ItO(7z2kfVEi>6DhJi+lFXF*QlZr?FaFAqcotA8sW+xP8K`3o{>-nbsHwc8q`!apUSf% zb8$FpM|mwVJ6LnyZ#c0TqFi>2r+Yz0xh|Dz6RdvD*4;&WTm0u%e7}Yj*!^RKFQhTc zR#d;oa8MiQ#aCM3iZQdGYFKwTox!>ak_#I6M{Ijq+_EO9jfhlt0%awCwA8UE$uriQ z_HPp|hk(1MJp7(oNF!Skg1K+w*kh7;CiDvTmBx@?ewc-Ytcn z@pjtfcPC5C@s0>Q=@RTxUB%=4x%qdSEHLno7REX1Gyw%@Qa81y?XNO=XApXm-9r(EA0{(+)){y?&4}pByltEwVY1PIz`<5x{%@gG>^nK$BmIWWA zHv-l1AIgiKwp#CFu37hqp&_3p(kcLqMzJm5PGjN@$irVRg+IQY>Su*4lP!b=$u17) z8N!AYITGY!ev%fT*!z}ixL);4seCQ`Yg9u)VQZprTp2WmxQSly#N>%}ER=DVTOIC= z@q57E&3NQzx=r<%S4IY<xuz7uZmXNyW37a{lo+MLyWo66A z?NMlDY+isu9daw^kMFo#)?V}~mc z6OO|ykpj5i+Mpi;xwiRl--zB!=F2F>XVmut{Gci=Kb{Sls#AL|jPHS&7Q69B57Uq9 z=*Y6FsCeXf9|C(%qDN*tIi`2d4)ze+yYuf^Tc=F2{#gNI*nl!mDOA`JMd3MwbHV*mL$N?rbTC!Gc_YCHfPLlKh$jRFKGAeq zMg&Ydp?hV6(2H5CFcH)Az#l7EdKrIqlP;=nlIa{D6`81ai5;yUP}UE!GD+oxmU{?* zjS2AZef@B4&qbhTIfA-10gErEomN@+?V_3e&E@mu%~Y>5*(GWX^L)O=UBStmi& z@^4uo3(JERVJQwTJKt57Ffw-HZtw;4v76hRX^Ss^x3#R~LjgnCw%Qy`1_Tn$**BBV}%55ECQDl}W7S&dpWr&>sD#}tZPZ&{Oa zH8_)c|1k4{K0ifv+_!q16iIJ`{s6fJ`;)1 z)n`Sa`2gBwQapF`!R<$r$pU4LEb{&sx(?e;vnA5|I|{cY32p>{ws9}@OhLslf-~1l z8q-A@@3?&m;bTy+;q)42zH5zZ%y^(#A8T~_e7zS1mXb$*SK7Ca3`oI@=2N$nt+-^( zkD%{C`r^~Cm^&5;Ls7aG8oVQW(M#GNeR_Yhtnn!MKLn>XkZCFJo)ekuy(x;Q^~ltofV*T zwp%AZ>1W(_ntZL9Xit;ck^WC^Q}G9xYXfLu&Q$vQ0K^D`rUw%b?wU$Ds(-w7x600?Um}!P z>?C6YO@8J6K$w?9IxDpZ2uTV&bQ3aquUPJ^e4O%=$PA6tqRNQA&RcF)z?cN<)N=T5 z6S&=;>sg{U<)rT)*oMnMec!WfRF}GA+*CnXro6=PLtfb4n zwP>ASQWHPR1Q*N(*i?Sf!9tao?cuCtS+2wZ?I#5xzXbYhgqv=CT^5x*7j-^CPiMHzbEXye@BU3>^ zw~{5GEq=c*88TFv1}0#!K%@^>35kJT2A1a)s!8HiZ{(xxJ0CP?_k~v;kLK@wKco+d zuVTbO)wXm*l?9ciJ-)mQe%bdpR&Ta<9hB)wz)zhCr)_~PD{#b-B`|V zXU6c5vktZ=e1K8rzu)X@Will=h3DxtN;9RMrowr}ZZP)pgF~4hwO0U`QC%~NN8xxI zuxvKdcCiR8k^egn%De;8_*{eA9{+KhWG2_qf`~Eq`@w~j08xbtPhwAuJ}(9=?`rx) z`VH^ITmeN1RTIn4+wx#lMa5WyY2C{BtGqrJ3PwrEUUe5K>9RK*cq8Q_wu_GKXpssa z{Mbb@Bd_JaU@zFFgN7Z4+aWB~ZljOjSF2G&S!m;B7M%7e#`kQjt}HAZoc0G7<4TL! z>djPF{5QtW3kwUOS68g|Tb}@M^S~U$j@c5*orckn>b!0Dr(lRQ6S3UmtrX<@8h`Y@ zDct=;jshG0|FHEHU{Q5lxPu6&l&EwH2uezKC@rOQgLHR|v?xe-!=TdLF?9FP-8n`OB`GoZQt|D#7G%x&1g~!jl5wJ^loedcqE=WRVGBv?W`F7!_U}RsmCLCyEbx+C> zP(I^kPKw$*EozMOJ`crfo4*LN_UkZ}I*q7+2_33Bg-H>OJy`--e_@>LR`W^h*uC~I zTO3;=ZX6ZVF=TUowd>Ntsn<0}sfD+kxt3Zgk2-~5`#a;QT58K3=mGM`K^U(IN-HQD z9JgI~Hom~O-^i|(&r>Z8Wxx-8Z@atLcwt4A;uR%zVLz#(H^0dV?pd0t0|4$$WW?X7 z$kDRxouksi01g64O>GZU(v{O(3&~Dv9Y{wavRF@6V{lY0K3*ahq_x2a#Twrw8qq>gqs@L-M=U2d-y~uJ{56TyZR0Gz_;F1jB@fX7ja_r_ z3q9r7YM!ZVhQ~EPj~^|Szkx*029jfuaOG$J{L%^Un_!yxJh(NmLSG?z^_{HjTGf=E z5i)^e-ur5=;pS}4^SGCOyihX+>;yZ$fTgD-=#Z(BzVDsG?|JdUvOXij|1UaGW%42v z&`Xp^9#L-nNN7=ltvdYO^(RBYD**0%Aa zH$vI9yj?}tqi54I(BDkiw&dW`$24u(@@!rb)vS2T z$6mgg61pw&ivzP60FVl}%+nhzVK09T2;W-`d60AQA`X=}yx!L2l!D=m52hHx4-(}T zxSjRv0s>xJ-F#R(Em8`aOWRHfCw?=NPyUI0ueferZ_wh9ny=jD#e$@_d|;VRSr6VS z3wHsjt@mx^gRMS#vDq>Dl03G@o3ImqS>WZ_HiW*>a!sBt%8KCVpoFL5sM4w5(42I6 zDT4BIbSmCjeb#Sw**S&vSN4H9+xE)M$et|Wmp7QbWjL= zr1R?ttFa1xJLedIZ;Qo$1J%|2&cVAR^ zaYF`$*+#DGIL{Dskzu-16H(yvxXcFJCy5drcn=L+a;O5Zkv&^XyNXrpk22M+a6&)b zI7kJqBv-mPCA;%oH-5vRX<~8Sx$C7iwF!N}fss2L+3hij><31ku*s{XH-T8cSQH#7 z7c}lVz8b$f8djp|**nPhvk zylbEysGPwFZ%Xsj<$e0A6ZNmdaptA3=LhlVNOMoDrJPSTmRbXAaSsbEbqZ;ZIHSN& zhgW&Ixi5c#Chwb5^=yT-zVSR|h%yU{Iw2DQK;YE~8}!2;ONST5sBbB`!cv#Dx;g)j z$tq+4Tt#aYoZ~4ohpi3yYG|L=fg$#ZgEo^})oZ@^dvlM51LILsCOZ%+_m;l+<6hJK z=n8vhqNWgBy#omS8wK63B><^anxxl0!cVzL^K@$%;*RS*x=o3MavSX|B``&LLg#zG zl);G|UJCel;p@{7kr8@5@}(gTf9+tPT!&uw=JF%08|ToAO`s;;Sl;1=V(**X zC*W|K;HJH@Ne0YrbnEjWyvcm+U6VEt=G_8Q+nMC9ECQ@&mLuDpp*QW!r=7E&B@ybd zA9aAx4>KFmfdkKnBk}=xc@hC5YYMw7Ra;J5+}l={K2-toQ8UX*AcFInj1u56zJca? zQrW@={Ug1l+Av0Uhc^8IM-Wsg0eu1`n%80eg8)gok03h@_v_VWSz$kxNQb^tIbKAR71RYD2){_aurBeg_-_RkK{Cgip4V?d!pHY^!5bUluP;J-}I7 zZwl7deZ1F(XtsPYDJHZ>s+OK&Q3xjHV4QrnF#ziHJik~@zi#?)v}V;8{{mTw5v5jX zBYY}R7lXYj}HDCV0+@V{9G;kQ!4OW zjlHc87CQyzN+I8=cIXh)$&4-p1FkYc?On3!P9AdN-a{!e*M3Un=T%RhX63gbW!QUv zo`id20xseJ*e_DlUDB zMqNMA6DL#yG-uDx4MI2EjP}@kk1O}4 zLU5BW_{04fZAcf}D*akjT>J*S=90+vDs6WZTxn5RZk@fF2-7R`-+A#E_0*@bdhyS9 zR@RgG83le5m%4)Hy8yG3kF=l=UBo5xI0NU^X!6mycQi^(i)-lC6z81|oJBq8jn^l* zcqHRGZJFx?%A@3a>W1Sn=kN<%m#uA>%x(VxRAy>!M+uFJF}_ivdWfYL_~t$&ZAPM{C@ohhA}M$jdJr1}{*~6>=eMwwC1@{Cc}UJTW}wuE1jJ+<3~; zHNS;x>|{J4nX_z18*>-T)i$T=aRe-QhXzxuaFyFuCbXjhj`24Q9xUoca{==mJx~>U z_&W2s(8yC*lLPCWtR&Wo&*jhI?#-)>@BVvBpmy{sue6+Cn9^x@_A?``WIjlT+|kLY zOyd#1ZhG8AL|0nf)6v{}lZEcCOdv=(ss;2f2S$E$cGhKCmD~@wk4`WVP_d3Y5R z=-60qPtWUw5_b#D&W9iS_!dS2v&CkYO^S`^P1im)Xiavn4I552RTdhUw>+M8#r@I| zW({;k?*!LvoI4OKq{a%tkU#SL(T|vlzyq$S{0Xe0A}?k=4Np z`lPPXeWe#um&3ee?SyZM&&->-fs|wG!gZWg-Ce7xHq8^UrO(9fmXB@Snd#lC!n`et z+UvvmeeP|}p=+4yfX7cZZ;7hUy)rNO7M|KEj_rJEn(Fy6yrb3TRJ_yJ2(0RH!tUuc zUit~fx}Lkx>G5F7QF=bJtIa!CnJrp2H1@U+4qaY!b~LW1Q6GZ-Fp==7FwU`U1j6Gh zgwiWg=UCzp20b(B%E!fVSSi4kaq^j%H>jV+aui1HZ2Lmz2c}th5(m?x3sDZBZfpm) z$)#p5ek}0?wz5V?@+6%dH}?pT_3j9-|8Vo>kpJDCH>w~mkZVqdA?_!ON079INoSc; zuS!=0dH?j0@;?=OqWY~Sm|W%Oq&6TiDiXkj>{*bvY zR_(B(T9*Q14`e@Sl`O^512orP2z&gI4~wg ze*Q<=z~#1&#Qh+}qm@Fg!i5CUayWdbk8@HkRj(uY-V1(38N=BF4Cz6(&nsifA)S^f~IL#?|staOzoGiVXI{0;aMWvx$zDpXe(^IkL-SaSSF|MBUAl{-<|@vh!TJSo%?_LLmU3Y>3NZZ z(?e}S+gxu;TOrA0_!-!Mwc%pxB5*>ukiE;K=3Y)~C0?wFQuF!%oQ=SrnYpC?L=_5* zK>J7&voPjy+&6It#`$fm9o}R7yOIz1Gx9_b)9$WS^dQ-C$@N*6rRMEbjE=o&7mK!T zy()9pA&$_!r(dwCZYc>E{#LPqA^T(N#2{NNb=%Y|n6ms+kf*1U;PC)Cm+$%w(~=>D zU`TW!9#{=Lo~6w3Ut9DLI#SMKFNg{C?DZIO%PA&|boE7ce9=`)g8e6Ki*%uQ%l@%Q z8ITuI>|@2c0)=Xf1?nZ-xN+}nZCMCHI(7a)EIw8{JG(-)0x*`Sfx&mM3IlSS&?kVc ztu13?AuS&XF9_{?q7&pEjcNsB^BTQZ zot;Sk`&aZ#47MlRu*3!PQ~fhII>j5YURsG%ATwFX@3gutZ8nx3} z*1XBRYQ>*>^-l??*k6In|8*!?|GRF>A1@O5C|!nZldTk>Rc+l%?l%9CMZeklc7D1N zPjvx;`ae$OSoNz7B-TXk2mfGv#^nAfdTl4rohL&~msjh_O#0Du81x}*%KVU);A`Mh z3gMIwdjlE&>oj~Xk*iO_iZf9rr0pRtuGz{Lzj^l`e#><=Bb5R41T9&fRLr-k5Wx)R zclADm+7Hi%pi9Alfj(`qlmr#jf~kjxU*(A0_fK3~*Psxaj%AsU2e`QOo@XzSMRNXN zhJPNTp8V=VJ;=zhb$!9Q6KO z)X_+OK%MDmD=P^^>t zhv3CJ;8UgD+cW^H4&>>(7XdW&ipe3=>eU0!Ruxl1Bucv-?dNQ%IR;P(H66|m9A7!! zMNc=n3hOtVQSLXwgm2-yp5bQ6FrC_gSogtcB**>#9@4K~`s!d&P=IQ|fEnTrsr?n{ zLSWYnZ8?u8H!^pKeZkwb|5bNvqS0;;H*-vZnvM<`vce7YxQd=J%~;A!-z*GW(eQ&i z?+SEo_d_LzqU#fT7-neN?bGJs2lRrTXtIQZ>toMr){HW?>+rZd_TlQqL7* zy~f4{AYzrl@h-gLdZz@javG)@zWeemwgS$c#jXd7pB4*1>AtdoknuaX;910-0Hd~f>CDEag zP|8Vv(}{kg3V=rGC$(&u2txOUA1S~~K760M&)^y_vFmxJTKB~sNLq3tz0R}FrRGuo z-rh{viWmP{D8HYAZ+}f5+ZA{D+VB?p4)r#ZX*Y7!G=ntJo_A+d2v zVA?K)NM!Nz=U;ej4@zWTE4~_V74W~Tq;7d+EK%`sJ7nHR{ehbZ;*s*5Oomot-}Hdh z;QQ+vM*BoDwmc%r7d=#Lbc2FFu;PsG-n|q+HBx>^YpgTQ{Kx$Lb5(m>21fH$3s$S) z%*rDp>UM|RwcQn!B_=B;;ps_7b9hqUMv?@0?jx2Zx$}-3GL88nfMb_Ux0QMO_vr6F zZsYDqJ5t26kNhv|*(3>6Dsn(&0Gl}R8xjVtR%UqyqV@xz$;sMzu@0P}@d0(Q*hLIG!uN2>7 zugnT$x&uFrET1Sf-B{ofLywdgH*QCQM&NomVRj52rouztWiTL5A3Zy&1x+CPtVNT~ z4gSyQdp1`0_QEDCxe`zwU}144yn8`S-KKMoN=PUzG_?G#c{zlcxqm|E zU%NgEbNM6lTz32;s)Uvc)E?Nf7v{OhCrnkpm^-@a3 zaI?<23E-H~8uI|NNEW=i#^g~&nZbikVRyRCx!9b8UQLbKGgXKA;J5kjQGJ859<2JFfRYa2+XG8vGIvrc(s%_HF&D<3E0SG0MZdiz&I?93y%E$TV+qWF?Xa?UWT|oR$E^5H)w??qF78 zhc|aJ2(8pw3@c2Yo|gajQXsEK555A*x@=!c7grdy-{?N2F*CeTuw-(bz168!6lPOkzPfnc>fh;Khz7w76A)qH{b(5tt zLc;YRl==mseZkwY`V=E(P7=AmuDDYC{J7`Z^89B$;W?kUI~D}#exJfoBBXQQ+%Gi^ zWIit=%_Y2vy}rH>RnY%KT*}N!Kr=icE>^A_(DV6O4)|YRnuPpPISG<+EC3b#_RBuX z5I})?DF;8PJ^f+vl>1#2RHgJYq!~ZL^Y9TlL(E-ZMfry8=B7i##irK@F?;+_x}7=5 zerx6$TxRjQwG~*yP^%4Knzqf=es8B0_~>iQY}WXqtyQ6*oSkL zv&&~O%Y+|Ve)ybwYeR&rlk_E>P~%|h&4OLmu&EyMg|8#fl;`*7*FNgqM-3Qu)gyug zVxVwa|M0Tl^}_n~w(B5KviCTe8!Mc;rGQ3>9Ayp8C?ul{vNtG|>AC&Xk zmed?^bum=KgmNx-KkS9p-SBh`=_ooAXv{M@!D-T8yIJry4_-@e@dNQVe? zJTHCzG5IkQ*ZxytZ6Bg_2fo&qY7d3vVeZz%Hpvv*0fmyIlhee;2Bv$H_P<@^g=4 zd;rtxyI7m5!(gVvY0G)jdJ-$4ErXu>?28Zl^C!P}l|mTGbkI`6prE^-wL6xFzqa14 zT--Zh4Y*hZx3$E^H~1GM^ET)8pl~CC*)FUAJAwpa&by19ADC+6fF}6K<;NnSf3EtJ zh>r(BN#FSHoeo}i{kJLm39RZbI3_lmysL5*m=+gy`1(iZ4-lDvjk5 z?%2Nrsqp8?xYT(V?w=W(_q;FmjX?Z`-aRiekwl~B(J>|!)%Rt!!HL>T+cKcQ#LXeU z6l}Qf)M^zs(kV4#E_kZqZLx5HT9M&KUmUbNRqRBO#+}a^MLUB2;}BZU3r4QsJ&}kF zWLC=yfvJX{OxDl@E{|{h125=Lsdf_Vcob{BRZ0|q-Ag@6My`%Mf-JxH^VDO?{7Re- z%ZSy@-3U!W{@5xSQ@kXDP_)IREc--W#f3pmn=LWhRpGM;%HG-GkJcK`-VhiRU9mF2 zFamFmJhBB;8+>CQoGH2wO-nXMStmC}i3=>NrlUvjxv<}wVYB6gR^f}~6OO~3?Ib!K zMf+G3ybqjpxkM7J=FbRVKP3<7YxBZH2zEPe`26LEa5*MLL;`Gf3?UWRddv^WhS6Eo zdw5GjnZvnu^e*CWFh&8CmN@RuN`CUcSrdcYrsbJe#2|(Qxv|LlX8o2N=N%f5)EhsJ zy8p3@g~kZB#{fyw<*o21`2(@(B|OPMD1*(Wi$BosFBZ}1eY^dps0zm4=u&fcGps|MKM|dpMfC)Z_RuMJ-H$ z-^JrIu1_3p_#s#BntQON!9V) z+xeDQI=J~(Cj$jP7;E(H{|d_vVp1)QrL|~rQw5{m@q{Oulyz|{J?TYwqYIerQlpk~ zn~QZ`+qv^AjCZX8+Z3x9!ZEA5 ze@gSkS?d9OH$=$uw$V~`daKn5VxSr^9tMnTC0`ttX_r+ad_5qH@aZ7U!{-80C9M>g z9kv^r+YbPRartYuAU!40X0t7|HNK9!Q=z9k$@H<ej(9)!jo0v%lWt&Y=17Ej3*zPUVFmYjM76?lMl~yMe*g%13`W z9?gMhhmJii{JR+`C4NyfL0j%Hl*|LeZp;d4##q3jgw`VLP&dq{mZv04AhPNl_a|fy z39|zd#eLto*D$|?dDp}d`M!IDfVnB7o6B^5q7Y@ui9c|psI}LF9Om7BEHSP_1JEIB zIt{aZ=0HkFc+|S55gkEykJ~mES$y_B72CDgnIan(6EBf0iEl^|7V9wO$1^M3(MBu6 z%e$o#$?pvV5~bD?>btL0Fk#bMZ_)l4t3vF;Da2~>gW4d`5aI|{FAuAs0Yf_ja=8Y` zL_kMt&C5AqNXXa!`SWMoQ>GH)LAQx>pY@W#3i5a zqRoSs7-Vxjy*{bXgbv89JAjsg{^A?((0W^W5Md~AT^Igiuc4H~776ka3Gt&X1XtSB zAO^CV?{d8pY;>3PxbtWFSf+N-jbtjA__jiDpqTRHK_m=#!I9pF3E(5_=Y;E-2vmMu=<-82L^VHQv4U?|25E ze}aJX%DDQzLpbwcGP69-T$_+qvgnzKOd7uWuk@MLRbwD5gkI) z8AW0QHkN4> zLD>O*ekOC+od0s%u$+)(apPUkvhYjZy-~SyrQ-4$IKB63&aBL;gTFjA1c^ecJ*a4H zjr%h=f(aFHw#3HX*c|fr%bJRmCNh^WF*c-m3aeZ@)1F#pMn$dqd0g^yW2Ju0h_Zc7 z(JnRGaLNaI48YsPCYRD#j3^SbU~jldTJWnd3Q8=+D``#3)97%j!XbKVe=ZvAeeMW# zO$~P&`21rxUCI2obL3c+u6YhGBLXg?*2w33XUnO`@O=h>xd#(YCJ>mHXUs`tS zj(DNDwi{k^+ugr+Oudej%O3bQI7tTD{;_9K1?m5x+wtL9B7a`P!vj+o&&dc>g`mcC zmqN&dS@I4wtv$>L@T3gO1a$ic-+f!&ynbjcXoVo6ib@nYkNS$SulKmw!69m8?a7rH z#auTN4S#F+NL;%@S6SGuCrpgGeW*!YQ@N{bC8m>%ODJdlL_8YT+V}wRN?~QK-+1ku zX(1U)^jd$3qpYy4J1goChq4fnVw8uL6IEv=_qiQ78^EKiht5tk&kt=2FV&>?isTbQ9fsOgE469dk$RwG+3s-!- z?syu}IdFZ93Tm!lS@iW<(pkJ~UK$wt`!DNE$o!M2kbxM>uoR`7wCxKEB6?!T&kA?Rru6gC7sH(`j$eEQ;37E9FXp1$Z5o?MzxykA<%`Md4FRNS>nJ zZVF;`d@=KHiq~)k((&dldujQ(;K&^x-TolEVk8oBx?y$6B8gm`UcJ1Kt+ulgt_YEF zP%?fEFuCHO8fk4v}rs@tel)T-m3*E(}<^+|NRrQp~Fid79 z6#Y+UmJ;}kk#z|d8InivA6TW`<nK|rs?;yr~)Ld z(z(2{5|n}&nfAtWog%y=*(|k}WIkYWq7Q|Smh##ktW3x5eeF&M(d#`^M#?~%c&ajN z@Zs;G3PjcTd&BojzPcEmG}yPbhtxq#RRrLk7(>~DoYzK8m$np`gv=&i;dL9b8i>7} zO70RA!IZPLgNO>P?;Br159@9!uyAjc7MX*S|yv{ z;(X&_vO-1@tbw)Jv-kVuV|$ov=$-d-S>!HgGJJ(;rno#Iz_`w8#p(Veq^h_T&z8H=M_1(kx5;8l%5)#=gnm`8;@5(7(*+4r-ENtfq)2nFve z6aE-_xz&1bKW}uj7059lze%{OReoV8J%k4@$?*ObyMm3`M*ad3v8l*)zUCv=XW-9i zf=Ng=g{jCF6bd?KLlvm`uBWOm*suBOq}bk}eSwS6=^58|n0dCRps%(aau&8VR#v;P zxwM9@5bB<1&KVqHJ%HL(ul~oNz1hTfpl};-t%F$Dq~P_w2FOwMHFV}bl(~kR&gL+}z_j3PL4}W^6i~=1#Jnm6b!Ws6BeMRjua1G*Y zX$Z1s(`mWYmBZdv9zmswsr_b?${ukrEo02!OOJ1*&JzJz`Pm@yX9oXyEEfG0ni*&c z+vRn>3}s2{G^|MnvoItX8YgZr{E9)U73)~A=x>zwBI`4)e))n`4r%|&Wae)E;Q4cf zn)9hB0}wQG!uQI8*Z{4Z*Xz*EJM9#WCIH98d~xsebyHG(;>}>ly-ysTEzCsyhBFn` zPiT;R#HQ!5#ZA{gcZLKA=|htO21AbBA5Wi17Isgv?PN?5U49~h*GR@BVG@*}JA2mZ zzOe^h*uNEf`v7Q4fia3Am^YIrG%?D}e%g5jX3QjQCm^HZB4iRiHTQ|glP&hZw#`q2 zoPlW&iV2V3)(Le^AGUXSmY~lgd4)&p{oJ<5ZWG9Q0%ei1k^{EhDMxrENU}-bDPj4R zI|JCa%J6Yo9Vm;`&J`VL2u0s^f%Wq|199=rqon0hfp3W!dT)~|Ej7tmeaAJz zC6lCB-%l{8Aj`v1?>VziZ^MV@(%C;=V_-Ek-lL_BtVsdk`@g)*)$jSAOL4}9X@AIfVbsJtH<}%f zTg(@=dtpQ0&4I}r$Ns*;R9;6%hu3la8!&gADZaw3Xt1Y;ikTUr)ATOQc&hreP_yaF z3r5$-NDwN$Tba!bNjxhitOWI8CSmHNnVS*)<-D4|W2PgmCr&dtqoAj9jkj~|2b^fv zLmhwVRzpABk0v`S zb9-$#ma z`}~~fZftABF>D7zW^ExKYwqWwE9Y@D0?#U9(!Scgq-pI&7P5t~6 z-Rm>hhjPB&{GlbYSy0I1AIy+jz6zK1Ewg&1?*6Z#8CD3#ZGI`9izj^|Jc<<_lg6u* z57ssYPxHv$r%Cdt!iKM_Vh^0@!x%Kmy7!l--ohk^8~?i<*XvfH(0~xxksE-gmlq^U ziO;KU(@ITK6A8uizucYI_mn#Z3)}v4|EE?G%IVwohiIpY(4HB zBQs=#tJ>4(_GU$6(Y+88Qc#zYga2eaH#q}y|HI^69VtvV=+=SHLjko`>Bl4lsa}t# zzioLm$np(R+dFC9IV` zk6Tx(0%cGRi{EDOhdaKf$p@m)mictvA?P!Psi~4BD4YAIvGUOvq9s`ahE zfk@BDwDU{J#9CUVpWGEgI)Vi{96k1!OTC9^&r zc6!F@k{?7mld5%vo#DgMzm%@lcM)gS9-GIs_$jq^^X6LE?2(U&YSz22PJt``!uh{V zc(D|c!DmwQNk&%Gzs0T0DpqId9qH!vM7)l2AfC?W%z*n)z*^5-N|UB~Rg1iDF30m? zrbg9*Uwp#LpxApSL8-_AOq7Qk-I%JP`?xpr6569>lK77d?5@)ifM|aVgjOXk)|Uwwb?=DwYxP7kyP%aM6srSVimaDm!x-`2I8h&=oV z>w9mQ0xqgSeN^(TBWY(U^u16Wst%na-yOgv0Y+h z45+PJ`)C}DGF^3b{}ljKWieI3H;A|7d&NI;i0*5>8d2Chpljuo=QRiUdWL(ic4w0G z2wggC<b=_=4nSxG-RjG^WlnQ_;=QYdakUWjFAq} zy3P8F9RF>3C7u~6a5?JvpMaSIGQJfy>-)UYWt;@(n60F?dI9g<+8r`dwn#*R`;a|C zk3ykDDu%ECb(zgGGvkcHZvleI@&hS4obD8(&82w^%t+@=#dtCY zIXR7%p_GiCdrW%Rz{8lkqfUXLV^NQ8wuepri~f4cNf`e9XJVL5KSC+a=^_@V0-qa+ zz%CfSUpUT9WwX0k>wiX_FEyqxQ=NV)c(iXwjp-A=M&|jDTqvA}vN1wftn2tfGkx5_ z`_e@KSsL!5>jY>9ghYD_xV|@bVmTUr&eWKyWonWbT93JlH{kii}b_r>E60j z)(@6+GU4?}GQjgkv}VT1va6A?pz}dwO}jzP#HiM!1IfSZ+bZGL`lfTOcNiLKW-)_? zc;!(TI7@fg(lbv9vN$&8D$b?M*>^VzZNdj$wzai|&RvOZ`3ynsUuyU)K)%*5||cL1{tub0}K{p)y6 zr#=3tGnm?wF_c|e+gW=PvcOzgvV?E&tTd*5bpc&t2MgL1EBe);Lj2YeEiqxD;gmR< zu;S(zTehK>n8nU@rwXP`m*(E;(72Lw(^=griw|8sBjyfc%yJ&B76Nv{9RtGhTh8%x z!}Kvq@^Q-Iw26Pj&Peh*h`mY2jk)}vtPeb^ir-luJZ1C!BWscg4@xfGh6@(L=9eF^ zYBgHo&2Q*wsiF5ghfaI{gvSTz_~)y$MqpF%1n!c-%^7-M^=4+yzDjK$dci0Zv`HhN z$+yLa9;)&AVu)iz1lz1dRv7^|V9L|%+MX|TeiCkDc)Ljtv|Zi5X5mOsWErVgH0_(s zpq+uAvBf3c6eL|AW6I6j%!*wR&uN|fIL54{uKW!Zb@SE?@;E@Mdr_ot*~z3#+(QrQA)4_!$x!*sHIFEesOrAj+njX zkJEC=PjG7p&STpZG^Y4nc;HnNNBz$KR#vc9aPFFU%IH0@*nw2_(`;4p38lQVj?G(N zm7G%BOFUc681SeQve}7k{^p~3Xguue-2hay$^TSO5ZfP@qjn`g3|C>Siyx`3cin-| zdnGIN4_OGau}2o^n86hMcrAe-pqU@!`MP>!Zm@gHf)H=TASq?1sGY>SYQQ4&9G zBhP=bC-YRQeYHXW5}s~$IEUB0a)O;bcT=M@x3;S;#-U(idJ$p8Yi#@VdDi6rr+w;A z5-NftGsq~As?lG9GKxBdnN7r?vtD!2lj6Mkq@w{2Pp1w+*z6cmCfW9OM=AWj!X}?2 zv!2sEx&TjP^}k66dYdIOJ$K7{R|Ee|TU}3H|0P&TzI%A~_8uxK637O9d&%#Y3<4=g zN(O*EJ7xPFA3b<99Nltvnq|rNP4hJ}z~l@4!3nGvTBLP!o|g_UVK0f2%~!)E3EvY% zgH2_eQ`z3Sc@>J6@;J{Yp-jqV)1^1K5;6;Z{EwEMXL%|(iS@p69POkxSISg4pS+zb zy)0#J7^&#$2Z9?S*%+zMd0)pa*+;A4H=gz>tiRHSwjSNu?Y#LUHLj3fB=im z*P(s6b?j?6`RvY99|Oc4?+UY~zSD!$W?^WgY`weTmj~|rP#b5!NV=Ll>^KqP)gLT$ z(PqtdISc$P!^gN3K6$BlAKdn%dGobN&Y1OIo5>jRUln;pj!~BL6YdK8Z6-BqK5=J5 z^b1z)@U+TW>9fMCpsfmg;ikz#%?imCBYIE~2Hd^?pyM~N=~dW>gDkxSQ;%c7pGT1n zn+YQMBgOhEOVf|ISA9=ATAC*g3mFpN46TN@)n53YLEm6d-fMadXnH1z1C&?fwAL>ft8NRN(;Rr_BY-u;fLyKi4M!@xsAg z=7;jFuCs&0Vq;}qNA-|)LE`;QS$G3bfybyGrqlsX;)7-EJT<}q9bCkUqnml+JZA|rR(`cK znPx4B-=CcheR{c+<>^j^4L@dIQA+UM^YpV0XSxmY^8u|&<3z^v9%xSu`z(HBM`>1* z-?Gy4c;g8?#i4nh*6e&!&b?Bl=KEFHKhmAF< zJl(uan7>ePyMBNJ`}`}q)N@aT{War@OO;pD)~RJ)=qZxcoC&h~KVCymG2751!693X z0qk>vuyV_R%ny6H`jh05aDHa`gOBrpR)`VcKxe}9yR9~_Qz1V&Zu#`VkQX=a@)~T1 z_;L_O?P#Hlv6N?-iZ%7_*91QEzC9*mK=ae2A4Y$deL)mbGTsx{yZG7fpaC+5d-h!_ zVb0>i5T5X$jNuaN#|EdgFA)-h{Sfj3Oi_lBxjz{dQL@WdHb_{eZFBu81*6r`?lC?f z+-BX_8$nd)4|?6!efBbZ@HWxqhnVJ^9c_xK9qkCtB)@L1K+n=vRO%7JD@>fTO zq;rk*Hs7Ct&KU!&-5aL6A5hL^@Fq8>@o0#ABxZZ%Tf|=F;l}g1*;F<3(4lLw)0&7k zw(6#<)TthC!`03)#o;dikvY{GVupKjSG+zi#$b|mm0hGTtz4r{Ib z@wi%IRl6drOguiC(6uE>o~}PCkNq|r6?1Q?5kFlj9#?Yh^fXa9cWQ%fVc@H%Ai%A= z07m2vLK%nZhPMopx<}q&JVGLh-3Hx5KT);z%0Xs)Tf8V*G46zV>(X4Ae!oE4E?YxYPQA`F@9Y$cs zxOI*~dky-^4jS$L>iB1B{>MWi0~WsTxL(;&ph_=wh7+1b#S_w@8~iu@eR90?_I1vl zD42>Qj9j{N@HX}WzZwTSl%=ywoA1Loe5O8elLkOB|5#-~@DY*|5c#COqSePX8cNq= zvdeROC9YWKw%3apBRAI&fOfgz!i%ocH&&x=C%mOfYR4X=Q}NBmr!grkCrz6mL2eF! zo0G1yHU2|jNn|uOe}f9xO`>=0jJ-xJOAh>7V+jmJ2KD|@OO8d_RmsX_zD3;LjhKDa zS1C#;J<}H$L4zMxKB*qwJG79213y*@txbM)Itzfv(bT_3Og}y!eLsohd+(m*81|9i z_<#e%1vGB_b9pwgX9s{0bd;x^teNk1>N>1%7rW9rc!R%DqPmX$4CO4d0+-KZ>m=58 z$gPE|a|r{RcoZv{aFfceg-?A1D> zAwWsmUVT{9K|kH=a@taFD@%e!AyZH$WPi@iTKk&CmfK0D_;nd^7f&8=X*`&~JCYb2 z^Uw&!vHITiHkF}C@-;8ZrHc37Tk>A}`;n+Aqg=z-(GkaMgd9LuqbI#cIC34z(%b96 zhKBiqDobR-5s+LKzC(=XkMWzHkDHQ0l_(8|(Dj&xh!%cbcIKf^HKZ73NUVMLo4s zFj)Pj*JvXsyZ6I_0EqEiJCMUiZVIltIWh?$0`w$| zl6SfS=Z0H7-I^DlSQoK4Blvg2*QIeppLk4Bng&`RvVFKV=olkCKf83x`QvoXDy)B` zjz%dDny!vna(aa8F@>9L0*G`9-kZaWhzp)tz)G)9{3Vu!XWVkK{}ag8G5Np7#4;^& zZ6xAqtztbxymWcn;$WEY48K-a>IB5ulHQUK_@TmwqmYZNRhN~rFl|oxw2{Xg;YfVn z_zL&u#dMR(u3obN>2ztOg<=+ISaC-${TaTNV{0`NY@H22nJ>Z>yM2e#v5L0rSL8uS z(lAY7N43*JnR?ybfOz7OD^R26ggT$=aUl9BrMrZiB@BF^e~Zjq*|^?vc2l-Yf&nsZ z*Ai^Sq`B44!_?EXqz zN-9N`+9X@I-c_SWO|3-FhJqn(eqQI79Zfw5Ahm6=ta?I9YDMt`4@^DH-_(9GWtS0c z^+ceFxWkD*SMP{fz$e{Kb{aFm6q3si~3di(3P2-6n}0*GERFG50FOjcoS@|B^cBb)fFj(Ra*a{Cxf;cXg@WL+o$1TA!nbpL z=kSL=FKbZqwqkll9BBBO@`nw2g`Raac)ytj{1IcobdB(H8M+%rx`!CLVMwWa@O}0D z{_ee>`?>SanLo~F=A6CPUVH7ep7lJxj?MhTN>oG8xXj-2EiEk#P>l}>xo+ybxO_7C zLceZdHX2F2C-f?Czp0t1tviH7$m}t2mmieMANKwqSH4~m6QymKo{YYi_4vA56NoWh z)briUkyQKdKjj}*^kfLK6g>5N9vTS!u&xduUm_E)g=gDp@Y2F1V@FT+3p+Fv1E3## zU%2ZSy2X{@%UaTb32t02d!$M?Ia|aM8E;Q7>-2Ox5O9 z3rsWd)2L@DmH&>5J-^F%#27tC%9Nb~J8w zW^FhUQm?sb_bj1%_36~$o{z8M9HH~i65x*kPb3vgj<|;!%x+~@p39qiq9Nw}P z%c~S`_<91fabFbXOW#D)^X0>Qa+NWVoeSiAw>+%*2c;C}BYgO*KF+h1{Q5VEP~M-| zxo5olu2)RQ;ZWsEXp?IbTBBi-@+6MdT2JUyZR$;2|9z_+M!4A@b z<*{HCGn?oYD{>*-Sbftj=&(5 zaM;O>a{mkq;i_q|s4Yc?+kvWM}5-Oi8` z&rR(tHD|SzaaVdPp#=DGc!= z+a%x|2NV=J(QL{9A0J4Jn}Jf?XcdG4Z(Txf>li}|XRhJA%}h;R$_JIO<6 zvxv!g(T&DYs73mxP5>t#Q$)Kj@0Bvk2zk`(Nukv6%OT=+@5S(8eam&f7cPU9=NDupVtAcCt{;~1#xxw*AwUnb!2IDeU6^IP3o=d|MYkyGMY%k!{ z_P-0>Uzkk|@U+&@5rQ19DROft)slRx@Y<{T=p`jAR8i4WRr{_?Q%TS5l;;OI1L`Ux zs>8mK^Sh104gSOz)VPZiS<^;X^E2Z^tJqlwF8g zItM8yb>6A+erL$cbsuOMV#!k1T%+gOtxG3%16w~dyu5Z93B4gWHLQC?PVlBBMlzqfuBMog8v`Nz6?nquNX=THbpIXCJe1Sh&s34;|$Frm`Bu#eIlE9(1O~-5BaPgYt6vfiD6}u zJTue>E|`wHx2QO%^$?0DfDchIj*5uI5qtFtZT%A4FRA~*8um_?2Yez5sn*rB)KitM zuJMMcdk4unx`ZfPbb;5ABy0}PJBHcN`KHZ_djFMl&%DripXyEM0L`S9zn8E`aB-;P z*4`n?CK=u{7BD_J(jWa?O1JBxHD2YB_=t&_vZW_b|d3009*|5!Uw%2)JOfoD<2zExK1I zMl?*0S&ir$^O7!53+wAVpA&bIOgx;!!|UN&5O?Nte;%OPU6LEzL9#r6-}e)BH!TT^ zVGTQL)&s6#lT2V-NG(xfU4kgEQ(07NBD(q22`XX5?e31;C9bMU$5J~LN-Zu$Bmy?v)cv>?9PXtxi5d3w_H~` zHmK#|-VByhG=1 zc;unQKa{!gP~PLvbFWVjsx-N>_MIGa5bBV=AC`eS+H7Iu&K&-@ER^43@QIJZVM42`h%J1>#av*6Ig}LX(G=3`Jxo-_CN{&fmw1wSkqi z{-odjUipcE{{JVY3zAM00#<&z-{_F^_2IDmOhM=lcS2QQo(3)5N5o-v23Tk1+W|vb zquG62Dfzjz=N?^2uLC(9&jYV)JA@XV&;o#=med5JtK^uWO+RL78J4GcS}Cbd9v)VS zbuD!^T()&H3q%8LbMx#mlX_|WFxAp?-tXQ?*Wr(& z2lyJ|&r+&^)^ZE?N>0q7!=f@T&(oa8=M)@|;d$$gIPDh)h3NC)>^6L_Vb`(Lpb)q} zNYo?@Kx;jpzA}k2CD_bUT&O^LgTPm*FRzNNzi5IZdYm z4k1xKXq48@TZuZKF=3JzFU`($V6Il!`ZuKk04QaBVS+We_i9i^QQHTTu%V1JDE@N7 zBKYD6eS)uOy20rQ*Sby40i$3UdJVtuUX44CE1&PLkmbqD1aF3y+OV$<_b*~`#3gHa z;p{;R)I(z|hsLWZ>(tj>Uz0OLzNzr+<7#(be$<_R_#4}DT#-yn$M2avCyMo^#51is z(5rX3nYY4eDYt>sj&gX~yEDjJ5_P}Bfx0>7P0FQ$$rX%Dz{f}5qzf!`)lSVf`!{I% zZ$AO$0&I__H4I5|{?nE&1Tlh91uB175Z*J!;0y9rfDt@6oq_^6IjJAk*9I+NPG1^S z0Ah-~MB?z?UaRKv&a3#xNqht#bfE_yl}EWX><`h^lxrVYia(mT!vZ0;!I$XyQj>^n z>vf6Yt6F#5Tx1<4pL1Xc`J(IUvc0U~P=h!z%_agr={AJ>Ar3tsU;Luv z@Xl9xf3BfS$;v!1O0k&JfQVXR+UQ!$4)amg^Pn>+q_a;pir!4dX>%P75+L_yUFpxd zODZpkp`QsYDAyxesYn$fDO>Pw6TwoYS>4d|FALhB)PF=v{u^vy_kIFkkDlB2a*$EL z9bPL84G=GUX0qwogUkGt`ysI2QeI)qej|dY`S{{nmjFuJCwqkrZVaRikGbdkgWP6IV0+elKWo7#`tsitq zb|w((ire@0^1u+$4&6Bmg(gpVnNr5MhWvfBUs)0ifh3MI-U^Lenu#Qn4&_`&Br8In zP8wz>+r0Og5gq8U$vGMlCQMdC8V({kk+w;_v87*1I>iSIq!j94P1rZ7J~W56(>5Dy z37#pJ_I2&rcy>yKRi~SR$zziTUR}nUJG=kN3b(I~+sb;rdD$-6eW~>hSgb}h>^6N= z2)|ddj#u9aF4c9ig+2e*3VwfAnTXziJdd2NG-EjA_YPtUsTXSf{yFS$$|wx65`{ZF zEE@Y7tXbGGO2NWdLT@iTbqCk?rpUN;kGrgM&UK5ZQAYFd+&fs2h-mg&B>m|}uFzgh zZn3P7Llbh2KP<`7++G%A4^yNT>4C3)tuKH)M|G;0wfpIial0~qIk5h!mBsPrda|8b z)}B#smNR&>_tsW_(rxp)V^&_LHlZh2hk5Tk*0sr0#5A@gY7G$+Fv9Hyr^X?w&((_KXsDT!U+Vl^}fAbwrrA= zOGd=F&{A@Fg?$g|LE)=RQ;AQeEJeCIA`$nzLAy@JmU|6e$a9nVPk51xyB&XhYm*1L0O^_D1_TIknu3~rF5m$razzdo~Baj!jLZx6Ci7$cP(?`%^{&&+x zLWQkPSS$r#0m7plQ;;eq#9KQiXU=0yMjZZB>Dx%7s1J2*iYtsrdLG($TSNy?jx-E8 zU)?!C^%3|#nneF=Sj0g9DCcckYRuKa39)>lNRVcQDTl|={hR?^g*T08V~6)j@g25G zAAgUw@EJ61H!K6(joJg|b1>IiO&~h!LAkw799H_24jykfE@T)dLm#bF^ndy~?{Z>~ zP5^%q%?CXcp7{j#rwHIO7eDN62!8=lVdpe}hf(>5G8WDv;<0KcbNZ9gIIhx9@7L^h zRZxeRh&fO*i%E8f4$MZ^n7(9kl8ky-iw^6w*cW|;z*H-}7~P7%b;*8h!k!3;)8+~ z{p)aplzSniR!(SU;2=XInQ9bOnvGWEE{mXtw@I#tTe&56+856vE0BqCfAFW&6sYe! zo8fy=yIpk#&H|$rP94~sU{E)VeD0G-CCGO1ni8HvlPSceM)qioi ze=la|JjNeLG*x~%IIwM7Z|J7X_W0j9`{(;41|SjfIe2s!$hkTQP}(*}2>NU?y};eXI`_?!U>GwoaG;wij&xG7{yV6cz7dZYH#Nm7+V!~zT(lQgy9o?A5DJv!W^pRSERIWikYUIw+wK;}mKsMN)^S2(j;!Q}T z6myjUw0+e5pP)mrU!kM75WB1L7d}&2j^)WPV*Z5u71#TV`wh?ZxkVpR59DcX^N}RI zsT*wr&`^Baby$xFmDs;L-y zt#{s8t1-Tv^)lVLy_PXQ?F7SZZcHA!Epzq`%B(K_TD1glP~WC%Rk)>`ij7AcP)N$2!OYlWR-%nnRCaR|k0Hj{ z439mG8JHM^yS*RFJ=Y*?H-atscE) zt6C@ne>L#K=3`EtRF^N^I72ke4kJ1UwA3Tdq}XvQOMDr^>%8c0wY7uCEWd_Q)6F$U z@&V@<(Hr@}CcXuF}XtKo+jAE5H^V-qn48O9O~Cl&o`%uL19#Blw@JNtmDU9AboR#z!EJH z6dx95%dP~t`vS+!O-{DP*V0vb2M0S1?JDD0UWHK!mrh#_qzFz5JzM_JKFSq0HfHGe zYIVzqNy*>dtb;+fWgE~F9_G@|4oUORD$?gX@Ye)V%@CyO(0%snli9o#y#57hQNXdV zsyQz*1rClz&5fzDF6I$K6<(kJY8K9ir*<{*vmK22d90ECHY?(e*;(>}adTFbM)d1Dsmbbg!d$ai}T#sJ_bJoSy z!RP;RQt-vk9Gt@DX3JQ3dGn>-OP%ux(nV7CdwnPdP(_n3>f5>}Io(G5{lm(J-G5$l z7kxnH{322dQC%{^Sc!lY<*i^&Gt{tqZB|i>pq1(e1FC($`&^{yOmlxx|E&@Jd8n1Q z=qrdHGJ#FPX3G}ENhzk?P_FY`>DDDn*eU~x`^5y{GEm7nCn2rD7}vBmcJF*;s&E;(6cro>BqMqW$BQM8x?0M>cs+9iC*DB7JgsrSWaH%n^Q(0OHi za(qWt<#v;ebhciED&H;sE|*Wq4gC!nhOlpW>)3C3TiXAFl|c`$a;DnW*G9Q(t**DD z>SQDDO?~in?LBDqAL6N&^v*&Lvjnh4v8Kp*Fqy0}KDG2*XYu1GX(7W@hY)*LTzUNa3%5DsGZkh*Fv;IZk1(2 zOo5sG{ipVHGSYPWoMqRJg=UY+M}w$EVuSM3?)Q%-Fd?DBk^x2M1~&T7YJsG^v2cFYdB3 z(9N*an=G+AXl7Q{KS-5k&(?}F(XI``Y(;80(t?d`MKxJWj_xyuIictOJQ=^0j6lV3 zYpce{z)MKO7I%)luGbu!E6RS?`OA{vxIt4YzOYZpGpW+7;YqbY@0bSQaZ^AMFojrh zHcT){isD~NC;zuL-r${kMTxDkKhq08&7D#K!DZrg<%Mo_Q04ZH{Xz7Cz^FwXyn1op z@RK|1e?(%CquIC^`v~8+*FI1j>rIjpab$q@}mGUOzX1(Toxk2!$O3nyA#bfvYw>?D_bNzg6 zR7#Af(x2quitC5}U**~osW7T3OP;l%l)G~6?@z>ba!n1dTEVyu8Vx)9Pk`R`%a;u8 z#hl#Ss0cgBg&`(At=IyHV+z(kRZr@YnT*T12b{4gnG;GNz=q{=}S#sHuOy+^(chVdCpS5aj%OEj&2)8 zfZumnh~aL{>ENXZGD!%t0^2$7vgsz)>BD?O8=3d;Ne zWKVY^_JtiDrRV;?Ir3Ov%t|5BSQ3P$*JSoE3ctlYEM;)8gyOnGRt2k9vvZK$7vS}K zHH)3do{2Zyin$m_C<3gRpoe_5Ib5%C6VMRJ>m1)y|Kp%jp%*GNnQeSU`9F*T#f(7h z7$9p25I~oUD-(4Dq$Hq3BB$N^na*b8H4i<(OGZfQQs=eLAI`jUTV^L)AdA1-E}{|L zC7*D$0bUF^QE z;Zl%tdd{~lXVk}chj=QYb;l0pam{)wsD4heF~6&?dWs~pHL}|qIM8m=R$$21HFw6N zyL|`oJf+`hR_v^ajr};ZM1> zT1LUCB_l z`Hn)ZtM2haxw9O>DW`e)(&RIYvnZx%c3Y}prpNOXD$`P+bwD~Z!tcW zS`z4!k zFw`d}_e4~&e*}+HNpu*)rwT8P=?nuXqoNi!zQRp9qfk!DN?Ir3N?(LowbOJEC+jA zJUMe@D2;viWx3@xgw|A(8G`iUtFgPNZwi-F1hNKsWx63t?x)*5*$`RpYd*IHvi4!K z`W^-3(x`e5CcFLMCx(fmS1K|q8-;0Q-CdEXaTy`=J?SMGJ;Edwm=nL0%N!oLIM$~;|!l`4q$Og>vpld*KQB0bfhq5Ias;l=0EOnA`HE_B(}*~l3wyniKs zwDr8ZNqp~f(HmTS843Wqs#lXpqfx2hY8_Aez@B(a7lk@)fkyOWg?ilwUALjDG8 z`zZz>Y#z`fA^Abj`2-LLv@xL;%IL)S#~o0pu>;hp3_cL99{y(V%V7p4h>ZA z9R01C*bFj}=gUzc9n-7EzYbn!#sPe!otmAQlOL+J&jebtl02gmH8yZb@CoVZ2|CabKpJGu~_ z)xmbLxpKB4KH?F@E@IYb|6|=97Xu=*(1U6~)dmJ^e5BiS)Gc@ArQ8eH$a^>I$oZA&i!pIoM9$tM`l3f2so0sK4OIwgBtEhh_d+ z^#i09&$=(AtVs3JUr|d9PWlSQy9**C!W9I{$IbR6B4bS*O<13YQ)xP0#cMN>cYAMf z$5Tnpg4brn%u|a_4zqzBxug}v>}Z!{hG3?KRh1jsXv_%T>x6_^xkTIPP0xM1vUOEF z)trc_8sIJu;%kNLkpY~G04tCo6$EYdRSx722k2A#2!Q)(GOPLKSI~HYdQv~J?(fQh zgw84vWP;l@);SI0D`vwKJI6fo?w#~k!A=gio75i^iEOlo>FrE~-6s~A0$nCn^({hB z(r#Z9$sXaG6KKFN_cII|YH}jPxrtGyR^&VJKaTNDFhS0`q$J|6-!pl3T>!-`AUQDoVcl!hT4Ro4aYzToQQspQKyV z>^XXUVAtQ}f)e}_8!ZEo-Sg2T=_k{nUTO#J^W67&bQ+=Z@j2aeWSP9@Nr4nT2S@1A z?Wwn)S9G)Z$>`${MSAR2+&Y8)cv`C)BXU4U5E5D|u^?&h9s@q?1YIbpoBz6(^|)e1 zdji)HmfNY*1a+5+h{XWFfaQQQ-!0o#AOuQ{K!f9wea)ie4wIff>dI-R z`jZ�dL?%eo0n09eI#3C;_SEjN-FHr9KAAPa&fB=_r?=3-H1A-%&MCZlIgCk2XeH zm^mL~6f&kfzz*>d!X*A&iSiae!V6d(SbU&Db#?w;j{8AW(%nUze=|Np?9_Uikgde$ z7sPma7fC}{{&M&`*GcNf-UK&wJV6-|ROJxNTM_Mss^IC}6(nUG^-()sMA1HmCADVz zO-Xg+DuzAEqK&W;&McqjhxRQaGR7)h0IP(;!w8!}EdY~{i#WgRMClPI?|*#6n*K#& z1c`BcDIgRU`O-@zD`u)H)fHxM30aQsWClyGx0%3{~)*}NpY|_ z$KvJs%>l5p<{r@Mi_Yb8K!YZ7 z%HC*gA>YYWQn%&+`$ySMFxp-r zR^nD`Ct?c6<>E!()Tk5%R_`RZRqPK4scc`$w2? z=@;b}>;NoO1xfo8S;I@eZz^;&g4~o7}N{k3Gp{d+1C?nkhEN?HPrVfQYH2hgR1>>ahOrxF-zz(_pIPlP|Rmtzt36Q*k0cYmG6L!H9d z5RP>9)n3-)Wt-Xgd{P~&N+II6M(xO6#N;m;7G=zETkE!JvoF7y8)76j-gSR&Enc1W z(SED%N0BnF^`@(G=;Pwk?^_-yhi31pU#xV*)b}72RtbzAXR@L~GbJB+?l;ORqmYO9 z9Clg^$g#p` zjZ?d>f4;gIFMO{tKzCqh$~PUf2O{YVRo*2lZUv98D>wSrozwNpX| z1;fdTxW`$mkP@=~_jz)0tbPFj0Ofz`KXmf;b|#VbsFcUTml@KiwronP6(0Bn|C)@> zlc!&Fs-YVnbuv)~MJZp;%CHUBeloG#e7uuVJ-i{L-J=$&s3|4Ovq&i2C~otcE;u(V z3tU3epwsI4fup`_hp}O&#}H=#CWZFlFkfjNXsO~7zH7qzh#1b~{-{Tql%G=Ku}6K^ z&SGNTd`@Mx9+Ez|h;ts}8|_t1_IZsn{G|b>8!kX}#~5&&-6^#{3=j|lB*oo>Y^T|J z+hRhNCexJQhi}P4K9p5Sq9?A|umL|Wsd63alVDarIqz#Vo`0EP1j~K>5l@907f(hI zw0O$*)=M%Rb&>oH+<-Qv8eg&zdB{QV9n`FFH@pF7^fR~Z4_<02)l%bL=N&y_Io3tJ z8i!WIxg*U(pR2MD45qE1H^qz^DxBK<)h!r;+E+>Z!eM70Uw(f`Xerhv>2#(4!gt8K z`=G5mPn=S!@kA{5*Q4Sr66@*9P<^LP5Z*TTR@A!w^grRN;r#KlYSFL{lt18EBX2)3;5DK{$BJ8qaow2u(Y0NI_)pA z=yMh$r+cTWFB)Wv?RkUa73HW_qA5zdiQg2x?)wJh>eZP| zmUc>9EqXm`{POvay8v4d4kc&>XH~xQb>fM9K3fL5Blf!${rZC zQE^>f(6(&gZvEB~|B8fztrY+IcE5)HWI|MnI5y25rHUqc%PACs#{A&{EL?`GT)PH7 zM~VvK5*qhTLsPFSj6rL+uWajVCKBiNL>|;#@Q?xMKvWLy!*zH`lM$>owadAE zEb~?Dq;^tl@;jnU5QSruq!RmHAYGwGQ+RI)EOfI*B|)I+mY{dq+XB^m-aQkn@$W8L z%w54y3Z9W%ez(&Fopw^eVKy3dfPWFxZsT<)nuR4ubVO+EOrx$A`eB3)rL}aa$C9S2 zM-}cfl$v!gKgH>YT$`d0vvy^+^BQ_;SdFk;?2$bUWC7Uqtli;w;Yp9gJAyeZ$vr4? z72;Hw?bt$$N3TLU_^q&Zrtpov-heo?MKm=(N|V`k$d(b}yO zM(*~in8}RjF-_}DxP$RSd{0=i*WvhO>?Qa(_D74E)0L3#LMQ=+>HZ>>&|DLdbyg{; zw}yGvOH=ROi_dd&a}H?1ZpRy+2ThIk<{)e71C zZ5Ku)%`(e`6QUCLUG4?5=A(|ni)cE}u*0-2SeN?ewO$3|?MdiC+pQwfhi!oXO}3hx z4FxNM3GI@D6YE^jq)%jn4U@BrN#MkDY~DHDA{UpLQey^N&Iwd%8(cVu#^AbJ=xB@W z8S*1QZvqakRuFsq*&^_-TmY*x8Gw2X@f{*>lsbspNAXke2v|JI-$}R-^2MY)Zu8Oq z6^px9%AN0?z-vb6~^cT;g0100T%{Mo~Y5X7{zmOrV$kEo$O0@pk zEL;4glnp%owWTvAppRq&gAU6hu`y2O>3x*{X0y^0flbbXtv@5;XOtJt^?0h(vhHA?o={t8ou8Fe`$_!u$dw)j!q zL+zH&V>+eh$|cvrV|@!_aSg&_Wkr+i@uzUkiCupGvi!9XkibXC_Y9o%|;ud{qjrb?R{?mM}kUR^&-NcZbt{OI$4 z+7ggN)x3oWPn%}hKy#!73XSW=e-q3=)Tw?~$NWY%w`g>1pb#P`5e$x}?GR{+7&;4( z^%ty`ErZn7+MdljBPO(M2$}qniAAho4g5}*33m1zmr+m{_q_^QdTWPh5%^#_FpO8P zJ^2h%)4B;Kr$o=sv!R;!3CLOH`|HkN(2br7a*z_GHKa(T)zOU^y~fHEaR*Ooi1lCa z?AloJ-Qe9)r_I~fjEZ$8BE=e7C)FTUYla@=Ykz_IjCm1N(2Z=Yt>`sz0)XMuDPPOk zov8@`aCSNUF#va~J5N8%(DDbJgbkzb`5oR(1_$UNOjD!@B2WpEMOV-Bu6aoFb>3lN zxr#&os6DY^eZwV{YrSInW1LP;8i1Yii`v;mr1{1?^(jbt zwPWaRSVLYPYKD$HaI%ICtXT#2FkOqfIa3k8Z@jg;dfDQQ@$?=eqioK{c+4d&10vqN zm_E9O>E%c*tpt;|Il9`GhfaeT%q%uqrqdZW+zMD&bd6e&_1HBk%m<9$F|gU0n?+5- zm%Pa{e45h2y1_bEtPVr&(1>CX48c#xf3wcWL-GqBK5+oz@n*Q0e91QZz?*czV)ii^ zne4}p&nGG8JC`WDXU=(quOX-{_TeXT)yj4KqVb$NIDk!lp*Tltjv>X4_kdKVSc#^m zPue+78d%mJtydz;PeXA)0@&Ok0h%oXJc%p?C9-bYRfhnKoh#tq7ahnWXI1}B=moWfz zqs(zr#r1fj)fZPp!vKT-*)u=jB!iEK*9MRS#J{wNwfmx4w%q0($X=>z&&g1PM4y~Y z>3K3{-QB`#TDB3GJ&?qaFg2xNXk?Udb@*JsO>jUoYDbl4pMxh>y?$N{7GL#+I(EpY z%Xrq)(1-zXm^YNXT|Cqar9Qg&@p^^8_uVdiu>5H>{yQ4E^X3`=inryo8Ij?Ir~4PbB|1g7FZ)WR6$@n%YQuJSFE|{ z?62Jdt<6mRbYEr#c)AIn%TBijG!k}(?Ix!vcbzZuX=7Zd1cs>>>+}M;UBQF)0&ChX zOPMm*dn3^TruY&U{Kg>AYz>7hroTEWf`#p1IIGpsObfjt|I1Z!UaL=%&UiAmfWoHKu?6JOaK*rkY-?A^^2CqkHmi)|2wbL66uUOZHC6`CO27Alt zUTKf<{t<`!`!0_Gec$+dJu%A)8O(6PXUuS`+O!n(e zEGvARDc@rkG3MvzH{9M_TFzF7Pzk#p0Kl%Lot@yYFkB7}4nIFXcCVYOASw|wKD*`n zW@ctxYXzab(=qm_o{f!m4y?0dE1x|b&33-!vxf{;P{D_&oQEqwN|&dsNs7^w`d|f@ z@A|}K^@-2rX9o(q_p-Wm=`J^Apw7H+P3rB#D?l$s^}Pa?uJ_kM1uqW|`bRa&FWAAI z^mAnMZ*HY**cJ2n8@5&I=VbmwJpa9DM8W5KJ?s71k5e*lWn3z>lpT{P`(w+~b$;=Y z%Z2Xd&QN*A^9>ch4>~`p;AfscAXXEydpT?I6qLHCJRYmp$f(Io)D=qEf2S0`8msMc zu^ILw*w3Lmtf{POOxu7#am=$bCzK?QjVNDBxXnSLi>!o{%@7-j|sk6!OQ_fG;9y8lmM=> zpAb8i=kg%+Hs@zM{V#D)a2%eFo<_wfp$LV{bGJ`Uo{oMUFzQmwe-_l))_i+y6U(YA z3-A;xB=ML4Y#njj?i3Uh@){cPWfxoU*_hk~&*TG1YcVSy*T$9(@h2_`-cji1CY$0& zBO~Rd_r|c+QlC5-b3h(w(pgyNhmp^Q2ryJUzn?ruKH32DkDgVaW$DKhYg}|A!BfGV zEiTT5I_z^)Yf~zk65dm+<+ZjJ48?jZ5FDrH8IVhL!LXE`4xD!J$Yx^d(SZ86ku^gp zk=PS(>^Dy@)l}zWa(Pb!cIO4bZ_c?QG8S-e#xa4sbe2jhnf0FrMkRkjsQ<1yy@0v} zgY|vK-FDwI*ef|U(6{o_3Uipw^6hi=UsJ3>*MVU(p2s%gd0x|c(4Vo~pHP4MHdQ3Z zR#r~V(aq&Pq8(S1-C}~-bTH9`!u~BPD(Y|p*ue<#e*OHj=nL$<{sR4ui11RsTM@$6 zmsJYN$%DnU`R>r9borw%1k<3?uD4vED_ttj9E26hvHb zJo!w$)(DLf)>^8xp(9r*^2Vj4x{bdO9a_V%zt=I=f&H}9$t zdbx|Mc)crg<(He5(7JVPwn8v7Mgl#;Z*Aq zz6*Ct?nnc%n-1WrE*1Cm_R%v5q8BWcINOP{GK5hXcYoUy;#i@JRkO2oMmQHt$D}IR zy~VlYWj3m}6C>>FDOE2i9`z$HZvGiL{BO?je=aIf@dAz;wwidQQeiVp_to9fk6ne5 zO;3Mhfb2Ko@|kB;>ylyV&ckOp7PckN*HWDQ2K$=~PLK=Aq<)kwFB{6|jOt@Ix}LOI z@^E?GxEoEz?AJxehlS)wu<%v8Qe5l(8s}SXNZ20BjHUC90u5cNBXfP)8gGlVE9a(Q z0TKPePPEsY#~L#wk?uKC_Zsq|0UfJ(AIo(bQC-| zo|@e2Tk?*Rnu zLv5}{D!`eeFj@8d{L0cZ%$ewB=5={c^9zUIG&rx7(Pr3_!9NO`-y99RJ?OD2Ke#2PnvWzeIW^>A!?Su%p)!P*z~j z^Stg9`#ue=r%=7f0QXQ`#(4v!T*2B0P0MTqNOrj>;E2E8) z>2E9rVPH-W|JT#m@z zG^XMXg^W>54Alivo_R{sM^x%O@~0N-!<+~`kL^D~mf&xlyS{oiH(y42GKYM6I7mr* z(ZTm`;umP%Lo3)qDvv7ruQ>7MM;V+ydXBz2RhrB3mBC&NrARU69&c550g*Rp3AwbUT*GnaSnXaTeAe;2nAAoKq@U8owb|sodzs?Vytq#FaS{mD zX1pqsJIpsa4fE-j;HKCqSJVMcOb`A;T=hVJI#L#FzpW28z8Sqdx zuiJ?LDQD;5{VIy-R2dHCWJnZkf5>&*YtD=5uIgvZ<1EzFPSm`pkb4);uFqMO9)8Qn z{qf&JB?FwEPv7@g@)&U@1P}i83B(e6hsLkv=)vm=5si#RIxV5-h8p<70hCeBWNrmN z6+=Tw+7mZOctSO71HL2t`<@}P4+}#=4R#mrChJt+ls{qh6(TnH+3S82yqVwtB(LiZ zKWDEG3mkByzeb)9*xcWLeEL$BOwBE^f_+j?@ze#!{}h%HN&SYkqQTN_WyUa_#9JOy zF0yBQRiH{eX-J^b-yHAjen!5Ulq{1C+vTkq2V$misIMAuNvkn38=t$mnRHGcZm${5`pU*S zq@0}6r7t?L_;Y8B(_urzJN&IdZJbqSxu<|OjIW!q=e6W;6^O>z+OU6dIye8Lj;m)M z-yp@=dN=+^FJJA`Rj5_3Ky*yc-bQa0c@pIX+7j_-7(I)J9WRPT8P#o+ufw{%qgg_f z){i-gS*_gS5L)mWec^Q11DyR`F$#;+ujqkBBC~bqbGOv=!jF7FUC@TJsqGH+gJ16r zrwE7^=6wpQZ0v8DL`mKLMs>v`d#vxZ3B>?kx>ES+)vFCyV}02LOcW}erAk;TdTHrewm5D*=$#k*{U{#`=X;=cn(%(X6{|G~5~yY0bk9Smsg*)Nislf3;NsEu*LmRf3q|De zBPaDxYWoU$5-ED}{7L3HAM0m7zxz#|5Z^NOf1UKo(+3@*^kiPREs3&6ZJULZcPo^m z&|~wRlZ{NLR`Iz%p@r7dKqAk+u3@;P_{eAYh1H&7IkGwGsHF=&{8Wiejd%Rq zXI2xE-KA^U{1hVA+gLl~@5wl;nTF))_zifP8;x-bCBux`1DSeC?+rbCutRPgZve-i zK*a@Nb;r&uKud9_O!Aq7T}=RgZ*2AMOvBE(}#-7N;b(k9IzJ`ycvyT^`W4hJ4e3 z=7qt5fRo0c7aiNmsp<0~Ot2&Ly`H%QKZ*2xg#6SN42QDwg3{ zcz9_tb+dj;p$si#uz6XNkjbNju(2fX>%!E;QswMqbedZ43I0EBFdy@5UR(^NC2?Sn zn8ALdB832iqVWRO6mzUjA9ZR&SFwZ0O+s@eIbgw~QR zj?d^x^`1U3Cm6IZb$S+69heDaTgxLns72eiACE1u7k0&4#5LYGUgxUloii3Kk@;%e z^thi|kVWh@8BCK$V{dI0M{H;>t(O%-RH|HRjxW^>NKo$Tl(M*A!->i#4Er?PKjQ?fSx;qU>2=J(qm{3@jTWe+ zjOWWoTVnL0h$6$oTf)+Wr38)6I_*QZ|LxdOdEfA=q!qPi_Oa40Ya<)FXw`;KKWbff z%Ce*S_d(ZGifi}A+8Z;|_Z=~@EN{1siw{hzO|B^-@e57JD{3MW&=&nKt4!*Povfcs zE~05T710aMPFc`d>_0{lLYax)PQY*vejYpyrr?i^ zprtmN`iZ_VTJ+E}m*vN%k5q-Sbt6JAK!|6A$7a!rTQCqQbxfW?F`0&C(1}a$N9Mw` z2lM2q(Pw|7_3MOJB1tgP*6&N1E|x8g!HJ)K@-5!e-8j^>T=#OJp|jos{`#in z^e-4s`>f6m`y}BT2>pMX5hH#)_LD;SzXVajP=rB|1ErGIMs9dYvrz2!dY!AZvK9ZEk`(1lh6#A$Q zjmFLN|Ey(egFyeyYBvib`8F!CQf;#n&}7vYD-6+?3-XJ;d$8@Mc=@8jkT^RC7bI71 zKPylk6;zdFC|#5+R>`A6M$Q_oI@@Zqr_jgis%N_S&MQ%|8=Tkx(Iir$971)2(Y$My zOQ0AQ#Nob(moN9}-UyhxxazwJza9~*rQ}3%oNhUKZJ^ssjAe9eqe2|S)NIa1HL|6u zQLzo5BIPor7>BqyG$S34PePZVx8PwmBtfy=9}d&Rqt)y~&H8>6kq2`&p7-~(`SU6g zTl!TURto)9iSt+gxi^t<2)$la`8p8&(%D@kG=Ip8Thp>b7waRK;)aCV&$S%q^YgEq z2Q@{`vTK>uuTLM6t230Lrt29VliKWegtGy9?&Ze?t;cB9|SP;Aj1K-Pim0 z-69mJ+^Z&I;+ZN+*}Q!MdFiZ{tobzL{Sn9Q42iz!L=E-t2e^;?%iX%b>KEsq3RoozoCHe zfj76VWZivlaM=uNleS0ZrSsfgGe4wW%DNy`%4~}{>9g8AOpza4eyOx%;`^;A01*Q2 z1H&wnosw_;*W)hE2E^c#m?}a7sHSN&b$FkXvOBXYy*t<~mT?F{vCS)byVRa5+d4m% zSPI<{f4<8_Jt}OlxwuV(2le6+v?vb9zt+BQ(2|fy;Tjw5Ov^NTS1ROsPh_o%j7I&P zkA|6CVn~$)Gw>C(Ns&F!(5f_8Q%JU>9*&~bH&RU|TK+b{r1r#ITe4;FlC4ghz%C8* z3AD&PR#tVn&a>yMs{%!zY{~MTVjMh&6Wa2nD7qtX#BjC6Pz^~_NADK*KXtk=Dth#Y z`MIiB8%M~*66C^qk;g}IFdvEL@a`NWv4S7FJtXjk{joaQOzdD}CNNoul$r+-cKMBZ zFn_wjuuSgh)2DoPi}TZZkUYR>kffE2>-pBNO|7qJXC#a4)NFNPZ2m_Xx65Gd(tSo^ z-0Qgo<-yT}G)>CP6QNcdw?(DmcjiheRMlIAKM}F86|u-eN6S%8{{$B#EBw^9IgNbI zTpPN#seXfmu2gK}F1C8t^=7>U;Xdm&`6?OhPEok@w4*~pJEw2kuYTA4z40rivFy-Y zQdbNrs-8v6dBX5Ij30VLY`~nifZbsV8%=HEBm28w~SQ}=ReRSeA+5A|wryt83(C-bD&p60&&8G}vVjZv{5GhZ2wZVdMI0=aXjL0qYi^2a1S`Hs|^3A(tY%~q8?yU5q^ zyALR~!pNf&VN7!@FTb?94p7~vbQgTNU`tcK5OKOC`hi&DM$$aIA-=Jv1BYYk???Y* zK*X1T6p5;%I{k{|%=32V zJr5atn5s{9XJu^ChmFdhenHSr0M*|CwNkK@W;}ttRM(`GD)SJah{9X?{)_?`<$K>U zp!ACi%S@ZlaKnp7^XKPyV(G|jsSW2)y#IWG8U}y%r}bw86GZ+{ehjEf@>78dR)BP(!yLauY!pk{xqdIQW7MW0~&tjv0?hb45g zNqy}x&;42F{g)6g-bMl!;m-cCU#qF#K{L^#;r(kn7WFTdzE0gcrM(le;;sqKMAhX>2HQ{J*EW=PP@>I((rvsA+_er59I6=VO^xVKD+Z7V@_2^_LA7OT>sST*hC}YzGptqTkptmc zp}-bi#hbO~E%FeHh3r*3HC`CVLWgOXm!7|gYVLrM0q4yd3aGGD4nyE{lw1s zoEvg)TMe4KI;eEq(riC4|1F`TN)NmbAQxu_yJ?VV9u z)1-aH#fLlxo7S5@`YXEaD&5Q(S{J-^9*K#bNIKhDz}0s`E5xoh3M*$`Tm5RXQD7ql zo4uqx27jTI7}mfh#UC09uh7@9@TI*CbxF>oH@Hm-dLQNhay#kg&q-6Qa?^4t0lGMO z-&cd{+sZmoWZ zYFLIe;g!vw4$_Fgg`YZ*=@Ji>(Rkf|k@~qdOOJRtENBL8qO<%g;*{#%xHo0TCZ6ccq2SA+_!wLBK13c6uFajMt5rA7N8`L z%gH|;Dm2X2o9HEj6sgr#J~o^vPu5{&Iz*x{{jdu~x_r-tmaC0C-Qk3|CF>+4gnrmN zRhidKHwN4ye%}%VX}gP zUzZ!3ZsqbA^Nnb$6qckZB+jf0%pYR5D($1&7+0bMKG*n&Z(g;ZH?ucqS&vgfR2f%W zX}95gBl1w<&UBw4HQ2$zkSswj=-x+-N?cpxb5TgK5MUSlSnfOjOyiOL9%w_UMQ-Jv z$n6J(e6OFhB5~Lpnr@X2IWVN^D@ro>9FiueJ&fjL2JmB@g(f%f$jD)+r(wKfEoSc{ z?!oI3eYD2)Bk0;bFE>Q()3yn`BFi12D2CYO2u@CF8#=bna1U8PVDMC zZWavIY)H~sK-=%u)j{`NTJ!=ysv>x<`M3te9!|3!(V%kYc*^z`e| z9CX1O^(fDoMW^lafrvvr@wW|cCPCS+d*dgi7FPmfMOJpz2Kyw5xZZTm0N2-L?!GQ~ z^|tjJ9jeLYx*T#6`Pg~}a&>jo-FvoA*pcESAJ{QPM=#g6s2*-5Td%0w_81-l=ugJ=2#v@UDaPMl1j{`K`Uyjhh%E|)YG>*X|7^&f zR_!Nj1?sScogc;8jn0J?+GUJwhxGgfB=$KDa4J1J_e$HKgN}(d3)MLn2Cmho86=Bw zpHEaLHCSt;0-?ji)KGVKqysHEJs4w@1b1@gh{098*i`CW8$EKr)$C`XNWIIuFI#{; z5E_D^h2c!c4g>dn#?`V1UXFM)SXGDo)wT;MuKIRN*?K6KI*m2ZI`q`4sES=qleWLR z{fadtv%;u!pV7Akc7AdUa$vjh!IX-C{L`i8-UlkkfG|7HcVeSK!x2_b--tBfx6-{a zEOST-*88DxG#<$p>Zdc5LvntFH0#miSIn6n)Y`{eH&HO1;8*H+c7+J)gHWvGH$){)GdQ@D4PY{?(%Mc7gdAHN!1PTC6(U= zzq!4M`Q-mylLLHa9w<)T)lT&Wr~GxQ-_g@%v2|@*E8{z%@}gf8!~+XAnF6SN78T98 zUlg#TGPd+1_M07$^}I!SPlu+q7rs+IrZJnty-qVr^F=PB?gl&Dqo=3m+We7PZz1gZ z=k^;@tUv@U7aP`H+NXbwTlWX)dGJ6Ol#fFxLP$=|`7uW}iY&E=TM(sD1{(DhX+Hh) zV=GBX&dV5f@bITV-n;wVkd+~>^ow*m(CxR5fyy1tC?J(WiID)9Z_VSXAN`YbV~ne~ z44rCl0s#Z)Lc+p$UG-PWKoelQv8GGmPXrQ`v18{goicuRypx5_9*A6$(Pej=T3?3* zu+!Zwv??y~Tw*21Heo66a>|4rQXwaCQ;e87mF*^WQCG=wb!r$V0eBTxqq;ui^D)H| z1i&0GN*B|7G`qF%A<3r`zFH0{uRke9wOo6=eX(%&jy(tFj-IY&BX-@Me<14h{{HjjjzM^xA5Ah4o?M`=UN}Z z&Rn!YIfe!C1bohUn|MnIiOs6)yv3TrQOisZ(9^_Hdiv7DFewu19!my=x4C|O3mXvQ z+tcd;gPcBGnoyaZf{JJ2V&x6`aKf;9#eIT@6bp{mx?WRA+({9DRel`k+t@k`3Saoo zQT^FW3^wBq-mnt%5Vc->gFpZlCUt2;b^ z@cdr2*&=rGyz>9`z+B8yy@uF_tQsV#|M@8XdA%}{UsxmolS*_#g89F++}~OX__x6r zU}OL9a|ry;PjESgh47D{q@ic7#)_C+z?$oSVr+X~%l&#VA)z!NfQ%U8H&9T(3<0L1 zng8dl{%$s4CIgiG3v26U!v7bg^^@v7Vg3*8o*1mRt>qyHS zGcq#j>+4BUISc;}3O!N!4=3CHKhmp?0hggfN(kIL3GHmU}6pn>1)K7Z9 zaLvE>U%;B?a^=5I`W(R3kW9Ztfu@3zd$bdSoY16ZCEMAbzMpV%vXE?uoc;*8$dnrQ zfM&E4i;F_uSPU=T+(Gy%a#jCBwZD=);P}{+kClztOY5jn^Y%x!>;5e8`5=Er76*?Q zLt`71GAR_T_dLCLY$z!IfkEGNu9y@&HfTQacvr^{j5qmvUF7xyNdua> zfMHJ4;|x-)4MbK2hlcV9gx$4A#NBR;*(yWrxnJr5Lu6b9EM;Tm1abc8NeUfe`$hz6 z8QwI=X^-pJ%&E7%7tJq5l%4fQT*n)SdFray#Tv4j-tt`jKj^t;VlhIaELiXL&)dBNu>M4UT|*5N zK>cSuIyxID($=*RN=sy*zTfRyB*Dn>2;hs#3ndDQuv$a}yB5&U3yVEYGDPc(L8=EH z$0j%=L4QudgegGXqI^lX&%nhQnNC5;9SD#&))W<=0nM#b6cf1)PoE+mpFSfcIUfqm zSsk$lyCo&*e|ApoXHI`ivAQi|AInQ!x;1k~O~~6$33tzWr0C=eUdc=@*dH~+gtf;E z?4r2)88=cgVFG;&mLj+>o&&oW(M-HCt7t?$BJ6UH@O7BgqfhT@Y$VNei{)u`{+^vR zO!J-5tbqMiD1+zgO0tU0Enpx5e_{&d)x%Ra1bhpx5j{_cuEk6VcbTCyt0OQ69x^ zPV+)Po9&n4DnFJk&#$?iOF^_&-~us$W8QFnzu$+W;QmvlVGPHItjGR`ga&jPYxoYn zK4Op3o0or+t?irpS~~m~16SMD9wr42n9GrKN62Uk6Zp8Cyo`R&u(X?|9G8oQD(iIj zA^w)f+gvH{;?woB29>Qz#hR+g?9Y)ugkyFs_~_-^zSI+4)3|OPO8Od`SREm*ZXQ?D zv^U^K>g3EfbcmUgQMW%GTAEC;RN*hVr1R9E)?hVWQs#YmBT_DAmsCB#MBuM(=6#t8QRUU* zu?bk&M|5#gap1V-%E40}l!IH`II6oHyb#c$=bp#2%12)0lTJd4u59&gp1eh?%d$O5RVG8Nv{p`6vuCG-){7_4$Z`kl&a>6 z_qfNv*?KEr#@eciO4W21#+mF$lhvs(w7vm|hrOLKmdZUoa;jupPXE0crCC-F)N10< zULjwWG+WM@;7P$hjMo-MiF@5&4%fAzMtMeCU$Df^$7`zAG6ypn9hLY{XA}DC*Lxb| znSOY*2isuw9(grLsX@zCC>FJ$_;GK)kfrsfoyVMEJ5EKem50ImbnQ9@Hy0#-<&kfN z2FM}W1e8uJK%l{m5}*+=Q6Z3kb#LxN=SS&CMgY>>uSztdqwc$HZ>d?3;g@&VP%n2v z%8ba|R`D12&F4{o(P1!y`GNZa5Qz=p&O6Q>n-f}G$t_=qzlV`8Qu{T@Cf`H4xZWk5 zm8mYsaFf3v(T$!Fjc!LavEXI-x`&3eX?3?6i2tA0nOEw1iRLX z=}cj-`~L@w=BLoK9%!c}#I@M^vs;`BOstN#tr(&N;cRv{1vz={wtokXbSC~C^2%w{ zjUS0B7#hF1!uy`t8^g)ui6ubX?@`JH9Wjw??+eZLbZwgDOOtO1HLb2t_lvhkY&_3o z5fS>k>c(JwMmnCU-jPmBFc4Dzz;K4^V;~l1sh-becBQAW*Z=vhJi31Du}ccfVt-^lGSA5)J`o>ur<{D6Rd?@v$QA;R zS;uk0PhdCe8>}D!ArMO^ls)Vw+p!-9lc545&oNfaNpy>lW4PmzTedLf=GP@F-4BAq z&fR?9eQI8u;vDY~ikSUyKcgLdLUhvQI46;KTc6Z$>m8A`a*bTfp=91>Hbg~@-l^=mBSIZW?|hK!WWmx2hi$ZfDn%P0|# z<_KJ46wF1w+50zFG<_^ZmiK2An zPp-+6n1w16Y*ZuPD4z=XD9Ys{wM&Ly2@2!jqY<-PZ(xVuW?Ouf?xbb_|lREbR4 z@0(#k9?&KaYb6D`C86$ZfRZ3m5eM~3R0lJ|grzomjuBaG_QzRWHk{+SnFoKn?q0Ke z-B+M0I8&=(qV&$8gO`9EX&Zz2obCJuV6<8)hN8U0eWKQLtJ z*&D2yGrg$SY=^6@o?nH?duRsM{5UV|8kH$`Jk>{t+O4=5?S<5?l)mv4_SE3w`5nj| z({JF)^PFF@j>o}KCWlt6y=e0~Zp_4tD%9c``sH4VlSQxK6pt6{;&VpIX()B0r*o8g zr+O-HbR%u+jKp8!r>oFih6$Be%4_Xu?-r@q(?kwM4L=g?!_?|cJr)n*MQ%Q_Z|2AU zj%;Y?X9|#muyPJd z)&*l6q2cS`q>s2Kt7RV7oPuLST1@GP?TYXj3oPGMZ(YY6$R`EMr&6kZUXk=S)NMLd z)W2RU(et<3wqgj?&k$~H`-FY7XH-l^C&(aEd;i5Jp=)<$! zL^)TY6*+_7s-nWMREg6iibRu~lR1Ma{i(temx(9hb;t;9lOHWSO2u4?y72m}dzgnE zy-Cy2-8(s-C3f&8an5t-4IRQFk<~CKRF|mU((C?9*LgvB3Qpi#kG>30n47@^P3OHmqJql;m&%yY9tyL1y^EYe1N8!lPd@cdGVK zqg8U4sM&j)&YBvzbPeIa{q0-$`VhJ=Y0#Xu+)_H`rABBdGG?E+gFi}TZyzglhz*%H zd1`WvuNsTu{eU+0WBIwdp%oX< z@eTRNN$=--Y>KrgHI|c5R|>jbLDx1MNn-26?HY%{Mb|WxV|m^4dm}9^v7c5$%i#Ia zOOu#$YF^-rB)5x4+#|KOE}2L3SYcn;pIhCyR&l@Ab2Kn^-=>2Hxv(yQ^px&%n=GwW z7hw9`--x8_{-C{dbS9ePZoFdv@xyWjdX^B~W72XMZdbvPP>_Hx7;eIJq(1ZUkJPr| zs>HCJ+UYzRqn>~FSt>VVn+HTuYIyJwl+@(l{IECgtJ1}E&vB3Yq%`>JIyT(fHvT9;H%pw@v`=%PJ} z0X?d9Tf4r?yV1Ge!xmz*Ya?H3tgj%{}RAMQCy0(q(r8;>p+=^Av zSFm-%#|O?EfYRPo&L5##vl*RgSDdU~u#dDD>Yb4#KD%toYp zBMu2ZvzK05p?X}`xG?uv?%hU6@?s+4Uh5>5eNaZv?Z%?r$TGtZZi8k9Z-wiX{Q5>=^ zn;)`|GD>Nd{H4o%r{9d(kvOaoyGp=(M-Rm|Zz1=dvp$|;BJmwO5~Djj!lys{F*EMC zn?a9~+ds9^HME$~i47w(bk+&GACS9oa`tprurp9val2nsf0lB;uYMyO5aU9D+Fe$! zvGeTV-)g<#)R}b`rM}wcxf8CRwHkn}*KwsN>gvoe#-uw2OKtMWt8D5$>+Ly$0(XLN zsWmp8I$LFMBxS^qTGp=Rx+WGAmxw5@HX{Lb15$;0-^-{?4VCnv9eS*8p_K=C3Tu9wN!RP=*B1yBb5)Oz zK!yC!>x*V;bjR&V)pJQaK|7H+6O$0Yr37nHb#FG&hZwb^gW$gG$5oLIozvba3Zr^U zz&E(w!6wskee%(x6>M7aCL~+;Nml)k6r-t6K@D`P98}(Rq`n(dS(aPr;n(L{jm1b2 zf9gQxS*q_#NrSNWCR!(J&Vbgp#`}>deTtt2WLeS3tf)oKp0){)PCL!oi1o2579?LS zJyo|u0>rvL{ef>HO(1U#-rtMjN_h?_G4dTxjmXd$>YOo4O{w(;A`=;-)9mxWQK;M0 zlgaN?o(xO0-X6RR^rDi_jI}xIeryoaU29U&OybLV?&ZN*-VLsh#IVO&gB>whxV_aw zk%y()!8JuG)X{Zsm+%z~4gU7aYdkNI8Y=bt>q||X^xkKGqO_j&{TTgFqKxb>e7w#UiUrIN7-od?>Go7i@Vr^13j_0RWq_0TK9CPk=)y=Px2UL6`GOv{?b9*vM)?~PaMUzg&gYY(8YK4( zCeksduPb~T;B|L00-@|AaX(y76qX6A$zD>ZvF8;9RH;(j=ncZ_i@w_l9TgMD6&pL8 zK?_*7T|vcheW-(r5tb_^6o2Dl>G$ao@meryG%j_f$Gt)*ezbF4S?vo5+E-+YPbVMv z^-MI4Snr9kAAV-6rN${UKa|yFmUG%Vby<0KkfcY1XdLTBWt(d?TT&;OwvQ55@*W|n z73U8c+$U!8qOia9y?Ep*a!(^mvyl`A&X8R%|(rt^|Fp-MULL<#0Yr|CY>-I|O0w6MjRGx>C>H6XoKipi`%>0N#d%$)gge?q;{N6p%D?HjVW`dQs@ru`Tj%sPm$41CtA;ky>+H&`e zT@Ky>-1Bb6u;0?qJls9lp`F1sIGkcm6}y~tn0**218t%qD5lPa*5(UtMF71GC z9DF+zLy}#oFT-df54C!}SEZP{)O zfe{!y%sn4_^n-ZlzhrY0hgi`(z?JAY%6M8&3NL!4t|M^Ne(&+{b=VRP8T{|b<}r5@ z3uqeMr~qvVi34(B;x~!)@g1J;LCohP|n9_F~uBqKTB$(N#Wf;iEyq1nY{y@JU=s~4OXT*1?bvd1b#$?sM@rk zTJKZYWV%^oYH*NPmyyc_kp~etjofej8U8KL$0f|Gf=->!YzRH#Oqe!=y+IdC zk>Spr;xnJ!9NIeFD;7+eObp^B*s1$x+;s9O z|4wID1~1u83EjN$aYtyLcNCyCZ{V(ZOz*9Uvcv#{%v9yZDm?2Q9v)6)@M?BkSI{WY zdSjhXtkl!VU@feJlrMs|L_TnOuWM^;`1TcEEg)TcHfIpvmMmr~8`yS@KO)AT*17^% zrDto&J#khVrPh64ppfF!bl#idQ+_0yt^BV)7wfUlL4+b1seW-0$J5A)2ctd$hO@o zna>BfIW3LHdE7!1dXQf~P9nm#ruH>2pk52E(EYvEt+AL~Zejf@oW>QPg&IG#NfbI4 zkH|Aa>Rt<%jjG^8-9)#Dj(^pP@P7R$;uO?#GS{h`OD#xYXig?O1+G}9>}qL=cbdK# z+u?zPl_s0+hl2*j34~sa+uqqT0Iw{-iiSUGlr#{P)M6MS8}WFqkW%zv$K6>rP%At4 zw^`Yxu8V$smja$Ftj>8Gw{ugs&Mh$;Ef?4oAz(H6# zTZ__eXB`yNC>R;FA^FW}TN{T`pb1B;|Hy8$f}5GL7_#)SnN5vgs$#22P)I*A^Ml)^ zm(=CdKW^&6w?9=5pB6zaAAT{HMLqL(x5&CBJ5lW2{Ig3C*Uf9PwG^U=Qb(jZzcOhA zT{zMlJ?ysm19csRXcx9?A&YVfUx#r;bcSuQ>C4x@l#d)YQum5e4;Gpro15V4%aaIw z&D#H6%h>an(T6uh#l`8u1a?=(OD*Unb=&j94coPA?$if#5?Q%OWmeOWZ;`vt?ZutC zqbde}37d}O`#wz4B|}yCrY3p9o&HLquJ3tDS;-8$2W|1Ht{(N*kw6v1YPF|$_PYA! zr)G1m9UfAHPO{G?B0_ULWXAiO*>`jQ>toq{A?UmRp|MyIdjpIm+$V<+J82 zCNA#azLA1)@Z0ztzbs)c_DH{Z`GT=FB173#C)r@|9Q(6!o6&ql z?C}}NjhIdU>!KRpo=PJNF7x9{$jM^(P5-}Mvoe_j4xAr%i~Il~!Vi~u3Dd{kBC|k9 zeTXEl8|)l}O>O7~#|wmQTVPRd(jTA9&v=y|K^fxG()89>Okz%A+ALYBoLeR)3gUqme|oYzj^1!r}*B+0C*<&*7S!j_~`nW0!-{^|KXBT zVqonMfO+NNH=$U)DlzlDV*MP?ak>w% z?e#*j>S7Lk7L@~QlCt-_OQM#{C|-R0E^N!n9HAMk+5Z{fOApBAG=BT$9}*w1K}I>% zwX6GT)3REqn6Bp;xn0H>rQBZ>6<57}f`fz8v#^j31X7STz2)=u^#zi~IjC zeuTZV6*f&1iz-*FU+nug~%(=pRfze7aqob>kKfnPFhr8-WubGBbI zYMA@}P@d)wkzYs9J5z|_xL$}-Dfjwr_83#tgDq@RrpoiGa(OsI{HisQn;vbK<;1yb z3LjShf6PJ-Q=EZIR7q@@%`YWs_UTB1w->K1edhIwklf#Tz04f&t*b@+jfQZFNQ{lm zK7#(Bl{~rb1lQxkE*7dwtxz0=8|?;O6E`kg95i_~0SaVb4)&5ipdT6MsHPhVNBzsK za}Fykn+;xS;g#A03=1STT{R+{G@fupFoY@`1T zu5xq$j;+^2NEdz0pyk~gfz)J4-_{Dn+`wYxC#f124YN0|%FmQP@D(X`E#uABS^G8J z3mu<)06)@d6@8QD$#36Xr%&Hvi=ACo6<=Mdn3NhVH&o30J1dts4iLO#01}y3l7SSdQaD4OZmxCy?GnTXtUAzn_fs+t#8ozY-*1kLq<$djZ8lXJ22) zPs}_~k3T!Axq1hqR%|0lpezi#HkM}x_0EpF5{Vmn`-O$EhssBJWWikl} z4u1JpZ7}eEV9hULO}{@;@nWVv3`G+S4Diu!L@HS^RYM*e-|w zi3Z_A{2E3^G&Zx<-2bD}M)=oWB@dbZ6v=59C0ms>1OkJw0Ha|AjGI?GCHjf~8~Aaz zYfskyt?E$=_$l^L>x#eQ-DcMRx#x+eu{lQ#x=))|Ofl2J?on%7y1xyxaZ*EdBU)xL z9IF2e=MX-}8~^}reJx&I zcg2C^tKeW7+Eq${48F+v;A52+5eeDNJ3d~CFBU`Ss|o+FedGtmhXMMSzkP9D%5>u< zi3gHapLmMNUTdhe*$DTXg3(ak75j4+$yP;+T`Gmd)yJAAfA;K^9iVdO`&IXfY;rdv z1?R4#c+O$8aFG_gGLf*GixgeEG%}fb*p5}M2_83l%BC+t8K}u?a=V?xy)+hPl^;<6 z5rccw*s%wZfE|(Hg*}b3;~N87jT}jGsW162W=VK?0iDf>j98}>?rD}TxulAqvUG0Inpr`d zjde52dQ7@Ox@LAG^%BFU#E697N~qk%-dwJ!o1Are4Jl3{tnMCx2CkvbqfEX?@{Nd2 z@U)wZzQyPy7J<<*W`4&~IO3rbDUW}2kX?7%dt#2^p2g5twvvhd9=%37zC1Cmo1|QB zZH3|nHAsW*3!}(x5Wf^!(M%M0R0B8;J6!{l2U|H&es{wTs`iSo)LV=@bDaMl7UTzz zy#QKQo}&sNnPp0=Rra=bW7p;Z&A)}?;o*0PBW$yhvnGP(Y8L82Mv}JJmMY$yZYs)7 zJdgu|Cvx8w=1Dcm_vjxq(5tDb{aii-3}Z?>n3ylqM)R5Ch!~=yUsSTM1Y{wPe!(!2 zOar??2ju0nhNHqZx%l(RUHS*n#cYkX$ayc@4p+4^qMtnLQAL>9RD0D$#qo! z=CR#i8aS3ZVJ_0;GKwwZ#L$4!%Y*@D#&Bpl6aTmXaW|jiB4&M{b-hj1Ex)$nHoIu+ zLIE&ObJp^n)-aiGsLeHcUDEa#94!YTbK;3Y6kE0NWv?B?`%Z=*Mo@8hc0vqg~3=AoO@z(;BGy}SjzC8ES?x+%CKB~>w~T-;j( zr`^B*TWl45zg!G!y88bs3nEuoRLiW!Qvz%JI+bqbNk*=d$CG%>$avcp+6^0!Z27SF zrHfNe!=DKk|LdOS1;|_Du0ZUb4OTfG*-C|-^fYCg=F|^ITLW|<*85>iRrk17R$pc= zi)L=DxBBEsk)N4-FA*diWx%6$1HtJu((O-dWx&3z-%j0v?Dsu)N zEMLAir&)|AcCz)L);mS-=nM5=x@ucEB=1$s8l84Q(dqBIsT}{OILnJzUM-&3`|O%L zZe)vOw$>#wJbhl4VpagqDe5kgj1hr&xaEWGGW6s?#WgCjE7ml;Tq74BUU;CvVFX6y zE(xLPitH1OXyK1W-{m%$lV|&};`+sn9A)syf%!&RQYQ}(+V|n_eSCrj=zfO|GB14e z@eA9)(3xhl>x3j5EV=dn^6VeQOVFdc6Y`dh2B@2~F3&)0moCIXRakx~mAGXq;L;$Q zuD4APkGGL}_f*n^p6ckNRA{e#f85K&L`@Sx(9q*-8PXkoK>1Yqn>w5PAVxbcS188~ zleVAAe=t*}T{RUA$U+QPwn@CBd2$Iuse3om}h?G zBcNZ~_d3LK;)d_riMCCeSnHnN3GkH08_S`-y>g_JJr3g*Y>k)AY^tf|Ib9t)jSQw) zEo7MHU{FQLX}H<#+zqgM_9OMxfOYObJ|T)xkl*2G-Gsy=I5k1s>Kr z9Ps+@t^NT^&~M9iGY>EBj!V=hY8*t@I0>kVU*3Z&8dQ(bEZ!al=p9BUz85IK9jQyBN4 z*9nJpV(dy<`VQ;zvYRT`#eOVnj{muShs&=D(lHPEQ~lrFJ?$mGzOfOTm z60(;Ge;}JUD>dX=;4A(os4Al&ggpSH?wbFPs?nR8P^V92QReviGPfMC>1{7Xy*W?e zO1Xdd{E1)F{&c0fe2Qei+Q-OlbcID+3b+}2jqQ%V$3Vd~tbg+RqhDz{M#*Q)Z+3{D z8{KkTT)tabwH6gnoHe-7srSoKR6KWj8!^K2N2FMtwPCua9!GeYx44&(kULu*I6akO(Id zYWV^5muc%+iUK_+>W=!tkt=*tMx+Pz*wjx(l_L)s;`Ulng}8QgE8P+)ZFB|U9s?1p zDUVk8te5SFyib_>u+#=rSjK9xBB)x^dz8Jn=X}wLC{)*uLJu!@tHd9A5xuOW!kOwT zJol(|av>iXb`L#>c(WvE;_>z>d2$(rFSDofh{)cNvH}YXl{E1s%|N>lYmcs7(c4_Q zi!12q@5k$BSG2r#hW=TWro6(c9kUr^ziviMHz(}%r5y8b=mZ8lhB?;2-QO0;ts z%Z$hDsG;7r&*M|XEvGI>skTRxkvKJV8#1riVsY43!>}`ti`5W+s$VtihNd)VV84et z`kLZ!>Q}34Ze$PNgU$0}w|jYauyAk~+1LgFi~0U+O&B(LQ5Wx3k<8|BddN@B_LSa? z{B^4bZ)#m)pg>ie59ntAaXFWlb@U076_M~Al{$RUTnwN?x^AIYnr~%@m`AP@s&};n z#DmL6Qzg?@tXZq8<7pfblvgZS-UBlAs2+W~!K?&;=dWucfRqp^1KWT9N3vqP@6rrC zqBVIawJwtZP-Tw1zfkqCyXwA2?bHEwi)3q9BSxOcLj+WOlaNgEt+uf!=5P{W*c9YU zW_JLLhDe?Q)bw4oTWI9O3r~kLt+dwMhiiLya%IsTJn(h9ORWxtmo1C@zJbLIjQpnA zKi#v-mn8UEzwn~*?=GLMJ$@Fnq=1Mu;xanM$?ZPk=G(C)F<>?aDskB$Wc?$DX$(M( zneuULC#!Y6Gq2!6cKl%7)AB7YHENO=*>~qr0=|f^=mrDwxh*TLElPjN~WBh3df zEc>6dCe)_?e_GR5C#~DCZ*(NLmXL<5AZUyG3aJ)pcgw4nq*T$;x1 zb$@;-E|sharL4N%i3Aajh!;qkeh+11o9Wb@PT6&4?5XfJ62pQkEfawY7e{!Z!=77s zTL5Ndf0p}ZVDa0jca&$*8bO&+;5frf9N3T1HPV7Cy~66AtS*IN*8IfW#1CbxlUnfCVEOYmkn_Wd_E%9oKdBx z@cT>Y`1+``O`V2g@*k0=hb2x>)Z7&cTqgcbAZzi=KHu&dx~Zf4f$1bp^~dZfG2s{+ zEWsF`>r*!a@6N{M73-fO&RP%EVa6FBYwX*_ZlQUONRzK(iD#!o-`%$|y!y1Dt5%T!$FKZvBo>@pC}U~kfS&xb2G>xaU_})B zz4_VN1sG;s!r3_Xu7`Yw{>$^foZI!R(M}``n!lZ`+?6>$^0kiw#6oC7=*+| zg+`^SHl&uh7GuBhp*1}%f*HiBbZnbS?SkM!Yr;3&saSNp(a?9qDT7uAitK*#rr(K} z)T}*)2I`K}dJE zbdGfA&REkhS z-_IJN+X;{5DJ%1z6AazK!r>E+$AuRG*VF(YFw|*wa$car6hJHJ`32Pqk}5A2ei>42 z$9xqZ%;aN{RPIP99;m{CDlgPIor=6K9@|iyQ|=bKll{RG{<+MF`=dqm2xmWDiM_ik zXOB^$z7zSKW%%U9^c_AlYUh^bAP2rjzpz{Duw+7C5MqM%1OtCUpJcWHjWcbw0y%mj zI-o@k|fuYR*%5UD*a-#&@a2<$U6VO!o+Yq8s(Tx@Ot z73Z(VaaV4a(Ngpt{c0tKgH2!ggdO*@m5IzDuTp+IzrYi5@xkwDVlFnmP=2?&&?cBG zy?|&S`{}vcyKiei)Y2Dzv=z`!HkC5B+p&61wcc%`beg>#|4K#xs1Hmd~yy zG_dqjU8cd+cUlzlZFNi;Gl9hWgXZF|V%2CS?l@?f(Pi$jr6daQjchZtS?bDyl2^O& zKRAx5_5B*?4pxgl_Oj(BAe^aQ?u@^zxgu#ml;OIUL{j6L$$vVi!m(vq_P0 zd~Y)Uy12G6O}P{(A1~a{PT<=`_F+jnAoMTW^@vWh*XbI8NJ|xBSs80|=+Iw#oh2Rn zEjW`L-RDKaadqOR5$is_5ii;q&V(SQf*E}N&N#arzalHTlSa{zkn6t|I(=QzM@6J| zPxdxct<_pm+Ur)W)x0^H(quT2B!2;v23Gn$9u%M$KT25I-FotN9opDN`Xqt+%Rel{ z>YFkbo**u?41F3O;lgyfbLzRSLN43tRlH%M5+qMh)1A?x32JG`w*Vubi`dwXMPhsY zHf#gyi^ERlrDsRotrDUL?pHO`7u>~;*5sq%+te9dkJmsDSV(cwagP|G9=bjt;pV+b zTsRx4+L)EFxjSK4C;LZ)5}hCN@x2$CTR%a#_xeLmLEmdP!Q_|r2L#!x5cme4-7Q;I zf)>^veIj)QG54*Rb=KcLz~Dczt7}|FKt4ql^6~4g6bide?n z8YZvR{VrfKUc86jO>EO>8j2TxrUF`nWXUz0Gi}YpKc94fw(rryFZFCGcmd z`N!&9*{6Al?RF@0U-FJeg$a%Bwuer~^$+7{JS7zo;zU0?M=)0$7bk8Yi4NRodQ;X) zyl@w9=}zg1$W%Z;7+wSIx;Y_MlTGlUW8ArEf^3&Q;T>B#zux16p9G!B&jeQ8>GP9s zIr&gUkolG@$jlp*)Lsw48Ct)locF!CcPku&TQo=zkE&mfgnk*6j#H zjaoD?x?ZZck4LIaqwRcpTtp!5=TDkVL1)fbzz)@q;}JSa6qV&Pd>y%0MgQ$Ckg?c< z)$<#!fAOtY9qoIEY42^NcPszh5J#?ezj6W#z*F28go|rMLjJIkqK{0z>;s)f#Khz! zkd3ER%r?yMns`%7HwnEDLvL2x#TsVtszdLMO4Nj9?vUTU-PR1;zx%w}w8~9eHH(_T zY9Rf_a2KLWcqT*;1&39vPMMS~0n=op zGI#IQ>O2cb-E-9beD$*S##UKX;XVU{zg-Ug*HQ;!v>+5>p?oUN@OILH^+8HR=Q^s#%~1fW3lHlg&kBVw)a4jBG|l0KZ) z3q`@*(K~?D-TWvNyytB$y^Bfii5ubIYAOutiR)is+r=u&Dc4!?W9ed0wk4_x$Q9{K%|R zN{<8vk&Pn=&9WS?-l8u+IuK!7#mW?elaxC5EHo&UQeGRKV=A;d<|EIwTZORe51!4c ztxor4YEwi7-kVS2k5pOKecfuaCxbhDBCI{2+a6YG-#$Ik_j}KnIth08O>(u>8=qmv zHzM?qM`$TXY&~~4o(S`lnVHL+h4*w0J}Wc5y4E9<6!j-K4TAs-D%w8mH-cX}qB3$s zBRFL3us>9`;Oj&d0{&n@n>RqjhtNYHIpH7hw3Ax;7wA``QPIaYVhOVKgb=K)O8Wkv za)j^*)|fjjQsknv8mOJOhT^-t|7^uq?qs4JiaIcUwg%BRoRYtKC#n%w3Dr7UBfjaX zjg#VrF@KkmV)1(r5*pU19-9R?hNq|7+9bTO0Jh0M4u&dq)rJseejES;d)hwOOn4H4 z9)ee5Mr+n~)7V>*V^Ma4ulutN8os*9BNXF2{3aETltQO_P~^#*SBA-@sO<$vdkZVd zc$1tk+dNuZhYy3Tm$7RlqPZF#hF6WOH=1;602m{j$buVx?@si@$J4?*hqImsh-c-` zb!_zZ5N&!%(wk&LqNKd8ao&8P1vYpugP7Zz#*^iVr<%Oj83UU-)sUBm4^i75cI}Tc zE{z}R5oSlnTS|AKkwzy%el+G$G^*cvt>1O+H(kJJBX09o&KSMb`#GQcc zB^vZsvcrcSx66b=ly$KxsN-QT|L$3x(W zO(X9A=u&+8R;}(uwaDen%;1|2*S;F^!GW-J!M+|Qy<1cK2ttX$Eb0@LR(DS{vluZq zp=4`1j4q3`L3P)v6+_S3?eFY1Vo%8*1t8DlHSb>SjK+2jpbdA=+JoN@lNW?GoA33j zRQY)ZWV=%b=||1DyLLVx_ig^<7Nq4oXK3mogZrHzs$;woezt7(Aj@q|@}OXmR5Q!Q znwEbe{Qfl$nl`n-ZMO{rd3CWz8bx^DXLa{4FYvsoL?Svu*TJui1$F*eS4ThNbJ_U$ zWma-fX=!8VTW9v0UsMf8nhB4|U8Z%W>;7toW`}|nks{8-&xErZ(H;IWikX?_8fc3z z6W3bPH%1S{s7G&w5SH&Ap$hl|TUOq?Y>yN80U;h$J+}fNQ(+kyJH6ZS-zuF4j>+z$ z`9Xq=0V69>#m01$lGe9gs2KDB=!nROvky#v&=JvZdVf>6FDSOSV}HX5dVO#!SV~Ca zlsukQlK$mzmyP@Mf{YNE_j~RD;I(7ANBQ*rH72W*%eaViUFu;0X~_t}Ij`1~UZ}=A zqq!D(_9wRY^i5>YSIEc7sgr!}RgHDLyc^&rKU8BHytul2FGi|B{YiDsU#1v`1P%iW zwCu{S(udzvNZEA_=ZXF$a;a5f{Pad+`1Zq|ryM$b>SW`9?`bI(ws%TNw}-cErp|tH zi~5JZ$hZUvk>Bexe&@|0%#xT|AMk~gk&7|xT4htYG#(g9* z(I4Eofd8v^P*gMn;hi&ok{sWF^NSdk>YD5zdhDJvntEplsE4 zjaqQxWQbn2;jOe%YeauBD#{z(4nx2tzgnGmFiAZ^B1JF34qW3lzcn7tr#k`%)d0C; ze0nH%`;#&uEWM|-qsluyl;4mB&3)-~-u)Feuq!6?J`(ihQq2^Zp>u*63N$z4NmYI} zUZ}RAZCutYxeP1TTN1>v=irjUWpEo{kGLvJY}i3Ia{#hd0??DxDnolSK^l;OA6K0b z)^4PEYm2d-*6L-t*F0-rZ6{dk3rc`Iu-XsUtQd$y+)!O8iG-UyyP3yCqpFu?cD`Tyh(?VOY@sE$uX+RF?=C*4iU8(ZEJO`PMi zu<(VTO?bI-HOGQ|;|E{QZSk|XzQ&Z*MJ((y-G`S%bmn24%zmhAIE~J4t%ga;NSz`g3#kP@$=W z^36hwjX5NK{DaW4$>ulr7VPr%$;?FoW_Nx9P?1;nJ~kWvnzQ*FC*sl$|EjAv=gm!V z^sfl7y92(t8C*U^X3b>;(Wc5?e$ro?6Mk%P7b|LYnVkTUp$g0$!s~Tmp|MP`MfI?w zI?pBh_ctkxJ{+j@X$=_VFeff7WAH%5#=}&IJm~oXD(Ern?jx2cxe@t$I)@Xr%538l zP(|2Ko-CH#?a#d7?L-SJ_D?zvUi@L#4Lz`Mv^sQ;(N4G&{nbWqqm>2w{NuC_g&D=s z3`)G9|7Os9f3~+GfxW)Sa5PmQVmwDz*g6|4Hc^~A2lqa7Q;aV#VGasXQ^*M!pNwWO zo@0+mfPdTP!0n(R%_%hB)^Ot9Pl3~pi^s}nuta&KZ0D%w>7gY*3Yr-r3}!(u-X?_@M?W%~!VOm3iwKI*(2U0;P%4 z&Ruozcpy99A*IbrmkK@InOk}PsghP6eE;L>kQZp}LoUznRI)Myy}4KnR~x>SuGn@02)0P5_Xw5%8w;qSTR_y->GVJJO{*O z=F9DePS)Sw7fY{HC$2@+@DPNy>2d$X+9fE~TEu{*5)AZ)yTx5p3w0t{V?<7Eg$8Cl z)fnI{CYHm7fL^P1TR=8pmZi)1R-TQL8% zmx!kBDJ6ddmQv)XaUHN0n7NcvsrLHh^tj3-47h3aA4;SK-bI~}o*V4ldDPk2ap#|H zhC+B2S{d+(QCcOVmFo=2O^*euwMATikCqUDuFlHzLfvO?N8qTiwa;NaCvowi;pkxPadVfJSKKv zS-XF*v`zncG85qI03MKE)#T&=vogC~5OJGlXmynNpYi`S1#SODJaP;c1zKVN8K{Q< zA0bJuiEE66$S*1kBKDrrnnQIP=*{?EZXZjw>}_u;R3{reL9wN`3b zt@g$j=#>7L=J;j7P))r|@XEhWT;DEVanJL2DBx6En4F(>oR|58al zF*W^KTHER9=9c5Sa|k*f*UWl6YM0FS9sDyRDOpJo@FQ62qj(`yNo@sc>(9cia&pWr z%o|K>yKYM7`_wtcRiZx^xu@=U@@@MwMbhQwt>uJ7gV>Sb-!xtQ=vhbg*L5fiWm(`R z`!~G6`xq2By$9N15~@se!a}`T+T__ z>r*CvZ75crsS`^T#k<^Q6LS5}7y<*SDGul#7_0rBzumb)BO(B`c1JT(i5`Lj3B3KJ z=B5|dO=-O?RP5-CJ;UcsG#^+qIrfa@X>Et(QF>)f{g*N=VQVoGc6Pc!X=;j?p_u8x z?w)GHHkc+`;_*`H&KZ=sap782D!IA<(4|EJ9gSaxdh zf#@RjW%|bOFrH;dN#=DcEYyRcBP0I&f(C#-fU6sN{oN2!E~sDqK(%C*ov32C_Aaw2PqO;2Kj=>Ds; zzJDsO#>C0^wx>(NGR=AYX_EtGHRoGgyylEnJs9%K$9$8506EEwrA8j| zgSNxrQ+}7@1*@OsD<@4WfXY&L(V^}6e3ED+vovS*j?;g{WQ;j_A-!zAUPk+apJ)zv z2_>}0B~&xAzQ4M-r=hzKcTDiUFv{5V3eJaW+j~i}ayY3;Rh>(Kzq`HT0mNT>SCu8#g>v6N=Pjh=Qp8(X87 z&M(q(+OtNIrEY}P^Z8s1TYuk83MVW2#t$i^22b9|eF?4qy~_SsMm~K~sQiX@IEABlAK6SwT!fa#c8mm$&69N2UEJ1EwovB!_w|7=`n#&VMy_MH4xjSq6NVlw5nxesvq+(S=|3_@4`eR@$`A1o-eW5uw z|6os{kRd@4Ga>@msUw-N6O^)_eDjHQzeyH>KzRc$4N-Upy z)HgTP1DsOH$QYC2Pl91Jvdv&ku@ugdLfhDV^YY9>=wM4*i@9m(T52X zIIW=@@`riBj8ojKDer~LVn))nfGh3ZxtrWEL5Hh*Zb3RYrk;^;V`$IFs}>xzMyI#5 zDbn;|4`6lnXfs&pf~_4pH8vfGDCQ~q!}@3{51jV-;cmSRmRSeO;Za0HPB_x%Oe z7g4ScChi_fr8hEV>mqLEdRJGXl>zG(XP>V1wYcteqWO+FkSznA{XzFqVrgUT(HK<3 zrrs)7@7>VL<#VUiJ;rn5H_+HRS8T?;2nm<;b%8yF{5w1$eXEy$xXpPK>+cbcOK`Z# zTOq>#i5BkN%S{N&)y-v}I9r5cMyb9lTkg^h1!#NafR`?y)8_xh1zlO8Otmih&~wQF zj9yY=a%pwurcrx)`+l)vsDfZbbzje92l%jur{XH2Y>j^XDMMO+PfoXp$Uby-xQvj+ zaq40xv|Tx3-B>EhH%yRU?r3@0d&TtYwM(?KOZVLQ=t;##GquY>3ix$L>d5+0AD{~1 zp7Oa$C~lflO)g*3pirA(^0_gky?N&(cI4{-5F50cuD0~5XjDzEOC@cb+~?HhFN7f= zs%{$Oqe{#2!P(uUM@?BY6DTo^TEvyB1dms30LjNFUmF5`x#qSlMoRXX8O-Y=@>k>F z$Ge72-r&HV1DsN>4r+@qpXiPY$5u_~_JjHaTnMzfoOS{BTGjF_(*0KKjXB;_lm5FG zlSQ$L=CSC9kkML~KUt4o`*-~(! zR=KBp^?j>+0BCt!3b2|DpEG?P;A1H&&$|*x)sLV6^xuSfP~2HlT2Dh@rysc_n7`ukugcgf z?(M2L`1tOL_YR2KoStI@KGo9Y>cqjC&S+l$^l9EpSK?+nz}pn?p?4b*vSaZ~2A-DU}L2je>& zla$tEf)DQbs_*7nKbwBF`c#l@I$ts@Af&`TP|4L- zQK3>_s~eYWon@deDEw?FRJX%jh0h9&pR@|?oI7wb*2yL(aO_HP$Mxf&oy$e_g-QaW(NGWJF99us3=HCQmZ`N19P~x z8esDX60u&Qb7l|9y}AEnO_{~?oe@MMD=+V=TbF=w+SCDK*6TI(Jrf)5RtZxP6Oi(^ zwzI0v>?8JVpS;F-gBtdq_UY#M|1OO;gv|G~{X8#(Dh{!`AE5S^Ozre5_uVXNv!H8MmG1INnU@7Gtqb!g zX(=fc?Cndyoa;ki0W>u<*xlWC(ivy&ZOAuFDs$*+c9#vXhzn$TBx*B~7BSk&`kllZ zMA_A^2GPA5QzsuxY2V+bhOd=@*6&MQZxT5oo_Dc+QuPKI&-SYY8(fhoQ4E?6?{8b_ zo8EOdy0|;CUc8t($=o2$2rN`Ud~3nCH#9iP?~2|3c2L@1G?^C0Tb1#RSbz?fz}|~} zk;;)`k7k`gDnpwwbvfydw0`#2{$4k)<-GoF7-G%KBu>U+GuH4;mN^hD-it1?he>uo zn|u!&^tx{vZD?5>lWCWf6Tk@{1m`I8X$^P8hrgF%jWGL6oX70uoId}7mtSaTpHF}b zrC49-l+-!$zjTgxJ8Nab{l)MiLcqqD`v`~9d=AE}RYNXC`);te!^ci!{K;7@apA;u zKd2`{;FaDX;qW-MxaRwpJ66QYk(Lkls>dX>F4D9N>89u96VF?czg=ElVajoC^tJr3 zMPd5z60*g*8yf5$x=Zg0yZ#{p18cm1x%Z9go!THrjfs~4Ev=`1>M#|MR`cJwq$MLC z8Bw<%Esy4S4B(mrunG7Ats8)zxO+4-*7y@TCMG7%JJVy7385yiwv>3;@!G1`T&DV_ zFTT#AJOquYtn)fQrfqQ)RAI~uZ-2}UN99k?bz>BJw>MC{y)|D z;KYpd3{*oLtAFn2xz3)oSbGWVVZC*vT5;&(xU`(Ua>mUIn$mpqwyF=TYn$~_=-|+~ z_$TPGXGY};s@{fGBR03&l7>uD>S5RU(P6tm>>odQ^*RLL`xQ!FmZ8k|^yxE8Bfi8P zH~I3VT}3AD+-`A}b#1MSTrp3P>oXA{{E)Tvu0+w@LuQH`YH3Rs+gVaSNOMDXzGAA# zYsBxY2e5Bbmth;o)~EitSvCIwg;vERi@z;mITyI!Bw%Qj^Z4G=B9sE_AYR*iJt#vJe%w39?!K4!KJ& z8}MqV!XLg)QcqrDbwictPk(%;Id61Ee^)`F^$m_h>OFcgxdV-m#ZGC#AB{TwOF2e9 zU0^a{-b4R<0yw)GV85&GUh#ErpOwl({x(IOfZ%|~#6kq7I{wuB3V(FA{5hJKng}X;fj_S0wzCIrvzITqY96GU9WH)-aQ5a9)`;6*P!6 z2Y+xoa`~VqOV$xmMHG~9cGC!08d0mz$_J_GpYn$j+7WbC6Q5!{y11RvBt7NGv!7FwC8dW`UZP#f;3i4=7Y$y?a#G5d`(JlHUfc;?_5bprKiL^Mnb7g`qz$)% zmh4%uTZShQvzIQuXwr0Tx5fHZ**;h5}wW^HdYK{dXLY+;Er*im` z2`XQ8?e0GaNpNC;(p4MN4qyv{2_NPxZ&zB0PfR*w^DZk)GLrbs^b;?n6<6T7H^ zh>3%QlY~0G$uo*y1YaqC>%)Oq2?tb+>WDUp>9I=GC8Y zS|b$_N{*4{&GNHC?;!h7(oz(2PB>moyR*#k8t<6PRz>@cbVJ{M#n8|7w{mX2P!BiX zjzbfNh@JW3B)5^LyU0VZYw=yX;tMLb5mTx9iCUge*$ zaq?~Kl@$85rUS3sj5t4i?A>d+O<}Y9bH#`6F9nE~n;-ZzAFvoMj!Y7*>rFnWe4?X6 zX;AMf7`XZ@=3LZhN93^-%a(zYv|Zy3wQqf;q{L$9@m za`P^d>0&O9HWLJkU)FPul=tq-J6g|El^Hk%dP?-RQ$Uw;qum_5my%Q`z`!Sxua)iI z(*}rW2(W#O)%8HbtN?Xh?QFrZ-7%e4if_(@hwd4s4RWK+mQ>u%-na`NFA*<9+kdbU zk?8KCAih=aFaL;$KI2+I_>4k?{Vq+W&o!C2>PkQ^{lx4I`Cc(yNF=M2&%M|>tzF3P#FoMiB%QD4Md4|3%~zdi{)jtGGdYNd;}fl* zW?>cw+vcjXvlJMMWS#E*>zN=z#&gm=!?)hJ9wuxIODg+F$GJ$VJ@R`Msgx>=GNz`r zvV_@f?X8&A`(65H3v9yt37;cAgr`0#@8&ZjF?=q=iSfr#m`hELM5=^8ocba|QmAEa0@~seIppD}HnD4os{Xc`wr=I?y+389Ak06{;9ZLx zLuYP2p3N?XCrzJ297GLbtEK&;WpJgmzgoGh&Ly;nGEe08AhvaKiX#|HMz^;@)kY!u z;zd0E;arwo9>QNxKUr~iji!^>iACcGiz&w+F3#UiTEo~|&l@HV?rXi8@2aV4_vuff z!5s1WNm-OWUA}UT-a?^dC7*KRNxM@g*jjFHn|k%yc?MqL(}K$F{bDCtH-VjmyPFNzEm9pCanU;jt@zUk)KVTtL=i035Vk2*0*jP#GXfa_?;LPSzO@C|IpJF+~Xg8frZ*vYiKmhKN2!lPkwi@h1CL( zB*Rq5Osi+W$^i00efegjFnK#E|5_4Jf$@S7JL%;*+Q%2Xe8zwxuHkAlOH*hJPCL>vQ zu%ZXAWh_A7&AO&GE4yfur!o-uOb59>!gi}V6gYM=hez(^98Tsh#4d$Z&>Vs0Ys$&i z=}x*%mAw&KO>TsDyqyW{=H~+Im?QcIa|>_GLvQG2Y4y)oy!jR)sea^(f7z5AL7jGU zpHVsS?z~3KrOWhL7yW3<@@0s8t9CekCVty`(XcG$uAjrMm>mD!i8!0s2Pehr46ux0 zmc916c+VBZvIVH8eJa)j=Y-MeYx@{~&5OzjwkXPu_R_m%MFJUV`d2=0<_DQLO zFzLyYgNQAezRruulVNH&oBd03g1t3797rfLQ9=<91ITr)KS4x&{8DbW$hqac=R(s5 ziP@k)i|(2FFfQ1Q6|GLymAe~@`{47GcQU)zB0lK{T;ohM*-;T^(qc*D4CLL@ZG-ab zF)k78`9P4&e*Q`rCaHGIOfSV-BxX%Ep35fF&akKH)TdoeK1B_rT8_;O>*0G#%YZI_ z201y+w3B>d$bOeJZ14NM3hO<*%)^VmSjG8W+Mk|mOR{8k3_RwHo`!mIFJr_rzJYRN z06?cA>w!_M$hpSQ+x-lYWWpu#!4q&oLETkb6#NuPnDRKKbWSUk zZMtEaLb@D&YO6J&mc=LzD#9OT+F?ZTTHXGnxa%KpzX3d3L}f`<4ChySIMd_k2)z8 zgdOc~e{2>ZbAngiFxOpmFvIE3N_mEG@NNV{lKkg-rjH05f>X~#S)(~GYo_f)hP=Ps zK17@GC#vgoWsYvqe_?or*MO8ulO-+o^FsfuiaIUmvvv}7fVt%BjJ#ll#545p|vYF zP7d}Ry#Mquy&ymJf_#4hu;Slew+}GVn;L7&A3C@$zf0h}tefrWdhkGSJb9qs%ZpO2 zsXSySNWG_jzzuav7QJ?gh10(%i?^)1yPv7=4ZqjW57i98^tg5duTvykVkPAg*Ua23 zCfU5h;>mw{gPAyq^w%KM9n_A^@(m{r1(B!(u?Uhw zh*fKr%1N3U;wZLo(1=tb#7bPD;iN%r<#?y{mTN@SMh+|>gk8Tg^j2Ss)P2B(SQ6K7 zvr!GoTEY63H;`Ek2Sbn7gCeHV__#x;8I$t2L`e+!G$uVpC;#rQ9;>B z+(6+~8b6Bi{=?|wL~;oP%NSmMQZMLkXQA_X8M=-5!+$TxA_4DA>YV8So;?E8gw=Sq~ zFfyIo$C-SfZtbN_ZnjKA9^O`@nsKuR?bZBCKt%hZcP%AO4?M7cj8zg-`s*~K;#Cdx zY2`D-fGdT{V_||s`e1PXi$gMa?R-G=JUmcrFl%@k)uI6AxQ7#4< zUH@po@=l*2^>J;Ya#`&9F*&tka|mwne9GgSL_s124@!GV>oNJ7Rw2}678W6BiL@JV zDU#`B{=g!IW~rd)%mTt7Wh&#}ku)d}KZHz0k6 z#C%>&H-SSA`uE7L3}wG>^qdu2Vknk;{atl`#}ahjqGKFSEeH7jcYwEC?Hj$0MZn${ zutMwlh4fTW=kQpZ`590Uo2-B7<}N`g;*IOOw;Z}T`qJWZNB5;>wU|bS!PEN}6CWkc&v3r~F7PXi zy}(*I1Ue3>GiUBkNA3j^MR{5=UPy;CdqYg#@1x>#`3_L}pG|Fakt6o+@J$$^kS8L@ zCL_M7(*4pw?N;R1yxBUI z7BHZu_F0L?x8>QiS}7B^qoQLY+qy`GQ%#L1$ABzi-5-2RNr|6JgYsBe{(7g&GWo#M zn%ef&qqREW$&+lxFO3^XRfFai)`1ZAopZ`O6)4ODc64idsx@qHbi!()6TeC`rddyM zc$m&Ib{Mw)mE(TU@Nq!b?YGA8vlxoW=I<;0_eA6Nhtf+Yk)@vdHg|lF@YV;bEWy%h zlU9_YiQ|1=Z}TD+E)ke@`g=yprxm;z^;a@J9v<>98rD9oboj$J)nPh2HDTO&Wd|L| zqorLMa;KXydV7}e4~3w^U4I?Iv~};Ud)~`6<)Tbi3r3Vzsr)8WvL<8e1qK>5H;L|$ z^1szKeU{&}p+bZD1VL-|{E$$6cEF$cnN-u)G=@gyp035Aojv@TYt|9`TGcBM zKmKP@Me6i(XBU3f-`90W5%~$B%clCWaO?=P&HlLbKa&Zhxa$V`&Rlw=9MNGw_)a4n z{HcTFcB%*(bdVx8L>GHUe8lPy^M`cu%LbsTWekBi-s$LDIEY=a4fGbN-hbQF`IyX= zB|olUs1k2ViYCxl zrRukB9JrNK9lymo?HH+ruVwn&afNzJB)W}Gq^P>(T*I^E_Od2WY}1&-&a)|Gf-tHA zlV-Enr+e?A5bp)aq@9Stx+;8H6k>&Zq^CUJdA&M>sb+`9Au6u~6uykf+v=(u+#JbKRN-31xCK;v z8Ul*G0A^wlT!&H_?pqJ9!6ydf4PG={jmpN-tvpK7tsW#QwZ!f?4k}A28>BtnY7!k9 zH%Yzqc!q0L35H1}PRof|Ug{NWu6Oyrf}*3NDJl)-x4ItH=N}xQb+5GhGn!xbYyQqM zt(dNGr3~;eT9&&xJxJCSQnv7=4m|yGg%`WY3;sIV-p&_F`z%O1ka&0;6q-0S`ATs% zSEF#@@oCpyxT3yz3BL~kXN}SDlcn5DX=(p(yG}=UU-nwm;{0dafd@gu=7X3iL8>wSvkm zt3Y3A%PQ+VLjfdCOjS(7)X!$}Lu6`yLA{)l({{GIC$%1j?qg1HX@Cb_6sV4gR8hz|PWYkE&Pv9~ z8M3f-oB1M|lk3FVM3mo@cY`m>?N!#{J3+_d{wEXWzZeNjQMZ| z(PQIW$XQ=j*7U3aC%=5WB{-frf1>U!u(JjeHXWazu-3#+y|{8c;`8xvOzQ&=tlGcs zu;A!!;*Ma1upafz!6!vH5*Qx}*&pRhc?W!dc%tg0k>)0NSR}uRs<7Zxp~Wp0&559- z$rq)mDsEofkByyjJjL|Ms1792_0#5mB6lp^g)%IF=!~FqCZ$r&q&-L#@!@eQ>k+Jq z*A-*+nU<&jS)u___H%<)>6AEJ=GYbcEpxZdF4T$>Y@?GUo zZ*vT0+F{sPr6i=i#jC+1Fgl6h_x{#85jh@$@vw4{dkEft?apLYNgFw7K)J zV-JTfhL)LeL zSc7M5M6!WS9P&;%ZO?9nDs+omMUm`TF|||+dSb@thlD!}eUtk1p+b%lIJXgxjH@Hg zDy#f<+#+}!b+_Z$Hkxw1>i81kkBmMSSC>}i&Gkalg69=ypsS4)S0Wng=vEtshj%oK z1bkE@7|t(LBCIzll(iGbxgN@+Tg}_lFmCGqUI?B(EmBBCN-9KiC|g;v6~3zcRc2UO zF(W47$am~twJg>7u2)?I=G6Wi57}elhRv-$ zD{6PtLt0<~L0USDl4+;9g-%7ryMGv z5|j%~1NNspA ze;O1>kqX|uN})QZa67m_pV1Z<#jUBPYOfr@SsdwWW_vwFN*n_F{-px72&Sg))cQE9 zxh`zxtvBKx#ji260NTR|9C^EIpCTG^_VRs|K)R@Z0u0$Meu+TA_aaX}xt`scjEFwb zD1A&8g9>6in3?rKYdyt=qHO9_XJlQrlcd-4Y1I{y*0Vw#jhVs z?VQwhiEMYyPII5?E5#u$ZE3phw9Ib#%x{wnq@T!yWZ=-elXJA(?&+uIjlIVW`Fo9I zBEc@3!>eVpxm#xr@pG|CIe^bPT7h2m;G`ylYdu};=B&#LYGQ~XXFg&2G7tptbI+mUSE(#T`y!L#$_*OX4Hrv9B~VNhw8<)WFkRig6oN$y=zj+S*8DhBAYR%` ztB#NaiS{wfaVJN`=#^T?gO+^v>LTEGdg2L9yoL ziSImxiL&>eH-v^d6T(vZqtAvpaG)2b57XQDSscu4mzoE3Iwj@1ZYMrHHXUpxf0ka4 zw<(l_gfpg%rD`aq@g;vtWCBb4+0gSR-b|*fdz_u@bgIVRkDYF0?=-v#}F z;0y{v{mYV@vGmmY8t(ir(`KE?r7H4-FCQGXHUCYeqNSJ1@){uOL!b zB4d5FGuFXGz1A>{2!~fUC+cG$3p8Wk6lG&kUiw#s3q_gY zQk|%akmT-u@)p zx|hf-1u5=SP?dD2ryjBBrlS(K-r5Udp?2Q$P@RZc*PJwSl|As~`(D-?Dn`mk`aj!`%k5}c}YFh-hC^Wkr0KNI-^%Pcn@u#j~es|snY z-0}KzXJ%SF|Gfh<1N6mOyejJv43i+$qLimUJU2TD|1&|+|I2n3kPAzmcI`4xg1V<4 z$He@{(=(5Z`G2&*z_crzEG^(eEumjD7FhhkSK>jj=7@hB{|AZjzw*bfkP>!UX`fk2 z;#>j1Y4w&d;3wRfeW!G0Ckb$0txOYN|E9 zw&%EnFa+pyc=PQZWMfFWMu*KBuJ5Wkqrv%yR0^EAxupY4IFKE1N0wzDij=XQ(e!v<9yIf6}(UCMypY7~XCy z-NNx#T3``g-l)aJM9WL!;J}D?+dtnANC4}}dI7&f8x(Nhe$)@nGJ-EX zkv101q6tj|;f3)zasx)=hUC)2(CaJM+tUaG<+9S>3Z=hC`{%8jITO`Oi9_~knODJ^=tn+DE~eH zfVUO(7q<>&5AnC2LdApjt6xO&zdN>)i}VSLCL%B~E_TJNq45-Vrr!ThzE@i%zG=f3 z5t9Nm7eM$nzdjqAF5SbdMVssvHS}cH(k2`DzXz-^#RjDOsDCys09eZs)}7P~lLNLi zK4FRN^LwQ9J#(kC3V5q&ApqVkHC`?O%toPd^e-{;pYea79zz_(lc^&7;MK*l9Qrt< zxy)x(Vy!==qo7tvC)PgZR?9%KmOru2U_71S*W zsgENV#=-CC^8t325t3&Yzs`S%U&^9Gc;Oxh1ar&RE<2l}puD?cL5Vdh6@KGGq2|>9 zxe!7sCGfn1`bvsN-6eupe!g*jDRR--)kSi%DlS2asd6C+L%^CI?AE(4H|q`bcG!{3 z^&QZ@at1nP8eHUg>CvQiD4Tw+Isji9dGlVENZ)*xaejhu^<&a0YNgqTm}Xssbpn0` zbJ_`$8O*OhaTGB|HaCWiEqM1)>`AGd)^)o6E}s7PkLYQ6+-{1pQ_rUPxc1VU4!)am zI}|fGRKjnT)V0IAFGAB#OaK@Ei~GG5w5?aP_v$5^bwV`>Lp$CF$-mZ(Tn9oxdp9@X zz*UJ`7#O&OghGK?(_&AaS!959Vmu|^HSW{;ZvKpWXVG;O-!qpg_a!>a#=5WS?1qIg z9p>^VN?D859vf}YOhGBMnZ=M3_v2{naLwfVZ=cJ`5mQn1Xc)0LV&8WX z1`)cn55C2ql)NEIOrL96*v0RH5tO8xE(B_Mog?ugATDo7=2N=L>=$8o! zd1Jc0Qmg@S&X8Z!uK!2bTSrCJhTX#hD2RZlNSBh*-K7H3prjz_(A`}Ef^X1C9h<9x0vgZ_t{ge zU0q!d)`tAo^Qwc0T~FY$hYUAlC4m8q=C+|GA|mRH;|Tut?HM@A{}VH2e%2Z_EiEbs z2glLI7}z+gPo>OQ3^*o?x*UL)R;kJ<41MI@Fy2Nhk*%pRv>Z^xZ}Umz5e99HwME*@ zJUyO5bQJ(ZnZj7^6eDOUW^srWLxua$at21nB}L{vxC!K=oKs>n$D`DQBwPl69=9BI zsW(AjRaaX)s3Ny&Cr$J=bOlZ(@6uk^%SUN-!+5RYO*A6+Dq5_6UVCzw zM^MI3pAsiJZn?NlT8&!dbkk93VIxzK*Ne*zpVC08!cPK4s0t`|rvj*)9d}4DqB=5+ ztzO*jRznd!K1eoeIQ7lyrF(mOt@RnzP4Wy5_oM8l?`AOYZpdwwV5R#y!hgClKja=R(R zqbzn|Ho;!DDZWs$&$5t{a$RUL!EQ^QxHL&jlWwtdevLbRyt~-rQvg4Rz7VV1f=9>; z|6sG%S2}^aFH;Wx(I(&8$~H`-puW(`VyxawQ`n<}$gY|3rv_PQXgT9W;JuMN9c^-; zIXvi)hhJ2()82b#JduJ3;+p#M_<}2*3kT}Mvb?u=#R8%D@8kvk7OEnK+=@9nU5A!i z16L%!Ge;J%qe%-z;B4d#TGT%?N$)8vqn4cW7UTow)TOqyq1B?YO zzq5U&SZn#TBMmzLiRSxjv$|8rXlj2vzf0`kU@@kua8^0|o|Smo2*p+CYa;i|N2c^L z@25xjn-5NDPAF`Vx4XZ-ptxaN$8My2x-nBj!I&pPo=&wH68z5_@nT zE`GLPIc2qF)1Xsp%@J#=%qxtu2>H(`efyVho=-zMI{3cDRh2a&ycC`8ikt~pg^G+_ z9Dc%LIB~bbreEw(nAvw*uUqU$MLC^aSBs|wt+77Oxnz5Jzw%Rel$UA*P!}tWfzsvg zBpDnJkY^C-$^rE-Wo;cUj_geWjfM;rk*NCS-s+^;Kysro+vhO{lg=9ycC={uKc@oG zcg98b?dQ_`r6TU6f&Y$rsMULT%SzVo117b|`*6l$#%HFal!0okq%@nHP?=>Mpl6#4 zTHKKioaT|4kd@AJrBM-Lm*)Od31oD1N^WjE!1$BfdZi&;hF=V5ni|cbCmlgXTy8QE z*`2_H-^8X4bnyLHX3XSmlmiIkF)%T8P&GcU%`PbvQ5U$mF!+gh;xc8R1WrjU5Y99@ zk(!aN1zjC%Z%;05JTLxm-SKi=sV;V(xepy(OLK4&)pY@L39|KkF@P{!?=c5IrynB> z=l4$ujn!SCRdglnXwL^LAMI8L)Kd}=gNgt8of4wRtvZX--?luj#Cx#R-A+LXEc*%rd;~`+}yA1{JM6a37+PLtC*L! zcYl@w4S;7!NJ~=yQa_x$LRu^oK(xr_o&UH^q7kFWF?Y`;6gj;?jn~ddc9RxJE<8wh_;C6kMq=`!Omg`n1dTp7k}c?V#VR=L6^cp? zri=*KUxse6?t*Hr0jg)3r5O*g3S1;jy1=aH#Vlwn!9F};JI=>T%I#=kU6LmrnRvvyDtdIG9*$u)t?; zJ@3!EKPb9YHQkpEK;o(#vo8{pu25?|3EF-+=oMK=CFL<*zmRlWcOb&CC+@qx;t0Nn z&+}(-slr={3|yDJB*NLpvhe8v5qTX{MFss8sFRRHef>}Vk_F*KUVGA#o8Vb6_}QZq zA?MnXZ9<0g>7MxBk8`!A)#+!V6Wz;6Ii!^zoG=`x?Kl!HJ}-1RADq5r_A8YTK3pHU zIq}L&rwom=9#>BVwP`bZ`=jD8f{MpFfyEFg#TFYCg>ADo_~NEjfIR8p)>NFkySs{A z0=*)@z^ZWtg1?lIf(fB@+dx!>kY9A(*bIMzfKib>m4dyNaT_ zvNN|b*SbHeT}R9kk+U*Vnfy}sTiX^ovChGTKmZzjtYy!E8%vSDhA#}Eb@K8>a8P0o zF=!LAlfgn+=VvVYOBxBnvJ;{I zpaT>MQO+vq?%E=AE*+}J@Z4%fw{Fbr zj*x8WOK!SsUd8kqY`#6$Ee$f^_wL(#YQW$#c>i-vPt3>7F%I)1N_sI|0^&tPB3%t? z?L$mWzLL*68pew$g<@H`G^OyCCfPWu6W!@K-HNaBa{OwEPS_8guWM68oDnPM;5s-i+WS~(^d1(sJT(3=5Oy+i7U7(;0Dh8**&UL%0L#*{gwAgUmUk$Q%x4NL z{hLw!`6QbPKnN+Q^Z)>%*OPd1a&l|(EElLaL5=9-;P4*s(Eyro1WGA+wdcFjM#Wy+ z99wA!k&0)BM_R3XTw<7LiKvXd#WD=B6kgE_D7@lNYJAw*mgAyz?|R(O_%Hou=EcKS z@mE+>o+#wcM-)rx;7-NU?`hxe)S(@M$gPX%_ZIf6_0Ar8tHC~R?r?NojfgaF4!Dfm zPSxM@wW3}6)v(&cAMgbDH^Tz*f$8P+rkLosnf7oNeyzok!Wy>z(SQ282??d_y#xl2 zzSXca3@`x#DE6N}f4+x=gx{nTK*&V8f{iPtosxAYaUXm*%qqP!8u0$_69h7tDuG?i z^Z_%7Silt3S3VZ#zyenQwB!KGeHq=f1)6OzsgeOQfB%-oBApJPJRf*)yA6OM>D3I4 zf4g@*snm9McD_xxvgM_v9|1ZqhbNT1WLMb`lZlJ?wcfymqtFd*vJ|gC^I!c23Y;A4wR{&D^0GI_&LSugwKlwq( z)0a8GcJI6j_v4?NUHC~b?>ROTvJmF^m$X|}5%@o$G}4xq%)Y+95cAx}1O$?R`#*qx zY*BB;)`T$Bt2S4gwLHeyH(Tl=5{WL1yE`)CHR|sddD%83kx%>H@9cM~hNzT}y_GbR@>hOaNhqenL`AXA@uE&%8Kfr^JYy z99n4t9?7c0P?fs~R+&ujhy#4gLavl&RdUG*V z#~*?biRFDfF8>G(D6<TTe)o5gDrvD_wYQj-gPCIj=15E?%33}Z7TByS0>B{_4$jx~bjq9O zn}(O^bi8!}7?%K|7~`zn-!%u&Z{q1YgqXVdZR<=u5-^nD>S*w(G|rzJ9ejKXDe}~5 zJKTsU|AG`(+Sx6!Xs(~ayKgSMV81@5OLMM+{%4c;+QK)3ll55PRo!>QlI>$a@4q2- zzT|qt!TKAj^0!(_se;Xvp}Sp9%v3B#5|x-uJ*%&>*|kQPaYPGDNcBcRLrVp`p&;gg zQ9#cyBLGUi3ldXR#(!-?yFr?}Cf@mX777dEtahxIX8u2~P2QtcT|Xhe+lhcd7mbjE z-}_Rs`c}wDWAbH#Urr^P|_FVy?%^FT$dQiC&pLte&sa5utuX7<1II9e}l3d&##})d5wRO1+aYmSM)B0-(M{G8TmFk3!K3XN zvkeki8td)pT1ENnvPJCK42HXM{lCLaG8FhUgr=d^1z*JhK|9p>q(Gub6@=!@M<9?U zm_2bVc@EiH(A5i1h(mXevy*EyyQFbZ;d~mM>P%08JQnR-1AHAgN<2I=@~xs`Bop9k z0tv(i`aZLH8{PMBc>`>)fpd6zdN%;rBh7;iHnK&I=N0>)#Mm?1DLP2vUpMmSqwEl% z5K-UyF*#U@N%ZRVo@v>|l1lb~htJq4Skow$K%?pB@${kqv2z2G)$-n|9rFK`HiHDN zPDryAbD6zUW7~kjBoLnCL>f|tPbJktCa-z?(026AF@GEe{B@GuZPO`PRB)C5m7|4* zL+HOSyDs_+RdaU9&~u^Bess% zdjPW$laL^#pjZl{B=&Djom0xE--3L$^ z9pi$U9!z1`iE}R~QQGd}-Aoh+ocyihrZGE^oB9E069D`y15@ug9bI60l51>}4?2Fe z1n!5w$`P6X3Y40SY#@(~p(${Q6ekz3`TrR8|3jDK7F76=i6T6X>E5fRDl91am?Cpv zpseW^u~t)4(u+B#!412Vc~xliD4dhlh3Nbr-PJ;=fk5-0 zyyYe@+-N?eRXzdo!UbHOs#aKevIrF@VEgQ)fies5^v%hLqU2vKeYVLtaim<%N~RqOo-!3M z`vQYQK-6T9Q?9bkEX;h1f`XC^1fn;a>3Zaq1_-0Rg z>K}{BIM*7dY9^BneRA}db8((WKS;_+sMq!B#O~R`X{jg z$D8gKRz{~&6(kN8v-VYkEM6?^Pl}!41#lX_yq$aoO?VAiaaE+X|K3~T?T+gUFBWHcWkFzyU&OMhN-ATLc}OtMNSGOyl z%>%gkuk!CKEiLIJdu<*7Q^{x~4`NOi(y?nobK28cZoi&euABiRlD}UdOKCzTq5ihq z1T!c3ywZ+QKC)OePly`lX@&R+jK}7w8qtPUg|M`Dl)OQ)j;N#3uO9Fw#3O+Sxzx=$|@=BAzW z2O#jHqBAuC8W3sCrspCa2aqU!ZGfhbb026R@q{Zqo(iIu^Gw<#vFkMA_@d_Lzja($ zZaJXC4z zh%ep*mmhx)>)j-9R7fTJm ziE-kv+!+8A+7Y((Kf=HpT$%AF2IRiQw&vZeQri^v!8ot7^XOs5nemflzRkz;tx|jw zL)(Ek<0bIxOO?h+pEBM~{wB4E&9ooQq4X%G(ghF)=rpldL^}#w9De1$$I1w9d~4b- zebl8Qx->0I)Iol4d6ePLRDqaNsV>#bnCB4=J(2mdQaO(9p9QCMd@YMMIUl|3HSRC| zK6ilA_{Lq(!q}dAm~*~y+%B!p<-O|AII;iOlXjkn);>0kedvYDGnWVNjQOKv84Wp2 zt_b|faa+qn56|(Ar2WV_Iu;92PJNy}c0up*I|2wKYj%(5lpA*y=&?&yV$-bbA@yw) zi`yk8>;6;%5zS-yR;*MWk=Hmv-x)^bIErAIEg$?h^LdT(7NSw|Hg72bII6>neQb7l z*;aneLB~lIz7!kF^%!sWf8|3SVfGq6>TvQ7DK&a{Y$ni@@%8<|Bh$5tw`G!HPx+Up zHi?S|*j`L=c?U=}(&9wm6t1087+Vw?^=+U&~Uy4Xi&9 zHGJ(FFx+ya1(|&QE#!SqpQD%C?v?bCfPiFmSzXZj{F02hyc>KnfbfARt{=R4%Vvf9 zK9Nl+u@tQ{ZZ3m)Gi=lpP?bL6(b!`$?;+&qZHE8wrUE?Z0$CEDBV-IQaEoa*$-Vhn zU1E=M^yO1iJDADJKXK?yv_~uJ3uM_6K;I%DRAd0$uIN6N;Q)k&LuK!#zy z>m{ax+c{YFsmeL|K?A z!m;9eFJ4_qUNl>>Xh(!HFo80ERAAH3F-Cm?e2ue^+KFJT>`oGVH12_`GR;Y=ZXy`u zxGZeZ*vK+iVg}&6Z}D!UdHx)8>P^jkCft?*d}IV?vxu>U*3t*hjjD#%VTesTqJokN zh$s8?TWVb?4Qv*DR%a$p-dI!E0V7&jP%^__%MatvFR_9 z$95J_ZO!=K66wO92}mZh=Yc`a$i_2XlYIVToI0d2pci}wW1I=3+fth6OV%J~k8#Yv zmm{$>(DA${@DooJv5Ou#L$Z?&bY`L8Q+qQ!&~)x~z&r5 z1*)yl(fR;(hUNbS5QqZ6#h+KH(!d$Oq}_sPs~WzS&2qWsXV&Y|8IdsZJ5DE+$~S}m zz*WWQP3j-S3DHgsuhxH2EimPYrjIjSZ6CH-TmEDar+`pv+kb$Q*A4xM7KDGK#v|(F zfaklf1`v_sg^r|S6|Dn~6#Tg?A2&$WD*k{>N5x-e6UN`$F_VBzdqbRf9%~}X9*4}! zW{&e;6JC|~S*Uf=ojvB?XKU1Zl>Yu&SC8f_QWJ8V_rTe`2H4A-4g~w>3B_uGyTQhW z&Ow*CgHoS4Ai+kjI^Xn{tH9Be9V=R~0tzVN5j>R3GmK{sv^TY6-n-psqo|lJs-O>9 zf3$7pMFb=m`;ygFKRI7wwT%B!Q#)g6AAxC9wA_2Y0@_TXLx1BYzb}U zzoTsk`jjKW@aK#+-cKHy7|f!jY`@Uap~BRTy6!e`hje8wKRwzUd}{aH&W^yqNez!a z2E38P+El=RKa3G1G|6or?r*J~=662HGv=`q{|8tJ4?Z+>xa%qa7I~%E`>Tw%tKB^QF|3 zItk7B1OWTmSCR1E;t=6nd(-`~T5KK8Q%iAQZJ%nfe=p1ptM+8eE)#NWJ=)ZOl0pi0cIvfyN59vV#i(UBI5lQv{y zXG?k~4$80?1p8Br(QPJfnQe(e>$UZ`_dTc~Z-%Kyy!}H(b~}fyvhim{S4ypkl-s$# z-bhDbAtVlpdZh~_F$<>bUJoOTx+1_PBhPF(!2!PmLEtCLlivPV01|jkP2G-z6^hqb zY=O`1%}#&yBK7x>s9Nk{buC4O78CmB{!dBA$u<6z>a|7TWM;BmJfWf~89B<%qn7+6 z7F!u?yqFhu$Bz9B-!9@xd`hTTM%lFCdEB_)5@|lFjEN*w%{q_mQrJVR&!o0}<1i90 z7&Bb<&ohws%W_-?OeRx00r0I$&29=P;J&EZz3>=nhC++ybDefL*fq#{@F6UASH{zh z$6zd(`s&GGImM|&jfS-o`+3;%{>2bP@pt%GX)Jrp_4K1pAh;>qYOH!HbaL%hN( zvu5!_iQdTW#WL53{Oz!cqJ;vFAqvcC{s7p^dV7majNFz>(00($UUNh-VTXh+w7xo8 zb;h5bh79)uc?YQCBW(-*_(AGnQv#C^1A1H_JgCOpNXwWu!K6)E8h)wZf%C+qyW8WC zA|H&nKt6^$6D(OYpFBWatk?pDEZG9aZ{SX6eP2x6nrE0h&NwCY* z!W>m@f1fLD;~G@U9!&&Br{01Of*;==;7Va9efrgrL5JS;;n!_h{mSZ~wsrZzpH$%1 z!2>w4`}_M_+pBsnF%~T+DlA9~3k!kRS@TX;Fcf&m>cbrPO9Bk7nj%XoL^zF~toKcl z#&=RxA0F_@_?h`KikVQO>A6ZNGAS*s*DOF28Scn`1Jzr!R-jY4{jN$f+Mb}~%Y|Pj zM<(7*eVZ1v6~}=;-^+pUagmNI14dhSZ%Fhz#W4M=S0a<1@vG_6brr{(Vgqd$51h(! zJ8%lg!%@2xY5d{MLdpaJis3`h@J;Ywnj z3A-!p)XAWzeou6 z_8PQ2F(ic+gRUPmE~7&8&BA{giNC|B*~XRZ;DYIoPcY_nwwEup*Ok$1CGwbl_q|`> zWe&|a59m_Z9(*sNpr3C$X)8sB24`mVEcyC^Var&3_!vb0qxF0Re~pu9Ux9;Zqs>n@ z_AZ@jUA$$D9>9VHif+2N0<0i_F?&0r#}B*#UMB+h9Jy zk;>mbJv!m5XCH|1Vqa{}mXpz05NeVdjlX%61m7v8&u-4CrYCI`@R89sFBpxaB3D2i z5?0~!X{=tGxYE6i<6FoR@YfHsqZ-Wzb0x@T9%H#Dk0<&M;|sWs2KH9l4nD94e(iJF z+4sol;vqopifCYV4co6F{1~U$ZC@Trzsq!jiv+(z3N7P)nmXlq)~j`<9mN!`uV}bu z@>>uE7|jqHpL3oS!k7ZqPR=mwFQ0Qu#=jEkgGrOlN6eKNtc!|wk5_~=d6yGyZo9N@ z)hdE$U%2QCBc@NEo|8+~Jt2+-$goLGi13xHrJOne+aV{?2gb&k zUg-G0g8`!-8#1HjC#bWp!@FTI9JE2f+ks0<*}SuZ6t+Zck3cv1hL!~_!=$4(=lo$6 z(8MO#;wq6dfCOSAdA-GH*(X>rKz_Ab8k*$zTmbX(+M~`&H8%vC}x6=_wk5d0Scj)T-vGJ z{IoUCEai@hz#0!7Al_>)Dg7llF)@{He0~?TQ-5aOnwn*D2 zB!-#u!BJ;iGUja`D(8e~?_6`V(SFhKs}~HeY@ZQ^7NcJ7x-2&FvEy48TaTnwrlO6BEfODDdJ8Yo=?R zFdjc{_)R>K1#DM-nYpx)60X1LHzab^q(-XZ>+xtS@9pICB(x#|dQRsZ`c?x+cH3ls zXF>p>mHkR4c}q672FK@MjVl(ZsZD~QOMG)K|{06S7O22Mo?b*X5f|sWjYVWQs0_yWAe@N9>aEuk|5b2DxbDZX1%hgy* z07f344x7;5IhT7ntapf*OMZbxSMk-mx0SOt@qv-7h#dVs$!Ya&w%MIR33GP~w{%)0 zWSEScbd;9pnsza}66RirzhARSA(|p~2!)=P!Lqz`Lat9%JX0IR=nT~t+)1$Bz zb6G}a*R?a36)q^(A<;);#&qL1*%O!rQD0kXE$cJ$9=2llr{O=zb+JeWtru!2*IN=< z>2`e@o+Qr^;}nCNT^dO=om#^cfnVJS)M%V<@kg7$HkPrlt@|j;! zR2&Q|*EI3u{_%pDnWUa*@jL^*hj(2|CsTPpdoR8nhC6a`kAkYxHm5f$%}2{u^S+(_ zIr>9!6M6NqXIm7LYhU{KvSJFG$SroEN?5N^UDipjxGHGqE(mTC|6=sOCdd8W4klhZ zHMOX#I0qLMS|qE@cN_scN!R$lgriaj!4zme16#Z<4{Wg{<6B&#CKuYnTYdNFe;?+d zG(ouA7VlY^5yS+-QWn6KKiSAqdJrTD>@cK2-5bm4E&l?9nQhz3OSFMcSthn}8w1Nsc+RWdE|yWR_Yz?Hxgz7<4*HW5>G07&6B9D-tI*Kr^sA{dv!INbu6RL+ktpABHsFNcp1>vj4-0pE+) z!a>`gN|&bB1v891HoSBKoQ_O}FG7(P!uDVUiHRkCf_e?lMk6QkZMt50r&NZYS}#*Q z+*w%5*bUe^f7W-YGG|Z}UUlk1;bQ3}PUEU-KP^+=IF@Oi19NL`mZg&Wy*bGG1I~Ou zp+z{3odjZWWp(a>7&OZvHDjZ43lFvV#QGtr7e>vG({j35{|L^3dOw=gu5z&~qSJ?J zZ6<$gx0W=402?}Ief+^errkE5u}zf{*_7Caf57?zdBM`$Pv*b@@$1(wfNl_Bp-%~@ zuVHRJ%<;%SaS6sN15GBbpg#hv(gR|7;q@mnFc2zUNVnUWaf8Yl zoI+piO=E6Y@qL>lP0{71o*v$>jy>x(FF)$q0lJO1Z74_Ri`uJoRMi@!DxrG!_wc|T z^OT!_C$1k^BxahoUIr=8UXINwM*7^5;$wuIFCwOFb872e+Z%ud44V=0& z1oE8V;)Q=W$=BK7Rsba`SuO8h8Y~)+Y>V=m)$J*y=K1t@S1;Uk`hL1+@4 zW8e*>1?zs3;Zb3=l62aen|gWu!O&wo@*XvtPuL8h^VNZpy05R+MN|0zOU{!-is#3S z7Wp!>;iOTN_XnrCPM^3QH={o%g#wgY=P5p-Fkq8vVeDp2PdTg{sCIYn z0FCYb|M2(q7cbc}7>Q2Hoaqf)LZ>wwx)%Ec`R`jUA$IZmQ=w zw;n0OI8Cx$WPe-+KBT;D4UB7Pzt+)2;?2Keal6j0FO-NFt=Z?t;~vx zYRFJNt&~73>I(6-DGRih%E=N6Vbio>h51uWTuMSQLH|orw=mfrwn+l|UnZ@p@<&Yf zN(`h)-95kr)5!<0@qd|MBoQya0CGlwt5$>z5uevG{p*(6VXmhL^=T8It4dBgvL(-q zfaa_2day*^vn34c?4<3C5Y<(@{db)o0|>c8Ui8#91wLzvf- zf|(hAVgQ27pK>!V0$i;@T8Up0a{$SdXGd}eFd>HTZ~C!xH8 zA(zk{e*bq$|NfLhAxkZaTX7DYoFSjB>)TQG21LJJ`<+~JK}?q)zlvK51ij?6#aRSc zPokA3uHob#Wn@9ocy`Obd<6ZjzS}vu!ezr?gknj%yUkF6HzIl-mc4Y_)y+5HQXaa> z+sclZLhE$W&%-od6NBqJ{LEm!Y6lOGt-Uta-I@#uxokk?u#M!nU~hCBY<-FXVsjuZ^WsAN6t(E-z|aDgWqFWovrJ?p<; zz{WEHx}5daqS&Vv0`BlV9Z;2*5kIE ze+A)>QtxlH;63g(JR;Kz)k7)(cU*XCU`c`Z5?MwW#E=(Coi7LAq?gCqdPFtlHlBce zI;aiio|)Rv7cQ%{yGsOq@qZFN^cdJ6epEX z)y2t^g5M)m=g*FX0W6eHYi^>n;%if#>jR&-qK$kU331N$^xud(L@1TPvcC^Y?O0%0 zcZ-hsZK2;8LEjCHtW)$&S|!%+NxyKi3xm94JESxvvcPcLvqgB$W5K7fh(z+FC%r?{jLf^{{L_fG>slaBT?19Z+T9#!)xl5DmH3Oe1w0+~EL%$Sof?%rND* zV0o~8T|TZy$`GfgYOqu%JU6RcDQ}%(XSc&GEz^mD=X#$5;;bsCetrYhs&fCP7S^ZQ z#QaSyggRN>-8Aqn%m16PAf6dW+2ZiNd}|Dya}9B(p*A>3_h2P#A|atCB6{4bcPXg< z+kKHo#d3hW$I>_BQ-M5wC1CV4U#c(UV&{%3Tb6Zor%}G!!bMGf-do7(C3_}vWdSGh zXj|wMDm!2gFtp1})ovdCoyZHQ>Q_qtLSsVECeYLHEBe!U{@-P3i1BX;T(x{I@_#!% zs7B;z{WV-!eZSe4uX4Ha$8Gsbm4Tw%?(s1tJNxh`8OhM34F>H z1VHtoq3HmKX1X^y8^!ApQB2Q2#mhWUdJ&a#+^8-0Q163NF5q!L?l>geJ%ne3IhkP7 zLLR-PYM-%{m)M?`uOx%aY%ZZ={vhP_d-<3cCQtx~CB2n_QT_~|~sbK`b zx03|u z=p@RDj~EC(4&Vc@+fKhtFQ&-PHvUZiX!S`fPw%+*!&!d6?uhqykCaV_nq3ch;UeA| zik@E7W><#w?CLpN$1iI=9V2{)?hf%`+&xgMCv8;S-Twd*?wJ4Rd;TB(1c0+XP^r}E zh{TOc!CRfG=758PqgAUK9F>O+m09iY?fnEKjiuCZ>bG+yPxW?jSK~J+P*YO+0IgWT z730NAmAHaR6=kgF=44$ibItAgu5n1cw}x=ff17*cTA;e$?|CnXTwBLBU8gGvI#pyo zHeHxaFVx;F!bSSV4M_}d_f1j^<23SN;VOGJEww|$gx%Vsd}4#3LyG2NI3;U;RyYa~!{bFWX_0am0PPv+ z$gIq$f)}59SZ!bz1SARBb7+7op3z%Pn{kc=t$n4;ytvxa4lBn$hrbl**Ae>IS?03g zs?WYE?Awy6+Vm4Ft-bvnHwDv;44bY(Zw()HeWj`G+Q}cPal`zM+B0N~2|l-j@~pBT zy4DBr@bEV^lmSM?puHY*W~xYsb-i7|!scU9bpp;r`or0VU>9Hhm2t$9*rvV27a<3@ z2=#6~y&qQKgWqTE1^g4>RWg5=q$zj&0<3p=lHxJv>3cd)sN!zD8~z~G-wJ$4o07*E zGZ+Co%Agq_VAkrb3=6*8bpk&fIWqE>(b{8jkGr2#=CZ>0Q)YK-7Kf#F0Vh04Adtjo zU^TGrTdeF%8YSIwK(b(pR{R)oz3F>aV$BteCuniA`#U&Qyi-wc(H(Lsxn4LOZkq1;7M^l%y0tO@}HL>%dI)amoH!XN{y6m4ytj*KPDOd z+4p@1ji>Bge;LlhV7^J$yh`|Ssj=pj(k*4Dyl#p9!<(n~bLg2U5>J=$<&}BOG%qGk zJ6+e+_|v z)-Oa>)NM81wxj8Z2ZpGlK?TPxfCp12OKKjSgp%j(hzlFXk|8wLvF+%{~9p1BCvJB^9zfebjiw#a38r#XQ~C?k(y& zge@*<+|{e3%LG`8d`jGs;sfvNC8z#l17!u+Z~(_s{|SaX0E?Q zWY=3!#JfT{G+eqpa2c3Ns_WIQ#fDZ!{cS%JMT5${Us%`Iv44BME}C!q*`8C{FJx;( z4!=7~)x&#lSmavUJUH7xwg;oU_?X$JW+0x)I{#?RM(?EAjpcfuL3uMw@^WZTxHFOS z+tyXoeoQfe+e}uq;|QbuMZo+%A$G@}aPcYB9qXIAMomq=22g2M8Mld+bZ_9dwl`v| zdr}7>YEhesM4WzgMkK2knG@8xH^YV2Tj5YxA5oW}GXH2{D~Qx2U!sZJD&R6)7w1T) z$&rN6o!dtIn#N9y@GLUf$>gS9TyA5)h~3rG)>RnP{wreXu|#5h+*P^oZbSAAXP!-} z1exik6WKHWmT>E;5C6{FyvI@d7;3Y zQTFJrvJ7#R5V4KcqB-t`tx?!lW9!`eRCdyrBS=gXTjY*TIVBCQ-OJKq zXR#Tj6MFlWyUfncoQZ9TLxvDS_XSKcqXTV0&7$QQF622ywS9iQ;L3g}kIxKeTVQI( ztVclEawR3e^;9j7^L0SHIWZyLAe^PxSY@mOfKL(7P zpee`8Lg6Qx1cEQ80|^g9SRQRMva6GDEkuQ_7$-3M-Vmqwb%F~sAC8J@T;sDh^wT#o z1%sxauo?n9F8(0b4bhN1<-Gz-9PmGfyk%;YYSPj$XX5LG6|3dPSL|yRKtq$RmA+u6 zy&o@7H->`TtuXt{kJD{}T~S>%$$|rkHgS$mNH0uU0i{ZP)=4h1a7z2XFh*(iCv%hH z7|+b^@o#!LDGPWL`Oo9fEp4e~Gi>GFFTsqUF!W=J)~0)vBFfj@q>~O-WTAGCmP@|~ zSj<`z*@h!m(}g?3-i5YDRwq)C63-T<6U|-y0-xqk=%ggd(XXcmEPRG0&WD~Ov6R`y z%a4^32?^1lP&@}Oze#j(q(-@cGs{ejKi0=%t{X|i`!A!xPM0xiAM7!|c11**UuLvD zNth(SZA)m!*HAC|}%pi$_g#iuA1Kx;bhz53E-9gXw8hNLcTU4TB4$L1iY zxT!rbMH_DV$H}1;WNY;eDa*o^x{PDwkNSTCT2Zv*a!-Z~xXp9E20;X;{<=zHtl&l} ztES>S;@5MblP*1hB``XdQmaBI<0gfr9kKqMfXRl1QavnJ!oB@hDi1?Jq_&3}QJoX?o znQoo!Bk%&;^ql7dyoLU4Al`OA$i3<3<85WiB1(f-f|R{~{z)Az7E_oeMb)===+~pL z@^pACJdGLL9rD?6%^PFd>@05U!^KV8yey)B7J#fApn!TaZcX~Ll)?0wCf^awx>7f| zJ4d-Qw%UsgnUV@Lc`-ha`*BzH{Z<0Wifsh;J3uaQNSnan0}M8kT^pWl>H^RS$h^zu z-~lbWq+tah5($rW{Gzys5&Ezo)M;E!mYMK5Au^{UH-}(0X-1J}iEO zgFdonSxRfr<_c)|RKBcSzzqnOg=;38n3P7OPNIi^Uv$mk{TdC97nggV2Dr%SR&rs9 zHK7K@O-&NGssQYe62hdnN?qrF9h5JS(5a?%X>Tzd)$D>TGZ{s^-}9?WyzW8nNP>v^ zbx2RW>gETt-I?7kZ2xFJiO%z zcsHnPT0m7^v(?jzJT(}5(z1j=X7Rt#Ov3WB9=jRmmSq%OM%MY65x`ruu~a{3iju>2L2< zmFD9gy1Aj0Geldskv>@KLD_cQa?Z2pZ0n?~mK$NLl60KuviX%5T@YTh>N2ewr?Te| zj#_u^%!MT0#!t0@V!T-Qacp2V-&5O+6o zTsNiD)RC2$#{;)_8FrjVZ$ciicm|Ia1pJt!ThsBRg-F^2E z2%rZ=#hgT)xV#a3K%IjqXnYf8M|RQ^pczd(Yb#tUI9#!yV~8{NjCVQUk!tP0jt|~T)lqPP9$y2ew2fHllAE3 zTyRI8GEDmg7gn~XhAvj**vI+TC+;4_wdQQ4)Ep4tL1DBa0Mho>!=f+-{Le)uKp1h%C1@Ku<>r-&E3LKN5> zn6;IgXW5zCrj5DkI8({gnLmf`#~+`tZ_kUS5xVO>0oxaRV4e^*N6{S> zgCp`*r0=A!(Fl*VTX|zdV=Q~b7p2x)@%86oWa_58l)_CXc`P`m@M8)POl{)&VGPMD zTd_%atl!G{&nP{s%Np|Ys`uF74h<%& zXfBf+qQ1x>4RmtOmB#yQ)A8=N3g^@_C>5^@@y|;82f6*werRcAx{l+8q$xAqq*Px) z)>@@HgQ#xWefs}#_7^}=hHb+z3?d+cpmeWF2@=vBihxK8(y4TJ$0DFecc*kWER9IF zbhC8B3P>!=F5hL{&vU>3|IRn_%`iK|%ynJIc^>C+9vyB6Ol!+C?l<9&;`v}1{9#7R zYs`2@4!jC`9zvry@}{`H!$RLLirnC%r`?$tgLSAhBibDr#S+G|!hJ1weMQ?0_sM<^ zhN^nPxO1t`g#P4L(=yji_8QtePg?3%wsvLgyINb$QB-(>uw2CVN)M(UdC#=e7s#|x z-UvYp@V2IYQ)`>USE+qs2i|*2Xov^(KGhl3!~*OSrv{w=iR2rsQaS_3ZeRj?8C_v+ zt*ymxUs4;uGwp-l3`{a z+Xw2;8h`#*_8ghT|4a7Vh12PIcdMOJieiSuY)v;2pN$;Ek??CX+ac69Od1)$DM%Ep z@QCI(RUl#oJLF!6Co2uw|EJ_Rfm6q)8o#aS^&6l6nA3GO)MA_NtMj=_+>3{s7?)!V z=0Y`BXU;8*rHn@u_5thei{6(LdUO=7(ZQ$xk(3%up@_F9Aql->3WM+iL_l5(r}wXh zaNpk0ObF(Wo6_}?3nv6SB~5Yx&htE{?LpL$Z=T$%SB8TLY(UI(s@2OaJ3AYw`TrCX z8!Ia>&jWNxZsDGbXlG`o*honET9aJVY5VARdt`@Fv9(J=xt<8YMH-6TFky&r=KEl) z)w3P~PSi3!Pa#Gl^MdBpfU<3H82#RIqGmPnHeWN-Fanic)m<&_#%(mtb*}v@Vd6PQ z1Y<7#cU!zap=2k|UbAbfu?J>cdYBOvNg^Z0!lF1u^CTl*aoXn z&}-+a-U|jfetL%<{QN5#_a94n-U6GPSGq3I!F}P{58~iNFcC*4ei>;8Sgm^uHGy=m zZ|5BspWp{PF*p~3ZmL-E3b@0`MLKqmyW=4%I;ULgm%!iF78mF(Zc z)lsw?#>P?GTLEBhr7WC#Ei{?e8H&qm*D-IU+&ey8G{7RB58Y&v*!Kz>-BZGENH5xt z6y6$B0hx*Md`}AMfy!a`W}7UGYmj@&(MP|zI2n&QOskwbwq8zbk33W~nQYbzk8>R^9)%&Tm>CpG8i!Sb96|?SOU=UUVtErxwR9aG*jg83 z#68l$;?>$6St@GJ$VT0oRr|3QD=g*O^SePo(~vRFK8#8y)<+44Qt;kiwo>N$RgB!g zNQJwXHxI{%ZvO}o-$7C#NGuRc%%)v1tV-b;I_T6707)y{o6AhhK@c98-15TjbuBkt zj;nepqqK^g5|-_+!^q&lZx2tUBE@HcUWHOmWm=_e$0htAG5~YM9*?+?{I$aD;rzJ` zVMVW>(=pi_1Yrqeu(+N~EvyIoIxs$U%lLZy_Q6OLSmf)Rmcfv!8!=e10=lQz#PfQ+ z;7=9_FlYMPHY4~O>e?(MzcTE_EhM#pf1mvNR~)1FhaPcf@CtI!3t_BSH+ve80)-r4 z_i3H&GgHg8(jh;66uL3e{m^c=Db)5#`mDW25Ewy`*|rs%Y?hfrJ*o<-zD`|xT68c2 zADyAhB`pP-Px zZ{%Zc{;4GZ8dKZl;ul_Ofh*{J_Zk0}xv#6v>4L+YwjK0MVtk)h>QU~3PBEd>AtpC5 zJ{z|fp~^3$!ydi=U<7_J5j4Trf@3OqQe?C1sW?lGuz>$G_GC?!?%EN%^msnKMH3OY z^&0TI_j@cylqozlYgu@ecnMTtVxV6}8|lylj~_h;yna5_gGsVH`!)vzQ6s^b=h4`g zz&wH9hNOGg(+;zng|7ZvS^7Fh?TB_(P<;}a-QG*d2@3pcJAyo9q;Ho3F!@2fA-o{; zv?oC0!a0~AZ77_CdfoDdGm&I#EUko2_g^OxpfjrONHp(L5*Gn@Al-3;KKBc4QZQND z^X-J&DSrMr)Jp~O8}sx$JaOt}X_>l{kL%cQCf`>nL&~e-zPy#GV}m32$z@c=CMt&3 zKhwP!x-`$uxp^*cz3H7cf3>`iQv@WR+)fDhN`TBdz`_L5V1}){ z8+LEQf%x#lRdy}kGHj{u1MhEf%{Km^$nZ5{rh)+GQL52rlsAXEurhyKMgZ>q1nv6U zgdEE<89nar{yp~=Uy0XduESj{?yupxuW!D)#U<#`uyz)S&ZX++l%i{m88IWsXGOV- z85Ql*lPq$Seb%ZAQXwOnqi3b+t+A_ECFsrG>=jhvwwQV}V)(f7@K9H06>sCW&G*Ze zUZ@X08-$u|S*^Rfo8#@Po_z4EZ9hR0?7qL;vwtXHv3wrtBE0KMnr4e z8N_v%R^=4E!cYkMMg*hLA+w(6)c$SVQk6L_z%9zKAB+vG6_z(SJsK53V#)E_8X-^k zDTt7b>!Adb47J-UD#a{`3i~ogxvHH)5`13bULo&{@~a;nnbq5buE!P`HMnh1ueEt) zGJR_8Bm3CiyQvEf_ilB-5NM3`Sl3@MfN_}RIOzd`DBNa0@w7h?$87tH$giBuDZmC3 zuD5{jIME+X@Au_orib?e&1KsGD~wsYp^Sof>~61Ww<4+7DUTiNQeXYqpofz@PbREx z&AoU>c1%@@dGBLA{r({M+3~55F|kC?1+)^;K({}+z+bos`RQ6=2n zB9dBDiTdRsjVShfbo-ELt4%Ia|T=zSkGLl;zm zsZS7dzV?>dq%LHUre3kotdoi?!h$onn=qXw-X9zqynb0!gQ(1WP4BSEoUS*WgyjVPh`zP9bYY;<^g z(T>P8?{IaWBj|xomLuv$i(952=^RsBe({zU-oamZux+njh@ zfw%7@7a&npM6XES$p_MVi_g@t$09s3j1TLkmpkGOjuggvHd|sSu2V`hs~q?@B~%a- z=leUg7Sk0TzMJeUpVv2V!2^LA4lmYWwW+vq2bt1qg4HjE_ChL{i(FL$(lxZ9c=JxEe}wq@@hZ5W8Nm(&tR~gN%b&_-v^-w?zSEo3B4_HYX*dCu-RE= zBH`3TAeQO#VB=gjYB@k4i}e&^>+OxF+IFI7DcDPYSloE_C%v5vyY`DiMN?y%Sg2Pf zs9F7VV8ZGg>@)k5y1fNf;jQnp@}#2uI(uN-Jaq!TY)89M|FmbU0{I0syP_$(z0vpF zZ1f6}tZ|xky0NhT82p67vbP^wc*kSkegETLud#vK3b^@*-@23&UgEY<0p_*QJAKT{ zaW-tcEtM^QcHAP~gd8j=q$kv~bh^5@91egubT{}?U#k=col;$}wQ*+g4&SNq>L$AW zkqKbGxy;&Jx^cs+TW ztcG1YWIEmRdyWi&P+fSA^S;c`&3^0(Px@Pl`PD zOs?}h-G&AZ%ikP+#!6wLCj#k?Fq$GSti|Tl(BHgyDMeCq91cDHAv~o`vYU0zisTk= zUL|DIcjd21w0WN0NqUAza*&e*@jGyeCej}P9~C7Kf;|LsVT`ydUk)I zg}16zD2g~;=_udD_>etEt@jaqFHqxnE5vcGZGRTK|I@jAMBsT!f#aOPdAljJ!HW3s zF&SGw!k|{BkfF0pJiWc;nzBOdR3K75a|CEZmW&byZxv5m3%Z%Zj>k`il;>e<&T|@>a{IAhhKjsF^g`YD#+s8wSQ{fo-Bwx=jcd@6wbGi$3x^dp5!5B3 zpyNv^#GYBY3#pl&J9kY9KJ0tOfme}ckFSjAy4+(95;hC3%78X2T$m|lsEdaBgNSRe zYDom1poEw!SmyM~jg{sQ)%q>0AhEhkfOugo~4||Y?SY(-$aGB|m z3j@a4ES*0zqjI=+?R89~UPxeuzbnhCT;)PCIF02Q7#Kt0J`%4BPk-?9^_|$nx?qmB zOMf=O?k;EQz6Nl6La6i^ef}0p(Q`^MZvmi8gdicWVzKgFXUlnnN<(eb_4PG|?cAJB zVc9BiGk;9HhU?0Rh7fD_0(9+op?qVF%Ix^K{O)JV?ysj6Mz3`3w>0Re*`W(QP7CGU z10TBhmMrGVXv(sk=Blp^j7<}dNIw(uWhYIp#Y_dIN7z zMC}a%;fgqqvIjz$3(wDR2V3?CdW&xmpV=5BLXYrhK(Y@Z;y2DsJ-19!FE8U=5H1hI zSXZBI%G6b6Q16~Wb_I_b4&M7-_ED3TX^z&5cE<(fd=9pg+n(rg9OlSei-bgq*l(X= zJ(9l^p#JHiQ}SY)MzBr^+5SWwjI{dfkAUnp3O5|Aflm6;q)Ij0!$rF{19RSgwp&P^ z=+W=w$h@KhzL4^C_u;nNHsf?f|3f+>nq)!jBs$rcy>dD6`DHhzf_tB_tKn-!ji`24 z45#6+MiJ!A(RR@G+2aRKkko>BdLQ zTZ4KYZ);yMiZVo)Kh%vHPAQh^Rh~kZ!CNXhXt&6V?nh2CU2sD%XAgTzIdD>48U4pOM)T{?!|}egF0WE4I1J-sQU> zigB@~nRcUs2n1xCNdM|kbkXt9L{|Xi&EAX>L;aY4?Hczf*m61DPS=aZizDw!pxw&M z;bM}iVf9mUavju#4LE!jRD?yCyRQ(PFZahbWQ?DRYsG=6(C+zQCe^7N{%9tBf{jqu z`up0ekVw(&Q~PHya2qdSg7sV~HZdb}H#~byIwMPt$7u$UrygXJOXE_RSXlz~?9sEM+-CvK6U%u&TVi>LB z6hIvqxA?i;0BSDN*liS4R|xE6|Jv;*dGcO+ijl6E*Bbpv15K@N3I3 z{B5_^V&d1deS(Qf#>O}m2?7k;-b@2so>ArFl`>& zFize6mEoCbP;e16!&zzEPq;ObPQ}WP{`vu%ZpCn zix@1>AH^&}_{T2Xa7D&9vpg&&2`ER=wBO9RQOr~zm$PgKZ407ZE%~*vZZN-_aEWK| z_)QOIJzSSMx4jb>_o2Z<%_q%S?tfEr4I0zb ziHdQC36E8Hf&0(p{bYd_g2Sf-uDXae@AZ6{hP|KaFVQhm#f;z4fVW%D;S8l5co=N2 z(tn~6-0UpiDIq9{>^$Ojp}TV4#L7P?>_m0gu$pYh;G1a2wTdB+z}wR2XjbCT=g)O- z=`BDQDQ8<<<{rIpFo-?`zgr&AdwXB1Z{zjEiD^rv0Yo<>D(w7#34#!K?%5WjNW&<44| z-ySs~g2IZ=oh<H)Cy8Lm@~$P)u-D!JgYTJQDNSF7z<%RWAF+27+NkZUcBKY|{_ z(DSkaT6NFbFO@rcLCXOt@5*|zr2bdp@hZI8-QA6@SM1ePS68o`^y?lo>2hxqY`{Gp z&Uy@UTK3)KRlhAn1)In`h6;!3S4CxVMv9Lb$PEW9raT~1Fnxt>*c&8*7}DX%&lw{J zYKbZRq(VtCgZAjD@rg)W));zv!zd(QzrO3VHH3p-^ufY%ySPX+BWK!#EAo1a1*^WC zjj%B&lD7R*su?UaqQ~b@-S>3eX=jP$Wm$!$m)P2|m8~f>hF#v{;KsqiQsfOLg$ObL z^o+aamfsf$l0*E)RO5Zl`Hy3e-9|#YH6xeG@Pi|RrLffuv!nO&2y-;PR#zI0Qv8J~@(mtxS9S5i zE*+;Tc&5BLW{;q1a6{&DSPi4-%s$AiW-U?`K4wsoSs>@W5f9_8luEKEuU>A7g)P@4 zt<^4ELb2Oq6Ysy2+B`i5Rcz+BPi`8}t3l16K)cy!jZ4&5(gXO8LwTbIACrb?`3f)j z1+Oh~I8v~{?dgj;gW5BDnzQYEagi=erryJwmOExOKkYyha2=yvT@sP9&6Y{`^2tIB zlg!g^bu-7Q+PA=Z`64|x%{;t>5~W|lMhyUAYpLVh@Cg-HaD;&PAbjp4GB{+wNU7TG z6eubf44Zimb9kbOq@O{pj9TLk1xmI`2hO#wMam<`4CY!cvdikhL4>n6l5MCrFMB3t z96N_B-|f=1p~f2y!Jw72a%`_@vYO*Muf|oBTaoAV%+&HJp*L!>7&jIKzo&7}TF)v; zti=lxoOVTr()Rd%k$yU_CK^O=#TKcY80y;}FoTyPsau9k^^L-+mLxpO`#5YqCRo+;a}82(ihOFf zdT}als;Vf~jUbiq;KUr(R`%)tzHqd@;Ikf_?H5J{+m4K1HS)XgJwtPvAtTCXC{RFA zsCrGECozy=vUN|6L%4|5(G~i|?KpO%zy}GE_NPv!RuPX>{kR@s$*2^42aQD`Qw7YY} zFPW7%7QU8e4^)8I^PZW}*oj=x9ch!oAqyQg@3(Bkjn0E~`k-FropGdFv8_OX5%wiv zs_Amis42KF;kvpt@~N)&E8lazdK&XL0rlw`%cEh=hM0x~R%C6uUYAJ&GoN87rqLs} z18%L8Aw69UI&WQmY#$^puuB7ZDd!cJgU=Q1BIg^Mu?7t=Z|Oj0wjmR*Etw(k20~XS za&?^!60F4!PQ3AUrJ!;pQEOul0-ku})X%->vh<;|sCOWL`)H*)6t>d;G4sH!d>|CJ zZ~2;eFrjsHWY6js4Ms%#*j{)5GgziQIXSgL9T-AKBZnflPWvt@?gd5`ox*)gMs%#7 z+$CZk-ssP& zQ!Y_CJ}2-%!tAP04K2J*6Q^)80r}Kg5mE1R9zlh4lNA4I3`>QIsJ{IxlfU)DaY%*+!o`xhK*x7P0kjvI3Nt5!8n(3UzDNNz3(v$aw(H_ zOhxEVmu)UJ|0qYle|MkFp=&9OAMg2jcVLp;Cn7R}OkIdRxv20% zxxX*H<#&}xpr?tV31Ikl^M0?>fES}L2A`RiH~x|2r`v;;ejyUCkL^}pcC#yC?`NF+GF`Xu4fLhhURZ?C@|$^F!eR!z_Q=u`LqF9^$VEPQIsebrjId~QVwx-qW>F!#3DMNVEGPqB;)myF%G!ymJwnldRY z3@?FAhm`f-uKs|}?YtkKkwJ@38u_ubwA4_xm_J7*63cKbTdL!W5Kd6%ODQSLL51Zc zPNP?{vhPOH1Unw_KFfIe3lEkJK-W*4k1sk-IMv?1eF+?L<>CrSf)w_DAne}h3K|H= z6XNtMW$Z_DL~>2eyStbF0$jAp<>&nq?;&=&-YXZ%x)jdmG&d(K%^X|<4HMwc4EuG|Hd~7@ohdSkA46YBv>dSXzw#AjcSP?4MrbKdN%KF-mak?;Gh&WM6 z#@yOmytq8ebE|o#obVedD(h-c^bOMv|JDzTd5xRR+~05e>tViKVq{-Rry&nK$?hZof%C2+axTCM`5NsK zmkpeId`S@m%l*5fOD`8zW^r=3ZD5V*6B(_sBS!v4LQ&P@;{%Z=PqZG8vohyCR{ndy zl(-muDx!N61q7mA=XeQJtz|lpnB-&%R(@?DnXT4*?6IbL0B2I>^*O<{>6LT75;)Cl ztL>Y?QG41EU-R#eFu(7n4e_rVtzpKSj;P7C8ewF>>Kipa46^|IqWf)!?#?-S0-+>R z=kJEERssWd<}&eHV}ga#5JX0d+&l2yFoB(E@_-?vg>WGT4rfp_WBXP9KrbAvm3;~9 zdrdRJ(hJK$1g@A*s5A**4(TeaqQLy$(Ms20+Yx3jrc5u4{@fiJe{^G6%NHrqn%6|r zcB2g9;+fyZpYd2dFL}?~FF?hRzR}k+Ce|5loB>&lca3kSJ4^9$LDC1^x<38unlJv-ydTXu9#XjOwNTzOAAP$iL%wO2UMwR)#+)M}2EB+6xzm`fErC?!#yK#~| zU_eFYXeT5593}D@}~KTHJoRC)IK|XCmRb zftCT=c#I5R;#v-f*1tWOSEJx$XU@&8R#(@iglDx_!$kgIqo|70k99Zd69H)CH%0B1 zXtX4`%^c?9WqA-T>0mMaw(6W8v*ec2D{EH;4alE72TC`039nGQ=%2*J(;ti~nRPi4 zdIk1S|e%=67xLANq{wHaOmS=^2M?J@q+u~;gHxJPt_mfZ*^3MkHR>fgzi3X#zBm{`$pmOdC*{Z zdXz4cKNLR=8i-Giyv(yV0rt=au z-P#-O8t04Eb5XHa(9NopzVDe7ZL-Q>-Cx%TjSN;4fvRpIPmjL4;EXR!I$tq;V6uq+ z*=EXMvOtqKGj_`atxRHVm99Iy&KuTK6z#(An`y<7*_T}4mgiyLL~kK(L%1G{^-$hI zEtUNDDJq(m%z3Nj#vaWx6YF={PGBB#-TS*Xp2^L24`i{$tu_sV)`{FbO zk?`F{010c1cazj>=O2R@a5<=LGYDnyNpigVdKiFF^R({h#deVGdTq^B z)b|#aAu364$eXnD6$Y2P??7dy$I-|_cRL&{Db89dp?XJS;zN~nX5G3#8XdP=_o3w% zZ6AGP#@3G)cEQpYzwup12kr&$lg%ltvsO(mfqLh8}8&w zf zhiZesKX=vdGb!T57-@{LVvKk%F;m~wt4>pGzNTH9H<(q^u(MV%VFO$0VJVYsUb)K{ zK?yu@e4sbY`suP>PvFdo=<)&=Yl5-zbF`9Fxwd5 z%+3PuZ5uP%THj}hq?U19C!44~M&Du(_Xy+Nw}kajHmx@GrHtEHry=jl{R-ne%|Grq z*xX2XQMm2yxpd#U%FFp%?Df{ZU&Ge!l`{}z z@^$twap#HURjX@V`0xTM^Th}x#3*r`(%2%tsZMHej8>w)b?ZLAq4VKIZ0yxoof)3V zLSoGxnOn8{_+g8<6)zmEENJ4;3w!^T_Z_v>Il$U=AJ=^hF?F1~GzTsVL8qkd3pHXl zL1C`DU(CSsjA)4&(y!(D77Pqp?N0RjYg-*|(GdAv%TJ5BLVahbaeQ{X6o#!<&C6{V zb-S56V}rA!XiiP5H$sZ7Y{i66K1>G)GLT;$x$-T2VnlDWPDYw$Dx*T2_n$gpQTvP7 zRhP^+Nvb;bjt|pEcF>5cO{Sgs#A}kDN%*pNj}E<`nkZq$oX4Y-oT3>?1E~?@(z2FP z;vI~bEvXSyI`lY(JngJkel>TU*q78!;irrkd3ki7#|I7@-U!GXDd3^%gGwJ^*lExX z^3@iO33_H$pE<#QTxo5vbKHK-?BmOr*+wMTZ=(`~T>zrXi-Y-JGi1JzcbnIzR#MR> zc(I^Sv);O*yOM_E3?s#`Hd-15ne<*Cq|7XqP7z~#Y3r2TRj=DIFF&ML={cjrpA`fP zS%KSA?8V||7Imjl-sp`Ix_kDPFCkuxGRsqy=G=1}X6ohWd^U79{aU4s@VQo+i2dOS zj?ow=8>lE}H0fRr@FSmI7 zQYW*sAK$%u*WZNKzS|?U50~_}-a7szeJVhmQ({cJ+H{7rue6&2X`YU@QErMDtSj|c z#dXD~g}ol0#ycw&aWsfG!^&%)kqhs@9+)R^0Bu+i4A&K}}F(K+SQvMN?6JUP)h z=)*KuTKhaO4+sgod>1mmVPjFP!OZ3gc-T^-ggmykI)OI_!hb1G;?y_oq>osohXEAe zg|Iu>-)A@e)=1`fIyB!T{a1_a9mR1Nt+ySRt^SFb;pOBLo|^q&p>L1?<2!5d)fa1- z{psk)dOO|j5lcF>IFm{?;c9lqB3@?T2vqVoW>w~j^4BSD`YUdXs9R&Q17d%xh~pj3 zN)fk}A$Dt((B=|18A7xetluiutR0m>`vosHUjCAg8`n++!Q{0VQ4_Y=p8Y1N^o#yu zwhPxHF}|w@nYzf+DQkn|d!mT%?55mvH+OQ2Ql-w>%N6Ju^_rR!l%JuM^p9eFeiWyy zC6r=?2rHIwHMQ7-UyoAktP1ZbW?X1uf6$?a`PP|;@|0mdS=8SUjyhsH4r zM+R!^{Jz)jj0!2lfKCq#S28(TZ0vB%iBCxxhM5GqVO)?xUNHTmTJQ)pi<`R84}1pOHo z{Exb*BcdDq81&*k6;a^i$UjC7t51;Z$H;vXGJy(IG zNPa1?+lcc$<_$%|Z8vmdj+#uLmJA3uvv^k4{8DT{>r&WtXtex+a(D=i8p1lWdi<3V zRN31QiU6!2mjJqsg{!yPuQwv>oU$=`E8o4&U;5rB>_=i+zC45Oq4C_rl>buT{byh> z=@s?QDm9;Y@)xonzEBF?2Y)~IBbDXBzB^-+gLmhQT5+MRL-uR8(QedjAilXx6F(3)}(x(U6P&5v(1F z3y^_?cOv<#Bl&R}2)FqQH_OON_D4^1?!U@MMA{YX$7A5Q6yaGh(m$Z&4^g~WlHH5k zrwmz?cA#&T9shdV#emUupV#}@A#GYurIRJ>iOw%7vE368iin6T72LmdrAVdpy3Y6h z*;e}S0ka8=Q+`KHr}E5QmflwvJDc!vrqmhbtAPN>cnO_yN`Fs#=`z1dx9bTDs8<#W zQk1_HZMB-QKrSHCnBaC=a#S<0Vk?r~#``gM75g;>$fVHie8GAU`@PV_MZ4`|6WvV| zJ^cglCb433A;F`EVE8RZI<~OLllkSL2$BUq@_9Xx@MTqOBFKez4-utkQk<~u`|n62 zEB59SEG$v3+yZyRt&*>zw5G0%#I)Nj}5*;yr5ZrERVuC3b{>o~c{qlEgb6f$4WZxQe#oCtGpR7s~Ef%L)FL7Mn+_2N?Z%0+d z$8sPTi*TZv_dp_NJy%GmtD*~+=TCDqQZ{X3>f%^?0q^hly=1!f=$xFKL&@As0+WS# z`T66(47^|75^BphKyd+_{;y+&l@uoE^V>z@Yj4js9y}^8_tUD6wKR9jV`uQMu>+1WTm5NQ&DdwH<#l_3!avHdBskt$Q-*-aQQvlY&WU!E5HsgHs` zeEv9DB!4*E`W`s~&Ui7CONSI|wDvdw_jx~RmOk@~%y|+!yua|h(ST4`zb(E_={A$Z zWITVgSk<{9tc^E*yKTBxMPUl1k?A(gkmlNHPl*2`jzf<>kwiRMN$YG45WIlp^JQUY zp(X*h@U^|dl%Po$5kbR1r0vxTgK)Y)KM<>0j0q>rC8V0ml`CU23e=23h zT;%a;?BHCo0VTYpCGOhg6uu%(%dm}h!QXE%ymjO57YWr=(6`-fLnLkH#U0J}Z1I~` z?OoJ;jEt0D#US``{7t^t^ismKV=>JI(m8Y+4ZfPYrfvTJ|9+^yCumLN# z8(KqN;e#+lkX$ftty^)GM2s;bxP7KaAh^hY*J&Tem1ht8+SymdpM$E*TNmX4mKC5z-I zJe&lox^$0^@p_@zpN|Xv%F91{qo;9`Zp7au9%;LxWW?Wn;m!TzPXNWFI|8}9!}jpy ziv`!8A})96dNMj8OyEc>rid&rAmYoZvy8w#)lm0Rdd`dU$JP2(=d`G!zpu}5uG$1p zlXKr>L_4ql<${E5yHr5`7URMxQ}4*TCR6WqtYQ zwxp?C>Xj=XrQ8d^p5_R+*U=S*(_hcsXo4+OaJ1{ySrlGcp2~+=@t+(`d@q5 zy$pS=q(p!n+WX>H=#_pdbu32)qtbjVdqp`tu=8DZwxw%RdAUKm@2@`k-tdzZ<(%m% zBbl|ewZ5StJbd2QouBT}2?{1RI&J@o5^x&zM8(8>OA~bRmkfVJeJJf48XDwA|H+#R z>K(12ZVj<&`OS7YezDyr#H=7yDJdypGBWJJd;0I@+T5P}QL}*+%CL*QrDdH8rise) zoGefRd7d8tK2uK}h^-A55UT?E8EV&>KVU8Cc*3~9WOjK-7x|-)jvL;*`qx|ecbAsR zcn{@Nh{h+st^KcU|8?8vN?I04IlQ$$yiuPhR%;#6)Mu zT2)khJPE7p8PI!c42@O0_KCn-rd9i-!oosBj|yO%V{}qdUyH{nfY|x}<<$Qo{mVVd zV>KP-@9)17_4{5JBzJYaeDOuO(p#Q{gyfB`F5m@s|7bGyKS2kfTgP8c42u+hUDzDR zvS&&E$H-KmwCr(b?|-3*o+fW6R#C`8yV9MN95tcwYtSj0^=#!MfwyMZ`+kdW&vwQF z0D)WyOr~0iihwL{J0at@xh%WxNWZn+ZPj^RlTd3%!pJ0?d4+5y`GueHv5@faJgu7b z1%j;7(y)`0o(ph@2}$oo!$0gz&UW+QV#~P@0^tq)c7x+*CchL1l~xK-Pad>TQntJJ zQ?dfoVvjKT{)_hVfXEFY8JUc!si}S8aq^Fc9Lq6^gN8JQ`>Bi0@KLyc=I^)-4H+=9 zHSon3LwFL){|e4#|4)R-`SvflmDM%QO87lCLK#qLoPIAvWK0?;T2C4W+IPN=SaB7vRB3Cu0!<27a_!T=u<0=ANH9m zxBkMhyaGV{1!?)_mA=Jhkk{Q=O7gshzOA|Us$lX%7#EoZbj@M#5-mjAbpRkiULG2;mD z8vCh)gING0nK8Mut?S63YFpU2701_+tkU@(Bka%GcrCp8MhK=|qguH&7ofWZ=h-?7 z=I;YA?nyu=^?zDGZ{d6EK=RZ4ThTXID(o(33jZQ5eHQRjsZcXTwtarePCJ;DaR{WJ zEqQ%U>F%1SAAC4+HSK{{&Y-*~bm+}!@XUYtFy?b;#ANYw8ZsT%N{^2BC5^CaE z=vwDaI=&=osXdSjL0fBz;j86%Gd1(^ireuO!CLBbspah!Fu_t-Na)xXPrw--nw72O!2u1i#v< zw{s>E)RUl_Y*grVEt*h0K~Kp8h7TEC;!~=DG zKH0*JOMkxI-~6<9)QX^X8>MsZbJP5(E^KID(DBBn*@WHK;&T(Uf8kR_ z`3@aOv6R-iqkiTMC!)2P)*~+N6MEqt4Lhd?*)ZAo)^N8{v&~O^X)GR~auXvQiCZ*H z&gGo>JvOV~KC)_7KGamlh>ec!07jMjoBY8SW(#V-x@Q?F(<4@8X?Iw~gzB4ZzUhNk zSXXdD*hfTR?`hjMc$(_Mm)vHA>M@tFBG;Bvr5$b0gy%d!C!rb;Tlm3^&T% z(tvSVQ{$`A4MIt*@a_{ObEWsq&PP|$T$MrHO05G%9=xP~??}vEH2MbJcQF@YGo_Zm zrv>t>)lTE)u@@Au(bVAs%>5!-@p4m_WtuFm#*fhofQo6;O~bOAvBJj}uHX&_UbT-?{Y@^>gOCfBH(2}AML?8q7f*FPZY zf+1P>gEALJ)9oG>z7vDd^2Y_Y2q)V6xit#LiYPV!{Bm$%IQ>(R&IZs<9(>-P3{Y*V zrDJBN^b2(LAs2YhL#Jdxl%}of%soV&Nq+TnS8_B%6j+2Eptl;E@})!P|M|iH-hy83 zf_i#>4D(S)z8=8s0~T&eQ*WGG>2Eb(AGL}~um%TzO@y5qR9N?J{S&`GrvL}=Z9ev- z?l1~&_v_&HlW*7E-c5B$JJWjP&gX6o_V$Nv9Y*g7S8FcZ&AO!>9cwh3e~OB+f1GC1 z!7BLcAQ=y4pnM-tM@LLmd@lQ%tH5jKZ*4$Ko8xkb=v@=Ip!I-e&ptS{yM3Ar-NBiL zCve$ofqGk26K?x~jZo$Ddoqep{f!DGvB4G-1C`g0t2pCho!n|Tb?X!@lYZBh82-7G zQ|$`PbGN>dX0#$A!$ep3fctZo{gY}?`rz;`+ZSWp%uWbC;q;MJll+$8tV>dg(2vzR?-c`f9(y4b88scG)=`v(Ie5Cp;Rh z=I>j)4?-bFw;rC?MvI74@-E@KGkFq#4WjS8blQWk8*R;&r=^qY36d&gD(W@vUORc9 zswYzhQ|h)+%yhe?{xMUb4M@6&b|eHX6madOp3Ko!JxlhYlZtR4t3EBRhoi?eTglq~ z(_dh_%J}XJ8fq_o%7PTMEy-etUU-!_lzGOup40c_N?%cFST77TS<0@_fJ`dC+|FVv zNJrY??R=-c6aC$bXFbT7j-*9toU_i+2+)fDzWjfJQfz%Fh8ffWj%bKgGonZw)0A0S+L5 zJ{4?7YlnTbCIvJKl^GwEX48k z$$1of_caRTiB&-=Wtt&$pi3*(PE-|l+7Cqy5s2OOE^^%k(nooOcmrBUDjasx!o4sC zyt)~kCLldrw#)tEk=wu@EHeb)lU+I$H}1mfTt5i{2Vu z4=%e)lt`vpDI65OuTgp3T4$VynV=T3&lSks&gW;_AKb+k5HkM4)kdc?$gXv;mVgj(h`wS~8G6P8T_$xE^deEw!$?+b}aA5S^ z(}bNiqOO;D2&U0p%M7P8p~fao+sY9-nbpzrp|Tbh@@Lnp0X zm5b(Vx(!i+PFuGJ|Lyq?lwZ46k0r(rLf5P{e^a&~1M(r(ef-}m_-z=D2qd7ri9gTPt-&AY9zUHMG0^j_2z0qXc?fbJ9% zaoRss_U|(AzoAvXjdX=AE4aG4R+eI|)zmG_=|w5|zGHru@&NiMeeej*Y(yBq{eK|w z1Ar*!Z%=?RH@cpv+l*=aA2M4`FuNs&WG%vOQgDp_dk6T5=*C5~XZtge9Rb+bgW>wp z(pW%?65Ir;D#CwX|Hckb+w|+b-HD(KQ7^2)@a5?;oqoWNkU#}d zXA6$RD$L3*6%qJ9>}dS2XeaMNti=7sw)MWn&-g}C6)2kQO%^R)BCQ&|*UYa90{thR zetfm_|4{Z7P+2Z**B}DYN=OMP-6@@dbV+x2cc;=NDP5A%-Q5k+-QCg+|Bb+T&+$87 z{LgYN6`niinwe|%wfCOL_K73P#Vt%+qYPCG!r7EBNBCGqDmCTrD4~g`+q@ANAbtLL zYEU5WPGb_oc6lYI%oh29eL55wr{*k~KEN;2B-KYD#+CzBtBAfMgC7pcspq+;Jg4WFLW~*1fDS_vy8seD`_SwC9tNL(+|+k3~(B=OHj82uxYZiVl_ec}}aeXLB0Z%jgb$QbDb z++tZ0q1I(h8Z;G5|C$~J)v&-i`3XK+?n{VVpT4MWjSMe2v{@@4hZ7ru+W1e=J6X!@ ze17IOLF3pM2#*7tN`s+>gPI>++wUqk z5MR;yI;EzbX9|f5X=uCy2M2Gm9DW%mkSW{rP;LF|K3HA?x1hfKMWHvE#-k(YoQ$eh ztv6FCz|VBn>w}(~4-6zG_v=6$&P2g*LLlRv12)*Yr|cHa{BWtKiNkgGM0QL-e)H{b z_XC_7QWNBYIAV3ZkVFf;;Lharoub8^|n4DAtCWdN1$?Rg%s(sdTG2J zi8al%gNJ{@7aq=)21f0!Ow^V;Q=34|#SIe%ApIEmvrfc-XpXPsleM|@UytznANNW| z7O25Y6{Qpq(q`%BKIw$GPn6rBsjkekWYHJNzB7HyO&U)T_HZ3*5TM(Td`vnXOiQ z^*=(h5ps$JLvctd*$7JC$*MQps8Y76Of?@g)h6)zcMAb zW@V+F?uq0?6E}Os^xn-DM4R3IoyB~Q4C;bhglLj<_25<{hlKeXt{!SaTb73$+_EJm zoZKft41`%96z`7rFOKF_m(rtDmty8`J>8P5>CdPpu>Q-Kk4wtV&i;lR8%9Avp(#w- zj4ud-7w{DS3o9ce-D+gdQ7SX!B}T?&Wo7*%QF>@KACiB=WEHFn(=hs167@{A1b}_n zU0q&rI^i*?4wFdf{+gfP;uTqxeSO<-w=iCCary)!jf}MYCJz^ZJxoQjE6tC~8bhJ} zl`ykoT!^>zcv^(>y&+GCHc)AOOhAB0NT41ovQX-(7nfq8Exaszien$(8`8o5vgUiZ ze?x1AUzr`Z!J?zIf8`-qB>xTh_93pbXfBUMW%qwv+fPGWmsmXp;ZP_ozd;?IaRW>fUeiJ6`PH;soHI4DmhN4CrqNJpEp9&Sn! z$rj)$(?t>C)CJuG;SduSd;68!;+S8Mo~9xee6UW#uzv?e$$J%u|gu>^?;k&Q=Ks-$(*T%~K@S{!c~nEDMM5 zBo$!jM50{j#@t&Ihp~O+rUFl8J@F^@ywVx!$^GuH603-`v~*$ZI5?^@7eGt@EuW&N z&7`(jvnD(-#J(s^@!iGg)0i@GTNy51c!0C4nf|Zj^55}cX=&LxFn|z=`Q663+yxc< z;sQCs`@4eGRHjO^+pNQD3JA zHm~;F;nmBO;bg1x>OZphr7FT!v0VGQtwH|KM}^2o*`P*Dq6w@bhZBI}2FEvt8omSK5<% z00~JJtpMy#a>B%N6$p#5iheEgxY!&;1STJ{f|$-9pehDRCZg8L0iCe{lxy3wVzqCL zr2a#su}s*Us%Dnpy66vbR{w|C0vM){V*hXO=+#oI3pz_Jqu}5tiOL}Hc~|CQ-F6XO zs!nx1fLIs_%HuOse0e6Bii(6Z{G1j4Y&jwv5rnw&Hoot_jQrd*0PoF#Ec)!s?)<$L zU~O~(y{~Kht^t;~N3UgR{9IpHTQT6g)i7Y}*IJJ7Wa%!_8pp+(0=E!!1s`lqs!~yN%S{wgTscXL4Y&{15ZaO z^03*;thm0E58d6w;ldL%me=9g$O~glE8ABfT|AkGox%O<5e(lKo<^mIpbWGALVc%* zng=%H$4$tGS?*A0jK$0r~ z6*}XDjMTvXg|cXMCD%FOg3QRk#u>zAiU0|`kTnXc=|h&p3n>ZJlI@CrUY>;pNMxVw zO7UBqwhC8Tb|X`=5(P0hBpuw=PWGx6CboRV@qzeRq4x~rLW!|>u)NFgm)Ig?!#N2m z0%uJ5cP#RoVG8G~`TC}GP9?;@(3!_a69c)IXLb-gi@UE+(c7zZh^;k)=Hxw!KlO>zSUi?K*e&7pdQmHw37_0U@2&msPFoA3uD7nxlGE6B&5` ze59E1J?A~6sTEAeC{b@xeEv(0nP#i!9>`8SXHZ~=rAH1tF(AH&#fW{ryn$>g^rJgYyr9l}WD|*VDD0i#$`{(FNpuORIoz0$f z^qRf3+lO)ADN(XBl4uYAXmue)Yg1&;VU58XAH2J2|6p5qUsogf5o+`XoPQolmcfnf z;3Ytj4kDQIKu$HfAzHlRHPUP~j7R@1JSw~?Qha^Vn87+NA^j_2o#7!j3=Y#Tz>;mY zwYh`7jnF#Hr0@YCOVcuv1C^=p6SmuTqsp$`_|?*(mQ}yJaibQ#IZE}C!!)oOE@Rol z&hpZVk-qUtE5X2NU^o7BC&3yU=?&|xW`4c@2q$}^fgM1Z+T=PEK4th!JXOX_Eia@1 z=HUM@2X3|kxyZjKhB$>ZroLh=ga-){v-)D-HL`X6wK^HWqw?BeF({iygSZ#kEKf@+ zYva5L9tl9>e0s1s;`mZ=J!yi194z6) zawI^qd)`->$`_n^gl89aJ7dkqb}x+Jz?Y?20o>^qfB~+Qf+%E&{rOp>>|*Px(q3Qun+4i%oTZ zg(?SD4R_Di&*x2G-2o_gpeo+4MJ$DRbw#_jiE~cA;*RRY2>$V3Yug#U+%D3$upcWn zyEkXQEI)J#7S+@Jud%`wy2;kc$Ei9UXvrZIB>+1ZlSr~>3{5k?#;>U&Fw>|)Tl3UA zQp?w1HeoN^Y`t`P7vc@_c>R_NgqD6d0~<9*@ei?P#BmE`h| zRq(F>q+)}ZB3@Je5meeU>qTI@4SG}SzH0Nh6VWS`(2n(hM6J_naRXl$Xlc&B;m~%b z8mmgTysxmJzdiuLn-kp`rNu2D93*rK?c_jLzonwc;JlIRYHbCJkhI0s2?p)2_PkxU zC4wMUW=ulitYsgvx$vP=sG*Fa4xzp#byTi(i<(CQ+`2)kR&=5ew;V78p++GF&_#r} zNgO7LI@?M)DvJFC%httR1y|XpXgdoSp6VT9|C~@iy}L{c#(!tVj>%|3z{+VEK$R~e zehG)w9U?U~{N~3*aYW`CxsLd1wp4$8ES%pMkk|~|2D1e+dRkt zTON1^@^<-7I~B@_Q_pNEmcQ@MwR2`uTK}E`tS5fwCSUi}PGA2G;B!*ygmwoQT}Q~( zfHW@(D8V9u0KM$mUb0hXIrTS((^~*?PB47K;Z}XB^sC={Sz@a6h-iVRxf3ge>(qQ9*ElnEY}Ab2o=%w-Q||Vis?AqYiRs7-TXASK-&seh2V~Z3rp~fr z-Ox@|@J7G(d0&v%JCHz0QLU}eIfndXgCI^fZ$TWpy>>V($#MCdO|M<7JCwR2jR`1z zX6_W(U`O+-IuE@2_Kj0p(Ag)LuPDdZk(l?P&gMD{*g3U!v$2n{V?XxiRFteCjOP0R zil13?l3B@y-6=dBP(`7-9}CbwG#b*`+CD1=w!mdlZ>qs;D4?d2_4c(olK6i^f9Wy(Dw<2gRs~P zYSw%BE>X#VvGnp9!uaQ;_0F@xm>{Eoi!^2XM_%)%&*Z5X1)3mX4?() zP9`xYywH|vf|g1lsorr=-^lB~#+eNEKZ^{JLFTFXu0_VrH%LjJ*eoMFQAB@u`{iKQ zabIb|4w)UhA>mdJ*dPUHLWAk=sOFc;j3Fa-^T)nuOJ)t>8WlhwUuFNYNW->jY##v> zM6Q?~U<2QOMB`7vR=W@1>CqmH)-dpB_hH$MUDgV3FhtjnyIz5GR7b8KH7n%a`>7#d za6nMTHKG%>Ju5n9F@r{h?087?(;NYy;(u5h^Y1wciGIZ4OFJO~u!K1kMI=m=4D%LF zhwIOBM$dO2PBOyOI%CQ1;?&0Fz@VgdPS=z1@ma61>mi!a*yz}26mCXkR0XN(pr8^Q z%`TwjoZH@WMAya7NCM)Y^U-aPcH&wGzZ>_zsD|=?Maf)Qk_F(X zs7^vL{+10Xm4$31=i@&!RvXxZ8*Nae$K=4HLVfGEmethMa@?9aT7Rc(%cVoKwoosF zkR=yB5}ePjMLb;}FVj#N-WG1Uexs1n1*y*-Z#vH!-#-8;iINy(Y&6E74RB0n%;vN0 zX1)sl@L!_Tw)1O?|`Ez@{cM}S1gUhw2}U{s{iU&@;ZiT54rTAy|oN`Jdm13RK)Ne+l4FfPeU6V2;C6D>%Nc25`YEygET*7vd$$5A+ z->2Rl*u7g6bG=z$6@9m-SZw5w)P$gRxm#ZaIjyQJ##AZKvRA)QHP6{(MOD`-OvT=_ z@5&M*D0jnn_gfI zGxCb)bMp(lhy#65wlAW5LI5YNoXHT1aJuqoS|UhZRD*#F_WKRX8aAuW_WI%CNlB-Q z>Wc`;%WDx17a~iIQb8>2c-O!P$$FT>C}%zzKu2l8CTPI^!bGj_>)98p&sKy#>y%&% zgKRk6BCw81j8rpAWTjv|FX4&Ol}1zgt=Vz_wzb*dzWmYlDzaHNn^IhPXal?1{P;YX zUxdYtFPLqAQvcL^0b#uOstrq28K)W-<;y6Rc#Of_#IE;{#8V=tJu$zVA-qK1>Zs&f z)2ZX}aZl}$4%DaSYPVi(TXW@y{3M$QSn7_ZMC_d1wQB{lHRA-+cwZFh?K&R}|b1q}u6hAvoJLS|pS!clUF$OT!W&~TzmgRS(pW!IXhS~|h_ zymwxX1>%c20(ev!_f2c(Wifp3?lCLELrrG)!y`3<0uwYuC=4>OqBRoXENm*>1BSI+ zBBRZa+BG`c^=C9#`pg-ME|aJ`l;E+zx{*I4gqAYqhhd9a5aY#(4Jz&83`~`Su8GQ% zkmPa@ZEsR2h!5Cg@Zv(og#2Nhlm?OPWQi|Q?SkavvY!1<*^W3sa#n#w6REk{)BXua zUxA&ntr7Z-k(Z>Zp9=@^m}=lhyqUK&)7#cc#X0C2WM}Ab;bG8T^+9}iD%f=;FUKk z`t6Jhti3s{$w0Qr=ZD)Ku?MO$TZ2kwb#3>=Kb_yb1Ap^8XN%C8DaFI9;O9SOIZk-+alFeN-r!yZK+AUF3ql|fQuw%o~ zFN+6qk1BY^j~JwJT7p{0H0eKFJ1dlV%SrbkgaGz@GT`!H zGKGkW_602k$q33q7MvZ(S~LMICC9=kF>_NHKfYgtp9kJ-=gz6T4z>62V3* z42)ILlwnuQ@Z8#yx+>RfBSjp1znN)E!nRst3Kwj%Klo zdNE1goFPH;-7slaN)}^t0Mj2~$7$Z;xZczp|D#e;Hh0c@A{KL4Bl+`J%oiZDn~uT_ z3&bHJ)54J9F90)z2208iHAJZE_I60qDI!3HoU;)xor?BpG>6Ii=ZAGHA=+JUTgYT; ztCM>8&KhV^x}ByDY@X}giotdD4zL&e@H4mT+1~K2I9>G>0x8U&DortUxPGjgPo2Vv zJ)^`H_Q6lKMq^YadjHi&0buZ6TBK$D*Y)=Y2)hXf#_UPf1f#)7-EQAmBXE zCLibYWxcj5Z9OpS$W-IpfBcV&53-t?MtD;|48ci*Fqi+2dT2*tOs4 z*|pq_8TuTI=8FSOkU`(?i$=v=z6Af7jJkw0{ox%Acz5|jU z&iLma-IRSYS7Uz)ajqVPINR&Lg*eQ`1(2a9c391F`TYXb9^6n-T|T z8S-~}E}Qks^h_MIcTSAd8{XZfGgp&;YI87riQYR>li1C)CSP@oDci2X!8&Twk|15~ zINiSF8Tlnq$_cnlAJ$*zwuiCQ7(dgCM}}z~+O8#0e)Hzh!emSEUYp~(uWC!K{YbK9 zGJgx$kZAtRMg(gsvibp!x&6I*G7NUaCi@$0`@TegXLA4tTlF5XJ@-3tIDJXUrsJym z()fVRLGao>rZ8aPaTmiqJ~oOvBK_O!qgYYiroiIZ)s>B8rTAd*zLUP0C%Qc~fMwy* zXNZpz7L-J^sOnL$7$t|!fv65jKl4BhkrXzizpD!-MrR3O02W+!B3eIxPBjd0#N@^t z17pqMhcmi!v~Yu~g{ChpUd9RR2~>%G&#myn^p6%2(#DDjfR=> zPxQz>ZMdv0ph=cD zP2fn%Rk=DPt0fEXRRCrPDn_Y>ia|VR9D8?!HSn*zdBt59UGF*UHJF)4PlFG+5637p zMrVhg0W5uGTd^xFqcI&R*|MY6s+HO!@*cB9ndONQ!t0fNc^V_ZzJ0sr!0K-$Pqneb zZoWe894-0R=R1v6r)lE38Z)nzysyfftcTCSJs8N6 z;v#CPKHjvKIA53CG`RjvL*ssysJ99>T972^&q$#+I^fzC$#@#40&L)lGis?ylK!4C zF86G$cxe%wUY7a_ox^n*&vuV=#!fXSf1gqJz}3Yk7&Or}#;%?mR7n0TI7aQm6Flx; z2v!`MrZo%7Trb5b$=dqifPq%ljM>}={`Vwl-TY{YBrSew%f(@yskiwP;MxEF-3S`s zU}UI&sXTz^c1{6obEbD`cGpZ+z|qqp1u=4I>+?9ZKFt64fGjCDN7lUqNu96jx=3xs+sUQ31ptleyW9WvA+!kp(obp&hkW&B@;WseC-CvSRY zrjJLP);lsXGI_(FOH&By6F;RkCyP-04vOA`kqW^~mYKle)Spo1*hYXyaVa}Nz+!f1 zv9k`5(j-oF;zHY*X!ZF3TcBoRjT8%=r~_5DQo~XIf0jw)==`1Z?gAx9B#^b)`Vh>b zyVf7`)=kfd=x70AWa!A?EI_T`9}wWN3QLS^{C6U`DU!U+p+05xl4d*nmS9a<_b&_WR|h6F)El{G7OLi<7!6|V`dFyo5hYGX z(0}S8vbydI6@HmNIm^KMJ|E6d*}L7oNwjx{pu=|i5*6tp_=-zeZpiyoM@?~iBqtN?w7X!OdICIRoy>cK(_XH(velD9;bY+Re>;+;X&~Pi^>iofN z%KOV-lAXuc#Q>;cHiM4T>@0ksZO#W5M4_J!-j(T^2Y>wt+NS2*Pvlb}5Y(m@e3aB+ zA8Q9VcxpJ~w9m77G}N8GX+T5zv-~i^^xv?S&4*mMyXw&C52gb*5QQE3g;bLo!gvaR zG84R%1UC621@@^!(Tn}AVx&Ww{q~q#C=MBWGt%g4&5?y=629cyKY+VDe5P+dR(Zw5i2 zb~otEpQrLCHh%!xg(VAygd)XVCHH*3#-aO3{P#3K7;2`7vuvgr04k$TpFX_=syJ0@ z&aD7(MB%nekzu#f#}j!f9e;RpMVjSX9+8%?ggMlTYZJe4RI&91QSNN1hclvpK@_)T zX1L(4bo*r>1<-kIM!xev0u;-Bz*P%iWZM3O?_}>^S}Ia8_<&x+;S^qlvkI>FK=VVA zHZ~^ji^ZCE1p&l75mDo2J|0@%bC=Gs86VaafOT6b|Dp~dOgNo9DP9zv-08-w_j26P-@P*51I_X$FHrf{ElveV2Jd38hTwll zIiDUp4C5X{m+bBCn}!0zFMUhGEkH0t_RGi8=UYOra_qB6ksnGb+`_M zH&jKqLUGv>CEaYpS^l+h;!6#w?TmA2N_mI*(Icpp`5gc;Hr;9jR&K300|{*#A73FK3D%AAwhsH1JN3}68cSvOQR1^ z6Rs2zEW50+vj)g{1dK2xu!ViNkrEW$6^$Az`sfufQMz!(_ABh*Q71^R`zTd%L93t_ z=PW~=o#I1sC@Az!H#4NVEH!JYugGWhe3B{+z(_ycRO`Jy?eF$`z{)WVsB8X1xfk2^ zNlC2Xmu9=Q2|TY_@p)U6LTy_)(eD7u%J^OJZxLm4rN8$dq@;w8#H?*D-cT3p=!BC~ z>mgwOOf|pW-NC61cHiny0PWggzoGYn$`pa}v*Gq(WPR^N!3f8?Ju@HDbC(Tkp$Y(uhVpq;J$n9GS$PTScl;C_!RV9|Iq=;D8S?N zI>hs1J9zSAC;jETD9R^hs0V@}elwS2T4@PA3C2a!CR1_i?@v~P8#V5{ z{%DLQ$npWmOGSrhb{}&dYqSSxu=DlE7)9mjUOS+2+ZVVTk#_-s++cIQ%xbCBQ6`;L z?BG+>i$E^aShc+UB?)ntjeRI8vb>3tIeah7aET#Pn-o(m(?GmBTFJX7(#I1}(QV@F z>`plgu*w|-_{^`KV64+bB=w0i%Iy2sdgvTKuz?tcSWwmw9z`L>&Zq0TWcXRB_yuJo_Nwb>rNz%}Q`aWfz*L<3PZTgTv&s&NVodV2)pu<@6VFs z2gclbBj)P{UcWsUsTavD%6IR$Jxg*9fh^q|2tt|)pP)>rsSkw(o>8jI=P!xyNy`H% z960qR(`J`@I7oTP`Rn;rI!8dw`bFbkvp z3g~TolDA3L(f;*%SU6b92&&Mzoa*U!6<^O_&Ja?kh?);_IK=E9)|{P!6yXy-Q>!0v zt1HHe!+C^ehU9wwb06ov-V^G(FWS7bUBbe8H|I{C-a|org(e^s&XysQl$3nJ$^^?M zOXDa&j29;Xf!G9?Y01TA#)2l3%|z8q{2U_iM!PO8eRrF_&F>Cm{lx@LbNrnN8yFY> z+8aXo#|wy&|NjY2UN=oTee)#a^bJT?QWtrgQmpO&quJaVUCOHg@ntu`M?f$ZtpJh1Y}YNalge>e9dN`skv1F z#CX|?-yh?at+5UN11kmUg@I-GCluN}^=HCo^0_{YOiL$0m@R1x^_qu)H#e zYSGrFg+5Y(C3tVUtV?rHx=ZU@Qg4IpVdPL#|8v3bT7648!}U%X;tf+tHep&}VF)1Z z{!@RR)djGO{|`$Lf@qq(m>ELJo_@k%h7KZ6K*&sMw>R_er~BWjSB=fcQ)QwcZyEJi z9X|iTcgN470c2}`P^$!tmi^6n)pO}g!a+$Vz8!%x{He*1l=_1Qo~GMb$*(lU0M_|V zujF7EiT6o#{YP>a3;XZv?>rj3A{V7F5NOMZ`qcK2*up1H@DpD0*vh|O;q$4?uS+|A zQRb@=BmiK6VRFc^eB)3OQ>V^OzufVYf|>d7!=7pR#9u0v$Abk$Jpm2l%keD8Olt#v zN#*xIMt(_c-m1@+I7CQYLPuhv1=`sMOj_!H@S+6`a{=c5=O7qez%Bt6+ij_!mfDRb z$&`Yy+6EOO;RcKHjWWCE%-6BJ|Mq>o0&)*m<-?+T!mlJAa)f3qR<6SoH(Bj}x(%Jhy`_~RA9JfoJ zK9BliAt@*dXqI^V)A6YSX1;I>k!FAE2_Wg5Plb9-odU1uJ`mL(s%!vg`Vz@#Tcj-rhF2&(mL7CZU%A}+cQw`HH2qgo-=FG1ymYU_`@^;xpC0|M3q1~ufMYq( z)BigB@gMMU_bRkBBvETRP1X(od4BU?Zt?VMia$v2fYr7D;I(ltyE6YF*1Sk9At3=M zx_C>?MCUQ9L*5{O?-yvbS$Q>~+aU8Eg_2lf&Bcz9Sa@=W8uGeQ#@y9>&cpn26z5xT zUCDqr`jrQcdHUj9Zn_I50)dz%@^t(-O|Af~*@0Ez?5a=O1VIeAk0H5t_ni4r&XwTs zf1**6aF5U}Nw7{sy>C+8t0%}EKVf5uXSGQ(0Z5XwQTz07_Fd2%+&C|Tf^%})e(pJC zQXk$*Z8+)fQV^z!?{{vV0RY=bx%6t+QvXhO$Q2~@ic?8dwi2&TS5r*Sy4IJ-VO%S|&oW{<(y^e(>>(uQ|c{MgM={!WH z%^H;rt69PLh&3v=;5>arm67ldsJ3s@m+JwOixORp?ttt8lXJ}>Nv4Zx%Hs=D=sZPm z{*!M)rXRM$aXF(>5RnW<&(0y#OF5GsFpkII`A)|}JQ|>{iJ0=4dX*Q&$*2fwkPE$S zy8GD?9(SIWF9fL&=|oMoj<0^QwH*|}=L?Y1z;}cY#5u{i*%}huz7z7vKRi=*8n06k zMSG5_Y0WM5SQ4cvHYUhpD_8KVl_%n)sDv6jr=W-_y<4&K1UC~tYGS&1z7hU@Y z=8G;@Tfd62vkg1W%_$wn^{%39U9E>oP_Bnb2?H?E&c;SyAGD5Lj=!E7I918Lm=v13 zG0?s`zeyEI&_gZU?vVs(c5v8HmAV=r#^U7QdxmPdM3KV4ToLx@*<1vxVBVO_=rT`;CURNrtgl?_wNQU=G@fj6oSZWAN`VlH%Hp? z!2c$m8^LZCsab#&$%|LIvVG@)cOgyh*yn2?F$v1tP#k5E^OB~BOM-@1=EI`l zXK;V2o#vFb+4N6oTrf6vg5mE)ml?u}03^ONJUmS_T(XXHoPKOISBkpotHm`tBXq{vO)WG4OoZ8WaPl5SS zs71T!{m&mhp?S2ctE;;a$WjIdSw#k@I+-2ONa(EZ!}Yb~@WeqI+hm8Tb4B^eQZ(^k zZp%We1Mx4FLq)S&n34%h{Z&=Fs#j%)sCESluqk|IgW^kMY4&f-SsauI+J}!~^R-() z+~mrT+s}{YB3d1XR>5M&xwk3xOx>+XDT5q$sc*EQw;E5a`9i(AnmH_w@x}foHVy{t z4PUPL^_jEk77Q6lqU483OSM)8rp~%DA{9xDhEs`y8^cIZy5Y@U^6KNjCf(2O!@R3I znzC+-t>XkSu2DAcCSRwW2Fs9NPRtr7;hs&?yTQTCWh3}BPZRBxCc$LhtRS4c|NDhPCqUx!oeQoMtFU&^AA|!~j9Tm{6jzXDEA<>F)1| zT#cP1rWpH;xVd8?M{|tIp9xPBOFJ=EaGj&*qmt;tkz_@7L>VVUCq_fMRb2uD78s`t zXMt|lxRTo9kNyK|W*Uz`V@L*=m%oczIxD&veeSiY(a|YEy1XB)S3%2a zImlC z9P)sFTii9w@Y=56Xo$Z?*b+Qf5-%QBta8m`kQCebcna)8rr8($qHbK8f+AJl63#~S z>*(5<$-t2FYw?ny^n-d$=%s=zy=Aku+^flwO^&fG8pC#T7hhlBPE!H^;tEJNxvRL# zshl-sOU%1V(v(|1j0qAQYW+TQ)StH#^{Jz`qko~Er2SSPNUKFH@}}Jd_1YuGTLReY z{qa+*XCcoL=4&K|5>i2!j{|n;-P~~-Th<$U{jQ+)7x_T_FX*H`yQ2%Y7|93yfHxUauOh$9z+3cphFkI11i)y9v` z1<#ih?OCjX@P^9r&p=Y$tsCMANqo>d@FYLN|1$)qxA;!!nV7 zYxVq82q%SHK7wDJ7|$!Dm0cbKpPMwv4Z!HJxp7^FcoqWiH&h~q8klx(q@wU0=f~pe z@xlZIV1eo+`Rf!`f>0|-Du~2a!Yc*4qGQCk960V>hVNMDKZ`-y>TCoh2<{I}VR-XS z`|ZVZjSsVihhjs2G39`WNn`My)6(jyt7T<}X!mLk@El{!H`8;(LTOQ}G5&D9mj}r} z!9$B?uOnAn;|2INO6aDrW)5j7i#y`z8lAAiG=}LDI9e0dAqc{IIYT8$v6pPv@Nr{4 z3v>GpOw!oMMJm6K7~F2A)PB#}j#bGqMF4FyAaeO8LAUZNQYS|lKg{-=^%cf|Rro01 zQX?g7ZAQAwRH(?u;6B}nQI)Pulo}Fe*q10KMBn8IT?%%@Ozq)j>2iMLLiW<6!3UTw zz6roB=O?#y-(n$+GPzNn!?i+|2=$@r4y2#^aM(zGsWKUk4T(8kbF$4?u1DmU6zX19 zwjX%^%;l&qK144u;c<{$3j`+_eLkaRhNBrpBfUmJ;y)#Mb%r7mjkMIsQjJ*`n}GB? z=vbRcp@lFQ++xQ3-i6LS1tRdbi8^3Hd{o!oj@7hjjS*)n3P+IJfA&gGsc$9NO^4S3 zYYyb)FG&@7cLF^$m8GUFa*g)lC0{>-V>E{7TzPXd4z5~}RMgtTeB??FmyS$j$74$l5GJrHeXsWaLS@ua zFse#;jk!&nAl2lJj$*8=Ag*Eq_E{hEt2|8kvAl7;VfVNx{CtdK#?aA*c1r5oPpbXh za^uBIEkU_zd)|8_*4<^VP;{Qjv14Kmq}%4<$X64EO_E`hwDE~N8zCpwJ<%gTrn2J6 zlwZLY9?KdF{||nr7|_M$>}mTXac$XA6F$I`z^JfpcuY(vG?oE;@D7QiD<< zOXB6RR6};ewHd>wU%~`3HlVpN@F{r`@+n|UQKO5{4GGclaMBdHY1(|?m}_I7q&C~J zsF4!1+Yp!Ea@2i3?A#?Q$V}_v_|T|H*sj_!_3JV!ZYRw;xZSt{F*hp(ms4PeMrC7i z;Hw|V2@H(s=S z84i#?Q9xh)=DIUkG?AyPX_@<;yda~V;eEP>1-RF|S=xvhQJ&m;-DfZN8=&sp@2lq~6xG^pLPOUd)uS8|v(suH>416{`{B&n>h&)JvlM1P`T7_}Sr_;FWQJ z@ugU)Xwvv2Z`o;9CRWS((ITLVbTeg*rz*4>HyIyK;1T0=?>~hYySwG!ETB2%&(`gq zLWXB4rGnCR7mBREK(PvJ`S{mIJ6JPl>yu9;< z8vVyfe^A1Ck7=vGZO_Sz8?c8UiwiZ3naPdqJQuNoQD7bQm`C_*VxvHL(_v#2%U`r$eqt`eFJ_%kw4ps|cfkZW&9aRKhy z$j!ZPj`EYbqtSkeoQsEIdv2O*vKpkR-R4{!kN$I{AwZYD5&6W)nnbWKEm1ecb8{^; z<}AyXheNI4w^wR)OI~PmhfG_N0>UZNkgu^QoF36r9*6>)godXN;qqk%c=)w8|eEUyD1cTDNYaGc5^z zC7rKzQ$>&=i~sHvGwV+=$D{$7ovkj~T0QPo5qiS=z2-Z`&Ef#1J8W)h430D$>C$UvG9iN1i&1@QphBC%RM? zYXm`|wFX5^_dfbok6Y(7rzZY z89}Ygx^)FJT4RC&X`S#BqyLI}ztEcmjlAs)Yrm6<%GJvFuJl(I`KWEA=&vB4M6fGd zAx7BPGBp~Yn75=?OgN~w;Tp)cqW_gs6ZQ&jS9tBUrCRY7O$XmA@biRv(a+gG^p7H` zsQHm~VHQr7OkiuL`l2h)R0Eqe=|EjEwlH}1C8AA~oYPgJgDPP$7cxz4*u}Ik@|t;E zbchQvXANNdTFVdj>`F>c~nuv2&9FD`?%m}bLUy?OS5`1Yfj4v zi!^i=(CqCWs)H+H0Ttbtjs*rSE`s;*qU|KS=R&+bZ$;)~4WX|-I${@X_6GkyxnVI% zjgg@RQAxSlyj8{O0YvsOHBD z+RL9NMd@YX;NL%I059r2ScLBUhRtn54!4vikC{Wr2=7~_Zj|d@J4lTULN_*HKB|3| zA=muc2*SBCK#>z+nsnjR=k(napLI$RBl)N|I|k3oYAo8FkE|fQ_*gU3pGIw7reg1j zRv269<1Ck(*H}85zsAH2`Z98(AZP>jGLGxgHEuhN<+2S?YcBCS6VhNR?Z%zk4Z4M# z+G$lbV@v*4U!l<@t@?Zq?XnFPV&XR>5tugTs90P9=d9a8w!M7#R6lT6dq-d72Q&j26K zzIioOy*I>v3k#WP8jF(ko(-qBK1t56QAMH~nfVd$s!YL4>K%g5Top^a)Wtx|WN3`! zC^g4w({m~EQ{I6@BW=*HF~N7iL56BqXmh^zxxvP6t>-7u1kYtJo@9I@fWg8NN&J?u zK#w@t+6ck|1YPjBO7CT=X)oa@C==qQjTTd@wF1#nAD0k>2M%`Y4^blub*E^RfYbw`bi-*zk?9DTSr4yNzIlkXrS~-%W~vRz!JU zj$vcR%SyVQkdb8o`t__`1=bAq-_}l+Kge7-;#2FZ>ga`vQoj z3~@J3c;86j+(vQ;4-IZJqBX9+Pvm?RY{;1CxQu^4{)aa7w+*=Vt*g;1!ALoOb~R@vzo-U90UZ!up_KjP&(`F&#Kj5UDu3jF6(Y^| z%q?9Q?619@f<0$v8ZY}qS)9k8<&F36;Y&z-tp-`vW=cF`UR@Pi*BAAZh?2hHpG;A# zq~X^nDl^BoJ-^(2p{l$HUDwKsRFmbn6&Wpv9nPHs_w3thuDv6T+Z<}qomtfIqOwD( zD2k+LbZQ(9$&N&{9e2@GAMV(IMn|Z|27{6xl$^lnm@$vc^z2}NHAjj_-+tWJ*Zl#a z(~x7@thPKABz<37KK!V5;q_Uro~G2iGTt0qL@+Lu;KkC(VRw&d{ovOc(=NzzA6d05ZKZd;ScxCst(vo=@W3zK&3H6Lk#(wV9C zhJj-@ijNMEMcZC+XUx_Ie#>!j4?Z-D@|wCWqmh=-)k5jnSL#gKk6eVf_S%kd`u(kt z$$8mj@Y)=h7?i<< zwKU=4BsjsH;7;(x77{`T!II#?-DPoypdq-s6C~&Y!C`TCf(2RJZE@#q?tkyieXrhD zo!VKcsdHv}db+>Bh5 zQEqDe;k3D z?XjqxD&?4eK6JG54L|?MDjO~~zgbt@kBG`0}(gR66TJO)yfO1w0i!k|*!kOsA% zD;P+%C{8;>ZVZj;xJ(lErDCtj3@ ztWU&&;jFVdts>fY#U#$0NueYfJY`@;L;=~^hx8mUv`fBFN%^%x3v?|(Z=iDNI7fW# z8p=3bDVm4~W9UJ?vbVKvX61ih$3y5YSFeP`6A0Y?0C8de0BR!g{0au= zk`)jaIj(sL+wiDPmu5Z`Sah(+0#F%0vJpXDFFNneT?~| zu<7QrkopdmODeMM=j2--^`AjXLC7u+!G|&xwaktz<>gQB>3Kf$8x`)G4rv$-OX5MA zc#uqbh^=r`%Q-BC0j9E5hGXzumH+6N_X6?54%q2Ii%VD&jv;*HDP-f1>>KD;o4_5-na=@t zZN83kGiUO~QCcEI5SSav@kXGP9HvqIdF7YP;Bi;Ru7exV9W(33lTv9q7pjQc^5C-K z@UeDC7J&ff9oN525aul!yjpa*VuMf_Cq{=aQ_8Ah!^fUj3KMoxJYIz%Z?cr)->OPL zfapq@zlbIy%KqDGSDH;g` z<@0I66rhN)?>y*?o+KOF!lqs(rW_s#gNa&iGdpSkrAOHHy zI{ju9oUtRm5M0PbDuNTLHbgaW;$Eb^!GNGq#kb?`@@ktjEzdrDB!BCRhWz~V-X;8{ zYXKEC;ZMg^^w{ZN`+XOY11IcvYr7XQtG?Le%R5(z8hWZ*wfXM>-O*Y4)d`j z{u$VL0b9>p4C2b81bB4C`Y3X>pa)sf-G2SfqBprtBsfPX7^T2)Tf_hH25yoA-U9g( z_Kggs5Eos-G$i5@es+5DUd3YZhKRnJF}MPl)H)2`lBj(6M2_N!wL>2#yK6n~1Ghi) z%5Z6Nj`DbMszS=G90|ueKW@9bh+H(jXPGmu)(^80>o{nJ6NBH8%n%@0i#NSE{w_Ze zGAjg0(j11n0(82CYY#VjQvXqfznl3+^6u{LoZo&uIDGr|t*2mdvXY=-w@q7~@c7m- zi&ykXbkA7lo7gpk(VGR!a>Lx=}aeCkn)*>5z$%!Wv`RTaXZY7q}1Rc z?fmLk z=co_B(X)t3D|vs1l-Qz0fia1MC*5JOxGf=8FvmaTUm+i*NLk1h8n|IEe<9)zi6Ci?U z$f6yuc7w#XCo6(7o(fjNdU=QPMB;vt&dxh63^8PmC-}Gz&qSqK3Ql*$#@K|CLe1x= zqnNB_JFZh=UT3ja$vThix|@^5zI^24`(jmm2Z}SA_bK2Cb&{yyOJOaO-oI11Z%$>@ z3%M8&X@rMi-nK7wT<>ardJX1`vlS;7npGGdyspRDk*!d$c>^4P=yadZXnlFt(-Z6I zuHKk0B>J(Q#4h{0GPB6HnpU5Ks-1dkgC;s_7%6H?ye}k}NP<7m@BqKJS$cjaS+Fk1 zamj7T7S>!7>tk`HVH*OEGSSu9sWP^f_XmbK*qLg(&hC;gml{qzex23$IUw{D403|h zQe-@fpu%IBYx8Fwu%8GvqRiR3vGu+`p02upToaOziqhWnX`@9c%xbf?w8n0X5`EAe zchYXkXBa#Es?EC^K_zK}TC$bgFKmA5*Y`s_6{p0)p$nvia z1B+r_t=k{*7SV#$W1`}WD#AQ?;a8_n+kM%^?ddWK@7wbN<#j>?-ksD0!W%OMf$p;P zug0$UK>|$Lhz&q5VunwZf@+NAypgy(y*A~3l#QkTpjK-Bl{lESJs5o}+VKJc+5!#V z4VqLEuwcrk7C=X4BCtUVy58C*%AMPQ70aCyBPR4K77fAo#Or>M#5)yPoqBRkAh+}2 zboJ_e<1w(7;mhsTIfs`iwiWTu_j+6OcpwFo7NGxl;xJmpgWWs^{$BJ()QOQJZ3mm>O~CEG}EZY`Cd`Dyz!${S0!Yg!UufZ z(0bA-;#EeqO4TRiOCR=-p|tXe1Uo|X=yXL_Z)8`t1nc>1Mz_-W?KQ`p2R%`b(f+*d z)k@+ zvu=aBEukV`rBm+?c;Q2nYGx8=aI>P&Tq~!Jm=?nl&)Mdr-mMmn{YV?6P2SZD|>e;c6K0Tnl)6?s2uA5PA9B6k8|CZcdIauczMvSp z?G>x`i(JePC`DRA0W`kJQa&xXa@Jg(J=6maI%}aFa_vWUu$}!L&8n9DSqwNChb4Ao zPxi6>0!Kxg-yjkZ;#jy~`^hx2U~o#jZ}@A+Utx6&)f{-;$-Ea@0auIjmBgm#4MG?B za%^sio7KxxzYa(Rp%lqqzVhHZp}9zIPrr`GM#A>JXYc1S4kJpvv@OWAz^X4Icxo!t zeN=90Q$o`*+bTc)A-Ne5Y2MyQ=zLWF7?rFmm3;PUhfPgkKDht4VPlhz(RWaY(f1!C zhbj-FPgI*&=LUD9jb3pxJ&bE>x1?edewlV$_k#3|uIg^|+ay#50$>{4VhrPyc`(cX)Ve#TFyl!GTrz!$B+B3=zx{&{NQ_9!CInA*GKufW?C7l_|1~&9rV$HGke{C)OWeC@Wq= zuz0cxmPVW+D!tDpzdWqDpz{ovHU9_?Pa2`xffyOXT!TCZB1sa=nGGN)v1r|KAY(I+ z2!-ds9+YzO6DWI5)dawZxiU5M(6BSr9NslOkUJCZXE_16!nlOh3%ODsa)k_ZhE6K_VTbyd?(h>8uzJ)73)=Hbgpz0lsi=V!tFOwL6@e24lpY-T)O32 z`_l62Y(*FgC>GNv!O}Xo*mPt3;%pVmF#G@k#e#*!pu5E6=TC~;-?v;FRbLO_NbEaG zb`h(akLX?F5j;B423CXr0_gsJsM^n+Ro9 z+*A>g!)-BA)juf1moIO7i~jrZ&ktYr`7!XQJHmL@F@3(H@!%bpIqt@2X{^-US zo8`)@2nDKKgsS}*c*VK0;_`CShr8>Z&9aXO|8(?A4srcL8DVaD8HCkht4HZ)cP%s2 ze6L==E%GLFjL2`G|FUZTntZL)X0v`xHyB5$O<+xWQ@o~YwnepokNL%V_yeFQ=juLm z9WpZ@D7~+g&)I70xmly$^=n$eJ3UB=(B>0`{fE1OjCEhz^cs4q0r8`S0iUPAo2g`C zE*Fb;*5<_j+;U9In4i3zXBXQ8V}%@nyhY1VG|B)$a6rHpocU=mP7gLm*iB z?@_oAQ&lAQmESMp5_0l~F1{OcMVF%@zx#~C-`!-Z=l}^5I({}z@F7v z%sfXwDN?9?IP{Dt$NVpbEGl9@H3u`z1izCSEY~NZq4C6G<_D_WYy5qauU|jB`tR)T z7o@7Mm$k>)ng5dYKWgb~=Cd`vA-jxyT*`dUG#gYmG^-Y7RRI38rj17n5vuxJ^mlw% z^A0nI>XS@)SEe-O-8OK!+)nsihJm4(8mzrTLaK{KHc-Ka3=ToR7@xpW(s zPHwcnaDLX1Mer!f?nEsFd=QEN5lF06Vvf=;8K#fnphLRcE8yyluVS3JdzSTY$=6bk z6w*eTJHkQtEJ?rWZ28>d+jjvjVA2Nt3aj9e(SiEmBGCMl2`Bnz`taxESo}C9UVad_ zU8_vvHhCXKNcll`;rzV5<-=@TILLj=-L1nniz5RlN9yGN}^t@=7omenbXlj zeWPFEX1?90Edkl@8QxCr1j)QQg|AiZ*BAmxCOQ= zMbevucLD&$8CJ^mo^kN`>!~HC8W`sG9@x_J9*85jp%DGEE4gUFr_br_vxZ(8S;f+) zzH?c3_Vew{psAP5pq+NF;|u4?o!s459$zVr-LVI?|!qe7do)gbp~kNNYvt3ftP z@0YpoI^(+ozOhpSM{EDAVHu5A<3U2a_dh8#uiqKX8>?k{&`&D0B;{tMMdHXZZooX} zwzeO-7&UkV1k9aAGV%d&s)^o(MJ(aUu?+^iJreH$hsjU3LaNTcSOn)okyS$k4kGx>GAn@ z24fcEk^1PUc$3dYV9CeI4cnY*NrGq*87e-W0(uKRY&2qCBzqK({CR!0eOkN;3EWxz zW=A96dTB)eyJg&s$nX+EKzWPmr#^K63juL_;i_a1313;oVV+Ppa zuVkwHVJcDJKSm&evaYV~*laz0d}1Pb+G`j0c741!3uPNZeXLFzpLBza+@hN`TcFni zI}(0NnOxy%3T`!Y+b5s#jgcTKOcc(pyd{RT++hf`e#z;~+#K4&r3J6zwq9Ric<6Yr zElwJ}NJDb7!q9x!Igo!e(43t4;l#K#giZ=<6zT^F<|5g$C3z1XK?*?2fyi&2%8Y*n zn1L%_IDFKi>fi3AG1Rxb8Vp71c#s!#5Vzu5WT^(nl&)$i4Q9AUBzg_yG%g{T=lZJG)JD@3X z>MQSk@0}jQyxqJDbz?$*)l8#tmi*%b1%e&8#Jm^WrO zJrwWdqxU^k{uXf?xQ090{^?Hq2nwQ5P4js6RssgX0T|MuWFROM8HmSV2xmBGUIr! z_F#+2Ce`s65Ug9rjdS^oSkWz}(PPaI(vh=pH*rq6K&IyHB{~C{9jJBg`?%UjE=TiE1n|me4jm88cJ|9gv{PEQ9h~XE zBOf}3ZAF_OTp~~9u{kA&GgS2P36n83i>=lHaN)k3M{@>C7G~097$beu zIDz8x_NCRHf7H<&G4Xr!u^l`#33uYB^v}ZSYiU0RhS*^RMU^C0Rw}Hl>5L8iQ1Se{ z{cz^#fMg+)Ke65`>KjVqz8@P0SecD`$UO%CYrFN02G$l^1AXr<-z_zBM3Hb?dxfu*$ZfQTY85#tob&r;&NF5k?fjU8!ZCq`vYLK@fd>#Zl}g8J&!WA+#tvE|(Se zS$9hwl9NBQ|HKfOHQ6q;nM?UT2j4qd$grAK# z+JT;2SNRML5icWqNgwf@Uc2^Jpv!&IYvkW7zomS+<>Y97 zmrtRg3}-xEZd7lUKrQa7q-vehD)ESjzK5={K18VcK76k3HxvOSCENkUTg+)M3I?;XeUmUVG=x{F13=)WfUYZqG7XzAJj$(_O>_w^@Ci;q~2 z#KdbDk}C2fXzBNMk@xemukFZFq+T^++ka@iL*GpklS39jHF3lGysEJ9Exv+HpjMDz zMmWN}t=a&eLQwFaL|5m81SL8L_{cmhvoC)xV+QMiFEqEwAB4ys`gZ`Dnp;zZL4zlHg)#DQ8I^u_ zNkRtd2{JA?lxB1uHkD_hC8fnf*jBJ^MlH&7>Q@Rr9h8y=2?N`xp~zNoz-$*}6V#Cc z8&${RqGKd5gOf#$r2|F2^KhFf(tuhFgp1M^=ai(ZaAZFJZ%5ns=wqw@*N{xZmFqg5 z`(GrQzzT-5)5x5{Snuy3`#f^CL!ShHHd+fkv?X7x=|$lpExlLw)fH9ytMgYz9cvuv z&u+$A1upSCbSJt2pZ<*Ur|jRJ%evMn8y}5mW}R(QR*D=uEG4&p!B_0w10D>c<60fJhM4PaT7a(OA45-~=O%68*Kdm!{uT)20X zDDjrYHS}MxI9^S!JFZxyPAu8NWjFhCtWf#tZN*j*hIO?~AA6T1A^Jj25p&ha{0RMWg`k zTB!F|mshbr|JEZ^@9P7<-ZE8rQ28ZXEwx0@3zq1;8t>Fo-b9tVq$l-w#4l5mUn+|y z`Ea?f%#opkEcxn!csg4LXJa!7xN%wm+~|LLACd8QZ}7J!PPiOCAbp-tN;y4YCY(mT zz!Jf>y4!fgsMkdu$Rbf1^b|)A}JGAlPXUv-_w(kUz;taLROa(i_M;TWVa1Y z-Tl=0Iu6xQ#y2kE7pKb3Y_`t)`e5PmA_TT0K<{>5zpz}041Xt0wfZ)=bRx!*G3_(Vjm*HT5Qj`w!| zO%ft!en?%m=CMdM^Ha|Fgr#X4-jjsK_Qp}$wJl%D-X8MyS=@eeVA(jlQL>UE1=; zCurIrg4-5AObm^J@wyCd6_h9>+8Py_dc^8A3MfWZW&*FDdU;9iA3wzs42)K$w(~mt z?HVHq=V)_VFu$&Wh~UU{QT9?@z8Ok+n{~Kyu|h!itAhSpfa8mK`}IS)im-BhNFmMB zDEC18x{P;f&)y59sXlUkP5&#f{y$MId$C#j+E~k})gRf;JMkrbh+$OePph4GBNl~0 zKJBX&1ztO`_Armc?+{WtE;Xwg6SG-^7mTPkzJzgoQ{JjM#_kJlais1q#%Enoot)Dk zLUBZEQECWDKkJ#b#j-%(m-%$InG=!DNR+eeJR?Qs((z=UxY9{D;C_Tu!BURVq0Tw& zEnUtLVm^6f6yv$RM+Dj1TFj_pJZMruhb=b6Q&k3^^r&8jACl5-K zgf|-V`f6TQn(_>`X3F3p;9gIXyx%~#y3oLBTC=!0kie2zBNuLrS;%9qEo+(AxScr2 z6#^KmFcFh$NZhkd2R|FBG-VXXq>WaL;(LtZ+7YZYjdj1#!P=efAZyXbVH}}-eQA7r zW%bs2A+ct1?(rB=!KeLi*rlq@{{B)SakQ_nyocgJLfntUP8EonB{o``#x%KKeO7kd zazf}R6co*!KO>#?j_v5zl4l62@}{!pMh6M3Fx5+7!#bHaRC7kYLzPC@K-BHl(MJ!8 zz+bsLNMa$^^M@&G?5Yp1Sv7xmx=HmnMm&g#7LY8Df9~wL*!#4OT8Wtg?`#F+ag6*y z4Sl?prA?-rCJMH3dbGC0-~Z;MxWZuw<{fIo8o#64O@o`!e|L90^S1{*{v*}`H?zB= zV_2%XJb4I4|Ixm}D`fNm#xjMFKY9LjhD?+W857Uke)YnCYNhRm7nifnWe(qPR>?5h zV;BK9jjR|`X(VashlmP%luJufai8aMGZf5-Pt9K3JlYydGHXp@bQ?_Z9c~0{0h#e0 zL}2lC!Yf@sfV^ZjV%?zJn04>;d)W-mjIM1AVsUpA$SAf_U%h|l;SGiOY8tSq!u+}P zc))flxliV~GQ6Ny-N%kv+ey-3i90~2$d7nav?r^dU_d?K$&-X&=(O~COHy8iL?&0{ zX2rzW><^{=preyP=;;^k`xFY9`v~rE`O;{&bDVA_y)IWp z0TfDT`|G;bs51Lm6KBeN?HJRXuxJ5VOm;-Ca$dDo*XPQ%1G*@>nJ*!6= z5_~|s=Tbw${WCQf46J@y1^y!;DE1n3Y<$+}oZU3i`DKC+UEk-zSXoQD4 zIWCIOb_dm}ui~1nH@o}b(H-Oj8lAonTu9fQ2}P0ft)P0w)0^X~yilA+i-8MP#>ei# z>xU+36@YiLHBT2Hw@B-POc{Dj;Hm{L1l2>S;AG9g1I~f>6VDl(JPXfLq}2G-RPwWV z^ypH%Z{5xe+*(R~RA6tx(njgzL@#LQcHX;oZ9TzFm?#QMvwqz>n`25=EpeNXng|pG z!`yv*E))_;rUNrYw>veDDU=`Y*%FqHBlrg1)X;mJM%bm*_WQ#Rrd+eSS$&z6c2ni0 z_Hf0a2b>mzr>$wYa6|9+ARO8{k<~fJ>ERag1Xl}_rhAv zF`j5>mH<5qQ%`t*6zP_S;Q=C0kT;$)$Py)F5z_43`pECbHdnvR?Bl*q2)tJ^z9Mbw zY$U1J+_Jd-S^sL3%HCGL+w2my#b$PO6WX!*rQMV-q;@L;=5-FUez(`G@4Wm4JW+Xw z?sM(Nv(#7kFIfHuv$H9HM*`=wHBr|WBeBD%yfV>@dJpMui6dK2tsN+XG5X4WAz!$& z*IL2<5FvmT1`Vx{)py6dk?4~ZqE-l2C5rxQFE{R0Mumq0P>o|aKl~fzfHUnyR3(XH zaLi}(0_r*EDK6c)R~Lhid0a$jDsfrWva7zt^8U-U>^m%FF7!GV$PxlBHDm=u{D;U= zO8~cXUK>Z5Dw0cD96!-x+rJ7*ca^>*ukuqoX8 ze><9Q+3(mBaTwS=#Gz@?O^n*;lxuW@k0R}l_4?YF0>I&8q#W`)tkY-9^}hNm8RTK0 z0)wWGWf`+YwCfijs0K z6Bm4H?r-(CS;fdSNvTH6yQ=_ zgW0;APT+|*z>aQjm)?66rPsHws=GEOSmn4%Z|Asp%pk8??RK)NbyYe5c(Yz%xE0r#pQnYt&!OQA5_hN{WECBa`*yHesZ$_*xM`ql^it*c=0jH z6L4r3GA29{&br;HqZd`38#wT_?`0<`tYU8AuHDeqXGOwT#-(5Fo6a%nj8`R~#s@zt z%nr~2a0(l`KisM*lvwun^&V!tguaRewc*qyS4B+0`vYhocuGt=~y7Y8ad7UW4;dp>33Bwc(D-|nx1IBpP<0o%jY5~#?)N;u;JdS zdUaL7cY?Fz_DGcJaiOZ~xi8Oq*)vKO`X9mj-L$5MYlAs7t?z!62_F1AA?fmO>PEV{ ze!U{{^|lq8l8zttNyGNN`znM-ZY8lyhDJ3_QL-YO%3ioVe9BVP8n{G~rD$1CyFcwV zGyLvWk-I1hm}1jJh(nH1_{lfokCU0%A^o!m%7G0nRv1{8Q=`x5KP}dAdcJUf12qL(wy1TwpGjO zdGD4f?2QnW0rcxtgv6mH^x)elUvF8zOMgi#x!5S7QCAWeh1fjVucsPG(v~RsstP12 zN6wPc5Rbv_mytZA*n_BLCJ{q$(ckqd`m40w>G&ZY5H0zfn+sscVbmK;{g^qQh_J=+ z;N1QpZ8QA-a)A?V-vqrfs~sez97^|gu6gv@&F%>ea{l^QnXBH-#ewXMB2|-wZJ4G6 zSN<#kioZ%(>PUq^5B~qFAq{J^dY((WFeW~~oV+}42XNpy5q-StTpRK}b1Q)Qit#;M zK11EJe2UiM_I-K$ ze4DAhm zdDgMr@6(edn=_xaydy%myhX;QVW*TX~;LD0*Q{RYOc-35^m$W z;iC^#$`6A7&l3L9cPNdxE?8xTWjF*f1GPiBHU|F36xtjZe1MAR`=j z3+fv$1RZvh@U46;Y0$U698PdBxg_-F(7~BQ4|!_OWj0Ka6!$o9yud}X+^KhlC|v>V ztm39InInOYaiEdeujxP#)JvjF+*%~FV}lK)^ddwFkFh;q2?7U>>MFrEwPDxswpdeA zKbW0t9T6J56l7Uwsi>5^V!^K5`1T*F*InOrMBQ;8E$^j8EmuPYSCKh%PDm?{B1=vh z7$3efT5Ic!-BB*?G{(dm_o7^t%zSKKnuS(fjj+xt_(8s&bhAe+YJq5zXr@9)2>|Mf z8)YW*mrA%_tmib!;RR1!+8XYdnKtYoO%2vB6@Zw;B=1W${xY%Vs%DYv@5a^LZsRJh z?@HiT#;x!y;LSgqJDjG3QPAAnobl`j3n!<5;6d*G8L1Q*Mj-aLmIpUHR6V$Ghs)D|a`nSUCA`W_=vW??WeX2io?v zN1`5Gd8;uZ=s!hYQj#oruvnOqSOEc1xKK??N<=K9E{LwFe3Us$+CXoGI*;+fij#t? zJ`d`#T7}tloh#pU%MK@+QJHu7RecM0Sn5fIQCg7&sPJm6oda!*66Rzy`i;0 zp~!G$YNMf911H5O+%mnfkfz4)+-sWSLEi_326TNwba}7L4-JBMdyuUbit=wobG5EU zv&ct|ZjT!NM83y6*d*X8&ElEiTh+Q{3th+&>MPI{=bq2rQF^WKv!7gWu^Kh}&;WY9 zx6cghn4iIB4vo0;cA-ocmHM)c%OqJ0c%lgfx(WdKstzxqOMK?qZL1 z6mF1(5n(0ozQA@K6imoU73M?t=B$(0t-_V4I*mZ|NI#E;Jl@&#hei)Uz19;fyL(N= z>TlSh-m4_q!W?MRN}E+6TyO{c7@ScH4D+=?qD`vK>$3Rmd z>!r-^no7J6iK9h7^!lo^wA&CoqwlzTK;vDjIJSDq7w~iMTLv$mXJX%ZZAmW(EgDgF z2rK#Y2R@IWL5Hn@ra!3Gr0-;j4Ljop-j8lc3UXkJ1G>z7A_2!Nqq_*2iS>AkD3H?Wd3Qi1#e7bJz!O%7y$c;-0)Jd23htHH&S__nNbU}Py#R3iaAW@ zns?+g?w%e!)M%`%EvBN%xFo&zE4JlKko)wN{eWyiU_=ecM(e#+sFfB|N)@Tdp@xSk z;E9QUUL}($-*LDZ0Jh+rx>#z6;~NCJ)EgM1X8p^5jtJcMCUxvYPhZ$gFClAHQsRv@ zOoeaExz>V1pAS6aY+^=bD!+wEn25=U!z3apr!v+_9j`Kab3u;Gtv#JvSj<9b&S}R*4AVyQt>eUk)Q`NRNu0yyajbWfJGP79zeGMF%bn@>m>frFKCEp+t=v{QbvKg zo)l)dkjv0`*MQx_hD`a^dZWy;yf1iMen_e361*ZZzMn#^w~V;A!6S zjhXkvuFm2#t^)PjVWr2WP@%&{0?n^WGplOepy0SB6{Gh@KSuwT@76DaI}KI!9kJL@dJBSReql| zUS&Q`KD50Bq3Gepaas+vJ^o!CxX^%4EYE$`T%;SZ{T)W3uT(&egfX^iLe3ARk){j+ zZsDk!K+~`p`Ga5#F@)k<$1ZW)$tIak^GnA#&B>h}-TEE<1I2dWp=4%jTqS{#VA^F` zE_&j=JegD?R~t@UN6KQI+|Tl{Yp+ztEQN;hVRk6_QIG(~5GH(SWQr}HE{PF|uVAC7 zmC~jq6WijXY|gaKipGQw!(p07iD)`6z8(ttVW=tGR|i?nz|N>*1K<^SnopKF7u+tZ z<#p8}a9=QDugzjM3SGZ=e@Nu&FrAGe?^kGym?hyTDztRZp~uCBJ31GF`QSs%x&q5& zBgv5>l3^BoQAJT~O7i0euoPrrJnyFnPbfwF&YEc({?=~(LV)k$8P0(PdOF9W#j|Tp z@ArsTyD9SxupWxPuG_h~YvTcVwIm_DF2&6v(1MGlVI1s2xqK!6^NKS|>Hr@+owO3% zYk$!~Q`t73b?-r5F%mr_cl4wpDwfGp2|Et-hg7@}@?BLO(-1BX+ICW0S@f(&USu^qES8GjPP0PdbD z5@^XzoV}!(w;Ms6K`j(i-6p-*wCyNv1DZGUN%9_+r#w%nLT0YcnhLU9W~WapLTk&j ze%s=GIQ)(3%%y&=4FsFV0kOiL_~TO4DIu7;LSVPrnVp z$W@fVDB|h2awP0?YalX5vND<=CXq}bgAi^ej@rb$x_8lOKmcb6Ji&G4Z^h)@ zJ!gi?Jg27syW_sv%_-4r`KHHpUZ2FreBW>K=!%d05&0+Kh}lh863<3mZG3i_K1_~; z&(4Ajk=%Z`F{2n#){B{J$R|7cON{p+n!aCwACYU?2ZWt{$Jy zh_6MF@Tv99+1Fl_dbQ?JW>>vYe=>;j*_yYP^C%`q2@aBr$W}yQ9E{a9aHynjaIg!> z?F6zOu&21vd%0wA9}De&Ji%#gK6C@#U!WH(xT5I!)bjEdx;(=qNnGiD{Sl(Pok4yb zDA*XWE0z1SNO$;qN?F;h_Ji9Mj?axFHqjwzBb75VNvqiZ5S_}BqK zw@?&sX1EuwI(+*CajZK=LYeoa3Q+wi%uWwQSCo>SG@d3n1Ka$?dob2xW6X1E^@+Z< zlE!*6Ao0xFFlA>Bbfg|PQ>VM@D;df!Yd`Qjx(-&$vz{PPzw^Dt6In3^6)brgWT0XC1eIc(T~kn zytMDcTJnN)0BDCgdrVG~MP0cZw?;N*qIB1Hd=~~7*D{SgG!for_acT6ki7(&I%U0g zn{Eiav~5a?QlEO()!C!>Fzh@!n$7lou8CkQ#8KwF4F{0a+^Ya5kp9GO9ou_6u`6$~ z<3$C&Fu`8#ddbNUt$;Y~?M{`nbcrT0lsZ^JBi?mx$N6M2OF`E7VDjeOzQ;sp6sGr} zHqVi?ZNnZ&_u*_*p_kT)a%pvtD267_y!V>KBI=XGZzOO|StIY2gt}(ku9Q*IAK!UD zh*0wIOTPEcbU%ulMw17AOb`ZVfXBEvd3Gkzq_1HFNhNb^W~QRYY9#L0gV0Rx3(d{p zv|d$po$f2xTEwD)v8k!Uq=FD=>?JyESO|JNrLIR7QWKECA}5#hUj1X_txWZMSle2v zy5C{~s@G?xEvK|AZKkYxs;G35W{I~{+*M5CEHr;4Pphqn^e-L- zw`zjN4AolQu&a|A>3)&3q}K+%lfZ*!EEzSvIiq?tQ=T)BJyp{B9HJTRDCduYG_RWq zAU(FbPCw+0%tL1~?C5PIvpcamJ!v|qiS{2DYoN;b85VeEY*^~EtHxoLCzQCpk3u?; zK*%nM&FDDTaeDF1ERo7y8L%a%M&atdBZl$)UQ&!KU;-cGC$6>LHyScbLFnq9?J19)02K6`ih>YnWNx)lc>zwgw9Z-m;iPoOaTpBM_#G{NMh|e_t`;ALMpq=h!fdTX5Fb zC*Q={oGOElJ(;+z$r#9rbDC@A1Txo0a?`}B4O1dkU(}Wkq91fqSuFC z+)p=Q@SHJTyCtz6IDf(Ni7+2u9DFZvJR6re{Vlb>tSUZe&NXMA*Uo<_WKx+D86ci- z4J$rzC0_F7pG*@U$4R{O~v#CjJl2p5ea=;_xV11?8t z+LVPPy21sh4XZ<5oY;*z_7x5DEJsqjdGqP&7bx-lNAv9qu5YVY+NUD+6rejqY8FkF|1$-dlpS8MRaruY1))Q{@VwE7W> z9ZU3aC}DA_=OrvGU+B^MqBBhf}5-4m+bo8{VD^xxm5EDAu+Ef3T z(>u50l}ddFxWra!8#wV=pgfousSkdV)XT?HsNKq4Q^bej4}JmlJ)S4jdjy&8o=g-J z{`!j9dp^DPu#C~ur8?+I_ie@LI&}0ocmI3oM)9q`%%wjn7#@Sm*GcJbQGEkN7QS1r zuGjBC84>m%?RRERJ|FgMzMLda?TMOv`|in1W7+4!UaFpKEKKS`fO%k{i=vZGh-37P#kIp7bC2*y@LWwl#|^?aM`i8dJpp> zztY&6?%JWcA;=k;eT*$O?{0dhdKnRP^x|Di=iq+gN+enWre9B1mOu@j6Gy141P7QdYZW&qExtME5!?FbtK0j zc+ZnotOS&j9afwE?L_Zl+=@<)Q;cm0UKNH^);1eB zrlH!aRdcEAi@d_CD9QXkl)VL1l<(FzOe2jV9RniWAe{mdO1E@_ba!`yG}7HA-6%11 zcXxNkFuddcoTEPPcb;#3A8XC58yIfx>)QL;y?^@w0tAE|Z(YPDvrvB5hPK|>faX4g zGdKW-_k#mP;!Y!!911Jv$F=m1F))M{9q1c_TBGb%cR1MA%G5s@wwNhxSxkr*}7oMkud&DcYd$p8zGny~+ zHrU>z%wArJcd&XNiyiQJLE!r&hmHH@kU)p!NsX=@i7;y#SgzoIa#2SM7V8-Ef2F0e zpf2jGO&oJ|oOqCK#$*=_Cf(T#xA4TS*(h0w=Lk@Woen-2@5{Dcu0Ef2BksPcuh%(L*XT<1Ndw=L}_Vhf6YDq<>W#^tZ2H5M!(!>fp73AIA=p_qZU9& zVFKC-8llMgMx)i++f#j_Dqx6Ah+ZnW?|DC0y{m%8kdeXXqMs}*DTc;`&E-!Ll1R#% z4yxF4)_c8eG0LZ!!$}Q*0dL^;`6;3G#$n}d)i_qS;c0iw3BPZN^M|@nj-6pSk_Q_ zx>ntqg}KpuAaTc&N6pnZbtn^BUI2Wa?0u^4H4ZLiwuaTG#&8QunKSe_!7wdlX(S*c%QEfwenUY##)1?2{lP%e1XpP; zf|esa?56aZRu{l}$g=H?OC}w+tr{y%>+5GQ+%CW#6)2vDV$ERrWoHK&sNyb+a(f|4 zRFBh6yj1HYfZX2l4J$R>IWn59@qAbXYtlXO8@$KG(XyuZ`Fxto(0Q6?k4P)%Nq@jo zH|%8OWsA$lk*haA`H9Md27%sm(971hKyzf8*Wqngo+ zpLEve{agF=%WFT<;)ln&WOwsCBgwX^7V<$C%lZQzk!S(~Nm4tb*x71%?Cj}j$1ybv zi`cJU+jOCE%0cE^{4x(yOfveloR@pvBNBJ}!J{-j-7MNR)q4kujs9FME<=HVK(ROw z;-`NTZ*lTomXMC_L4`Yi>)^DD8?E5NhWym6) zA!_x;C-a+Mu|;t`ngmVm|a$+S?Lh{V0%&!puJ8wo)5#%tcTFxzqjE*7Enk%tIV;`L3w=prlG(##Bymc@ z3l)b61`a^oZ_!A}8O?Zu#ejA@eMA;Q!#S0%3?2Ejy?UOklC@A4x+j;JinaUhD4kyK zo_OmV&}B@NznBseY29viQyxM^@cXRvGkEfHr39Mu`y`wMYYyV4@UEp|S7wftbNG&k zAO=Lqrq1u%A5mz!pUXRZ@xppWahYqhV2uNFhq~rx!|K%mtL|Uao`U|d^XBV_AkDKL z5YV%>p14_(4;%^fo~M%9cU@dEe`Te_7${^6={&BnUo)om-vXpIlGv>}Wk$xH_NIk7 z3Y)ny6U|O=RD(ZhQc}hkAH_S-P*bn@B9iE?w7P0~-ER{)jJ@}OSbCt#%xBQ2pFk+3 zj?o7})d0Uh7SBB|Fp2~J8Z{BpI#0QLzgkXtxoT5XSnq->bCe1I66}koTFefL$#@Ct zQ>u^V2(hU0oJPB$*UGZKPt7f|)Cqb5n-6U8ZbE=u_XDMU$KH5tz_uUzyXa&qa4s{| zphX>W<4EIG^UO`3@Dsz%vvI>44E|;!H!Q@JbLK8P5#Z43+FRC1-0v}tr5{D6E8>D| z?>w!UFOm&@j@t6Uj<%#i!}8!w(Ypq3)0rc|+*kqae?G~wy4;yUZh`?^apv~KlJ*I{ zuF*E|w`GK~r=G`r&=s-BWaGa0^*9jU=w%WMC~Ozc(-e`;VOB=}2PpCmO3^|Gz1~vS*1krw`9hKMfT}rhOWLAkZ9VB4Bkxhg#(uM=g^(}_8EZI2|e>cxTvsEN$sngW$S@=)AL1613z?Qyre zFa`!*?G6Dmw}KRJOy+zG?Izix?LVH5UGgS9|6C;G99@vp3I>o;hQfVfNH{$3-7cfC z$0D9hxO$5sCw+c(j73nFX-g!cm`kcQG{h+o{UWO&s}a&YB19FWWlPJyDeaRU(Q4nc zdryZ?q>RsV%24ev8oGM5ayf4OsTd80<%w%E+np=lgpcybkjQ~TR;!xnyggBPl%n-! zS-8Ys_v1n^((^sjZJIZp=On>o8ge{CZNqscRqt1QqA{{y+v6u^yUlA{>y-?>F0QjF zB#`F@djzWYUEiT?&~?YHxrLg`@2&9tg=V?Lr%%R$k$Foau%`uf}4m>VGK)FuzPs2l{%u z^MQz-H8e}h#B69qwSXriUm@71M#x1$NQn6%YsdHG>r+z?VIkj`pzj2jlNfJ2soF1fD$xs7nW0xcZ%Wg?*^$!GhyqCLEoU;$ zxY4Qx&=TV+O|6Zde6U88)97!OTj}seusuHa59RcA9tO0sH2mPoD*l|wR#*gG70do$ zrVYA6EReeY=1kHS;NQ~x`Rqx!1SB-`e(8Ck5OfBKHbV2A3ibfQ&0grg;n^jva6MP_ zO1@w}U2^B~2i=#InKd0W>~3~kcF&d;U;A=rQz7a&P>punYTbbz15`7<^$A*YeeXH- zUNAvs@~lh#MyeZ@3oNQ>I$8ghL)oA+L<59uz*&Q=7;KFqr!pW)vJcr4%GkYsF>{?9 zWAhFRXt|ORiq-UdV2{U3AC8B$*1!As+%9UE*!XY1v9DrV{Dk)rPljugH3Mvi?YLv; zmSKZE8^Ij)Cv5VyO9kZP+s8%+DY*JLvP_J(Qm2n2pZx%{NFQ(n=DU9p0H z)l|%w)-^>Xvm4j2BdQta(?X!C#m{IRr}E;#W;U_L>pa@Sez)4=`P1h~uo!mb`Efeu zmOg2 zh6UGozYQngb%+sW-kLh{BWpCW=6ihDz;R6Cm5j*_zxZV@N@mU54nH?%7ecaDbQuDe z9hDWef@(WSNm0i!p0Hhyg&+*wD>6DrZkFDnD7#<+Vvu%2^Gke(aj{9PeNBVGdxk2u zHRr3I=yA(4SYOr7@Ni96P+roDd!^WNB->8IjBctVCap!?X~pVVL_YvTV;0Oru0)FK zhvO>Nw3Iq@Prm8i*0g!@zO#ByhV4iKdU=ZP6&+WUOus>1C`$VPXjpzkx1X7{7BKPG z*1X1!k5?MATdE6rUUp(S9r`T$v?J-Fb*&U1FE@UXcl`76oXw+Q0@kN(_NAt`*(ci@ z%jziA$~GU~)yxli2~Hd~}k znGC+V8%w9dJLB{`g(@n~t*9Vrs8(aE(p}YL2had1mYRdgfG+Ne`M}Ac~j+Sr}AB4mXI)2)(p)Sxw&_pW{aE=9ED8 zwKlSscetzx=SZxU7Be0yBK$353Re5}QLbIptIj4p1`2k8R`}ENr%UT`Gla@#@-+8Z zB!~L)>_gr+2r9MN2jxeh7F*hn422}|p-meY0&VMWRO(~HPaIa>qT}I- zD?#Rqw7k`4hcEti4pk|5Aw!q_S+{eUh!+BLBVV28a4eNGEou{9X|UTZzOvK2E-80` z;@z>QT>zQ;Y54V~#=dv+)b?KBT#hd=mFWS|UY);bu`fgPd;Yh92g|#e5rL>e!RF-m z=+v1vP@AWq>g&gci~%o%=niM@$L`dkZ`W@(t?w-+Z|#41kBb_RDw9M8a6)27#;+C( z7B3gG+1-n}bFA@;B<{P6+a_00B1dmfxc$lF_#DF@hqHw%IKOVg>3GsE`(tR#ha|8F z_gP3B_ioN$)5AgOuUCo<*p_GxPoP2rGUYuL(z@)(=CyU}Xhn)Fp2Qc$|NN?={lSc} zV^37fDUvD-%CXdY7dJ54h*R$3fFD%fhzyYLb~t!{E8w}ueV=vy3804~570F!|hdfFtq)%D)ev&j(UBI>yfXTEMCD z5z-MnCwzw|fnN{cf4ItauVlu zMiek0iQT*P#}Y&OlI3>sLOrCz0KIwJ^nnK|%|eaU`V<8a*SX%1*`4KrwU*;@ZjB81 ztBU>~ldIO3meg8VRxGm>(5A2v66K_89%1na!3v|JR%45vXH!uip^dPBIIkTd+`2-z zuQq?$!)j|PQ_yEV(oNzdjc>f0H##oIjuEloa?AX1Up8@q>DYik@BOA`@Div@)IA zv`FnusIMJz!m0?j61`IVfhekS^>tXSBCqy%8WH=~{cQ35dzncYzK-|@vF5qcLOOsr zK||@d%fKrJEjD*3cQKs@G{TYhuoE_UPPsZAUJlZAdN6)QlP+61y!RD#7!1EQ%mT5* zXUVI?r}gr05UaiuOLtWv6(~sK!qGD17+6*V$N`>TVOtg~H;kp|*TY z|M(Bwlt z%SgEYtA-kqK;4>AbvcWXaYuN#j?Gk@LN9rd4&=ye6zp8Y^1O#KTo;rD;I!Gmn;Y8pmoLtV^F4R5AKdzA4=Iq(jz)OmLy``mZEy!GMq9ee*@9*^w2RWTrw} zlsXKxDj;*!J;{0XDOPaS+6pZ+*V{?Obe;2cRpZRqU%K%RV|+(pgN*&e$NJ7I`epw_ z^L|#?tnzCFx66z9C{oDOj`gR^nKgEFbIB!en$tSi5Sq}qwHMKw+83mX$7=W@G&EEy zoo9M(n(P;P6r{s!CC_@by&DK*v)_^WT?FvIGD9rC)IShG#93)|xBPuUu21_H2XJ<; zpdb)IX^(HNdnF^$1rnf>h$Vz;lfGr@7@aGAf*{bGNJ;#+Cg#rs^35axZKXkTFQE-GfZ4;%L>vZfhU>HH}Ua1k9gJPR|T z%=dgI+iITnpBX9N5mJ>=6sKEBylBV)<-ro7r(d*2IAKWuEx=>7mL&vFzuJ2=<2$nBvf z3u(WnCj2boZr5n*bEHImD;O|a*%1>RHOlN%S2VMHH^_s#jU6Nt9c(8O_is7WmmvYY zWvM%QAl7l@@aiZ#jIMgW4WHExHTQvnFu49r6f}4Coo$v2@xWiUh)+YaiqDN)!s0hd&f6EGl{?*=O_ z!gV0sVi}al1zvf!?Q65P?*cI>+pS2y9@$9q21kpWc>4chu$zICf&FeZsFZJJ1%!+L zd4L-sWYPxz%67k>Mr`+Be@W2yUbMM>{ANI#RPR^m{Of))Vp2KbAFq0%|L78^CFS)H zTmNvO#^GJGS>l^Vj>tutnW;5|&hSdgC6!en(^3yP0E<&_X+9$nQN?9|Nb^XL%{hwT z9eGe#pHodMk4Kv;-#Z-hO2=TPMkvcNVa9(8mGADqBno_FB(Ly4lZ0>I9f?-(Fymsu zgTPCLLspLLlX#vp^GB)Zz8iwBn_Ys+(DzL54Er!4lg?9k980Q8Mf`q{QYs!wUjBd4 zEbmI0TK?6II6tZBKuj*Xhs<7+;=KM=(`Pfv9iN8}dd6e;DwUFx*!y4jV^Ia=jmE8g zt-@R~C?{c5M+=#8P6v@5Yo6DTV?=3ibt?BPumWv>;x(>!orTwi|nI#3*@ z%d)bg=In6bM|2KS2CR-;Gg-MV)@vbPtlGek$LaOpJ{$bZgo1TB2g_d-BdY~4bB)$LWaQDr^6e=7o=Z#JW?2TYzX4EKa zz14aX;jLipQ^(b6=3*3qkkv=iM*>>pyNfn}i`mo8+Z!6H%r3{r-p<~prL!aY9Z^1w z+!`6)OuC>2Q?(nc!uBTGH&SLhXXC*jL3d{ofU@JWH$*YA_8YEBNC=Y#FdWHqV9Wo2 zmwQPU5|;3$zNlRPDW^PMy|!HaP=yW*HwD93M30;UL%Z=a=O`5u#*o}_l`^h>$Z+Qt znEo~{6)6_}#hI%B$)(HN-8A0Yk+$8H&h%T4pndXJA!rqgh$ERCl$5D6Nlho>iC`JOh6IiPZZ zvic)2q^p~On8Gi}0)D;mK*hCt(&4)0rbLE?;0L5Woa!B&TLEZnfY(vM2sV|6&FY~du}+DLMtmuS+lJ53(H)H;^7d{1sffue&M$L! zIM4tM-;Zbv5zyTQ1?OX3Sjm(5*-hoWQ7N5| z9W7~kTKszYQ03_M4zCQX4+fd1UF?(A>Am&{#7cm`+pUU6s&gq`%f-EFtYlAnfI!cq$LnD!pOEkEsFCzX@Rbb*!hShrig0kFZQ-^!BHti<`m(BRR`lV3%?J zLUwn=lW_CIY1nwg9(E9kO*&t9FKm-byF+Pc^7|xP$wDZb?W*S**sXNda{2{YN=gK8 zZ}0ElzfWmdI+Y;zv9q(ke*IcbTf5@q)qe=y{b9uPUCwld)5SrA4Nv;iOucoWf6b45 zo`y$h2cZ=AKAjtQ?u=l^K1b02-^*_Fiw-)4Xcegk2H47I@nHX0zzPKErcGqWZ?x~; z3pangEi@Fv=NsRJ>hX_a?fSY0E#MZk{2r89_}nEJx1^-pWL37s>EdQKL%dnA<74>G zkiaLM!pk@}W~l6;rc-rBH0X~}HAhdQ$t`w-mmMozw}PsZyzIU&VB?8a`VTa(zca+A zu2fm->`s{L%5F#(x}6A#VVp{~_D6^8K{?#$bF{UjAtpX|iw7^Eo&v-}1bkk2;tWpk zk2{&e(-apwpj-jBxPD&bmb5(3_}_0*tnbn9w>G>9&*UFAuqFUbk$`oXj0Xos?=BHj zUGGczJ-nA>PUA;JlVSF7Dz|qnU8i>2>Fit@z}H9J@$Zt`^>j!1hgyP531*7*PrT8X zRxpEKR^?fnjJF)#b;vec-rNTF_19w&<{Ld%XhzL2WJ#~hKL(8tW6j z&|y4V^i-g);DL1mvU86g-BNyJ)TDcNv`103&y7$CX1p!>#Hrv{Cs!2?%zTQ!*9LN3 zhuIFbzbo?ceqMhX9(pzw6c0zeIw1LAY6#<>9;}wJeMUb%(InSr1_1Vcw`89McbvBF ztjnydIQn|z7XFqIjBW^)gx2WAed&)nuRrasz&3ge!4*h75@QJ5k^XwI6+~@bXpi5) zG7ieOKbbAkln?-8Vlkn8=r~i`9B;e2;di(C;~;gdC8@s|A0mEsl}vcF()~!PJRS*x zGgseBN9~GP$_OaKUprbY&bpW0uLlrU#hvfN2*}-LVmuVGuo6@OOxEdiSf7)p6|b8T zuUg-PnNOjlk>Btow$+9;?K{y6^@nQFujDV)VMIrr6R7oXXu(5u-yHoQtt9d3f5M7i zJN4}Fyk10MVPLDBIhm2aH$8yPmq-TbI-8*;I1QE+(Mj2IV3$gf2g!1_4w)`+ma za_5GkMNMVzel28qAV{Po`Fy7Sp`-;WlpKMz4>fIW#NgA-_nCs)B()0{*M4BxFzk?I z#L;1$9G&Hz12$HYg)zAL;{^Y_Wq5f+;2`Jsd>GPrMoDdy0IIlG@N%7nD9+V0IjxDW zK9P(vEQ#R=Ilz#^wkDD#x(AEDTUe!lV&qM|u`uqv*qrm7gKkSD5;X1C{7swyOiME8 zEgi#dE1JBY>XFMWX111SGqHT=md|NMpJu-|OqI$qP{LDqAHCspXNncX`Hvugi1Uyd z>MFq><52foOoFNKaP2$y?$$fK)tr}Se*Om|mp|t!#_^yZe{|6wcdi~K)p;>oDsx|; z4Z{uAc=eGk!;4a}FfO%F`RdV#WknEcB*d^nwdqSv!35&df4U_()tm20wD5uAaYRwL zt(Vf$omK99xQ4&;f|yE&1ZYAtBU%Eq78>(kwUGAqVv8bp4Gxfx4wa9G&pGww8lbx= z#~~$^hBYfda6QowITk5Tklw)v&ItU!^wuwBH176F2>bybRAq)AzN*!%<>_(1dcIOxc>yU_JwHbe1?cpFo}Q3im@1XL|F;VKbt~HntyjKC zS#NMZB{nOjeN3|1_S)@Fvv5=mR+x;nj&>l{HFsmc{Al(~RkzU_Ljl$&t&5)C!j$Co zI=(@56-1?ceY*b`xJ)V-*Ia58`z8dQnn}Myd3pa6T=mxh+0e~`8e8p2U>cbv>Icpo z1BV4096`PWM_ISZU9q6RVjv6!71tN&5aXpbSbXytXiSoX&ej$L+RC~!xYSl1a|_IM zs2}_($D7v{X`E@VE7#2!zM=e~W6tzjA9_(<0v-oy&LtcF=FfjEjBLalSG~x7J&|0^ zXD)*uZ|h479Xm96OuISL3imZ>?yGP7bXSEM0*z7Vxy}0#NV+?<#Y0U^7@_N85rx<2 ziX^h0DTqES0+I7Ir+S;M`(<}ebrBJ>zNN7YnHJ_mM^@(of3e~&WMmJ?Z&mYaW64G| z&=DDZ)?R?7J$(emR7;^cFVTEn%D+4y8AHI_DzxI0H8hN|g3gI+q(CN3S*PK#F|N5T z>=og}R*wh3NMGw2)lK2$57-#Fra-nS!Jt0+Y;qOe{R+W~hy-mBK+E44vEBrwx#w_jWGl|jOsR)&)*0|oHr``I>z3!h@0 z)Up6If9Irtws@BTguTO&f3sIejW|r-pG}5bFE{Ln+7Yib2p0`?5?3$^-E)E5*+x|mn8Z$4lo z^1FXl!;LMsf1AnYvu8-@EDxEKmd`>DofUDFd3>;v*iS<`iL@7o=iIY@yu`pgz;X~9 z8}WI0K1%E+Oc7G{_u|uxVO9Umbn*21ZmwMIeXJKMtH04H#*oV)Y=!3p7Kzt|C1rbE zWPZEwpyMMcebiU^gAulFtXj^8^n1~^`vq=#?I-+FRqwuzf{e`Bzm64znf{^Nzuz`~ zHYCIvpm&NMF|fTLvGD8EmAV}^>>T<5R}$QiM8nDYX5Ig1iWD-34kHn2uU=B zpMAYhy#-n$?$Oaka#d!~q!it7K1~{clSnjuIhU?_b|!!6$8sTRa&KN=+(~_WCDlAl zb3?gV!yW1Ngq~Tk(nRyN=B&2rRU7Rx!FH40yi`VVDa5^@t7b<8#nCx|`bfFOuhW8iS2aLZN zP;(2?JL%S@z3ibfZ^2<^W;QiUtX(%L{#n{qp6zL05<&BT{CG}+GMK8pEWiYsFWa?}?E zLnl90tzmRY7Nh(uNg!HlQstn1HIirMY@kW#hX+^#OCLV`Ow-6CfNwJmHZwYem82 z2?$p?G4gjlH?@2>%9ls9xbrTf&@v6B(9Lp~Ve+FYcw38vSo`)TJ?IK#2|A{mCjGtP zA#9y_mCBrTlJ*TAf1d#15ENiE-Q4Yw8}c9#wK6x+)Yj2j=V}J|DP5tP!=sBeyC5X= z=EN$XWfRo}EN{4K!$|HZhx)W^^<6aZC#Z*)+MLGVS=Brx9|9T`TIy4{%e1~f%GO6)&8d{)1aTU` zZ>%*2k1M@_i4bYlR#-FqQTqWpv8~#dLVLJepTE28*$X%VW8THVeH>4;I5_d#KKKbS zeqcCKjaALLZ}J2(*M5P}I+Mj`Zi;NkRhP@4ZME7C`|NHIaCV^z>@PiYGZ<11Z_7(g z*D2rJcSIw*=cpmkpYiT6jB|TVhhA+x6Z9&PYxpB4)4cOwzVBgvKT#{SGJA8rTzybr zoL-9jm*^b+3V{}*!j1{V+yK3ETY}IV&2IuyRJ(erwokkw2CM?-CSR7m9h&<7oZH~4 z8}0lYc$nzzIb^L6Nu0;p{9C^;;wV6W)~tGWVocev<@vWIDI9Vs^)A;P4mzgJv4++I zRc#EdL_=#aJ3m?^J!_Q0BiSzk_uHttm)ux zXJAQ7>U4*emy(f?35B+6u`c34=$rHP)Xd}{w;IxsM`)Y(8D zIjN2g# z)=QOQ?6et^>s!|f=YXj~T;FSn+0R=0h9z5fgehA_?!xwLo1s~00=ZuwFZ-_$1bz$7i0^8db)3slJ0Wt z=cv_`MO~ZRx|^nRj^i%H*6RQSG&H)W_V9(z1#-E&wsMV>{^YCN)4AQ;Q#qF+zgC(< zED66g6ZiMYf^e(oB7_RIKQ1BSq|vbuWLJ+a984yM*jQZGbg&rV%hM?--F~{3A~5BH zZZoen#e|(Fj>t7|Jg2+~{eo4O8A=9sr1=&F_8KqJXE>@{jlEs@-JQ;4%2cX;$k7%O z0M@j2PXs2k^{ua0O>^wKA)iXFdQ4nc%jD3rlWbAS6y}Fw_tCbfc28o3_xQoj_0hx8 zbkS{#mIp5j3+F!0BW;UNS%n8)bhSStgxHIZVEpIc{dLYl>c2u=e7Oie8N}YCw7(5JzuD#6jBH_AG9#xz z(#2%XBrG~&)JNIW_u(>#jmwRTn+Oq6d66+3bKF&Rik`p~-N6@y| zUE9oiT4iGjYc81`ziv$#xp^iseynLaR!Y$R&5VC9ElLEYucI&8N_DAsN-`I3j6F;@ zuG*{c3mWCmO+=e(hD|)80`&+y#g|Wmj7vVHOJe<0+>rQ-JUZjZ z8~5NzX9;zPrR!A3c+})X^pVW)zYq-aduoV8tF?~>(Ek!>j#4|&5gkv+v7qew4X&1Y zD-&5U5@onpmwrc0cS$0!&-EG4in?>7KtwUX1?yHTBLoLGF*o&oBeiWynfn|<_%r#g zJ;Gy*_^QWT=lh#Hvvl0-GTxaDRTrP+>u5jKyT;b%sRx$O)2M6pjj4y*Q&|a#7pq>6 z<;|ti5QIL0NQG%UNG%=QgBGrx+xv@Hm>U6A9}1p$63AI)E=Ii5C%+vgHzel3bkt#8 zZedrn)aYsE_g@CMP4t(Koi!iKRFc>tBap9iI=*R_C586odFL~zEP23UN3T6R;`t2TN+t!qBq|fxbkbw0maAfkP6H4N%uH8#X0IdE64>w_ zDG@QgHIc^0E=4W>ld zb}N7B9Z_E9)Zx9hXL3jMxj!a$4S4hEuR{zu*rxDt?UCzmv5S2`pXgieJ+?7G9(#l< zI;}y&!52k#Fe9|z>&pyeGg#?64XtX`EKIAu+n0;qt=FV)DbqN9F-N|sK2Oc0K_E># zYYbvO?2*}2p=_u;tLADki&AxmsQ`;cB9pHrO{#}=3cY$BOV3C`z}iEKhBk$j3c!#d zz2=k;fGbHnA45nE?VyO-qoyEzLE!MiSie#>=aIelVHal9&1*Kns@Rxf2_cJ9_XJ=3 z_7H}EHmU(J9jE*j%ghnaPbwh4cj6Fi*sKL9n&OxM24T$(`?SoUiFxN#gyd>`!%tsC zQ|I5AGSz={hosm8mSz&5ImIEa0+M&mQFLdXY4d8jgs}HD$vM5fIeqmq3=6zVaJ2kQ zo4h~Ib@?ek`rt`xZ-OacEv9$f04nl*uu8hP#W2`>=MI6=`OS8{F6!{pwpka5m#(#e zDeL3|^=0ei^zz`Q)V#fHZ1Uyi) zu5Za3-BI~ZPe10E02aJxb8C^u;4xXoI`*6V6FaSZ@1Wx~sQIT7&(81d!S#Em!GDaA zKUb!>&5ezXR1i1Lf7(o$(^0MBfG>)Kp|ho+@TZK@K>phxQ=0^ni^=RDguf!O!U zdO+}(@sy`?XI0yaL(M2pc`4gKLPZZKv9>6mG4+9Z6+LHI(-*DTZ2fJZv9%mC9S%gW z4mxm#<`C%wH)viqTm>bnb)x5v)gtSirhGDU)S)+)pG@ZRHJ|Ns$Dm~JQ(>@Qc7La z62#f%zPjt8l_XnNM#&wUfkloq1?@!mL2%pF!xFlwg|7PG8cqy-p8eD7=fI*#xAyf76Pb3ltQXIL0tRB;4b}o_g2&b@$Iif@VN8Qy7?i z2W3@+XNtg#288v~(l>Szq+gdozUl+*qm%}g*zD+85L|eSd!iM&Fk)s3_?LzXOsKl# zw@*^69ThpWi(>adaYTz>l*7&t?$!bFoFma=TTrz=Y>#inS4y**2edjeBFdE;7i-qc z%2C{{(5C0vl}r|?pWEZH09#eh-mdZU(2Asb#qCZKbkkf#G|&x`_Lep?8)bGPk>LxJ z!K&hYkboV5kxt%oWx$1S18d=T+CFtVzn`?E??TSBwUZv3@5B5ztYu=F3L+-D94p5! zc^cHK+;+a_Shf2-nF`>aVk_Pm)XlQ8D;*c-(JNvvn4g0m12s$!tt zG(SMEaU}w?Im4;BHASW~SfiMR)(yD_&ZW>Oh1!nhRaAbmR6c3fRrp{v4NUjq?F3hQ z>C)}$q?(S!V}GsAd;O{Dd&M{Y9-^m@-0g5FNXMU@p~qe>$<7Ou406}MOIjM9Rsxjg zoVC$U+|uZ9m!A@S6jeQSOk?2rS@D8O4P%au8{C09p_-`ZRU35Bg>$jWJ#ykK6Lb9; z!97XE1!jnm){DOIJI)ICSTUYSGtNMGd;;N}HEWz)*SVURO-oIz}_1ve{Z>QXZB@UPjd6`{C2?2o0h z@|AwDo`R~IV{m*g%fPcqGdsnjJiXQSb);h3bu05qDec|EORq1u2m*;h7M^{hxjRaU&ZM9Vp1t<|6F%- zSMLZ;|Khk992eGwW3f_MS`wC%`!UkfvMRLGw6fj4Gtu(LglR{M4ed6NC4k@y*H(F& zx254=dCtc=M&Jta06ml#%;v$2a)akuUuhoSth>sTLVFDiozFR1|@&^a4HBC6Xe@N$nwdxKKj*oR?!yj&|7Arte0*EWbb7?wpJHF57J;9Y4+lV-A} z=9Q3Gsn?|oQB0P7g!p@#k^A6F&Y||0;oM3n7Y}9wSmY47s<%1PLoGLZZwy$(b$EuN zD~-O!eHJMve7A!cO9_0n{8>?OqOvCQRR9~@*DE2NAi-QWK1}f_S z2;woF@_EiB*b@8}J9XcJ>KRepN-#vgbN;>C(HV{YSr>3WB7ec!VpZ zzBG?!N|MoN7Y@CwncW7f(R|jRt8R%um|-D*wP){b9VDVj=9u=MdO@j)88tf`A}Gq2cm_uSL^a9nw@x>9oTfA+0mh!w$2>AoQiDA61(TM z69}Zx;C)^g*tU8lm%kJ$Lh~|0>@X$xdU5xW=ZLW+yZKuI*+NZ^`D0oFI^a3VRIEI5 z!!v;k+CYJNJ^hde^g{AV9A%D`uGm89M{B|s2>bsg&{1fMyjud059)Y&i;(Yq|CPes1RMXytxR-oT- zH(XruIXh${j-4yrM?2cPP;SFo!gvgjgM@UY4rg=1w6P|LQDc29c@d?eSJNbv`vLGu zVv7RlDm6h=*t^ny^4rtw%%*$6?rO@8)23By#qpgw$1@K}!y#y4zdmz-x*R6LHz1!@ zIewo=NTL?KO3`*oMJKXj`hTHOAu040PTxvnysD08!tAPBlJa50*=0Y0L-si;-V>b_ z$r4vUn$100#1TnE1!XD&fPUlHs8p@S7;SJ>t25R7RxvCz**CXIPggE?ThzaEQwOX0 zkl`I8VEa`M_eO7ZZbVMp$69G?4mnlMTFP0)AJayg-R{Bj;3=}zXz!hv%)_TN1|^;M zivA<3K#C#R@QU=X6oR0DNg5f%I*toL{-OE~(9yLzE4OP!xIONaZYhF|VjXWB@qwq- zvjmlATOnT=u4;Bdj;xH`UA}`C=n%S|iEm`QO_^&sD?IegI)V4~n>8D`rs9xIYa)Z) zIK=szQ*sbU`6V_71uDL?G#+$DcKL#LIbXYpCAd!E=_VTjyZMkyMG!Hbfn3GGTW_cA zEI!?PKWnCrCT(V!>M<1la11Mw4Dc$klFkEAVtrITh~)q}YK)jaM#zKjxE8;u>Synn zqGgfiS-&tQzPn~N;DEtF$wyoOyZvuO*dQl<;}u#{5|Ab$SiPQ`Z^2^>3zERIL0Ue< zlK0$OB!BIb<1cuoD8C71IXrGXpScMGk~msH3?=1M#ub>EAMlCNNcp~VXyN4sfGq8o+)Kpc}e$6c)FfO;NTMgVVz`SI^BCEC$`Q?y_1Vd$N zq9zt{FIK&#>9P>@McUMBZBURH%r&H9Fwha5yKFW~-?Zpt)?HgvcYDOVL7t-2H?bus zwNH*1eoj(t`9SzeOp+TAq$i6Pn4T$3w?f{_^f#grg0*Ff3I?H~>0H9}s0(~3fQD*2 ze5x-vUBfSIFFfq#I3=GD^bBgR;e$1Nm`TX4@0i&Y2f|n=Jvq*gW{mfH&1i8`X;~3M zDtg)Vpz;e}(TToyf>EFsfvhM6`Q5gX{JFNR%y3BQZ#Z+62SjiNlZ%NeaP7K%ju2m{ zFZL%(uT776oyX(Hc{0MN2*VHr4Y$xh$m$8hH8=n5VID#LD?MEzsT_1jmjBklw@HW_ zvh$Yx45%pwoewRDk1EotB*OBL1>q=hIK8R;lihj!Vw!2H@!f;ms9 zV6@>?=Vr%wbfj4m4wyMk)dm&EsizWqwCpzuTXp86ReTelMIb3o&LC}8cnoB6YDVi2 z*4HdcZwArn2C_D>7h(2d&23b?=yvAz2z7m7tt`W751mG%XA&kQ6}>v`N`>Ks6kZ5k ziC#?#bJ#JzAP3e=hvSe@rXIxkAGij}M}RvWA?%RuiEklS9O8cbt>yEMN>z1{DAZV3 z(hY}t2hM6_KVzHirVPITdxWG{cor9VL&h6Ia9on`U4lt}!U@SDzl7+%l}|DF_{v-) zZGv-sGk_wFTjG_E9gPv-DEX42GJvH@#W-9+bvV752>rUr?Xev8vs8a zs#Yr%QD32^%?n0t@8ghKa!rjR7T4VQvdRa35RI1?4BQ#~omT}H?IB-z@= zo)wnC;)Y6TE&YzxFDZR+e3k8M45zkeTRPDMClgD^olcU8xirfwcQv?6Za6yVHTCP| z00P)o`3Z*}4jim0=|>aH*FW-lb#cT3rP>%~&lvM7C()aszJ-2%MPLDUl1N(ZU2NA7 zmDynj5c;Qd#<=S``|Qvk?AK5{qrZ0WDGs-kuH`C#wNw4)3kn{h`~>5!OSg5w^mTq) z^;rQza=QD|ZT@xVLC{d|1FSYh6>L0y841~VoHc`6O1v~ZwOZTk;*fF!^1qPw7xkHc z$(QrDUp2F2yp8S??Yn6O!g$8gMuX+@OYq2QM5& zKG_s4>dP)kaT}7^nnC~?>Lh*{8fJAy7(Ab_DcT`S3wc&AAL2&nj+Z#suQS_&^i*Ks z?cpV;5JW6LE36mJw^toihAuEu=EgHHtg0GH7k+Q-I_4^4KlJo5c!It?EXt=I2+ zAoeHxn(#f)wpDRiNZ<=O+`}chZ)B!qogwZ zoWH;tzaIZDNZ)62h0(?dr2UIKzy6JAI4Wf+Emva7loXZD;jH$BOl?Rif>`+UHkbDE z^TP?>elRT{_}Z{iIP#2-C=n+>_jT|ruNN&mcP18z4;`7h*aGVVxi?2(No8z4X)-s_ zh9rBILnKAH1WVkfqvUY1p9V;ISx=KT&;?02HuB1e6>up!4Pc@I!S*IIO}Y0^=yEX* zBl`w~YJ84j2-vSIg!8$L&z38NOdjBHb`0c{p8Y*c__aUPDUi2ivzfsCl;NHVsLy8IT;bV^Z@aDc;v5 z1C@Z$-;Y`CgZ1Gj!Pk1v?)Is?8_!yPfW4*XDXWt-4XFwlYj%CeRPk{L=w-%+H53th zp1O7O=?F(w^BcxWmovi=ciNgf(TDfnnjbcC?fOp;Rn=l2wRq=F72!IzAqS+?l!CiQ zr3$M3zpzj-el~(A%ewt~lE9h6@AbD&k15eJZuX!Ip6l8aefffMe_`Ifk$PphSE2Of z6z|XqhbRK(vL77J;OVPzm3oX>q2_xG6su7)X<@&ddf{@^}y5!dLM7__s5;jo37o_bk09?YciA0$)DJ+rJ{Yg?WnMO!QM(^+l+2*XFspme9 zwPqPlo0OIWo;2y$ez{PUW2$M8nUg8}VXH#I8^)-r##aO!w}vpbUu(pI=9Jvq)WtqO zw0oCjb@$<)_@BdhY0b^fYJ`X{3C-Exbe$@yR2P3e;f`@}?SRkdaoOS}*2KvAHRy~w z?mnENtlTxl%{IiVAJuji%agU}jKXuHPGUy972QU-!cDyh$}u5Y(fN?n7d~=)9~Q zq=LIjrv)tU=G#%}J&sFVuRKS%XJPX)SMuYjNA}iFU0AA$xBz5zY}>q1RQ&EZ2~uON z3Z^TuSXpJt+J`aXm@!q4+c45!iGeG7)pLsNLMhMSp&w+R*Wk+H>e%}ZS!b6M^!oQ?l$Q{tM0u*S09dAaPp zew)o)wE`!;=Em+PI(Le$o1E|ac&UAx?9Ca9Vfs`?A<4#>KmcQn;$CI6>p^6bGkf60 zcpP1K_vTr4G;}Gdd%*0|dEcvP_`u)%5?qLx?aMPVGVHZ!PIE7Uz7fuNH89J4fJOLlWrcKNRUcIlh zpD+Z4;V!I<7krb7{ytGF4|i<4!^l5;sb?4SF$aS;;4glT_JA!{{zA@54BuZAw)9?p z{#I)XAAd4ASF!Z87*{1b&0vjr`Mp06{n@E#YU5iP&)jwBaDG$Up$n%5m>zD24-DSZ|D7)Jz^-y-G$TuRT|?aG|2_dwW>vt2t_x2e2Ur|@ z#l@@Nf)8c#iX6u=nT>$T#TQ5|><|?1@W?466*}p$I0Q|b20s!IV76Z|A(VOs8G%V) zH59(@W1BtY21I>`fu1QHko+##+yH6^(FS7`j_1+UzuXv8^3uwaF^?6)YL6;M3QB;olI5*2RCBSaON4Otl_C9vm z2MJ;%5f*g~p-Hd1&I5cIG5fp%@VW*?L0%6(DShgE*x4REhEv}_e3>E-@Bmu`>przc z#Dx2@n{7j|O??X(k&uyYcG8dG+|Hk)@S9}iv*U>%%^vQj28@gYOX+0+>P8qc#_P3G zr5VTg;2l6|)4B7RT{RyCW|Ug@0EEI{`msgRM#((BPrY+QCb@<|?;hapN#HTZs1Ei{ zd@kd1Nv~dq7o7zJvId8}-xyBkMPI2u+q6krQ$yw}p`G9ix7dQV!}5T1;Nf(uuo+syY>cSPWp0|Bh7L6RNA001iUIWC*WEmlBQdiT_Y( zoP+UY?U@yU0Qu~dv{)@Px?&rM=W)I2mnt(oldLi{K9t=T(hq*H7nt)nlbj)go&h)7 zxy`{Q4i+wK*O^ZV<*fgNpS%>Lkz}^2@LTImuzwJVUfzu(A2dMuLIpE7y|p2m(xuMB z*8^=$V^#ROh73jBy{Q0*;j@spe+Og=2yo!3%zwCg=Z!D3jybBC!1$h!w zB~~2@jil*->O=k^g=*#?BAxWvYF2+OU~gMKG7vTC&{PBA&hOfI{Z?*e?;*b_X@U^k z<`D(-8|;7;Q{&@QGO0=B1ejQyA#Xa)B^p7g20ZWo=CW3NU2q%q)4T}V{C?+CH&;UC zH-R>m-^1?MO>OVkSUa@LBCud4%-KZFjb*{ zm72$K5gIPR(G113%;GU}f(C}A7t8gBh3_xJW?7Z)0Z#nmrRT7LMK*{|?`30!XsA#< zImaC{2pD8OrHMsb^a}5^&sMuSV9t&4JvP~P;oiLyew_GWVCV)s9L zC+iUk-BX! zRI{z2ER%)Gk@W2}dw6(%q_3DtnlLihGzsh$U@LsQHl7 zfY0giY8zaY`D5j4RX&B;N!_S=K`lVpUa@-sSaCdrsp(bvb~29b8|f#3ZH5*|3w4x( zZM<0pAZ2InNPN;lW?I*~|6-4dWU^xmza5j_Jt7s#b2Q+L zzG`;ZrF{>|ns9u*uc&8mtC`nyP4`3lYAEXDxnrVmT#1gjh5c6XCV)BC2^;A|ezviH zq;4o0q}3oni>ljv@27t*ITxeTlRZ$KqRz^_77b6sTNTmQLI;f$qkben&$d`OPzFmjr!%R%M_?m zuR4qM{&pTZ$=2%_U~W5V9zpG>YfNjt)bF<1R_+Ac?tKM|Z;BvqyI)1+qh(VOMaN5P$+qGVz7^$iuKxhaw1kl_?~WwcvlH#?+H zy}z5YM6-4SV;Knn9b}u3{#LraeDAh-VYhf^7Z_FvB_{3(-(ItHQ{$|wf>=@_ALcAm zp_rTe%#T4c@`up;#YF^W@LP{#x`|(C3}?Cf6P*aT*nmf4;9AVN$u)!y_*4M<#B+|d zk|S*aCZy)Q%qIqNGkuNt3*r1Z8oow4-!|oQS#-~raq8l4{-_eRbe*GH-BGVRGxZ`i z{O^7}QtZJ%0ivG^h>m_cvjqmsd$*T-w7GDa5y-(gC$gR05_m%Q65_Hh#!a8b4~!?y zxxYh5HD*|P6Brb`15uiA4Q_oJaM*_EwH!ez2AQC|(NdsIT7~y3LKNSQ;v^yB0T?`$ z_{NbIid@W3TTm8UIssSK=Wk@by`>p7OsnWEH^bGbhK4zqElVRiwIbkP333d~{M5QP z)CZ1t(#wY)TyBbZK9Vmz87zl$b(K=>|9E#e`^lH)Ru#b^Bd%?W1vXg}QFCuw=zb*n z4J;O6PVN~@XFFKt?4>sM@@bTo8dJCgWN!x1=O#kAN*Nb}r10lHwJQbgA`!a#)cr}J zzSNW-wyBbjziL=FSPNO#{*ou4v-Up{l*wu4&xibwbVaBu&?Coe@hdI?2{#vGhKbPZC%rbaiG*RqNHGWR*eQ!DtcToX(3E^s!G&GVkA3 zo-B=sR8-pf^%+KdW<(#0I+@TYW<jp&&Y|oD%(EY)DS%!Dv9~KhUmWVr0 z?`^c88^Y)oGnaM*uYE{1m%TOkv7{|~W55I?gjtOHwk)x3n@5X@} z#apEF57r!to|$UAQ3LjLH*oH-a%k48YRsJCMK?NH>-DiV-g(MWOL(r$Y7wY|H>GTT zWADVr>|R*?qo^e_4f0gq%=b^`H^DhO<0`{I(ecKWoZXBaY_H`vFPJRVHxJ!%c(=L< zw^^2$K05|J%aaMFt-uEEkldid`aD3t)tbxyqblfK+S`5`k7Z%7)nXEGIfd zWOrL-F+l=+_h%3x@{oB$k?o`$>2|NEZ@|8&BXUc$Q;p?1HVG-Ru-jJMH8%t*w!<_` zt+reCx8PkqrsgZu`8HeW3V1)FPY>O>wUov|rCBYD-$;D^RfBoJC|RM&K+#Y)kDrWwfS1s(X7&{gZPW#;%?sRG&3}N-S_h`MUjQm(uGJ3 zt*Tpgb>@Hwi3&hj8}q*->ZAmjrC1`F`!Mza8RxerMhhAhY~pLQ|9*fx(ASzSF(>$O zBhUaY5PEE~OYHerDpYqvi?ba_lymmu^&}{DFgIW=6y*ocUY}RitPQ}G9hp`Uj{h~8`#2|&3mexNDr^jNivYVBYxkiZ83Vb3s{&FxBdn^v<0UcG1kfZ%=#6aI zl8yo)j8PnxW*8gb0dT8^nQ z&$94kz?5rC(`h~k)V>XQQuQ2mv4QAdnoWJOL6UsR7YdI=>WQA<4gY%)9GCQqD@pa0V?%&a zp)PMdHeyo3eD!BqMOi{DvszIBnVg?L+qJn+_b%=K7K9wFGhux*L29+Y&)EC^O7#vs z1}$ft00{LTEQ>h!w=Tq?gRZ2^sAj3Ks7Q(1!*foaN|stI>mr~-IHp|jc^k$Zd~LWr zPJeFPDf)?3?e7!#Ek_24Pdv2fC&Cz@qlT&>CPx2+Pg?Un`CkWaIF`R8X9Op8>+7!m zJ_yKNZ%jL$D4RBDCJJ(Hv0xoeM6NA-dtB-xx@xrUZq$0AAf=H#+3kDcqYoAfqgFF% zt7>b@sb(wI{S~wqPjAQAYwS%m@xE7R^+wticZv2_JNYsYt&A2nU=;E}8+$q3Y9d&b zX+FWS{B$tg;vZ8T<^&mak%lI0Rh(VW@OHXX*qWD>h-i`_ zK>dk9IgcZtZqthS`X7x;x>oOiH%uz89d<$smq%GC)%i-@sHVZBWk4hecQ|i*cYnQY zJyC?A=6T@zf$mi^o(PD8L+jEN7bI)hh9#*esXgJu^=+Fscsty>u`xVIv)d4Vp3Lr) zh23ax5dO5c3wD&z6UjtVkM@h@R?Ghr8{-!5{mPn)8n;IL|9S}E-zp}RQ zjVrT}^-vJA{8b0&OnP@ipmjq|y^tsy?v_Tv9ix{5fW& zJH9E47Lrr%o*7Hq>L+yBQ#pXS^tBf_Vp{@MwCw|)`Rj>Fde3f-Dz%1WggUFaxhi{0 z@JmMnY>*WK{4)h27Wj1QG*O~!u7FgsN)R;~mv3|E{R&FuY*~*PHROL~`}-3U()o4Z zn+>gwt}$1bm3eP0ohD`u8lLhWdydoluDV0^D>Ys4wV*=X{%uNM#&rkTfW(yTf99_U z(q;za!1@h7y(F;o+?zB-zAb8}mqpeay)kPxaX+EP1)hH+AY(dOS<~_ty{z{gn@Dj? z<^6fW(n2|R@Mj0{r>zGgjM+`PB?+r4H9+pzavh9wjMwVT0grL(NXhJS5fd||1H^QO z(JxH;oExCMJ^Xo<*A5Fh>G5u_qGQjorG4f7KjTE4qAF|;fJlo%unHVb##B4;eeJjU}S?`(E-wVDVt__>aUP zf=|(6@1W>Wf!Hwd5EylLf3Lmn0V7%sR$xU9{u@L?>g^TKf4jR}qW%_=XN1J_yl7*? zaL&H9+a4JxHywhd&~n~L^1WF%zba!Q+FeR~GX$y+zn0wn|B5+w3P*Jk@cd|f~nO;sA zQeUD=NR^yEKpM!pf?J3fOHm1QXrH$}%nJy{#bn>3Qq!qMAUlyb?F5X(aZ_IoRqLtF z1`E35J@>mRVg45`1G!7}J+K2WYR5?a_p{`>5&89(u~9!Wb{oCoO=4+?g6?;P9CR_7 z^(@H&Lxt#RrG@YoIC-mTQ^0X8p)Tlw=ph8`OVdmF_sM(VMG}&e_td$w(LaCweDt22 zPv9@n@1=E9uuRT9H!Qyu?FwJY?)8GRH^@H!OU4aaO60*u(=S4NQ5o7cv|cUiyN}!( z-?|SuUH_Tw()CyOHixbq4)65xVJ59~dFY&Zo-Vg-@2?cMfk!Q|EQhpA#Bn02OJC;A z@^2<_pDy$$QzjZCV~;3oUmi_wcRiV1o6F65JHS%bA^RFWTq`*ly;ZXdO%Khg-7q)5 zY85M=?YNjMpR{YU3RqnqUN`rJy)v8)9*N%Vg}$xu%mnbyF2GR)r2zWg;7DNMk_2K< z&wYMex_rQfIrm8p${Jn*PviM7{)mjuYRbM8?WrR6%2CLE!QBN;s5G?AnFg?Zn$RG! z)%+OIfPABOPH}phaY#TaEgUG+{BZcI+iA;2@bTLAs;z#1*4IHr27bLJQqE->iS8*> zB2>X5%3E0GCUrnACYs`>IqAj|aDj;F@+>lM|0D))i5kjck1iPuq$Q{ZB2%sdJ*Gq5 zsYg%3hI#w7G^Uo1E zf*W7+?S0p{@UItv3ztu+OBbqf9%aT)2XJU!gm)JTF-qziII^>H;Js?rqL7T|$<0o0 zXKtak`s_iim?>(3jv<$S0R7%HbFPf3W8l$V&9Hfml8Q>c{n1ZE-DhWe=qFn&7w)jy*Gvm1K>PU7<2$N!KRJY75-h`D=tq|v<)FNp2bfv zze0|x+ZL|njZGb@%S1-w*0R|0i+dKVh1;8XI(iITLk^rt4-yK86NNAH@@{p+4ePR_ zlaGyC8lR3*rrmuaSp)n7+t=PD<}wG)f8q5f6FoN={W9{6Kppzd;8tKkfly9F42rLT zIHL|h&Qgf!8TeWR%`V_gt#yX}<{q`b3^+Rr3FQW&TC|+9UYqs%)?-%Dul?hsfS_Rd zs9FbL=N4mq;(lK1C6S`A_abW>C|F831)O0DO4M>-H+Q9r``{0}R&j`a=QL|;|FSJR z3Mmi4&73AzxgtaBsJ-DTAOT;L-?Tkbm3R11w#$E-dE+&(64_?PqV&PvGHXs%hxj0W z29I_7ZSb?NedNFK3Zc(r@^?lyL7*y#71hfnv`0k(1lsw}h=@s0(K3)Pe~;XD=ok-z zp+4x5Y<6dA=%@}Y`MD}ynd4pB?Rh_gMu;7?ZbtzQOSwD1?9p;OcPrIxPf#vEPyrF* z3A~qT!%M}MLXzHZ3D6z*<^wU1x>qyRX!<}ZI(+^sa{UIhHp48yi^JB%Y zEW~odOM|!Zq>JI5{S(V51~qo$lIvc-XfaY?QooSQA});MYTf>N)5`9GvH{kG#|xKi zBB9FX`|{F}Eo_{NU@_!h-_8-#a3-}~MxAZ6&q{Ca#{n1sNXzPNsS2e-b7N5GVvYwh z+IpTyCkwE|Q-Q!=<)}@{~CEWrvSg_`L4KQXGTbWd>T=_dgq0IskQ(O zSDX(WObj5{?@%x}kVVi<{-U(q?flD5Fw=`yhO{vl47f!TI&Q|s&D9tF!$SJ+D=`^K zD6KE`#dooc;0Ht@Vur3w1Uq4dwF;li75%4>k#fS^;w=K{RVo0|d?0Yr$M?P;Tblml z7sEG8;InT7RgShC))XL&8}93|nxpS&4v%Le60W=TEuinr?+4Vu*k!6fe((d3d=;}0 z0t|(nEAG-(9uNCvWeJ&1zZg^>DR?@P_-vHxEI3_Z>>I&8iI^mv9tj48Xj07Xx%@iy zS*oEYr2aMf9GkPpOi_b!69+idO=&q}4Q-^!V)H*_h<>x2uZK)iSa*Pib|!)w>~&X*{3#nxSM z3NI3%YA*|XCON#RBG?Rdj`>7K*IgkVr9IlybDrKjZjE>~^wg~{hot%XTQ@g^VzPk< znY=^~zc`1FzslnJ_12>!7OJD;{wF5>C#OZ4VuK)}--sJggv|GAOf>nYWvBW2J%yrsNGYKB#<9zOn9imP z?@Nc46^gV}Zz(K8(~d=9{{o*=3n6!%xJ(0td_Bt8gA8|@F?3qtHx|N+is!uo=oQly zqjGcO;~?lf?cRlvBxjMt)T9w;RO0NgrWvB|N4|r1>zORXEEJGZs;_g>Sz?lwCK4v= zf`G4bg6MI2-&m~!zoD_!2yB|iEU)5d5=Eu|?;H6ySE-GKi~VdVF>c~;J#)S71*-CT zx^bbEj}l@P^_Q!#ccU<74SRrnk8Q6#9NEyo?Y+uNm1Le@YR_HXuQjaV+>%u*Di5li zoqz38{H^x`@~b;&G2MR8Ih*XuTN$*I?=~FzCD*#?nRBxo@>UHQ$@VylLk3q+gn}y{ zKm5RnVofbP`*bLD23oy2cW8Vxh`{IJk**v4EU~~FO$KX7`=c6#EId~c*6`;_;+)*x z%_;Z$$HF~7y12Ux*T5`-2)fKw27kj7L9V`Ea?nn7Izi{OW zg4zH8FU?`wz=)%#d{bhMT5Aq(j-!5;?`#m$#7GF{&X0I_~N1-ZH+O31PxKC8a zue(zN)u`EgPegZ}-f@S`3{MOI$!LU8RZRd|KhNyCU)zck zzo{sgQc5xVe@$e6%5l!|3<)aX9T`CCwgTv0J01-Y-gM9VXi+q}O=TYvl;^b1TG{EJYj-Hhg+dll+ATIEcX4@#U>L?3t!( zkqiE$&}&9{d6K(86e$1iwS@?iMy|vr6A?##g#zvV9;dV=ru0u=2>HKi2IQXbEImrA zJ!{t@oHsgQioAnKe3?UJj>lz`aIkf|ufe{PGe*?sEyS;RKac`~=}7~!+9^V@6;n`dWylzs zB)0dh^M>0HX^Nml9unLqvf#Y4WmfJz&GvQ)~?qf|P=$&j}BwIHPBGcSSv$oy= zaLFkAovcDWdj1GuuZMKWM$qG+ZFouwDQsS}hl)$l6Zsy{Gs!pqgiYnSwo8Yw&huIb zaKOLOPt_1b)~yk~3YqeGaG=&mq`Au}30)}=y)rT@Or1NCoN_&W;#-Yvgi*f&*6tcR zsAWSc;gzXF5oRi@tBlpFNw;3SE=obilmffVAul#P;s*VaycYACNM@f@yxd=%OYT}{? z8y1!?A;rrRk{=&Fp$5$@4fs+@@ZsEAH+w_W-YbUC;V1|qzdKnhCT?`|$2R>?{0A>==}URP1SuZK;3S(mbtVuGi}%W&k^ZfnjIot zk_X2sK=2Y}wOt##3F@Yo%}ZA; zr2V-$GPUU3<#{d~kMozis!2`D)P08Iu7y$5iAW=Bj9vioM2y>h+CPb8CcJqKep@?w z4xc?DS4LENI)R%@n8vB?8F>4D}aP*Jx+MIbA`6U&Mk4l`73!B*{WGv4AjnWh)nNx*>0o(K^Ux$*rCH8D&WKf}g8& zMIn4HydO_EP%2_~lG^w2h0C_s(Kh1n=@Ju-l1%Zp=SLJY92^2RmZ8YTs@jh&QUAMR zKq=UmpZaUD<7}8r5>(AfqFC<2#Z^+rUc!%zLE3ud64ou!Ux;tQ7B7b|Rqq}a#kzdk zy@13R`>He(r|!mMFWC+TJ{ihk``tyHTMkzajTd#688?=hoU=TH6|1MUN@|HiaTNx; zGy543p`315w&|LSL*=<8tGZ8ql41$+&T2c*mtYmcma2*9*`B;4r%~^XAVMK|z#*~k zUAHoZ>5huo^@HfHyJ1fb)OmG}dLZ{C~rj@i($#83g$`!7qN}|EsjBgnyj$5N!tJzqfj`xTzV|gj z5;m~uTcWA-MQH<%kHzRJ)PA5N`=`<}E?5+}>J70u`kybbB>as*S;9WT6ZZm7jeW;( zUrC^L58S=-SR(627q()$|9Is8|G2jThyw{^QZZRJPYQ~Ck1SYl*f5&Cz`JRr5xgU( zE}o_MQ}Ot-4neLmG32zh({g7-|j>kk_wkR`}<^#u;js zBK0dYxO3G0D*|SVGJ4_PKW#8i=FCJ_8Tl>vCtMVszK54nYR|<1%Td4 z!WQ8^4EK$i#E9*;!Yp0aBzYbihJ`$XT}`NkgUT_y9vlhgli>RG)IzfJ9^ApFToJ%d zsm%iK!g^?=8Khtbb=CeHNPA3=5Aer!35A1pcW2V-E_DTp?C1YOR3U?umdtIhkFJ&x z*V7|$wv4faGC4uOu2SoITb;2xPWa+^7g~r_SxfYkllTd>`{nBK%2F=LyJb0Juu>}* zD?Yord%5O#DPaG;A*jFLa(xXpoO5|-*dqA+sx>U1-j%H|eB-<>e`lJF|@LxwyK7xXt z&HJIh`SMXRbCk28*lXN)=()&eayOhuFSnCv7rz-_W(t${(rz0?pV3dFq!15+!-@G+ z*ZkyN4|pv~@|es`X)9FS<`pSG^}Qdd6Vn>x4f`^s1aKVfy8u=Ujnq*eJKBOhNE*NH2DF8h<@kL% z&20Di1Ed=e?H-BkekhJmliPY-uUuIH!~LtAN3tvB_v40LK(oRTpdCdUs5S%1=IVs) z;JtJjid!baF#!A0r9kK%K0*|R;$*&mxM*4LD7j4;{wnE~Ztg4O+N@}ckLm4rm72>G zc?UK&_Of~k!);2}1Gf@t(k_AfJ@1l5@A0j z4_#Y?r~AwEP!;y45MU@!=F{e)h3);eHxL`jvb-^f$peFm?HyhlMrTluQKw<*Yxpgem`RFn5e%k9!G6Tmt(7JiAW)8LeGc3F?2PlI`MuC>FwlA zDOY67&Vzd#vkFM5IxOmGH*Bl_y3n5RFZcO@G~ZL2_n6^St4NL3v_p&IHY2yyT^i)_ zznPNl!Qm-lIA`a);V15sgdb23ZQin)WO(QCxm8@I^g}~vp2n|dBq*^6Pqbpn;%`XX zcYj2AooHYpZjA7h?GW<~A|AU}RZG1&Qn#!<$G{?4h_Rsl5eL!gXfP(@glS%#nI~(_ zqITy(Yz_%#5_%QQ42&A$>l_eKg?M{^ns*=URcfAfxWiGXh_Dg@qUxoqudI>JOHDRb z^j&^5-9vZqbc`BFjb^ii7$JWcesY>K=5-2DD`__|a)FnPdL`gGLeuZ*kntn(dBxvw z^QVCOeB00FQ$BRT{l3HU0!D!q%fDSU@e=tmwYI(EE!CrKr24t^z4rrSws%a%pSJ|; zSRgWo_Z~m|+s2S%q<^i)K`Q>pR06s7_A9hQg1ukWRy4JR8VJX$3}#KUIcjVZob+2k zE86^+YwqYK-l4|x^!xhr(POQRw~Z4fiOX(epqOJhYg1u%vwGi&xtZyH-u+|W%9J;i zCNr1)dZXTM{QPa%W#cQ#*+bO-LDI^;eH*p9B@0Z>0$pi`+t~ZqLgh+XrymLTwpa! z2jW@P*puZ=IU_uM^GOh}LTf+Q4>3x2gk3YpkOG-fK`e)mBgr?f>cAdWY9}fIM5D0c9@V_UU)?1h@^Tr%#ei-VDt0$1|=gNWTrD zvEE}6k34n|ehYyZLS^x`W3pSIS=3tJEjlH5Iynnr_q5$6 zu?F^brLdppZAJpCz@xw0CgfE`$^lVE@=n5f%Zux|6z5&gDO$XPeP`Q}vsc}~l-X7f z_poM6quHH|M|SLC@!mj4!1k7uF^SwM>Ad{+i$fH?|X91*aIhr}I zYdIe-PR=XLP6EG#LLPKo(sRLo^>GqzPs}Su=yCM&`R+Wwfo`D49^(*JlE@idVJ&`g z=ON*@&#b%*1!r^9#br|h-!Fo7kdq)@j|o24ruuqL{HpvwUEL^Jwm=YW!A%Loj1sR0 zr*XHPFzvLiM#DcR&-ad!FWhMp{S+M=FWDH*T54nejo`dEyT#9#+n?=dDpBW?3k)cpPkkRF(~pTML6;8DfA6z=WRS#P3{?_- zCm9(JXz*T^P|7xU+-0WYOO`^+&WPSooPKwKBQR3}FBa>|cuh$}M?h?Y{*yF7InyQ- zue-T#i>Dn`29YNFjTeaE=jtrF5R~pBiu?8RHK_8saKD7zPwIs*;l-UW{AIYJ{0Igk z;EKgYXjKh7m+{Y*JEi{}#`#zZV)WVjJx$ly2gREx@u@O$6a-RE*bcnIgE?qqI2zU zi2O}TsM{dpTx#5_1A5YT=!y1L>@(E#-3?P}5yiR1D6af{EgV0Ee1He@`u| zsXX3c?Hq&7UsvvdaAPx92pQZQHlFuOpfxy$1YGW4Bz{y(m?IBwI#DqmObq!3-Z%W2 zc<$EAVljvkugn9B%dVFmZJQUG zm(8b9N!jaC;>DbwT2sSgS_?>=-)1$7Fmf1dy5BS+uaag@Je=QFkvGJ<@~1wFHu)EnnZVfy?YlYY*`N)(kZj(fyKC8G8Yps{#{bB5BvZ$|9o? zemBh69%JA){{cr#TLnmaKHmHems#vhJ*FCJO9%Xl{Ny1Ew-~W>H6)(Bq9Ps+@yc$u z8f`7YtP&s80h6dePSO(q;j7)!dpCvATbTLwYbw+jEn4XAu~(5zrep~eE~$4@ z@@wrbUed5jUze)Im&BX($GZ(cl)5@2FEQ(TL4e_Rdx=V|LY(+D5WaRZvzN6Dv=$S@ zANP^vv7Y>UYFD)naRc+jEeh28>kXlpcXLR$qiqZiru-u)HXk5{+9@0_Y-O06N7m_M zqnIDruVpeiBVzPN$k;Wf8!nf_AVlC#jxG!;4)}eHw9pWq@5?oz)}C8_U3qmez=EH- zttF){SzA~_+Lovxl1Rk%!P(r)Ao#eh6x;H6VMfI5vHYx2-L&TFIf`MpDuKRX^sT@$?1;k3y{m_yj`XSKWZZNeX+RTulo-V#rv1(`h;0^??Qe5x z7=^yGx4gT)SBh$vbwsP9Gb5OlefYh44Xd4Pu)&>4W7X6PoW8$AGDZXTBj2fEQnhej zO&8$XR(~*dWve+O>dR%>1omlzZ{S%&^=h)Q17MoB($=?@XrFo^c#8L5uy|tYCO#U zf;yY$#9yVRX2eLFJG#$L>W08>dYg|XjV4y$v|U3c^N@2kdCoKTlj?My#UM~J~~DK~YKeg~5u8dfOrFK7E(&w*?~Ns?p5 z?#cqs(-Q{OYqZz|1+}1x10|L?6Yp)r#PWb33J0QIJ$S_^muCw|QcdwD&|o5O)*=x4 zAkv_ct=u^b(w;|AEy(5B+nMzCmEcd*Diesc(t1^378n!`k!8^^{WJ$sn{uF#mhjSO zKNBIo+!IF|=Xzo;ZJ5L4#+;qpU%0LC#`yJ@Z!rEamf-x=S!E0@1kMeK>GsA@!xXwM z?}%dxMBnlH=%;u6qKv@+kVMZtXH6XJ-xYfkEsFu()w-Ie>#_nh-;%F(hn?bIEPmz{ zYTpw%)WvG8Tj?IIWTMAtU>X#kG0r;y*eKZ!=_27Q(C)zKU0xsq?yx_9eF}bs9W^FP z!L4EN!VIOp*#Nvu(+LX#%8UF>`z4|4S3F(Ldsbf(n&; zcVVOui#ee?xYuNMio3(Fb6ivaFO6}rihAtHI}i=O*aW1W;NA2isdLV*AO4FHr$tz^ zq*!d5z!u={1ooy{v^se`COM~Up)*uJxB9XUUB3veK_#PcMzIEODSEi|+%CF7seB{c zNB)*c1CkGF!Q+o=J0YPOp2Tg0#osGP>0=-+*5LLz$dOv zfQz;EX=o@ayZntvn%@s<-&Q})2Ii$X$FdroB}YqK_ST+({Vq8JC z$?o%S82`$d_nrshi<0={%<_XpOr3R~7f`dUS9}G{!C{4i;w9Dzi>xWHET)C+$Xg8B z(lsDHDHUrV`g)5Z3GYJ>TtIW`q@v8!UV*3I5moE$@rV}CA63X`;{%)?h}8aNfX@E; z7}XVY )Lj*1Xa8x!^x+&NnSmg&%s`~S##3$Qkq?Ryw0r4(z?;oUH zao6Bb+}&w$x8knBp*RG0cZVeZ^qh0gy}$39=Sd(BuS|Gn_Uyg(ti49Z!fRR2J#d85 zClD6bSgG|k4qJ-HDi-CZ+#lzXFBYfYGDEK3@c6#9dDX@y@F`{f9PyC6F>uaXBt?C< zf*Sn%jbJQr8}ru`-|PRHgf^5LI(}lx?5AxR_SV zNErMLr~nmPIFmAVBEEX9tWR@qbxsl!NUUX$Lgcp&qwJW$#~%T8Lwrnk$01Sf23Q!p}vjuMvIvM+Iw>7MAbVp=R7OzFabhWW|V$oiaiRbP+GV7!D^%a~SUAiX>nM6K>!f1QDV#X9vAN&u^ zLzV{{;lDJGnT6C`xw!c+e6@FrCK4X)sTFOR<=4}-mtMVP21i$i+0vz3#$%|+ph^( zLKbP~vM@yfzfd#*CiL&m_Ne=LJbak`BO}zth<6)C^93ExtZD?b)L#?MvVef-bU=`I zVx4+sqVy9%9r19kbVrd(@ZsO#ulcxCiuZy02S*Bf-_<+ABfvgjTknY^(|SFn@*W#P zyZlkkir%A*j<<|wgD_U^sd0DTar0XKetus%+ElWdLSfBJ$Y|Xn8n$s!RZdQD}S6jZ+;u);N^fBUgmU)p!do z^F5AzC%*6z{8)_KiLG3yaDXT}L#_h~eU~2=%TY9}=@b7qF8F7n7w*Ts*oGbBH)$wb z8@k$39U#?Z?pJT2d@uhDFpHUJys8ck2rFFKaq~mQcs2^n*}mDoH? zPkv5KkJujuEKzWMY@II!KFjOS96AY&uG-GZFD%Mf!eT5*{9MBz)zsiha391VIq>CV z^BURimFe7@iO2cV3N1zFgK#xCzZ%YRZxSW9yqFFUaf(nN8QTahtT~ZBEg6LCfyjx> zYO~x-Vc@!=soO+kjSQ-})>Y0^b|dSV_z4J8TCO%L zPz!6!;I!FQ!I;4>&W39@CWVudS~fjt*neQl%{ezDuj=)WUlD&c<^Qf0nBfSc<$cpB zW0q8ycTM#AeOY0O^alU63q!PkI1m-1p$rz0k6NCCNqK(pW#sV^PAP_mZIU$XWn{Q{ z=9VJY7G-#k{B%1V`56IR_3p%4LWw7GFJ+k2oFl0nss0I1P;LC|^66>(IY`kD$hw{&<<{55a z+EFtSa0^S?ca>W!d5<;_OwGnT^GI0jxejwDS3JKgpkQEVK-PMSqnofEnRqzpZO*!# zGX1I^LCjb8Nk~ecJE;7!!W`_Q|L)R_#^x#oRe|n2ZcNW|Pwq~24(D|aqWNpO4z~!3 z?Di}cSVsSfV#m*-UMymkX~rL)2lBS21oXgHK1X_uA@SP?yfQv+?e?p2KFUhB5QpPV zUF}k#qEh*Ezi8F%{gJ4oW=; zp5#n`G5&Ik#$#EZ{>tp#p>7t*DuikgPxgT+7j+9H2}-Xyy;guRzKL+SV_Dzs?|Zw^ zzx6sco}B%l$weafzmLPfDT3edkeel5TXD{wa+h~~bbCUmXg4JdNC!>|C;0Knn|5z| zNx$IZdD>OB?8GBiJTT>R*p(0XR+GYS+jQ)%l0ARu4pdy|@36b8+Z`BlMe^`huIMR4 z*^}E_^Lw_BIPA^2a?jP*lrKZGurWUPxQ3~KH{(wmGVn{N7*<&dqx3gQVDl+mfzhUF zyDA?dk-Do``$SPyiu6M9$L+q3((&scC|Jnecv?^Cl_+j846pU8dX?Ghuh=WBEQ2GAQ5ouy?q0va&pm0AYOLCYh(X_v8y_A%osE;Y9nPH6J0N}MZ;ozn zHr8n+yH6lr49b^Swm(5}#sb0RA%^J}vwxi2lq+-kc@yU(g)NP$6g~c%3H{B%Puhs! zPX#1!pFn;13ZczrMBo$ z2nn%qEAc>-$QSgKt^JxDw=KJ4p=6IN#hr4B4X)~gG>KkD z4||1>p>}a&yScwR%LzLwjP_Fs8%9KW@lyTP-3d*d@kXosmoZrm7aLW86Z)&nUY0Ki zgBXgx{kYQ00X*c*#S^uc$p0h!*0hT>sReg9J|h&~LXvmK|6TRJ1P@d>f8Ei$+9wJC zeAc$lI6h3ioIQkV{TPrjpG}fq&sZZGN$7w=KKgWiT3C-{_BnLc^SV|g z09M6%q40`@GjK$%dyThfPyD1IUH#%jsoVr2eih`y|H?_=ZM9jayy}C6%&_Y4{H(GY zB?(;)=Ba{RM&a@jTEH$2f(&! zoo`gk9n*_1w~shZ=*c2&p{OgXEy^)0SV?@i_Yfs8q|Z{{YE5* ze5Jr<(E&BK&>7e`P|fuC(0ft11%vgLyCJfNNteQ+^L@x{t$9nrNbgSddQL^WZvUWr zzP?#HkhnR{ze4YNF8hmbCxo5nkat+y(+I>!?@&wH3x?&cdV}PkKH${r9~V#2tt~-9 z*>@zmx!_acO~@j#$WBL;Yq^a+h6u$@nh$W)_V9Mqn_RCZwOA3B+`dfQZc{oH!REG? zi!9uHBwInc<_Qk)uJeBc2zKUDc!Qj&yvE~-wlrv3^;JJgw>LKsEkEKAy)eiZzD#NU zhv^P?WXSrkn^Xi5exkL1nQ?`)TH6=rP{G&+31i#=9 z`=ZnI%>*F?@t4(HUV*GaWiNiR9wUi4@4TnLk2%!B*i*bE8Q01Yq@tk9ITcJeBR|kH zn4~xIN0h|M*Dn?tuPHUJwYxVj(}Oa0?B|~d10s&_3rE0VRLq+3U9{=yg=)e2j<+(} zUx%e>uJN;C#Ef#Y5X_(+c*V!6!kmS^a&)f5DLA;J6taxPL#dV_lE4h7Bl`D-?qA$H< zqa1YLhc&QHAJH)FzN&?;^5&so7S1NNXWb^% zv}=NfLBy!~J=cYzUB}M0V)sO7hCjg;%A{sYGravCq z2B_|bf@y!orv7!Y{m)+&a>)O*lNsSd};ZBMSMb zOKd!Dz?l1gIHcBxOn85x$UjZT)&S0IuacI5?H8!tqF0!wY8*1G&~6 zcZH|@TjTGpG$VPJ8J=1gKhCe(_fO>@+LyyRJ~NcfPA_RYJ8T57%U|b?slp5&xj>=A z+m`jclZVygjh9icsXwMnxbrGlV$ixwSjz!VkSCk#uI20M&!~1z6jRQxyf6-4i+_ed zr*im$UQHb`R=^&$Dw^Km_{L{@$w%Q&IR6CCv@?oG#!R$uA^NsZXHtP@(7f?IX%aiw z^B7R;KQQD{{U-tDq@&Fsu!A7iyTTc;)-Gr?lM1{> zpcO;!00PRAlt4V+tZ%-sUys@ut-@+`bKPe0Jh^PODTikQ^sI$0mk0EIRpd>zxDVTFpH-VN^-DZc z0--&X26LXIKEC<9+Ef+xEr`sdZw#X|nN{(2Gq=9$7wg@N<-x?+YjGm3HPM3AFTc3$ z8iXFsmVIw9%1-6y7b;0I&`MXs>bvF{KT<@qaqdn^Z5NiG`*Mw*xW^2>e(NNp{k|u) zYVFnc>yy4E)Nq2Pl!ik*g3jyjH?0ekgg!Y6#{_cZzk^xxRn0|WKMMOwiaab6_yX2c zX=0MI8lw?|!j*b=cu+&fQ0Mwz0|7<&$o?YqZf-u62+x@Uw_CEK6ARI?B6P{j>iD>E3^RQB z()JVIW9?Q)Ow2OKa`G9Wi-@~ivv);SS&l$z< zE1Oaev16V$&K~!+1ZYhRL$$6;a0Vt2A?_E?%?A`sk1qng!#eD?wObA-e~Z~|N?>4? zhB6Tyt?G4$(-{g`Dk8W|*?$J~*IngD;reosku;b;#Hc+J@-u~D74#C*Wmg6;FDD_2 zogLqv>vr^;&ymupH*?$cZFL>*@33!9c9{FAx41Z!e)BwUoVDsh=F=@BoGiy^6q$CJ z!@Q+z#sX%!^uK-grxC^$TZ8LqXck$U=jUI-4mZLk4o=MXbwD4X)v9u79ew1q)_u$4 zE(K9&3NX0SG?!0Yn3*ZLL}2FC5oT%3!&?4rssNE4cxQ)$Gvqq;lYm8w-DZj3`{`V= zKq1rOh&6u3qR+%3BKy0N*TiGstSg=IrA9rA#`c8yrv0u;n(K>x21@{H#{(hSd)ujv z6<(FMS@EmFBj*AAL}GPxXr50L&r}BAt&b^g#rp= za85O{Hgp7WFDliGx09uxDA;OzoTY^->DgMrSlc^Y*uR+vFqKz`c%x52@EzMiCzjTJ^#+lr^o80W9h6tvTMJjanvytX@gV}&eDo_h58SIjP zjHJ=HroYBrhYvch@i~lF3kDTEPl@mb^Z2w`#U9{oQb0qG28nrvIcyFa&oi@$Lf_CfzDAz{Wv>BqYpMu@3ABX0>>cp&k`cOG5`l=(! zo$9&c#5p=mGIUAc+*#{ReB*&H<$UGH=ujcnk~+Cpx1(y~5S!nP{$y#@JVOK(-na0M z4vOIdeC>gnZRn?ue;VV+@bKXzl)m|yrlzKec{W7-Se1%v?k?Q0>15fBZ4|cL`5*O#*9y#;mS@S+sm z!sEYu_vp&vdv(=iJk4}`qS6<2{M^DzB=8kQh&H=F^7g}CO__1+;1e8|S*=D5Yr&!h zYNR^`W-EteK^Y`>6R)IkQpkxUz~0Ub`h?bVX$HsK z!l3t&%X0c1El~j`+0!%R1F(&~j|&$2Sr^C(Q5bm`$^%TD&-iiN7Cy@0R_5pc6rC@T zk0O~3j`IE#EFh!Z;k$xvJO(|rX?wi1c>H;r?r0*=`h8v5wR&pazI{leCC;7Q$<9an za|?Iy+$#>}WbpvDN{JQ82AqrhZ!Hau+(js(VY*Be7-Uzkkh^8(6}wq^&AIcPtv5-( z&bAX$Tw@jF$qw}2>7ZB4ybB=Rs`gp8`d-$6f*yJBt0{m1@*kD#4A z0)&CJ`*Yq?8d3eb2v3Nog?UU8Tnu%`MZ&u|tFGS=K~FyQNr$dmrZhXDD*RSdv*%s# zrQ_Q<-uY5=z;d`v7fq|Jp$>AoEHrDTNimiJ_aj?sR{)djgGA}igR{Fk#irdOp9$;> z@W!xaQ?W#;@(m^ze{p6l-bRTVGa4%{BwJ>Z9=?`7|HMN=gmQ%>x1VL!RqsWU@j?pyY(MrL@6 zmh_zvdd^E#AUez*E=%zBrd(9aMRaY;PjPC68?tNFP2q(JBhH6}Lvdbu1wgsxF0VRJ zr=|hf3AkX5$G)j2_pvj362aDnP(xh6&P~1d7G0*nsqN|c6dg@)bR^Df^m;?n;lNNp zYhGVjR&@rKfbU><2_^|17KTSi)%TSY-x06cl-q~PqxmyJIvDBb&oBB_Y<({qzmy&u z^RMJhwUs0vtM?RpYFw^%l|9*Tf?QyIss7fNd`I+efjSGq9smgqXLHKvEnKGR@~>XR z&Q>7}Xan=(2dPes&{bibqAGBQ_G zfY*aQmK7wN>riyJvcbmupf|`WgD3Etts*swRRdbYqRa>K6r{AtUzWc;)dgG+q{#rd zF(bv!9f{2+_EW)UmrKs1(Zk)jzn8DPY*DCay%bnB#UxSn&DTMzRS-KQg7JPQj~nfG z=H+WQYW;qHEs*ctIV?a#lF3e4-|{kmpiNd-3v|5Ihmp4-QH;k}+UL}*>}jKyys>sJ zjt^X6WHX@>A?#VGOq`Ly^CizlX56sjNY-cWk+6=oBS?(~7B zoR-w}uu?f%1kScegU`;lckh5Q=x~wbLZANG)I*Fz_0XSY)sRR{QV{~hs2 zdvowihXF{L(Flvl~Rn93ZW1&VIA2ODfAwsxO1{I9IvR zb{gT=Mcwbs8=QSpXvFX)JE?c9@6v6ng$j=?8QH*alJ7EXY*2oCz69_FI!Jtzct(I) z{R`6f5SXkcTaQds?BAG^hd zu=ir>N3CxZ-TFq~P+hBTBD~s|37CnJx)1Sl!uXs8SI1hSdGk}n9X}<&(gHqpjF>gr zTX|<4r#)_fhdoDCLxR}m$Uy$6dU~(gOl1e*MfZN`Vz_q%9@4UeW_}5OWAlPSUCJB7 zm~PbIXd|F+R+wey1z=Q5pIO6aaJ3ey1)5Z@530&o?2ecrJnVCdsHBDThlXFD{WZ=^ zKcj4+8`0(%O4{r%#x0(x;kYpG1e*pkJYmpgtiW1>JQQMD9Ywid!e)2u^@t6C*>I9p z->uA%?0Y&8>#%0ygUG{Pt}y&%wcMOo*#S)}*KQyVMzGt>biIIE8~-+k3)>Ee8L1NM zZi#P3)1~Z9`GT?SZCCjhHh)_yc~w`soT`l~@M!kr(+#`M5q_-kCMkJ^@09g)C@?gw zJwZcrp}4{SiQh(0mqg7Eh@LvgL8=W1z}Fw8Y)YWe!BcBs`Jq)Mjt{<@iIs!U z56!N)Gm2-Oim7r6T=j^LWY*0Y#*rAp^b}U>MGcD7bj;^Dq4u(r3S05^4yZ!vklrlQ zO95Q%8}*NbYKQ`UnfmR$1ynaK6d;+ddbZ~;DGo(XGA?LYfDB6bO}>}qQ|=oJE_Vrc z8P-F}eU03v<1E7MS7h-i91l|$%X%aEYz@&AvWH^3Mweq*(Y+n1w@N#hx};+ zD9Z3BTljL|UE-D%%2w`Ci>vTY zLQRuQKMBVrpRLK@$O5u@G$5!76-<0*O9|Nfdbs?Tbd?z1*k0u{e}+!}7nH4G`YL<# z=4+BpD)H}U2GMGF`N!s)8w(?>@J$^21F2uZ9l+9+i6CupGRCPiO)I{j(d+^*N2xEw>XApGe7w^Xlmtk`eW{_%WJK7b-*OuotL zQ(bh#dhmOBBG?8$RMe=e)J0ZM^j_d~W(l#R;C$CA75EI=31tou^zUoojXZqzJT4vSWoc68T0W}AOh6zQV58S6_rYpuGyM14luB~mrJ(O8~@a9Jx zMD^L$C|)-%;@v}kSek;P30$E*ibfBc>%h+vcPSIUmw1leYE9qv2-skDbSws8pQ_(! z{N?@rd85yoY_m434F_=Snd9O z&sO7gqf&E35wb;_AA@S(&~>zLoMO7W9$ktq#@duhvx=P`7tl!>HmumcZYy5HSD9@kiNe_Oewv znXhAOkA0DE=2k0n-vt~x2tp3CGX@ZrEWX?(CDr#AxW2~OL&|gT06RPehtgaYYmO2u zMy(EnMbo{CR~qs{B14($n_GTHU2~gmzR*s0f;6l3@8f`nB#Pwq*oPutXLE5CpL?$9 zPi$|=B=?u62+jNP(^1LE zE4DHghHNht)~v=e3-V|QQt1fI;PY`Ll`Ht{%uXWWjbBFxfJN)qTGMV;uE+HO5PH8$ zC<4r=c4xK(UGcY+d+t(LWedjbv3Q1ke`9Ft0UlK_|;<0>Is*1_y zJMu9uY&nf7G7$gtY**VW1^SUhl;{H6OfhpjYidD5zG8asikol6Aj9#;#=LMn!znD+ zz;f&k)XZ*q6n-R{py{;NWlasv!Xuxt>D4e_Y=UcpmLizMI_$t5l^r{vUY9N!`%U(6 z=-|;x_8(Vr8F$+0yTx%D&f3d7(w&J#2T=-j+tx^QWm>`#6<7KImVaEbKR=QO^}H7i zm}WPLmlFKMDNIgM=rnkD(2^p_dsBkJI6EjB8=8Wyn6i;mpDIk<32>sJql%{@y;QbP zHE2uhUyn=$2PKA0%*F$W?#9MoKblL^o*&(gin^5y6H|IJuWk75e0V%X*qET!Y}BYf zL0T@eHZB*FWh(d%pb*b)i*SL1?N}4RGcLQ> zSFJBm;{uV^#@92S!?E2@%(ks-YZvi<(Y|iw)Yb)h*drC(bbf{@b$eKA6U~3<{*Q47i;ECC-N=o_`1Ue@(0{PK{r7ygh~1CgyU_ z2|!?~9k}}_Z3KHzjqtK1H;xv(3e}l{ZbhPcOcpBYlM@MsRVEehrT#UQ$hXJ+rO|2@ z>^b`h*srcS^`;wBjUD>QudS5aVvEjSDwGiN%zGI;CpmOk6;WPU$#a zTPhLkqQ$iD$da4-!`QfMcoP9@erz~6CeJMe(ZFW$dY?rFobW~^f$}{qROi+cZwNhS zr?>R5$D?c~bDUo(h}j(JDQ%M8y1J{B5{3wgNg$zB_pc~TclaS-`^v>BlJVWBjqKYl z_)yM}e?Jz_<`XS&Z%Hq5KK$wt=^iE_N=um@Ptvgy(eo<@mDT|q)9`h!7L~`Qaj?G5 zJ4H=VvC#9QuJ+89FnW?zu(S(Wx=XwPVv$U|WP9$Mv3QZOPeik8TYsd8(83Bj>%8Gj zxrkT^zFn#0?Z4aNrKPg{~j`1aqM;0;F}t117qXibg9<3RJ23U7$FkR_fQTs`LyvQ$Fc(L zW0xg+PtOAnAw;+i^9@h9%hsD2dl^I=x0#d6u)}8 za`Hx-g8fyoymmLDU87yU!MVsgIKu?HaqY&>J$FaDjj$(e}G;y);)2C!ZttMREmBj{rru%we`EFZQ09EAFk7TNr4y}(l7pF%0w4H z)RMT~F-6ad!;3geN_9_@x7aJo2z-C6BD#;2{T>E9CbwNu%w&AkAsAOo@d^Lt9(!l3 zL1N-(v+?~VlG@}gaQJ9$^_^&CPPqVIRJy|4chxW~mUqeXGq#+ExfE9TuHi2V28V0+ z-Hj1wjye8D0V~QUFBdjh*99`Cnr;o{!NWBikOjA*gj-_Rk1KaTT)$`^TW@Q8_`S>g z3vEiUq3*Df)ADlB`I+3ql}%Ws?70_0j90=;QG6aos)Gl0kohrz80Y-|o!H6r{R#JwRF3ZD#l%T1RUq^*Vxi_VcKi^~wf@^~g%4vYLzs@xBB4!2YziLt~ zS1h%;ovf=SM7TFsaIBKLn~x~;aX#R)lmvy4FY%o*WbMtJ!{x&tPW3f@o}GWHbvxgX z!KP5Jc&0>%ZF+cgpAo9?j&IKUd`o*E3NR3=uV=Em)P2SwHde7fy8J7A>kiMsfmE=p zX{(?ww$V{k2teTxb#|Vgn3^Gh<*R6I!6R&L)%@AvU8(@Wii+h*!NqD0PQeBEj83y= zhdngaz5ZZ(R;MOTp~@4=G(r#etEK3KL^2~rNWt^9Wm{$iI6hW{Bpu|vd^B(}|IdX> z8=svKUC?33zb3X!Dj-G- zduZROg4yG_io;8+@ibXA4;*_A^8p?VfX_n4{f>756#omG^y%bAY28g+u6aS6`a`#oV8MQ2d5@TI4$ zW}9!=+c|HwsNb6A97Io%%(puy}yF?3%;#(SHr70TiWwLaYB6qj&G{b6*Z5XD}uG(oo{$ z3|GmeVM~t){CZ=fGg6k?4<8P!?oDdo#sm$8qvfF*eKU6F1Ja=A?T4yE9_XDZ;b7C+ zmS%upB=MIHcano?UM#0yX*}^hTVhJ&{O+II-cud$StxuR?(g?=$h1TA31|s+`q4~O z6U}UXc`zy71+})x^Ujr+w$&IW5FHa9`(6n;=kQq6!n916JHJpxBPEI)i+2FvMPJimcl;7Xg~YdI9ZzfONXnd1jg zXsXVDUiy%V#(pEEnWIc5&MbD7buGC^RVufaJ%Ibzqt9kX#Nng^Y|Go7H-#D7N8q4- z*JWu{^#OslK#?!QQcR#af~8}I0~A%iGAdGC!Fc8B=GniO?u|cS#GjD$zYSk`0Y3&` zj6eYUC1tpdj$eS!wqMJ^#=%5Vws#&=QjtGFDNFlmpT7-#Nmeu;(a&#fAqIK z`SUK-SAZ&`>@9C(D8=CW)#9&huR zbp?ZOPtn+2NZgcKG`S8`X3A6k{&E#WnnWff;J6Kr{`gAk)U943u+rd?ffeq*-D?8k zi4nYxE!E+tYS-$W%$LOk%R#3`_-}M{n$Cly<eT{lOLZxS%B-yXXf1)di(o{ z(?2rG4=1q{Yt=C_saJ5jsD}JWjB*mCTkE8)5uU&CizMFI+zqP5H5jGg6A4W~_OY|L z$C!*`n@gtQDe?e?t5lxIcpur4K6=GM!o!*RK8d#H_)OmYZaH`4a!D9*SR<+T5z3us zVcV9$9}B5m`Jql`?6o@e5tC#9EQC3l&-lPUs7-r59q!1l{_JM5ND}s?XJ4yFZnjIH z_1+Lqy8rF&E71+6m3>u^8id~+JwD4LRv;NBomjRhRS>D1LiN>mQ4+#EU zt*9%t&^rq4U0=WY0K~YI-8j=gk+WX>T(0QqPyD#~ zr4I#HOb=_Hj?dm{NJq&f;VCqx3A8+YGUrF^cTz9VmuClj`l`U{`qg5^Z;h_yORX9L z>y0cw-#-2CVFG)L2k$T+_SMYqj-E=R=he-BQhp>$0z>Vdpm>7}2Q{*X=ttl8ENxOc zmmDzQCEsL(IOCC;9t=vecu%@js22+~J^F*Y{MBNa(pm(@=T+X8_%bV@jbH}<-qWX; zY*c1?LOO!ippGZ1Rp)uq#>`p3JyjOjbMPA63$l#-u7YkIp&yy1iwWYjM_v7pU`P8O_U?^2a*Q88iGu{+2b(LS%V; ziuczf;bREw*yIwWh3{T4@&I-G!Otg+Mi35W889f2Q@jpwCQl75T%1XD4A4;F+b0%1 z$^1u#&1v#IN z_wLH)`A((FQQEdPG9ewysyIyNP~z;zWwO&Mi}#H~pux~oH8 z4PNHAW($Zlu6YP2DJ`yaSA#E}bSPa{b$BCmRQdaAxqj#`%Q_QEt#TxL>6cMGK1&gF z${Cxw#W~XMCIuGV41Oh^b;*cF&JsoI`+KBQw zwrhsuvROYxaBUqZqjyv+Td!{}y-|RW4rbl2#$?5W;KL<0((mqJCcCZU3y{(6mlWvk zXeN@jbe9P|NpaTbZ?qi;B3Txb^UuzmM?Nh19jkx7Yjn`qz^a1Cz^met1useCvf-ML z8(o)Oz?uA$0@aR%-Jri0WhDh!{=4}W((I~1ayU{-?K%j&VHH606{sSxiBdkX@XJPz zsR>2SRa@}y-7?T^2+QT-i$zO0T=Fg4B(+%IL5`6=p!I|&-$a6zXk_{Cf$+2^BCpr@ zDR>eAKJ!Jg)jj?Ppv|L)`^3rY&Dibn{aJPor{A6$S!Y(fqh1c|6kGw1ct4bi!QJH9 zC*r;xt|m9$qmPv#b`~mrPmL@i5bqgx^_qkl-I|vJ?_;^W%lN!2)Hq$e$HoKz@rV3B zFcf&6f35{BKbyS(%~<&Z(KXFoUbk)F8r@_u68o3mP#Z?W0(=?2^*&v^=1sd%`D{IogiNr8#Gl-f*ShYAWhC;P2W-1<#77`&;XZA>Ihb{N3-CY#cRcyZfp*cwSV}~$ z;POiz2J#xi6>Jjm7JiJnbiZ3Iy+#)IQyzgrz#}%lrz4C>rJn-uY`9z+dv;IXWO-1cXFRX>NY%Zw`+4dKH`+yap9u{g zOO*IwyWx>1y1;e}1t!Hg{^?aofaRowC^>I-Gn=URE$w@^zxwvt^()zGgjaM>R6J>> zBFfg;Yv8p4+d(6-&=AL5El+^HcN#c7817hOn&WCXa!Ct1hR9!v?gV6G5n?`N%{|~( z=%!A(a9*bx0v&GvwE5#AWIPdmyB`sZBUQlDQ{ENL=BA7#t&jc`Pi*TIJ&+Imsm_!0jbsQ9;Au{UPMLSor( ze~|%^Sv6Z0pc5u9ZL7BZ#~Z7;t+$W_&NHCKh;%zvRD3J;7c)}$G5*)>!8xCvrY9)-qWB}tSaVn#{O@1KV5hYZ7s19je1 ztvZO7NNCzV0+nagP5bm|q8urU&d65q6_rlC5IXLQXl%d=IFe0T@iYB#pInk*7nifQ$abbHN0sY zSPV`SxD8jdi`+Lc!k9^l&kTK3?{4MVCy=*1C_VhxDs3doZ8kdRd=r+h#nWT#ah-oM zb^2cxuG@*QL&2fh_X%WcOJU-8{dgCffQYx`~fMfUWhXfSeLHemIViv1I2w1iAzJ z=Ri+6oR@l>iVkhjDZrqI*>507rB7FIz9=2nif;C9s!ikzdJSpuT&Pj|VZ?xs|Foum~^;rg3+4@ahb=`m8HL36p#^+^*VA?5A7 zO)xx8$6~Pc9*s>QwL_yU0vZ2Ge_|~MP)L+Z+H|V?()n*NH=rtwxOGvqt%xAzje{mR zq|Cl3mz(y!9Aa9vV6;(tlXPpnt~Ii_Xe`R-2=s+P7L?{*wS z=(hgnRd){LI>nx)2%#B%Suq2)hn=C)pU0t;Oj}AUkFWp%7jHeB4<%Ly#x>l9NtkF` z({BvFW+QCk6)8oVIdd&sK%~eIW+H98sP3({?Q6;nE^K77e|TA)j^bBei<6(i|FnDr zrb{2f>S$i+DrH-e_QJ;1s103cO4vV*+6p3EF&o;j&KRpfs!*}Dr&~rQ+sT}Ju-@(g zc`igBq7abP=7XnP6if}estK$hq@@%jyDquf4T|$BJMk|!VtoK|mOkd4ACAWQfY3$s zR$i!ieZPj)Zt()7m7Bkp|24w+1i^Fzx#>0EDd$Dd+?$TCglJ7_?Cw*)9>st0o1fHE zMLSzY6ryormz&LwO?rRh$xc4u5WeLcQ{SaKEy#xm(46QGZ=BNMWg$Ph-+8Q@&clnM z5njE3c{lRZf2%&yc@n5>J0sroS&@n8yU9pfhT+h_6%2l`92koEi#vKkOna9cm<2$#L5AlEIj;`Rx z8|neV0fC_(m1;!h?;mUw0GEs;h0L2

7VBI(Fz~vQiyyol>5VW`q(PVgE%l)Qct^ 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℞n2Mh?vG)!^HY`pt}w06|Y%!&g;acppbg? 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

Xk(>`Zhf>VZ>61QX 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 MK6+4o-}>eM0EC9b7XSbN literal 0 HcmV?d00001 diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AAgent.cs index fd24832755..061b988137 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AAgent.cs @@ -69,7 +69,7 @@ internal sealed class A2AAgent : AIAgent if (a2aResponse is Message message) { - UpdateThreadConversationId(thread, message); + UpdateThreadConversationId(thread, message.ContextId); return new AgentRunResponse { @@ -80,8 +80,21 @@ internal sealed class A2AAgent : AIAgent AdditionalProperties = message.Metadata.ToAdditionalProperties(), }; } + if (a2aResponse is AgentTask agentTask) + { + UpdateThreadConversationId(thread, agentTask.ContextId); - throw new NotSupportedException($"Only message responses are supported from A2A agents. Received: {a2aResponse.GetType().FullName ?? "null"}"); + return new AgentRunResponse + { + AgentId = this.Id, + ResponseId = agentTask.Id, + RawRepresentation = agentTask, + Messages = agentTask.ToChatMessages(), + AdditionalProperties = agentTask.Metadata.ToAdditionalProperties(), + }; + } + + throw new NotSupportedException($"Only Message and AgentTask responses are supported from A2A agents. Received: {a2aResponse.GetType().FullName ?? "null"}"); } /// @@ -107,7 +120,7 @@ internal sealed class A2AAgent : AIAgent throw new NotSupportedException($"Only message responses are supported from A2A agents. Received: {sseEvent.Data?.GetType().FullName ?? "null"}"); } - UpdateThreadConversationId(thread, message); + UpdateThreadConversationId(thread, message.ContextId); yield return new AgentRunResponseUpdate { @@ -147,22 +160,22 @@ internal sealed class A2AAgent : AIAgent } } - private static void UpdateThreadConversationId(AgentThread? thread, Message message) + private static void UpdateThreadConversationId(AgentThread? thread, string? contextId) { if (thread is null) { return; } - // Surface cases where the A2A agent responds with a message that + // Surface cases where the A2A agent responds with a response that // has a different context Id than the thread's conversation Id. - if (thread.ConversationId is not null && message.ContextId is not null && thread.ConversationId != message.ContextId) + if (thread.ConversationId is not null && contextId is not null && thread.ConversationId != contextId) { throw new InvalidOperationException( - $"The {nameof(message.ContextId)} returned from the A2A agent is different from the conversation Id of the provided {nameof(AgentThread)}."); + $"The {nameof(contextId)} returned from the A2A agent is different from the conversation Id of the provided {nameof(AgentThread)}."); } // Assign a server-generated context Id to the thread if it's not already set. - thread.ConversationId ??= message.ContextId; + thread.ConversationId ??= contextId; } } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AHostAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AHostAgent.cs new file mode 100644 index 0000000000..86f273bc0e --- /dev/null +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AHostAgent.cs @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Threading; +using System.Threading.Tasks; +using A2A; +using Microsoft.Shared.Diagnostics; + +namespace Microsoft.Extensions.AI.Agents.A2A; + +///

+/// Host which will attach an to a +/// +/// +/// This implementation only handles: +/// +/// TaskManager.OnMessageReceived +/// TaskManager.OnAgentCardQuery +/// +/// Support for task management will be added later as part of the long-running task execution work. +/// +public sealed class A2AHostAgent +{ + /// + /// Initializes a new instance of the SemanticKernelTravelAgent + /// + public A2AHostAgent(AIAgent agent, AgentCard agentCard, TaskManager? taskManager = null) + { + Throw.IfNull(agent); + Throw.IfNull(agentCard); + + this.Agent = agent; + this._agentCard = agentCard; + + this.Attach(taskManager ?? new TaskManager()); + } + + /// + /// The associated + /// + public AIAgent? Agent { get; private set; } + + /// + /// The associated + /// + public TaskManager? TaskManager => this._taskManager; + + /// + /// Attach the to the provided + /// + /// + public void Attach(TaskManager taskManager) + { + Throw.IfNull(taskManager); + + this._taskManager = taskManager; + taskManager.OnMessageReceived = this.OnMessageReceivedAsync; + taskManager.OnAgentCardQuery = this.GetAgentCardAsync; + } + + /// + /// Handle a received message + /// + /// The to handle + /// The to cancel the operation + public async Task OnMessageReceivedAsync(MessageSendParams messageSend, CancellationToken cancellationToken = default) + { + Throw.IfNull(messageSend); + Throw.IfNull(this.Agent); + + if (this._taskManager is null) + { + throw new InvalidOperationException("TaskManager must be attached before handling an agent message."); + } + + // Get message from the user + var userMessage = messageSend.Message.ToChatMessage(); + + // Get the response from the agent + var message = new Message(); + var agentResponse = await this.Agent.RunAsync(userMessage, cancellationToken: cancellationToken).ConfigureAwait(false); + foreach (var chatMessage in agentResponse.Messages) + { + var content = chatMessage.Text; + message.Parts.Add(new TextPart() { Text = content! }); + } + + return message; + } + + /// + /// Return the associated with this hosted agent. + /// + /// Current URL for the agent + /// The to cancel the operation +#pragma warning disable CA1054 // URI-like parameters should not be strings + public Task GetAgentCardAsync(string agentUrl, CancellationToken cancellationToken) + { + // Ensure the URL is in the correct format + Uri uri = new(agentUrl); + agentUrl = $"{uri.Scheme}://{uri.Host}:{uri.Port}/"; + + this._agentCard.Url = agentUrl; + return Task.FromResult(this._agentCard); + } +#pragma warning restore CA1054 // URI-like parameters should not be strings + + #region private + private readonly AgentCard _agentCard; + private TaskManager? _taskManager; + #endregion +} diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AAgentTaskExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AAgentTaskExtensions.cs new file mode 100644 index 0000000000..858be41753 --- /dev/null +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AAgentTaskExtensions.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Collections.Generic; +using A2A; +using Microsoft.Shared.Diagnostics; + +namespace Microsoft.Extensions.AI.Agents.A2A; + +/// +/// Extension methods for the class. +/// +internal static class A2AAgentTaskExtensions +{ + internal static IList ToChatMessages(this AgentTask agentTask) + { + _ = Throw.IfNull(agentTask); + + List messages = []; + + if (agentTask.Artifacts is not null) + { + foreach (var artifact in agentTask.Artifacts) + { + messages.Add(artifact.ToChatMessage()); + } + } + + return messages; + } +} diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AArtifactExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AArtifactExtensions.cs new file mode 100644 index 0000000000..66f1b6e5a2 --- /dev/null +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AArtifactExtensions.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Collections.Generic; +using A2A; + +namespace Microsoft.Extensions.AI.Agents.A2A; + +/// +/// Extension methods for the class. +/// +internal static class A2AArtifactExtensions +{ + internal static ChatMessage ToChatMessage(this Artifact artifact) + { + List? aiContents = null; + + foreach (var part in artifact.Parts) + { + (aiContents ??= []).Add(part.ToAIContent()); + } + + return new ChatMessage(ChatRole.Assistant, aiContents) + { + AdditionalProperties = artifact.Metadata.ToAdditionalProperties(), + RawRepresentation = artifact, + }; + } +} diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AMessageExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AMessageExtensions.cs index b7acca952d..918235869e 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AMessageExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AMessageExtensions.cs @@ -10,7 +10,7 @@ namespace Microsoft.Extensions.AI.Agents.A2A; ///

=|^_EcXxxTEauf)+@lTp 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

q` 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%0AD0{kT(? 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

internal static class A2AMessageExtensions { - public static ChatMessage ToChatMessage(this Message message) + internal static ChatMessage ToChatMessage(this Message message) { List? aiContents = null; diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AMetadataExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AMetadataExtensions.cs index c41f5f6b53..05b1293c24 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AMetadataExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2AMetadataExtensions.cs @@ -15,7 +15,7 @@ internal static class A2AMetadataExtensions ///
/// The metadata dictionary to convert. /// The converted , or null if the input is null or empty. - public static AdditionalPropertiesDictionary? ToAdditionalProperties(this Dictionary? metadata) + internal static AdditionalPropertiesDictionary? ToAdditionalProperties(this Dictionary? metadata) { if (metadata is not { Count: > 0 }) { diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2APartExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2APartExtensions.cs index d3b1ef9f83..4e14b07ff5 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2APartExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/A2APartExtensions.cs @@ -15,7 +15,7 @@ internal static class A2APartExtensions ///
/// The A2A part to convert. /// The corresponding . - public static AIContent ToAIContent(this Part part) + internal static AIContent ToAIContent(this Part part) { return part switch { diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/AIContentExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/AIContentExtensions.cs index ca4442ba66..eb80439e0e 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/AIContentExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/AIContentExtensions.cs @@ -16,7 +16,7 @@ internal static class AIContentExtensions ///
/// The collection of AI contents to convert." /// The list of A2A objects. - public static List? ToA2AParts(this IEnumerable contents) + internal static List? ToA2AParts(this IEnumerable contents) { List? parts = null; @@ -33,7 +33,7 @@ internal static class AIContentExtensions /// /// AI content to convert. /// The corresponding A2A object. - public static Part ToA2APart(this AIContent content) + internal static Part ToA2APart(this AIContent content) { return content switch { diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/ChatMessageExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/ChatMessageExtensions.cs index 930b8c4295..f030750dcd 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/ChatMessageExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/ChatMessageExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.Extensions.AI.Agents.A2A; /// internal static class ChatMessageExtensions { - public static Message ToA2AMessage(this IReadOnlyCollection messages) + internal static Message ToA2AMessage(this IReadOnlyCollection messages) { List allParts = []; diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/AgentAIFunctionFactory.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/AgentAIFunctionFactory.cs new file mode 100644 index 0000000000..76f8f8fe12 --- /dev/null +++ b/dotnet/src/Microsoft.Extensions.AI.Agents/AgentAIFunctionFactory.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Shared.Diagnostics; +using static Microsoft.Extensions.AI.Agents.OpenTelemetryConsts.GenAI; + +namespace Microsoft.Extensions.AI.Agents; + +/// +/// Provides factory methods for creating implementations of backed by an . +/// +public static class AgentAIFunctionFactory +{ + /// + /// Creates a that will invoke the provided Agent. + /// + /// The to be represented via the created . + /// Metadata to use to override defaults inferred from . + /// The created for invoking the . + public static AIFunction CreateFromAgent( + AIAgent agent, + AIFunctionFactoryOptions? options = null) + { + Throw.IfNull(agent); + + async Task RunAgentAsync(string query, CancellationToken cancellationToken) + { + var response = await agent.RunAsync(query, cancellationToken: cancellationToken).ConfigureAwait(false); + return response.Text; + } + + return AIFunctionFactory.Create(RunAgentAsync, options ?? new() + { + Name = agent.Name, + Description = agent.Description, + }); + } +} diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/AgentAIFunctionFactoryTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/AgentAIFunctionFactoryTests.cs new file mode 100644 index 0000000000..d1c10c5f40 --- /dev/null +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/AgentAIFunctionFactoryTests.cs @@ -0,0 +1,339 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Moq; + +namespace Microsoft.Extensions.AI.Agents.UnitTests; + +/// +/// Unit tests for the class. +/// +public class AgentAIFunctionFactoryTests +{ + [Fact] + public void CreateFromAgent_WithNullAgent_ThrowsArgumentNullException() + { + // Act & Assert + var exception = Assert.Throws(() => + AgentAIFunctionFactory.CreateFromAgent(null!)); + + Assert.Equal("agent", exception.ParamName); + } + + [Fact] + public void CreateFromAgent_WithValidAgent_ReturnsAIFunction() + { + // Arrange + var mockAgent = new Mock(); + mockAgent.Setup(a => a.Name).Returns("TestAgent"); + mockAgent.Setup(a => a.Description).Returns("Test agent description"); + + // Act + var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object); + + // Assert + Assert.NotNull(result); + Assert.True(result is AIFunction); + Assert.Equal("TestAgent", result.Name); + Assert.Equal("Test agent description", result.Description); + } + + [Fact] + public void CreateFromAgent_WithAgentHavingNullName_UsesDefaultName() + { + // Arrange + var mockAgent = new Mock(); + mockAgent.Setup(a => a.Name).Returns((string?)null); + mockAgent.Setup(a => a.Description).Returns("Test description"); + + // Act + var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object); + + // Assert + Assert.NotNull(result); + Assert.NotNull(result.Name); + Assert.Equal("Test description", result.Description); + } + + [Fact] + public void CreateFromAgent_WithAgentHavingNullDescription_UsesEmptyDescription() + { + // Arrange + var mockAgent = new Mock(); + mockAgent.Setup(a => a.Name).Returns("TestAgent"); + mockAgent.Setup(a => a.Description).Returns((string?)null); + + // Act + var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object); + + // Assert + Assert.NotNull(result); + Assert.Equal("TestAgent", result.Name); + Assert.Equal(string.Empty, result.Description); + } + + [Fact] + public void CreateFromAgent_WithCustomOptions_UsesCustomOptions() + { + // Arrange + var mockAgent = new Mock(); + mockAgent.Setup(a => a.Name).Returns("TestAgent"); + mockAgent.Setup(a => a.Description).Returns("Test agent description"); + + var customOptions = new AIFunctionFactoryOptions + { + Name = "CustomName", + Description = "Custom description" + }; + + // Act + var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object, customOptions); + + // Assert + Assert.NotNull(result); + Assert.Equal("CustomName", result.Name); + Assert.Equal("Custom description", result.Description); + } + + [Fact] + public void CreateFromAgent_WithNullOptions_UsesAgentProperties() + { + // Arrange + var mockAgent = new Mock(); + mockAgent.Setup(a => a.Name).Returns("TestAgent"); + mockAgent.Setup(a => a.Description).Returns("Test agent description"); + + // Act + var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object, null); + + // Assert + Assert.NotNull(result); + Assert.Equal("TestAgent", result.Name); + Assert.Equal("Test agent description", result.Description); + } + + [Fact] + public async Task CreateFromAgent_WhenFunctionInvokedAsync_CallsAgentRunAsync() + { + // Arrange + var expectedResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Test response")); + var testAgent = new TestAgent("TestAgent", "Test description", expectedResponse); + + var aiFunction = AgentAIFunctionFactory.CreateFromAgent(testAgent); + + // Act + var arguments = new AIFunctionArguments() { ["query"] = "Test query" }; + var result = await aiFunction.InvokeAsync(arguments); + + // Assert + Assert.NotNull(result); + Assert.Equal("Test response", result.ToString()); + } + + [Fact] + public async Task CreateFromAgent_WhenFunctionInvokedWithCancellationTokenAsync_PassesCancellationTokenAsync() + { + // Arrange + var expectedResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Test response")); + var testAgent = new TestAgent("TestAgent", "Test description", expectedResponse); + using var cancellationTokenSource = new CancellationTokenSource(); + var cancellationToken = cancellationTokenSource.Token; + + var aiFunction = AgentAIFunctionFactory.CreateFromAgent(testAgent); + + // Act + var arguments = new AIFunctionArguments() { ["query"] = "Test query" }; + var result = await aiFunction.InvokeAsync(arguments, cancellationToken); + + // Assert + Assert.NotNull(result); + Assert.Equal("Test response", result.ToString()); + } + + [Fact] + public async Task CreateFromAgent_WhenAgentThrowsExceptionAsync_PropagatesExceptionAsync() + { + // Arrange + var expectedException = new InvalidOperationException("Test exception"); + var testAgent = new TestAgent("TestAgent", "Test description", expectedException); + + var aiFunction = AgentAIFunctionFactory.CreateFromAgent(testAgent); + + // Act & Assert + var arguments = new AIFunctionArguments() { ["query"] = "Test query" }; + var actualException = await Assert.ThrowsAsync(async () => + await aiFunction.InvokeAsync(arguments)); + + Assert.Same(expectedException, actualException); + } + + [Fact] + public void CreateFromAgent_ReturnsInvokableFunction() + { + // Arrange + var mockAgent = new Mock(); + mockAgent.Setup(a => a.Name).Returns("TestAgent"); + mockAgent.Setup(a => a.Description).Returns("Test description"); + + // Act + var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object); + + // Assert + Assert.NotNull(result); + + // Verify the function has the expected parameter schema + var parameters = result.JsonSchema; + + // Verify it has a query parameter + Assert.True(parameters.TryGetProperty("properties", out var properties)); + Assert.True(properties.TryGetProperty("query", out var queryProperty)); + Assert.True(queryProperty.TryGetProperty("type", out var typeProperty)); + Assert.Equal("string", typeProperty.GetString()); + } + + [Fact] + public void CreateFromAgent_WithEmptyAgentName_CreatesValidFunction() + { + // Arrange + var mockAgent = new Mock(); + mockAgent.Setup(a => a.Name).Returns(string.Empty); + mockAgent.Setup(a => a.Description).Returns("Test description"); + + // Act + var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object); + + // Assert + Assert.NotNull(result); + Assert.Equal(string.Empty, result.Name); + Assert.Equal("Test description", result.Description); + } + + [Fact] + public void CreateFromAgent_WithEmptyAgentDescription_CreatesValidFunction() + { + // Arrange + var mockAgent = new Mock(); + mockAgent.Setup(a => a.Name).Returns("TestAgent"); + mockAgent.Setup(a => a.Description).Returns(string.Empty); + + // Act + var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object); + + // Assert + Assert.NotNull(result); + Assert.Equal("TestAgent", result.Name); + Assert.Equal(string.Empty, result.Description); + } + + [Fact] + public void CreateFromAgent_WithCustomOptionsOverridingNullAgentProperties_UsesCustomOptions() + { + // Arrange + var mockAgent = new Mock(); + mockAgent.Setup(a => a.Name).Returns((string?)null); + mockAgent.Setup(a => a.Description).Returns((string?)null); + + var customOptions = new AIFunctionFactoryOptions + { + Name = "OverrideName", + Description = "Override description" + }; + + // Act + var result = AgentAIFunctionFactory.CreateFromAgent(mockAgent.Object, customOptions); + + // Assert + Assert.NotNull(result); + Assert.Equal("OverrideName", result.Name); + Assert.Equal("Override description", result.Description); + } + + [Fact] + public async Task CreateFromAgent_InvokeWithComplexResponseFromAgentAsync_ReturnsCorrectResponseAsync() + { + // Arrange + var expectedResponse = new AgentRunResponse + { + AgentId = "agent-123", + ResponseId = "response-456", + CreatedAt = DateTimeOffset.UtcNow, + Messages = { new ChatMessage(ChatRole.Assistant, "Complex response") } + }; + + var testAgent = new TestAgent("TestAgent", "Test description", expectedResponse); + var aiFunction = AgentAIFunctionFactory.CreateFromAgent(testAgent); + + // Act + var arguments = new AIFunctionArguments() { ["query"] = "Test query" }; + var result = await aiFunction.InvokeAsync(arguments); + + // Assert + Assert.NotNull(result); + Assert.Equal("Complex response", result.ToString()); + } + + /// + /// Test implementation of AIAgent for testing purposes. + /// + private sealed class TestAgent : AIAgent + { + private readonly AgentRunResponse? _responseToReturn; + private readonly Exception? _exceptionToThrow; + + public TestAgent(string? name, string? description, AgentRunResponse responseToReturn) + { + this.Name = name; + this.Description = description; + this._responseToReturn = responseToReturn; + } + + public TestAgent(string? name, string? description, Exception exceptionToThrow) + { + this.Name = name; + this.Description = description; + this._exceptionToThrow = exceptionToThrow; + } + + public override string? Name { get; } + public override string? Description { get; } + + public List ReceivedMessages { get; } = new(); + public CancellationToken LastCancellationToken { get; private set; } + public int RunAsyncCallCount { get; private set; } + + public override Task RunAsync( + IReadOnlyCollection messages, + AgentThread? thread = null, + AgentRunOptions? options = null, + CancellationToken cancellationToken = default) + { + this.RunAsyncCallCount++; + this.LastCancellationToken = cancellationToken; + this.ReceivedMessages.AddRange(messages); + + if (this._exceptionToThrow != null) + { + throw this._exceptionToThrow; + } + + return Task.FromResult(this._responseToReturn!); + } + + public override async IAsyncEnumerable RunStreamingAsync( + IReadOnlyCollection messages, + AgentThread? thread = null, + AgentRunOptions? options = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var response = await this.RunAsync(messages, thread, options, cancellationToken); + foreach (var update in response.ToAgentRunResponseUpdates()) + { + yield return update; + } + } + } +} From 9f8823192e7d708205083490e83a10b71fa1ee6b Mon Sep 17 00:00:00 2001 From: Giles Odigwe <79032838+giles17@users.noreply.github.com> Date: Tue, 9 Sep 2025 13:07:20 -0700 Subject: [PATCH 15/32] MCP support for Foundry (#652) Co-authored-by: Giles Odigwe --- .../foundry/tests/test_foundry_chat_client.py | 22 +++++ .../getting_started/agents/foundry/README.md | 1 + .../agents/foundry/foundry_with_local_mcp.py | 81 +++++++++++++++++++ .../samples/getting_started/test_agents.py | 12 +++ .../user-guide/agent-types.md | 47 +++++++++++ 5 files changed, 163 insertions(+) create mode 100644 python/samples/getting_started/agents/foundry/foundry_with_local_mcp.py diff --git a/python/packages/foundry/tests/test_foundry_chat_client.py b/python/packages/foundry/tests/test_foundry_chat_client.py index d2bd6e6c3c..5696af9d81 100644 --- a/python/packages/foundry/tests/test_foundry_chat_client.py +++ b/python/packages/foundry/tests/test_foundry_chat_client.py @@ -18,6 +18,7 @@ from agent_framework import ( FunctionCallContent, FunctionResultContent, HostedCodeInterpreterTool, + MCPStreamableHTTPTool, Role, TextContent, UriContent, @@ -880,6 +881,27 @@ async def test_foundry_chat_client_agent_code_interpreter(): assert "120" in response.text or "factorial" in response.text.lower() +@skip_if_foundry_integration_tests_disabled +async def test_foundry_chat_client_agent_with_mcp_tools() -> None: + """Test MCP tools defined at agent creation with FoundryChatClient.""" + async with ChatAgent( + chat_client=FoundryChatClient(async_credential=AzureCliCredential()), + name="DocsAgent", + instructions="You are a helpful assistant that can help with microsoft documentation questions.", + tools=MCPStreamableHTTPTool( + name="Microsoft Learn MCP", + url="https://learn.microsoft.com/api/mcp", + ), + ) as agent: + # Test that the agent can use MCP tools to answer questions + response = await agent.run("What is Azure App Service?") + + assert isinstance(response, AgentRunResponse) + assert response.text is not None + # Verify the response contains relevant information about Azure App Service + assert any(term in response.text.lower() for term in ["app service", "azure", "web", "application"]) + + @skip_if_foundry_integration_tests_disabled async def test_foundry_chat_client_agent_level_tool_persistence(): """Test that agent-level tools persist across multiple runs with FoundryChatClient.""" diff --git a/python/samples/getting_started/agents/foundry/README.md b/python/samples/getting_started/agents/foundry/README.md index 2a2981f2b8..39d8c9aa6e 100644 --- a/python/samples/getting_started/agents/foundry/README.md +++ b/python/samples/getting_started/agents/foundry/README.md @@ -11,6 +11,7 @@ This folder contains examples demonstrating different ways to create and use age | [`foundry_with_existing_agent.py`](foundry_with_existing_agent.py) | Shows how to work with a pre-existing agent by providing the agent ID to the Foundry chat client. This example also demonstrates proper cleanup of manually created agents. | | [`foundry_with_function_tools.py`](foundry_with_function_tools.py) | Demonstrates how to use function tools with agents. Shows both agent-level tools (defined when creating the agent) and query-level tools (provided with specific queries). | | [`foundry_with_code_interpreter.py`](foundry_with_code_interpreter.py) | Shows how to use the HostedCodeInterpreterTool with Foundry agents to write and execute Python code. Includes helper methods for accessing code interpreter data from response chunks. | +| [`foundry_with_local_mcp.py`](foundry_with_local_mcp.py) | Shows how to integrate Foundry agents with Model Context Protocol (MCP) servers for enhanced functionality and tool integration. Demonstrates both agent-level and run-level tool configuration. | | [`foundry_with_thread.py`](foundry_with_thread.py) | Demonstrates thread management with Foundry agents, including automatic thread creation for stateless conversations and explicit thread management for maintaining conversation context across multiple interactions. | ## Environment Variables diff --git a/python/samples/getting_started/agents/foundry/foundry_with_local_mcp.py b/python/samples/getting_started/agents/foundry/foundry_with_local_mcp.py new file mode 100644 index 0000000000..401a62f2a7 --- /dev/null +++ b/python/samples/getting_started/agents/foundry/foundry_with_local_mcp.py @@ -0,0 +1,81 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio + +from agent_framework import ChatAgent, MCPStreamableHTTPTool +from agent_framework.foundry import FoundryChatClient +from azure.identity.aio import AzureCliCredential + + +async def mcp_tools_on_run_level() -> None: + """Example showing MCP tools defined when running the agent.""" + print("=== Tools Defined on Run Level ===") + + # Tools are provided when running the agent + # This means we have to ensure we connect to the MCP server before running the agent + # and pass the tools to the run method. + async with ( + AzureCliCredential() as credential, + MCPStreamableHTTPTool( + name="Microsoft Learn MCP", + url="https://learn.microsoft.com/api/mcp", + ) as mcp_server, + ChatAgent( + chat_client=FoundryChatClient(async_credential=credential), + name="DocsAgent", + instructions="You are a helpful assistant that can help with microsoft documentation questions.", + ) as agent, + ): + # First query + query1 = "How to create an Azure storage account using az cli?" + print(f"User: {query1}") + result1 = await agent.run(query1, tools=mcp_server) + print(f"{agent.name}: {result1}\n") + print("\n=======================================\n") + # Second query + query2 = "What is Microsoft Semantic Kernel?" + print(f"User: {query2}") + result2 = await agent.run(query2, tools=mcp_server) + print(f"{agent.name}: {result2}\n") + + +async def mcp_tools_on_agent_level() -> None: + """Example showing tools defined when creating the agent.""" + print("=== Tools Defined on Agent Level ===") + + # Tools are provided when creating the agent + # The agent can use these tools for any query during its lifetime + # The agent will connect to the MCP server through its context manager. + async with ( + AzureCliCredential() as credential, + FoundryChatClient(async_credential=credential).create_agent( + name="DocsAgent", + instructions="You are a helpful assistant that can help with microsoft documentation questions.", + tools=MCPStreamableHTTPTool( # Tools defined at agent creation + name="Microsoft Learn MCP", + url="https://learn.microsoft.com/api/mcp", + ), + ) as agent, + ): + # First query + query1 = "How to create an Azure storage account using az cli?" + print(f"User: {query1}") + result1 = await agent.run(query1) + print(f"{agent.name}: {result1}\n") + print("\n=======================================\n") + # Second query + query2 = "What is Microsoft Semantic Kernel?" + print(f"User: {query2}") + result2 = await agent.run(query2) + print(f"{agent.name}: {result2}\n") + + +async def main() -> None: + print("=== Foundry Chat Client Agent with MCP Tools Examples ===\n") + + await mcp_tools_on_agent_level() + await mcp_tools_on_run_level() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/tests/samples/getting_started/test_agents.py b/python/tests/samples/getting_started/test_agents.py index 5e04367061..c4a94f10ff 100644 --- a/python/tests/samples/getting_started/test_agents.py +++ b/python/tests/samples/getting_started/test_agents.py @@ -68,6 +68,9 @@ from samples.getting_started.agents.foundry.foundry_with_explicit_settings impor from samples.getting_started.agents.foundry.foundry_with_function_tools import ( main as foundry_with_function_tools, ) +from samples.getting_started.agents.foundry.foundry_with_local_mcp import ( + main as foundry_with_local_mcp, +) from samples.getting_started.agents.foundry.foundry_with_thread import ( main as foundry_with_thread, ) @@ -337,6 +340,15 @@ agent_samples = [ pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), ], ), + param( + foundry_with_local_mcp, + [], # Non-interactive sample + id="foundry_with_local_mcp", + marks=[ + pytest.mark.foundry, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), # OpenAI Assistants Agent samples param( openai_assistants_basic, diff --git a/user-documentation-python/user-guide/agent-types.md b/user-documentation-python/user-guide/agent-types.md index 55dfc0637d..7364795c23 100644 --- a/user-documentation-python/user-guide/agent-types.md +++ b/user-documentation-python/user-guide/agent-types.md @@ -120,6 +120,53 @@ async with ( For code interpreter examples, see: - [Foundry with code interpreter](../../../python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py) +### Model Context Protocol (MCP) Tools + +Foundry agents support Model Context Protocol (MCP) tools for connecting to external services and data sources. + +Learn more about MCP tools in the [Azure AI Foundry documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/how-to/tools/model-context-protocol). + +```python +from agent_framework import ChatAgent, MCPStreamableHTTPTool +from agent_framework.foundry import FoundryChatClient +from azure.identity.aio import AzureCliCredential + +# Tools can be defined at agent creation +async with ( + AzureCliCredential() as credential, + FoundryChatClient(async_credential=credential).create_agent( + name="DocsAgent", + instructions="You are a helpful assistant that can help with microsoft documentation questions.", + tools=MCPStreamableHTTPTool( + name="Microsoft Learn MCP", + url="https://learn.microsoft.com/api/mcp", + ), + ) as agent +): + response = await agent.run("How to create an Azure storage account using az cli?") +``` + +You can also provide MCP tools when running the agent: + +```python +async with ( + AzureCliCredential() as credential, + MCPStreamableHTTPTool( + name="Microsoft Learn MCP", + url="https://learn.microsoft.com/api/mcp", + ) as mcp_server, + ChatAgent( + chat_client=FoundryChatClient(async_credential=credential), + name="DocsAgent", + instructions="You are a helpful assistant that can help with microsoft documentation questions.", + ) as agent, +): + response = await agent.run("What is Microsoft Semantic Kernel?", tools=mcp_server) +``` + +For complete MCP examples, see: +- [Foundry with MCP tools](../../../python/samples/getting_started/agents/foundry/foundry_with_local_mcp.py) + ## Custom agents It is also possible to create fully custom agents that are not just wrappers around a chat client. From c7a1a1e991a6a2cca0cb79517131e5b421451ca7 Mon Sep 17 00:00:00 2001 From: Ben Thomas Date: Tue, 9 Sep 2025 13:32:08 -0700 Subject: [PATCH 16/32] Fix link to Declarative Workflow demo (#674) --- workflows/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/README.md b/workflows/README.md index c7ceb0d517..56911c1783 100644 --- a/workflows/README.md +++ b/workflows/README.md @@ -1,7 +1,7 @@ # Declarative Workflows This folder contains sample workflow definitions than be ran using the -[Declarative Workflow](../dotnet/demos/DeclarativeWorkflow) demo. +[Declarative Workflow](../dotnet/samples/GettingStarted/Workflows/Declarative) demo. Each workflow is defined in a single YAML file and contains comments with additional information specific to that workflow. From 0fa68836910d1872b97d82d4b4772c9de07cbbe1 Mon Sep 17 00:00:00 2001 From: Chris <66376200+crickman@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:29:25 -0700 Subject: [PATCH 17/32] Clean-up (#677) --- .../DeclarativeWorkflow.csproj | 33 -- dotnet/samples/DeclarativeWorkflow/Program.cs | 304 ------------------ dotnet/samples/DeclarativeWorkflow/README.md | 88 ----- .../Workflows/Declarative/README.md | 4 +- 4 files changed, 2 insertions(+), 427 deletions(-) delete mode 100644 dotnet/samples/DeclarativeWorkflow/DeclarativeWorkflow.csproj delete mode 100644 dotnet/samples/DeclarativeWorkflow/Program.cs delete mode 100644 dotnet/samples/DeclarativeWorkflow/README.md diff --git a/dotnet/samples/DeclarativeWorkflow/DeclarativeWorkflow.csproj b/dotnet/samples/DeclarativeWorkflow/DeclarativeWorkflow.csproj deleted file mode 100644 index ee1a58754c..0000000000 --- a/dotnet/samples/DeclarativeWorkflow/DeclarativeWorkflow.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - - Exe - net9.0 - net9.0 - $(ProjectsDebugTargetFrameworks) - enable - disable - 5ee045b0-aea3-4f08-8d31-32d1a6f8fed0 - $(NoWarn);CA1812 - - - - true - - - - - - - - - - - - - - - - - - diff --git a/dotnet/samples/DeclarativeWorkflow/Program.cs b/dotnet/samples/DeclarativeWorkflow/Program.cs deleted file mode 100644 index c480e96c23..0000000000 --- a/dotnet/samples/DeclarativeWorkflow/Program.cs +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using Azure.AI.Agents.Persistent; -using Azure.Identity; -using Microsoft.Agents.Workflows; -using Microsoft.Agents.Workflows.Declarative; -using Microsoft.Extensions.AI; -using Microsoft.Extensions.Configuration; - -namespace Demo.DeclarativeWorkflow; - -/// -/// HOW TO: Create a workflow from a declartive (yaml based) definition. -/// -/// -/// Configuration -/// Define FOUNDRY_PROJECT_ENDPOINT as a user-secret or environment variable that -/// points to your Foundry project endpoint. -/// Usage -/// Provide the path to the workflow definition file as the first argument. -/// All other arguments are intepreted as a queue of inputs. -/// When no input is queued, interactive input is requested from the console. -/// -internal sealed class Program -{ - public static async Task Main(string[] args) - { - Program program = new(args); - await program.ExecuteAsync(); - } - - private async Task ExecuteAsync() - { - // Read and parse the declarative workflow. - Notify($"WORKFLOW: Parsing {Path.GetFullPath(this.WorkflowFile)}"); - - Stopwatch timer = Stopwatch.StartNew(); - - // Use DeclarativeWorkflowBuilder to build a workflow based on a YAML file. - DeclarativeWorkflowOptions options = - new(new FoundryAgentProvider(this.FoundryEndpoint, new AzureCliCredential())) - { - Configuration = this.Configuration - }; - Workflow workflow = DeclarativeWorkflowBuilder.Build(this.WorkflowFile, options); - - Notify($"\nWORKFLOW: Defined {timer.Elapsed}"); - - Notify("\nWORKFLOW: Starting..."); - - // Run the workflow, just like any other workflow - string input = this.GetWorkflowInput(); - StreamingRun run = await InProcessExecution.StreamAsync(workflow, input); - await this.MonitorWorkflowRunAsync(run); - - Notify("\nWORKFLOW: Done!"); - } - - private const string DefaultWorkflow = "HelloWorld.yaml"; - private const string ConfigKeyFoundryEndpoint = "FOUNDRY_PROJECT_ENDPOINT"; - - private static readonly Dictionary s_nameCache = []; - private static readonly HashSet s_fileCache = []; - - private string WorkflowFile { get; } - private string? WorkflowInput { get; } - private string FoundryEndpoint { get; } - private PersistentAgentsClient FoundryClient { get; } - private IConfiguration Configuration { get; } - - private Program(string[] args) - { - this.WorkflowFile = ParseWorkflowFile(args); - this.WorkflowInput = ParseWorkflowInput(args); - - this.Configuration = InitializeConfig(); - - this.FoundryEndpoint = this.Configuration[ConfigKeyFoundryEndpoint] ?? throw new InvalidOperationException($"Undefined configuration setting: {ConfigKeyFoundryEndpoint}"); - this.FoundryClient = new PersistentAgentsClient(this.FoundryEndpoint, new AzureCliCredential()); - } - - private async Task MonitorWorkflowRunAsync(StreamingRun run) - { - string? messageId = null; - - await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false)) - { - if (evt is ExecutorInvokeEvent executorInvoked) - { - Debug.WriteLine($"STEP ENTER #{executorInvoked.ExecutorId}"); - } - else if (evt is ExecutorCompleteEvent executorComplete) - { - Debug.WriteLine($"STEP EXIT #{executorComplete.ExecutorId}"); - } - else if (evt is ExecutorFailureEvent executorFailure) - { - Debug.WriteLine($"STEP ERROR #{executorFailure.ExecutorId}: {executorFailure.Data?.Message ?? "Unknown"}"); - } - else if (evt is ConversationUpdateEvent invokeEvent) - { - Debug.WriteLine($"CONVERSATION: {invokeEvent.Data}"); - } - else if (evt is AgentRunUpdateEvent streamEvent) - { - if (!string.Equals(messageId, streamEvent.Update.MessageId, StringComparison.Ordinal)) - { - messageId = streamEvent.Update.MessageId; - - if (messageId is not null) - { - string? agentId = streamEvent.Update.AuthorName; - if (agentId is not null) - { - if (!s_nameCache.TryGetValue(agentId, out string? realName)) - { - PersistentAgent agent = await this.FoundryClient.Administration.GetAgentAsync(agentId); - s_nameCache[agentId] = agent.Name; - realName = agent.Name; - } - agentId = realName; - } - agentId ??= nameof(ChatRole.Assistant); - Console.ForegroundColor = ConsoleColor.Cyan; - Console.Write($"\n{agentId.ToUpperInvariant()}:"); - Console.ForegroundColor = ConsoleColor.DarkGray; - Console.WriteLine($" [{messageId}]"); - } - } - - ChatResponseUpdate? chatUpdate = streamEvent.Update.RawRepresentation as ChatResponseUpdate; - switch (chatUpdate?.RawRepresentation) - { - case MessageContentUpdate messageUpdate: - string? fileId = messageUpdate.ImageFileId ?? messageUpdate.TextAnnotation?.OutputFileId; - if (fileId is not null && s_fileCache.Add(fileId)) - { - BinaryData content = await this.FoundryClient.Files.GetFileContentAsync(fileId); - await DownloadFileContentAsync(Path.GetFileName(messageUpdate.TextAnnotation?.TextToReplace ?? "response.png"), content); - } - break; - } - try - { - Console.ResetColor(); - Console.Write(streamEvent.Data); - } - finally - { - Console.ResetColor(); - } - } - else if (evt is AgentRunResponseEvent messageEvent) - { - try - { - Console.WriteLine(); - if (messageEvent.Response.AgentId is null) - { - Console.ForegroundColor = ConsoleColor.Cyan; - Console.WriteLine("ACTIVITY:"); - Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine(messageEvent.Response?.Text.Trim()); - } - else - { - if (messageEvent.Response.Usage is not null) - { - Console.ForegroundColor = ConsoleColor.DarkGray; - Console.WriteLine($"[Tokens Total: {messageEvent.Response.Usage.TotalTokenCount}, Input: {messageEvent.Response.Usage.InputTokenCount}, Output: {messageEvent.Response.Usage.OutputTokenCount}]"); - } - } - } - finally - { - Console.ResetColor(); - } - } - } - } - - private static string ParseWorkflowFile(string[] args) - { - string workflowFile = args.FirstOrDefault() ?? DefaultWorkflow; - - if (!File.Exists(workflowFile) && !Path.IsPathFullyQualified(workflowFile)) - { - string? repoFolder = GetRepoFolder(); - if (repoFolder is not null) - { - workflowFile = Path.Combine(repoFolder, "Workflows", workflowFile); - workflowFile = Path.ChangeExtension(workflowFile, ".yaml"); - } - } - - if (!File.Exists(workflowFile)) - { - throw new InvalidOperationException($"Unable to locate workflow: {Path.GetFullPath(workflowFile)}."); - } - - return workflowFile; - - static string? GetRepoFolder() - { - DirectoryInfo? current = new(Directory.GetCurrentDirectory()); - - while (current is not null) - { - if (Directory.Exists(Path.Combine(current.FullName, ".git"))) - { - return current.FullName; - } - - current = current.Parent; - } - - return null; - } - } - - private string GetWorkflowInput() - { - string? input = this.WorkflowInput; - - try - { - Console.ForegroundColor = ConsoleColor.DarkGreen; - - Console.Write("\nINPUT: "); - - Console.ForegroundColor = ConsoleColor.White; - - if (!string.IsNullOrWhiteSpace(input)) - { - Console.WriteLine(input); - return input; - } - while (string.IsNullOrWhiteSpace(input)) - { - input = Console.ReadLine(); - } - - return input.Trim(); - } - finally - { - Console.ResetColor(); - } - } - - private static string? ParseWorkflowInput(string[] args) - { - if (args.Length == 0) - { - return null; - } - - string[] workflowInput = [.. args.Skip(1)]; - - return workflowInput.FirstOrDefault(); - } - - // Load configuration from user-secrets - private static IConfigurationRoot InitializeConfig() => - new ConfigurationBuilder() - .AddUserSecrets(Assembly.GetExecutingAssembly()) - .AddEnvironmentVariables() - .Build(); - - private static void Notify(string message) - { - Console.ForegroundColor = ConsoleColor.Cyan; - try - { - Console.WriteLine(message); - } - finally - { - Console.ResetColor(); - } - } - - private static async ValueTask DownloadFileContentAsync(string filename, BinaryData content) - { - string filePath = Path.Combine(Path.GetTempPath(), Path.GetFileName(filename)); - filePath = Path.ChangeExtension(filePath, ".png"); - - await File.WriteAllBytesAsync(filePath, content.ToArray()); - - Process.Start( - new ProcessStartInfo - { - FileName = "cmd.exe", - Arguments = $"/C start {filePath}" - }); - } -} diff --git a/dotnet/samples/DeclarativeWorkflow/README.md b/dotnet/samples/DeclarativeWorkflow/README.md deleted file mode 100644 index 5229bd50c2..0000000000 --- a/dotnet/samples/DeclarativeWorkflow/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# Summary - -This demo showcases the ability to parse a declarative Foundry Workflow file (YAML) to build a `Workflow<>` -be executed using the same pattern as any code-based workflow. - -## Configuration - -This demo requires configuration to access agents an [Azure Foundry Project](https://learn.microsoft.com/azure/ai-foundry). - -#### Settings - -We suggest using .NET [Secret Manager](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets) -to avoid the risk of leaking secrets into the repository, branches and pull requests. -You can also use environment variables if you prefer. - -To set your secrets as an environment variable (PowerShell): - -```pwsh -$env:FOUNDRY_PROJECT_ENDPOINT="https://..." -``` - -To set your secrets with .NET Secret Manager: - -1. From the root of the respository, navigate the console to the project folder: - - ``` - cd dotnet/demos/DeclarativeWorkflow - ``` - -2. Examine existing secret definitions: - - ``` - dotnet user-secrets list - ``` - -3. If needed, perform first time initialization: - - ``` - dotnet user-secrets init - ``` - -4. Define setting that identifies your Azure Foundry Project (endpoint): - - ``` - dotnet user-secrets set "FOUNDRY_PROJECT_ENDPOINT" "https://..." - ``` - -#### Authorization - -Use [_Azure CLI_](https://learn.microsoft.com/cli/azure/authenticate-azure-cli) to authorize access to your Azure Foundry Project: - - ``` - az login - az account get-access-token - ``` - -#### Agents - -The sample workflows rely on agents defined in your Azure Foundry Project. - -To create agents, run the [`Create.ps1`](../../../workflows/) script. -This will create the agents used in the sample workflows in your Azure Foundry Project and format a script you can copy and use to configure your environment. - -> Note: `Create.ps1` relies upon the `FOUNDRY_PROJECT_ENDPOINT` setting. - -## Execution - -Run the demo from the console by specifying a path to a declarative (YAML) workflow file. -The repository has example workflows available in the root [`/workflows`](../../../workflows) folder. - -1. From the root of the respository, navigate the console to the project folder: - - ```sh - cd dotnet/demos/DeclarativeWorkflow - - ``` - -2. Run the demo referencing a sample workflow by name: - - ```sh - dotnet run HelloWorld - ``` - -3. Run the demo with a path to any workflow file: - - ```sh - dotnet run c:/myworkflows/HelloWorld.yaml - ``` diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/README.md b/dotnet/samples/GettingStarted/Workflows/Declarative/README.md index 57101c9b21..0ddda6992d 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/README.md +++ b/dotnet/samples/GettingStarted/Workflows/Declarative/README.md @@ -24,7 +24,7 @@ To set your secrets with .NET Secret Manager: 1. From the root of the repository, navigate the console to the project folder: ``` - cd dotnet/samples/GettingStarted/Workflows/Declarative + cd dotnet/samples/GettingStarted/Workflows/Declarative/DeclarativeWorkflow ``` 2. Examine existing secret definitions: @@ -71,7 +71,7 @@ The repository has example workflows available in the root [`/workflows`](../../ 1. From the root of the repository, navigate the console to the project folder: ```sh - cd dotnet/samples/GettingStarted/Workflows/Declarative + cd dotnet/samples/GettingStarted/Workflows/Declarative/DeclarativeWorkflow ``` 2. Run the demo referencing a sample workflow by name: From 383d51443c9eeb1df1a68ecce6cfd258f21bc4ca Mon Sep 17 00:00:00 2001 From: Eduard van Valkenburg Date: Wed, 10 Sep 2025 01:31:26 +0200 Subject: [PATCH 18/32] Python: add base_url parameter for openai chat client (#661) * added base_url option for openai chat client * fix test url --- .../agent_framework/openai/_chat_client.py | 3 +++ .../main/agent_framework/openai/_shared.py | 26 +++++++++++-------- .../tests/openai/test_openai_chat_client.py | 6 +++++ python/pyproject.toml | 4 +-- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/python/packages/main/agent_framework/openai/_chat_client.py b/python/packages/main/agent_framework/openai/_chat_client.py index 754e17dbd9..bd41ac48d3 100644 --- a/python/packages/main/agent_framework/openai/_chat_client.py +++ b/python/packages/main/agent_framework/openai/_chat_client.py @@ -387,6 +387,7 @@ class OpenAIChatClient(OpenAIConfigMixin, OpenAIBaseChatClient): default_headers: Mapping[str, str] | None = None, async_client: AsyncOpenAI | None = None, instruction_role: str | None = None, + base_url: str | None = None, env_file_path: str | None = None, env_file_encoding: str | None = None, ) -> None: @@ -404,6 +405,7 @@ class OpenAIChatClient(OpenAIConfigMixin, OpenAIBaseChatClient): async_client: An existing client to use. (Optional) instruction_role: The role to use for 'instruction' messages, for example, "system" or "developer". If not provided, the default is "system". + base_url: The optional base URL to use. If provided will override the standard value for a OpenAI connector. env_file_path: Use the environment settings file as a fallback to environment variables. (Optional) env_file_encoding: The encoding of the environment settings file. (Optional) @@ -436,6 +438,7 @@ class OpenAIChatClient(OpenAIConfigMixin, OpenAIBaseChatClient): default_headers=default_headers, client=async_client, instruction_role=instruction_role, + base_url=base_url, ) @classmethod diff --git a/python/packages/main/agent_framework/openai/_shared.py b/python/packages/main/agent_framework/openai/_shared.py index 1e7c29ec8d..5a7b5c5a75 100644 --- a/python/packages/main/agent_framework/openai/_shared.py +++ b/python/packages/main/agent_framework/openai/_shared.py @@ -138,6 +138,7 @@ class OpenAIConfigMixin(OpenAIBase): default_headers: Mapping[str, str] | None = None, client: AsyncOpenAI | None = None, instruction_role: str | None = None, + base_url: str | None = None, **kwargs: Any, ) -> None: """Initialize a client for OpenAI services. @@ -146,17 +147,19 @@ class OpenAIConfigMixin(OpenAIBase): different types of AI model interactions, like chat or text completion. Args: - ai_model_id (str): OpenAI model identifier. Must be non-empty. + ai_model_id: OpenAI model identifier. Must be non-empty. Default to a preset value. - api_key (str): OpenAI API key for authentication. + api_key: OpenAI API key for authentication. Must be non-empty. (Optional) - org_id (str): OpenAI organization ID. This is optional + org_id: OpenAI organization ID. This is optional unless the account belongs to multiple organizations. - default_headers (Mapping[str, str]): Default headers + default_headers: Default headers for HTTP requests. (Optional) - client (AsyncOpenAI): An existing OpenAI client, optional. - instruction_role (str): The role to use for 'instruction' + client: An existing OpenAI client, optional. + instruction_role: The role to use for 'instruction' messages, for example, summarization prompts could use `developer` or `system`. (Optional) + base_url: The optional base URL to use. If provided will override the standard value for a OpenAI connector. + Will not be used when supplying a custom client. kwargs: Additional keyword arguments. """ @@ -169,11 +172,12 @@ class OpenAIConfigMixin(OpenAIBase): if not client: if not api_key: raise ServiceInitializationError("Please provide an api_key") - client = AsyncOpenAI( - api_key=api_key, - organization=org_id, - default_headers=merged_headers, - ) + args: dict[str, Any] = {"api_key": api_key, "default_headers": merged_headers} + if org_id: + args["organization"] = org_id + if base_url: + args["base_url"] = base_url + client = AsyncOpenAI(**args) args = { "ai_model_id": ai_model_id, "client": client, diff --git a/python/packages/main/tests/openai/test_openai_chat_client.py b/python/packages/main/tests/openai/test_openai_chat_client.py index cd0baf5629..35e75095a1 100644 --- a/python/packages/main/tests/openai/test_openai_chat_client.py +++ b/python/packages/main/tests/openai/test_openai_chat_client.py @@ -74,6 +74,12 @@ def test_init_with_default_header(openai_unit_test_env: dict[str, str]) -> None: assert open_ai_chat_completion.client.default_headers[key] == value +def test_init_base_url(openai_unit_test_env: dict[str, str]) -> None: + # Test successful initialization + open_ai_chat_completion = OpenAIChatClient(base_url="http://localhost:1234/v1") + assert str(open_ai_chat_completion.client.base_url) == "http://localhost:1234/v1/" + + @pytest.mark.parametrize("exclude_list", [["OPENAI_CHAT_MODEL_ID"]], indirect=True) def test_init_with_empty_model_id(openai_unit_test_env: dict[str, str]) -> None: with pytest.raises(ServiceInitializationError): diff --git a/python/pyproject.toml b/python/pyproject.toml index 503de388d4..55f7c6fdb5 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -125,7 +125,7 @@ notice-rgx = "^# Copyright \\(c\\) Microsoft\\. All rights reserved\\." min-file-size = 1 [tool.pytest.ini_options] -testpaths = 'tests' +testpaths = '**/tests' addopts = "-ra -q -r fEX" asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" @@ -133,7 +133,7 @@ filterwarnings = [] timeout = 120 markers = [ "azure: marks tests as Azure provider specific", - "foundry: marks tests as Foundry provider specific", + "foundry: marks tests as Foundry provider specific", "openai: marks tests as OpenAI provider specific", ] From e837741da1f1e6dd67393c93a07454fd5a67ee58 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Tue, 9 Sep 2025 18:20:05 -0700 Subject: [PATCH 19/32] Update PR template with breaking change note (#666) --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 33faf7afd9..6658ebc9fd 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -20,4 +20,4 @@ Please help reviewers and future users, providing the following information: - [ ] The code builds clean without any errors or warnings - [ ] The PR follows the [Contribution Guidelines](https://github.com/microsoft/agent-framework/blob/main/CONTRIBUTING.md) - [ ] All unit tests pass, and I have added new tests where possible -- [ ] I didn't break anyone :smile: +- [ ] **Is this a breaking change?** If yes, add "[BREAKING]" prefix to the title of the PR. \ No newline at end of file From 7786e2f4057ece88ad69b87050d7044033279937 Mon Sep 17 00:00:00 2001 From: Mark Wallace <127216156+markwallace-microsoft@users.noreply.github.com> Date: Wed, 10 Sep 2025 06:52:30 +0100 Subject: [PATCH 20/32] .NET: Add getting started samples that show how to call MCP tools (#635) * Add getting started samples that show how to call MCP tools * Add getting started samples that show how to call MCP tools * Update dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix README * Address code review feedback --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dotnet/Directory.Packages.props | 2 + dotnet/agent-framework-dotnet.slnx | 4 + .../Agent_MCP_Server/Agent_MCP_Server.csproj | 25 +++ .../Agent_MCP_Server/Program.cs | 35 +++++ .../Agent_MCP_Server/README.md | 31 ++++ .../Agent_MCP_Server_Auth.csproj | 27 ++++ .../Agent_MCP_Server_Auth/Program.cs | 148 ++++++++++++++++++ .../Agent_MCP_Server_Auth/README.md | 125 +++++++++++++++ .../ModelContextProtocol/README.md | 64 ++++++++ dotnet/samples/GettingStarted/README.md | 1 + 10 files changed, 462 insertions(+) create mode 100644 dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/Agent_MCP_Server.csproj create mode 100644 dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/Program.cs create mode 100644 dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/README.md create mode 100644 dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Agent_MCP_Server_Auth.csproj create mode 100644 dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Program.cs create mode 100644 dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/README.md create mode 100644 dotnet/samples/GettingStarted/ModelContextProtocol/README.md diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index 280cdd7743..07fbcf93b4 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -75,8 +75,10 @@ + + diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index 2bf90eda9b..cd28a9e723 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -53,6 +53,10 @@ + + + + diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/Agent_MCP_Server.csproj b/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/Agent_MCP_Server.csproj new file mode 100644 index 0000000000..759eaae572 --- /dev/null +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/Agent_MCP_Server.csproj @@ -0,0 +1,25 @@ + + + + Exe + net9.0 + 12 + + enable + disable + + + + + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/Program.cs b/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/Program.cs new file mode 100644 index 0000000000..8faffd1339 --- /dev/null +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/Program.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to create and use a simple AI agent with tools from an MCP Server. + +using System; +using System.Linq; +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.AI.Agents; +using ModelContextProtocol.Client; +using OpenAI; + +var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); +var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +// Create an MCPClient for the GitHub server +await using var mcpClient = await McpClientFactory.CreateAsync(new StdioClientTransport(new() +{ + Name = "MCPServer", + Command = "npx", + Arguments = ["-y", "--verbose", "@modelcontextprotocol/server-github"], +})); + +// Retrieve the list of tools available on the GitHub server +var mcpTools = await mcpClient.ListToolsAsync().ConfigureAwait(false); + +AIAgent agent = new AzureOpenAIClient( + new Uri(endpoint), + new AzureCliCredential()) + .GetChatClient(deploymentName) + .CreateAIAgent(instructions: "You answer questions related to GitHub repositories only.", tools: [.. mcpTools.Cast()]); + +// Invoke the agent and output the text result. +Console.WriteLine(await agent.RunAsync("Summarize the last four commits to the microsoft/semantic-kernel repository?")); diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/README.md b/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/README.md new file mode 100644 index 0000000000..f0996dc1fd --- /dev/null +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server/README.md @@ -0,0 +1,31 @@ +# Model Context Protocol Sample + +This example demonstrates how to use tools from a Model Context Protocol server with Agent Framework. + +MCP is an open protocol that standardizes how applications provide context to LLMs. + +For information on Model Context Protocol (MCP) please refer to the [documentation](https://modelcontextprotocol.io/introduction). + +The sample shows: + +1. How to connect to an MCP Server +1. Retrieve the list of tools the MCP Server makes available +1. Convert the MCP tools to `AIFunction`'s so they can be added to an agent +1. Invoke the tools from an agent using function calling + +## Configuring Environment Variables + +Set the following environment variables: + +```powershell +$env:AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" # Replace with your Azure OpenAI resource endpoint +$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +``` + +## Setup and Running + +Run the ModelContextProtocolPluginAuth sample + +```bash +dotnet run +``` diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Agent_MCP_Server_Auth.csproj b/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Agent_MCP_Server_Auth.csproj new file mode 100644 index 0000000000..d26676a576 --- /dev/null +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Agent_MCP_Server_Auth.csproj @@ -0,0 +1,27 @@ + + + + Exe + net9.0 + 12 + + enable + disable + + + + + + + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Program.cs b/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Program.cs new file mode 100644 index 0000000000..26e2ae22f1 --- /dev/null +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/Program.cs @@ -0,0 +1,148 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to create and use a simple AI agent with tools from an MCP Server that requires authentication. + +using System; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.AI.Agents; +using Microsoft.Extensions.Logging; +using ModelContextProtocol.Client; +using OpenAI; + +var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); +var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +// We can customize a shared HttpClient with a custom handler if desired +using var sharedHandler = new SocketsHttpHandler +{ + PooledConnectionLifetime = TimeSpan.FromMinutes(2), + PooledConnectionIdleTimeout = TimeSpan.FromMinutes(1) +}; +using var httpClient = new HttpClient(sharedHandler); + +var consoleLoggerFactory = LoggerFactory.Create(builder => +{ + builder.AddConsole(); +}); + +// Create SSE client transport for the MCP server +var serverUrl = "http://localhost:7071/"; +var transport = new SseClientTransport(new() +{ + Endpoint = new Uri(serverUrl), + Name = "Secure Weather Client", + OAuth = new() + { + ClientName = "ProtectedMcpClient", + RedirectUri = new Uri("http://localhost:1179/callback"), + AuthorizationRedirectDelegate = HandleAuthorizationUrlAsync, + } +}, httpClient, consoleLoggerFactory); + +// Create an MCPClient for the protected MCP server +await using var mcpClient = await McpClientFactory.CreateAsync(transport, loggerFactory: consoleLoggerFactory); + +// Retrieve the list of tools available on the GitHub server +var mcpTools = await mcpClient.ListToolsAsync().ConfigureAwait(false); + +AIAgent agent = new AzureOpenAIClient( + new Uri(endpoint), + new AzureCliCredential()) + .GetChatClient(deploymentName) + .CreateAIAgent(instructions: "You answer questions related to the weather.", tools: [.. mcpTools.Select(mcpTool => (AITool)mcpTool)]); + +// Invoke the agent and output the text result. +Console.WriteLine(await agent.RunAsync("Get current weather alerts for New York?")); + +// Handles the OAuth authorization URL by starting a local HTTP server and opening a browser. +// This implementation demonstrates how SDK consumers can provide their own authorization flow. +static async Task HandleAuthorizationUrlAsync(Uri authorizationUrl, Uri redirectUri, CancellationToken cancellationToken) +{ + Console.WriteLine("Starting OAuth authorization flow..."); + Console.WriteLine($"Opening browser to: {authorizationUrl}"); + + var listenerPrefix = redirectUri.GetLeftPart(UriPartial.Authority); + if (!listenerPrefix.EndsWith("/", StringComparison.InvariantCultureIgnoreCase)) + { + listenerPrefix += "/"; + } + + using var listener = new HttpListener(); + listener.Prefixes.Add(listenerPrefix); + + try + { + listener.Start(); + Console.WriteLine($"Listening for OAuth callback on: {listenerPrefix}"); + + OpenBrowser(authorizationUrl); + + var context = await listener.GetContextAsync(); + var query = HttpUtility.ParseQueryString(context.Request.Url?.Query ?? string.Empty); + var code = query["code"]; + var error = query["error"]; + + string responseHtml = "

Authentication complete

You can close this window now.

"; + byte[] buffer = Encoding.UTF8.GetBytes(responseHtml); + context.Response.ContentLength64 = buffer.Length; + context.Response.ContentType = "text/html"; + context.Response.OutputStream.Write(buffer, 0, buffer.Length); + context.Response.Close(); + + if (!string.IsNullOrEmpty(error)) + { + Console.WriteLine($"Auth error: {error}"); + return null; + } + + if (string.IsNullOrEmpty(code)) + { + Console.WriteLine("No authorization code received"); + return null; + } + + Console.WriteLine("Authorization code received successfully."); + return code; + } + catch (Exception ex) + { + Console.WriteLine($"Error getting auth code: {ex.Message}"); + return null; + } + finally + { + if (listener.IsListening) + { + listener.Stop(); + } + } +} + +// Opens the specified URL in the default browser. +static void OpenBrowser(Uri url) +{ + try + { + var psi = new ProcessStartInfo + { + FileName = url.ToString(), + UseShellExecute = true + }; + Process.Start(psi); + } + catch (Exception ex) + { + Console.WriteLine($"Error opening browser. {ex.Message}"); + Console.WriteLine($"Please manually open this URL: {url}"); + } +} diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/README.md b/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/README.md new file mode 100644 index 0000000000..ae88df95ee --- /dev/null +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/Agent_MCP_Server_Auth/README.md @@ -0,0 +1,125 @@ +# Model Context Protocol Sample + +This example demonstrates how to use tools from a protected Model Context Protocol server with Agent Framework. + +MCP is an open protocol that standardizes how applications provide context to LLMs. + +For information on Model Context Protocol (MCP) please refer to the [documentation](https://modelcontextprotocol.io/introduction). + +The sample shows: + +1. How to connect to a protected MCP Server using OAuth 2.0 authentication +1. How to implement a custom OAuth authorization flow with browser-based authentication +1. Retrieve the list of tools the MCP Server makes available +1. Convert the MCP tools to `AIFunction`'s so they can be added to an agent +1. Invoke the tools from an agent using function calling + +## Installing Prerequisites + +- A self-signed certificate to enable HTTPS use in development, see [dotnet dev-certs](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-dev-certs) +- .NET 9.0 or later +- A running TestOAuthServer (for OAuth authentication), see [Start the Test OAuth Server](https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples/ProtectedMcpClient#step-1-start-the-test-oauth-server) +- A running ProtectedMCPServer (for MCP services), see [Start the Protected MCP Server](https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples/ProtectedMcpClient#step-2-start-the-protected-mcp-server) + +## Configuring Environment Variables + +Set the following environment variables: + +```powershell +$env:AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" # Replace with your Azure OpenAI resource endpoint +$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +``` + +## Setup and Running + +### Step 1: Start the Test OAuth Server + +First, you need to start the TestOAuthServer which provides OAuth authentication: + +```bash +cd \tests\ModelContextProtocol.TestOAuthServer +dotnet run --framework net9.0 +``` + +The OAuth server will start at `https://localhost:7029` + +### Step 2: Start the Protected MCP Server + +Next, start the ProtectedMCPServer which provides the weather tools: + +```bash +cd \samples\ProtectedMCPServer +dotnet run +``` + +The protected server will start at `http://localhost:7071` + +### Step 3: Run the ModelContextProtocolPluginAuth sample + +Finally, run this client: + +```bash +dotnet run +``` + +## What Happens + +1. The client attempts to connect to the protected MCP server at `http://localhost:7071` +2. The server responds with OAuth metadata indicating authentication is required +3. The client initiates OAuth 2.0 authorization code flow: + - Opens a browser to the authorization URL at the OAuth server + - Starts a local HTTP listener on `http://localhost:1179/callback` to receive the authorization code + - Exchanges the authorization code for an access token +4. The client uses the access token to authenticate with the MCP server +5. The client lists available tools and calls the `GetAlerts` tool for New York state + +The following diagram outlines an example OAuth flow: + +```mermaid +sequenceDiagram + participant Client as Client + participant Server as MCP Server (Resource Server) + participant AuthServer as Authorization Server + + Client->>Server: MCP request without access token + Server-->>Client: HTTP 401 Unauthorized with WWW-Authenticate header + Note over Client: Analyze and delegate tasks + Client->>Server: GET /.well-known/oauth-protected-resource + Server-->>Client: Resource metadata with authorization server URL + Note over Client: Validate RS metadata, build AS metadata URL + Client->>AuthServer: GET /.well-known/oauth-authorization-server + AuthServer-->>Client: Authorization server metadata + Note over Client,AuthServer: OAuth 2.0 authorization flow happens here + Client->>AuthServer: Token request + AuthServer-->>Client: Access token + Client->>Server: MCP request with access token + Server-->>Client: MCP response + Note over Client,Server: MCP communication continues with valid token +``` + +## OAuth Configuration + +The client is configured with: +- **Client ID**: `demo-client` +- **Client Secret**: `demo-secret` +- **Redirect URI**: `http://localhost:1179/callback` +- **OAuth Server**: `https://localhost:7029` +- **Protected Resource**: `http://localhost:7071` + +## Available Tools + +Once authenticated, the client can access weather tools including: +- **GetAlerts**: Get weather alerts for a US state +- **GetForecast**: Get weather forecast for a location (latitude/longitude) + +## Troubleshooting + +- Ensure the ASP.NET Core dev certificate is trusted. + ``` + dotnet dev-certs https --clean + dotnet dev-certs https --trust + ``` +- Ensure all three services are running in the correct order +- Check that ports 7029, 7071, and 1179 are available +- If the browser doesn't open automatically, copy the authorization URL from the console and open it manually +- Make sure to allow the OAuth server's self-signed certificate in your browser \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/README.md b/dotnet/samples/GettingStarted/ModelContextProtocol/README.md new file mode 100644 index 0000000000..be84bff51f --- /dev/null +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/README.md @@ -0,0 +1,64 @@ +# Getting started with Model Content Protocol + +The getting started with Model Content Protocol samples demonstrate how to use MCP Server tools from an agent. + +## Getting started with agents prerequisites + +Before you begin, ensure you have the following prerequisites: + +- .NET 9.0 SDK or later +- Azure OpenAI service endpoint and deployment configured +- Azure CLI installed and authenticated (for Azure credential authentication) +- User has the `Cognitive Services OpenAI Contributor` role for the Azure OpenAI resource. + +**Note**: These samples use Azure OpenAI models. For more information, see [how to deploy Azure OpenAI models with Azure AI Foundry](https://learn.microsoft.com/en-us/azure/ai-foundry/how-to/deploy-models-openai). + +**Note**: These samples use Azure CLI credentials for authentication. Make sure you're logged in with `az login` and have access to the Azure OpenAI resource and have the `Cognitive Services OpenAI Contributor` role. For more information, see the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli-interactively). + +## Samples + +|Sample|Description| +|---|---| +|[Agent with MCP server tools](./Agent_MCP_Server/)|This sample demonstrates how to use MCP server tools with a simple agent| +|[Agent with MCP server tools and authorization](./Agent_MCP_Server_Auth/)|This sample demonstrates how to use MCP Server tools from a protected MCP server with a simple agent| + +## Running the samples from the console + +To run the samples, navigate to the desired sample directory, e.g. + +```powershell +cd Agents_Step01_Running +``` + +Set the following environment variables: + +```powershell +$env:AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" # Replace with your Azure OpenAI resource endpoint +$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini +``` + +If the variables are not set, you will be prompted for the values when running the samples. + +Execute the following command to build the sample: + +```powershell +dotnet build +``` + +Execute the following command to run the sample: + +```powershell +dotnet run --no-build +``` + +Or just build and run in one step: + +```powershell +dotnet run +``` + +## Running the samples from Visual Studio + +Open the solution in Visual Studio and set the desired sample project as the startup project. Then, run the project using the built-in debugger or by pressing `F5`. + +You will be prompted for any required environment variables if they are not already set. diff --git a/dotnet/samples/GettingStarted/README.md b/dotnet/samples/GettingStarted/README.md index e95b96c923..dacf0b92cd 100644 --- a/dotnet/samples/GettingStarted/README.md +++ b/dotnet/samples/GettingStarted/README.md @@ -12,3 +12,4 @@ of the agent framework. |[Agent Open Telemetry](./AgentOpenTelemetry/README.md)|Getting started with OpenTelemetry for agents| |[Agent With OpenAI exchange types](./AgentWithOpenAI/README.md)|Using OpenAI exchange types with agents| |[Workflow](./Workflow/README.md)|Getting started with Workflow| +|[Model Context Protocol](./ModelContextProtocol/README.md)|Getting started with Model Context Protocol| From 3b81164a6dde9f19d3132c6c9f4e81cd93c8cb4f Mon Sep 17 00:00:00 2001 From: Jacob Alber Date: Wed, 10 Sep 2025 02:10:30 -0400 Subject: [PATCH 21/32] .NET: fix: MessageMerger crashes when there are no dangling messages (#660) * fix: MessageMerger crashes when there are no dangling messages * refactor: Better logic for AgentId in Workflow-as-Agent If the parent "agent" instance received an Id or Name when being instantiated, we should avoid stomping over it with the subagents' ids. But if there is no parent identifier, and only a single subagent yielded identified messages, pull that in. --- .../MessageMerger.cs | 91 ++++++++++++++++-- .../WorkflowHostAgent.cs | 2 +- .../ChatMessageBuilder.cs | 92 +++++++++++++++++++ .../MessageMergerTests.cs | 42 +++++++++ 4 files changed, 219 insertions(+), 8 deletions(-) create mode 100644 dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ChatMessageBuilder.cs create mode 100644 dotnet/tests/Microsoft.Agents.Workflows.UnitTests/MessageMergerTests.cs diff --git a/dotnet/src/Microsoft.Agents.Workflows/MessageMerger.cs b/dotnet/src/Microsoft.Agents.Workflows/MessageMerger.cs index 021330a90d..52e0df960c 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/MessageMerger.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/MessageMerger.cs @@ -62,7 +62,11 @@ internal class MessageMerger List result = this.UpdatesByMessageId.Keys.Select(AggregateUpdatesToMessage) .ToList(); - result.AddRange(this.ComputeDangling().Messages); + if (this.DanglingUpdates.Count > 0) + { + result.AddRange(this.ComputeDangling().Messages); + } + return result; ChatMessage AggregateUpdatesToMessage(string messageId) @@ -126,16 +130,17 @@ internal class MessageMerger return left.CreatedAt.Value.CompareTo(right.CreatedAt.Value); } - public AgentRunResponse ComputeMerged(string primaryResponseId) + public AgentRunResponse ComputeMerged(string primaryResponseId, string? primaryAgentId = null, string? primaryAgentName = null) { List messages = []; Dictionary responses = new(); + HashSet agentIds = new(); foreach (string responseId in this._mergeStates.Keys) { ResponseMergeState mergeState = this._mergeStates[responseId]; - List responseList = mergeState.UpdatesByMessageId.Keys.Select(messageId => mergeState.ComputeMerged(messageId)).ToList(); + List responseList = mergeState.UpdatesByMessageId.Keys.Select(mergeState.ComputeMerged).ToList(); if (mergeState.DanglingUpdates.Count > 0) { responseList.Add(mergeState.ComputeDangling()); @@ -143,13 +148,39 @@ internal class MessageMerger responseList.Sort(this.CompareByDateTimeOffset); responses[responseId] = responseList.Aggregate(MergeResponses); - messages.AddRange(responses[responseId].Messages); + messages.AddRange(GetMessagesWithCreatedAt(responses[responseId])); + } + + UsageDetails? usage = null; + AdditionalPropertiesDictionary? additionalProperties = null; + HashSet createdTimes = new(); + + foreach (AgentRunResponse response in responses.Values) + { + if (response.AgentId != null) + { + agentIds.Add(response.AgentId); + } + + if (response.CreatedAt.HasValue) + { + createdTimes.Add(response.CreatedAt.Value); + } + + usage = MergeUsage(usage, response.Usage); + additionalProperties = MergeProperties(additionalProperties, response.AdditionalProperties); } messages.AddRange(this._danglingState.ComputeFlattened()); return new AgentRunResponse(messages) { ResponseId = primaryResponseId, + AgentId = primaryAgentId + ?? primaryAgentName + ?? (agentIds.Count == 1 ? agentIds.First() : null), + CreatedAt = DateTimeOffset.Now, + Usage = usage, + AdditionalProperties = additionalProperties }; AgentRunResponse MergeResponses(AgentRunResponse? current, AgentRunResponse incoming) @@ -170,16 +201,62 @@ internal class MessageMerger return new() { AgentId = incoming.AgentId ?? current.AgentId, - AdditionalProperties = incoming.AdditionalProperties ?? current.AdditionalProperties, + AdditionalProperties = MergeProperties(current.AdditionalProperties, incoming.AdditionalProperties), CreatedAt = incoming.CreatedAt ?? current.CreatedAt, Messages = current.Messages.Concat(incoming.Messages).ToList(), ResponseId = current.ResponseId, RawRepresentation = rawRepresentation, - Usage = Merge(current.Usage, incoming.Usage), + Usage = MergeUsage(current.Usage, incoming.Usage), }; } - static UsageDetails? Merge(UsageDetails? current, UsageDetails? incoming) + static IEnumerable GetMessagesWithCreatedAt(AgentRunResponse response) + { + if (response.Messages.Count == 0) + { + return []; + } + + if (response.CreatedAt is null) + { + return response.Messages; + } + + DateTimeOffset? createdAt = response.CreatedAt; + return response.Messages.Select( + message => new ChatMessage + { + Role = message.Role, + AuthorName = message.AuthorName, + Contents = message.Contents, + MessageId = message.MessageId, + CreatedAt = createdAt, + RawRepresentation = message.RawRepresentation + }); + } + + static AdditionalPropertiesDictionary? MergeProperties(AdditionalPropertiesDictionary? current, AdditionalPropertiesDictionary? incoming) + { + if (current == null) + { + return incoming; + } + + if (incoming == null) + { + return current; + } + + AdditionalPropertiesDictionary merged = new(current); + foreach (string key in incoming.Keys) + { + merged[key] = incoming[key]; + } + + return merged; + } + + static UsageDetails? MergeUsage(UsageDetails? current, UsageDetails? incoming) { if (current == null) { diff --git a/dotnet/src/Microsoft.Agents.Workflows/WorkflowHostAgent.cs b/dotnet/src/Microsoft.Agents.Workflows/WorkflowHostAgent.cs index 98b7a05fda..3751cd2c9f 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/WorkflowHostAgent.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/WorkflowHostAgent.cs @@ -129,7 +129,7 @@ internal class WorkflowHostAgent : AIAgent merger.AddUpdate(update); } - return merger.ComputeMerged(workflowThread.ResponseId); + return merger.ComputeMerged(workflowThread.ResponseId, this.Id, this.Name); } public override async diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ChatMessageBuilder.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ChatMessageBuilder.cs new file mode 100644 index 0000000000..54495efbd4 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/ChatMessageBuilder.cs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.AI.Agents; + +namespace Microsoft.Agents.Workflows.UnitTests; + +internal static class TextMessageStreamingExtensions +{ + public static IEnumerable ToContentStream(this string? message) + { + if (string.IsNullOrEmpty(message)) + { + return []; + } + + string[] splits = message.Split(' '); + for (int i = 0; i < splits.Length - 1; i++) + { + splits[i] = splits[i] + ' '; + } + + return splits.Select(text => (AIContent)new TextContent(text) { RawRepresentation = text }); + } + + public static AgentRunResponseUpdate ToResponseUpdate(this AIContent content, string? messageId = null, DateTimeOffset? createdAt = null, string? responseId = null, string? agentId = null, string? authorName = null) + { + return new AgentRunResponseUpdate() + { + Role = ChatRole.Assistant, + CreatedAt = createdAt ?? DateTimeOffset.Now, + MessageId = messageId ?? Guid.NewGuid().ToString("N"), + ResponseId = responseId, + AgentId = agentId, + AuthorName = authorName, + Contents = [content], + }; + } + + public static IEnumerable ToAgentRunStream(this string message, DateTimeOffset? createdAt = null, string? messageId = null, string? responseId = null, string? agentId = null, string? authorName = null) + { + messageId ??= Guid.NewGuid().ToString("N"); + + IEnumerable contents = message.ToContentStream(); + return contents.Select(content => content.ToResponseUpdate(messageId, createdAt, responseId, agentId, authorName)); + } + + public static ChatMessage ToChatMessage(this IEnumerable contents, string? messageId = null, DateTimeOffset? createdAt = null, string? responseId = null, string? agentId = null, string? authorName = null, string? rawRepresentation = null) + { + return new ChatMessage(ChatRole.Assistant, contents is List contentsList ? contentsList : contents.ToList()) + { + AuthorName = authorName, + CreatedAt = createdAt ?? DateTimeOffset.Now, + MessageId = messageId ?? Guid.NewGuid().ToString("N"), + RawRepresentation = rawRepresentation, + }; + } + + public static IEnumerable StreamMessage(this ChatMessage message, string? responseId = null, string? agentId = null) + { + responseId ??= Guid.NewGuid().ToString("N"); + string messageId = message.MessageId ?? Guid.NewGuid().ToString("N"); + + return message.Contents.Select(content => content.ToResponseUpdate(messageId, message.CreatedAt, responseId: responseId, agentId: agentId, authorName: message.AuthorName)); + } + + public static IEnumerable StreamMessages(this List messages, string? agentId = null) + { + return messages.SelectMany(message => message.StreamMessage(agentId)); + } + + public static List ToChatMessages(this IEnumerable messages, string? authorName = null) + { + List result = messages.Select(ToMessage).ToList(); + + ChatMessage ToMessage(string text) + { + return new(ChatRole.Assistant, text.ToContentStream().ToList()) + { + AuthorName = authorName, + MessageId = Guid.NewGuid().ToString("N"), + RawRepresentation = text, + CreatedAt = DateTimeOffset.Now, + }; + } + + return result; + } +} diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/MessageMergerTests.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/MessageMergerTests.cs new file mode 100644 index 0000000000..6654d42000 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/MessageMergerTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using FluentAssertions; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.AI.Agents; + +namespace Microsoft.Agents.Workflows.UnitTests; + +public class MessageMergerTests +{ + public static string TestAgentId1 => "TestAgent1"; + public static string TestAgentId2 => "TestAgent2"; + + public static string TestAuthorName1 => "Assistant1"; + public static string TestAuthorName2 => "Assistant2"; + + [Fact] + public void Test_MessageMerger_AssemblesMessage() + { + DateTimeOffset creationTime = DateTimeOffset.UtcNow; + string responseId = Guid.NewGuid().ToString("N"); + string messageId = Guid.NewGuid().ToString("N"); + + MessageMerger merger = new(); + + foreach (AgentRunResponseUpdate update in "Hello Agent Framework Workflows!".ToAgentRunStream(authorName: TestAuthorName1, agentId: TestAgentId1, messageId: messageId, createdAt: creationTime, responseId: responseId)) + { + merger.AddUpdate(update); + } + + AgentRunResponse response = merger.ComputeMerged(responseId); + + response.Messages.Should().HaveCount(1); + response.Messages[0].Role.Should().Be(ChatRole.Assistant); + response.Messages[0].AuthorName.Should().Be(TestAuthorName1); + response.AgentId.Should().Be(TestAgentId1); + response.CreatedAt.Should().NotBe(creationTime); + response.Messages[0].CreatedAt.Should().Be(creationTime); + response.Messages[0].Contents.Should().HaveCount(1); + } +} From 947f2bf642db46f42f4646f4168976b38ae7df05 Mon Sep 17 00:00:00 2001 From: Eduard van Valkenburg Date: Wed, 10 Sep 2025 10:57:37 +0200 Subject: [PATCH 22/32] Python: added user agents to foundry and azure openai (#658) * added user agents to foundry and azure openai * improvement * improvement for disabled setup --- .../azure/agent_framework_azure/_shared.py | 6 +++-- .../agent_framework_foundry/_chat_client.py | 26 ++++++++++++++----- .../foundry/tests/test_foundry_chat_client.py | 18 +++++++++---- .../main/agent_framework/telemetry.py | 11 +++++++- .../samples/getting_started/test_telemetry.py | 0 5 files changed, 47 insertions(+), 14 deletions(-) delete mode 100644 python/tests/samples/getting_started/test_telemetry.py diff --git a/python/packages/azure/agent_framework_azure/_shared.py b/python/packages/azure/agent_framework_azure/_shared.py index b4beba5da0..6f3a6a72fd 100644 --- a/python/packages/azure/agent_framework_azure/_shared.py +++ b/python/packages/azure/agent_framework_azure/_shared.py @@ -9,7 +9,7 @@ from typing import Any, ClassVar, Final from agent_framework._pydantic import AFBaseSettings, HTTPsUrl from agent_framework.exceptions import ServiceInitializationError from agent_framework.openai._shared import OpenAIBase -from agent_framework.telemetry import USER_AGENT_KEY +from agent_framework.telemetry import APP_INFO, USER_AGENT_KEY, prepend_agent_framework_to_user_agent from azure.core.credentials import TokenCredential from openai.lib.azure import AsyncAzureOpenAI from pydantic import ConfigDict, SecretStr, model_validator, validate_call @@ -212,7 +212,9 @@ class AzureOpenAIConfigMixin(OpenAIBase): """ # Merge APP_INFO into the headers if it exists merged_headers = dict(copy(default_headers)) if default_headers else {} - + if APP_INFO: + merged_headers.update(APP_INFO) + merged_headers = prepend_agent_framework_to_user_agent(merged_headers) if not client: # If the client is None, the api_key is none, the ad_token is none, and the ad_token_provider is none, # then we will attempt to get the ad_token using the default endpoint specified in the Azure OpenAI diff --git a/python/packages/foundry/agent_framework_foundry/_chat_client.py b/python/packages/foundry/agent_framework_foundry/_chat_client.py index 130eeebc47..811daed8b8 100644 --- a/python/packages/foundry/agent_framework_foundry/_chat_client.py +++ b/python/packages/foundry/agent_framework_foundry/_chat_client.py @@ -28,7 +28,7 @@ from agent_framework import ( ) from agent_framework._pydantic import AFBaseSettings from agent_framework.exceptions import ServiceInitializationError, ServiceResponseException -from agent_framework.telemetry import use_telemetry +from agent_framework.telemetry import prepend_agent_framework_to_user_agent, use_telemetry from azure.ai.agents.models import ( AgentsNamedToolChoice, AgentsNamedToolChoiceType, @@ -95,6 +95,8 @@ class FoundrySettings(AFBaseSettings): TFoundryChatClient = TypeVar("TFoundryChatClient", bound="FoundryChatClient") +HEADERS = prepend_agent_framework_to_user_agent() + @use_telemetry @use_tool_calling @@ -267,7 +269,11 @@ class FoundryChatClient(BaseChatClient): raise ServiceInitializationError("Model deployment name is required for agent creation.") agent_name = self.agent_name - args = {"model": self.ai_model_deployment_name, "name": agent_name} + args = { + "model": self.ai_model_deployment_name, + "name": agent_name, + "headers": HEADERS, + } if run_options: if "tools" in run_options: args["tools"] = run_options["tools"] @@ -303,7 +309,11 @@ class FoundryChatClient(BaseChatClient): if thread_run is not None and tool_run_id is not None and tool_run_id == thread_run.id and tool_outputs: # There's an active run and we have tool results to submit, so submit the results. await self.client.agents.runs.submit_tool_outputs_stream( # type: ignore[reportUnknownMemberType] - thread_run.thread_id, tool_run_id, tool_outputs=tool_outputs, event_handler=handler + thread_run.thread_id, + tool_run_id, + tool_outputs=tool_outputs, + event_handler=handler, + headers=HEADERS, ) # Pass the handler to the stream to continue processing stream = handler # type: ignore @@ -316,6 +326,7 @@ class FoundryChatClient(BaseChatClient): stream = await self.client.agents.runs.stream( # type: ignore[reportUnknownMemberType] final_thread_id, agent_id=agent_id, + headers=HEADERS, **run_options, ) @@ -326,7 +337,9 @@ class FoundryChatClient(BaseChatClient): if thread_id is None: return None - async for run in self.client.agents.runs.list(thread_id=thread_id, limit=1, order=ListSortOrder.DESCENDING): + async for run in self.client.agents.runs.list( + thread_id=thread_id, limit=1, order=ListSortOrder.DESCENDING, headers=HEADERS + ): # type: ignore[reportUnknownMemberType] if run.status not in [ RunStatus.COMPLETED, RunStatus.CANCELLED, @@ -346,13 +359,14 @@ class FoundryChatClient(BaseChatClient): messages=run_options["additional_messages"], tool_resources=run_options.get("tool_resources"), metadata=run_options.get("metadata"), + headers=HEADERS, ) run_options["additional_messages"] = [] return thread.id if thread_run is not None: # There was an active run; we need to cancel it before starting a new run. - await self.client.agents.runs.cancel(thread_id, thread_run.id) + await self.client.agents.runs.cancel(thread_id, thread_run.id, headers=HEADERS) return thread_id @@ -474,7 +488,7 @@ class FoundryChatClient(BaseChatClient): async def _cleanup_agent_if_needed(self) -> None: """Clean up the agent if we created it.""" if self._should_delete_agent and self.agent_id is not None: - await self.client.agents.delete_agent(self.agent_id) + await self.client.agents.delete_agent(self.agent_id, headers=HEADERS) self.agent_id = None self._should_delete_agent = False diff --git a/python/packages/foundry/tests/test_foundry_chat_client.py b/python/packages/foundry/tests/test_foundry_chat_client.py index 5696af9d81..967b178ede 100644 --- a/python/packages/foundry/tests/test_foundry_chat_client.py +++ b/python/packages/foundry/tests/test_foundry_chat_client.py @@ -24,6 +24,7 @@ from agent_framework import ( UriContent, ai_function, ) +from agent_framework import __version__ as AF_VERSION from agent_framework.exceptions import ServiceInitializationError from azure.ai.agents.models import ( RequiredFunctionToolCall, @@ -316,9 +317,10 @@ async def test_foundry_chat_client_cleanup_agent_if_needed_should_delete( ) await chat_client._cleanup_agent_if_needed() # type: ignore - # Verify agent deletion was called - mock_ai_project_client.agents.delete_agent.assert_called_once_with("agent-to-delete") + mock_ai_project_client.agents.delete_agent.assert_called_once_with( + "agent-to-delete", headers={"User-Agent": f"agent-framework-python/{AF_VERSION}"} + ) assert not chat_client._should_delete_agent # type: ignore @@ -359,7 +361,9 @@ async def test_foundry_chat_client_aclose(mock_ai_project_client: MagicMock) -> await chat_client.close() # Verify agent deletion was called - mock_ai_project_client.agents.delete_agent.assert_called_once_with("agent-to-delete") + mock_ai_project_client.agents.delete_agent.assert_called_once_with( + "agent-to-delete", headers={"User-Agent": f"agent-framework-python/{AF_VERSION}"} + ) async def test_foundry_chat_client_async_context_manager(mock_ai_project_client: MagicMock) -> None: @@ -373,7 +377,9 @@ async def test_foundry_chat_client_async_context_manager(mock_ai_project_client: pass # Just test that we can enter and exit # Verify cleanup was called on exit - mock_ai_project_client.agents.delete_agent.assert_called_once_with("agent-to-delete") + mock_ai_project_client.agents.delete_agent.assert_called_once_with( + "agent-to-delete", headers={"User-Agent": f"agent-framework-python/{AF_VERSION}"} + ) def test_foundry_chat_client_create_run_options_basic(mock_ai_project_client: MagicMock) -> None: @@ -600,7 +606,9 @@ async def test_foundry_chat_client_prepare_thread_cancels_active_run(mock_ai_pro result = await chat_client._prepare_thread("test-thread", mock_thread_run, run_options) # type: ignore assert result == "test-thread" - mock_ai_project_client.agents.runs.cancel.assert_called_once_with("test-thread", "run_123") + mock_ai_project_client.agents.runs.cancel.assert_called_once_with( + "test-thread", "run_123", headers={"User-Agent": f"agent-framework-python/{AF_VERSION}"} + ) def test_foundry_chat_client_create_function_call_contents_basic(mock_ai_project_client: MagicMock) -> None: diff --git a/python/packages/main/agent_framework/telemetry.py b/python/packages/main/agent_framework/telemetry.py index dd909837a2..b52708fac3 100644 --- a/python/packages/main/agent_framework/telemetry.py +++ b/python/packages/main/agent_framework/telemetry.py @@ -165,15 +165,24 @@ HTTP_USER_AGENT: Final[str] = "agent-framework-python" AGENT_FRAMEWORK_USER_AGENT = f"{HTTP_USER_AGENT}/{version_info}" # type: ignore[has-type] -def prepend_agent_framework_to_user_agent(headers: dict[str, Any]) -> dict[str, Any]: +def prepend_agent_framework_to_user_agent(headers: dict[str, Any] | None = None) -> dict[str, Any]: """Prepend "agent-framework" to the User-Agent in the headers. + When user agent telemetry is disabled, through the AZURE_TELEMETRY_DISABLED environment variable, + the User-Agent header will not include the agent-framework information, it will be sent back as is, + or as a empty dict when None is passed. + Args: headers: The existing headers dictionary. Returns: + A new dict with "User-Agent" set to "agent-framework-python/{version}" if headers is None. The modified headers dictionary with "agent-framework-python/{version}" prepended to the User-Agent. """ + if not IS_TELEMETRY_ENABLED: + return headers or {} + if not headers: + return {USER_AGENT_KEY: AGENT_FRAMEWORK_USER_AGENT} headers[USER_AGENT_KEY] = ( f"{AGENT_FRAMEWORK_USER_AGENT} {headers[USER_AGENT_KEY]}" if USER_AGENT_KEY in headers diff --git a/python/tests/samples/getting_started/test_telemetry.py b/python/tests/samples/getting_started/test_telemetry.py deleted file mode 100644 index e69de29bb2..0000000000 From 6aa746d891f5006b46bba962df10e3ded01beeb3 Mon Sep 17 00:00:00 2001 From: Eduard van Valkenburg Date: Wed, 10 Sep 2025 15:37:34 +0200 Subject: [PATCH 23/32] Python: Introducing UserInputRequest and Response types and HostedMcpTool (#405) * initial work on User Approval (and hosted mcp to validate) * small update to the comments in the sample * enable local MCP tools in chatClient get methods * working streaming and improved setup * fix for pyright * updated create_approval -> create_response method * added tests * updated HostedMcpTool and addressed feedback * update type name * naming updates * small docstring update * mypy fix * fixes and updates * fixes for responses * fix int tests * removed broken tests * updated test running * removed specific content check on websearch * increased timeout * split slow foundry test * don't parallel run samples * add dist load to unit tests --------- Co-authored-by: Eric Zhu --- .github/workflows/python-merge-tests.yml | 22 +- .github/workflows/python-test-coverage.yml | 2 +- .github/workflows/python-tests.yml | 8 +- .../tests/test_azure_assistants_client.py | 40 -- .../azure/tests/test_azure_chat_client.py | 40 -- .../tests/test_azure_responses_client.py | 39 -- .../foundry/tests/test_foundry_chat_client.py | 40 -- .../packages/main/agent_framework/_clients.py | 24 +- python/packages/main/agent_framework/_mcp.py | 80 +--- .../packages/main/agent_framework/_tools.py | 96 +++- .../packages/main/agent_framework/_types.py | 149 ++++++- .../main/agent_framework/exceptions.py | 17 +- .../openai/_responses_client.py | 421 ++++++++++++++---- .../packages/main/tests/main/test_logging.py | 2 +- python/packages/main/tests/main/test_tools.py | 115 ++++- python/packages/main/tests/main/test_types.py | 42 +- .../openai/test_openai_assistants_client.py | 39 -- .../tests/openai/test_openai_chat_client.py | 4 +- .../openai/test_openai_responses_client.py | 201 +++++++-- ...openai_responses_client_with_hosted_mcp.py | 224 ++++++++++ .../samples/getting_started/test_agents.py | 28 +- 21 files changed, 1186 insertions(+), 447 deletions(-) create mode 100644 python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_hosted_mcp.py diff --git a/.github/workflows/python-merge-tests.yml b/.github/workflows/python-merge-tests.yml index 819822ae27..9868dc6c80 100644 --- a/.github/workflows/python-merge-tests.yml +++ b/.github/workflows/python-merge-tests.yml @@ -78,11 +78,13 @@ jobs: run: | uv sync --all-packages --all-extras --dev -U --prerelease=if-necessary-or-explicit - name: Test with pytest - run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test --junitxml=coverage.xml + timeout-minutes: 10 + run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test -n logical --dist loadfile --dist worksteal --junitxml=coverage.xml working-directory: ./python - - name: Test openai samples + - name: Test main samples + timeout-minutes: 10 if: env.RUN_SAMPLES_TESTS == 'true' - run: uv run pytest tests/samples/ -m "openai" --junitxml=coverage_samples_main.xml + run: uv run pytest tests/samples/ -m "openai" working-directory: ./python - name: Move coverage file run: | @@ -144,15 +146,17 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Test with pytest - run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test --junitxml=coverage.xml + timeout-minutes: 10 + run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test -n logical --dist loadfile --dist worksteal --junitxml=coverage.xml working-directory: ./python - name: Test azure samples + timeout-minutes: 10 if: env.RUN_SAMPLES_TESTS == 'true' - run: uv run pytest tests/samples/ -m "azure" --junitxml=coverage_samples_azure.xml + run: uv run pytest tests/samples/ -m "azure" working-directory: ./python - name: Move coverage file run: | - mv ./packages/${{ env.PACKAGE_NAME }}/coverage.xml coverage_${{ env.PACKAGE_NAME }}.xml + mv ./packages/${{ env.PACKAGE_NAME }}/coverage.xml ./coverage_${{ env.PACKAGE_NAME }}.xml working-directory: ./python - name: Upload coverage artifact uses: actions/upload-artifact@v4 @@ -209,11 +213,13 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Test with pytest - run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test --junitxml=coverage.xml + timeout-minutes: 10 + run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test -n logical --dist loadfile --dist worksteal --junitxml=coverage.xml working-directory: ./python - name: Test foundry samples + timeout-minutes: 10 if: env.RUN_SAMPLES_TESTS == 'true' - run: uv run pytest tests/samples/ -m "foundry" --junitxml=coverage_samples_foundry.xml + run: uv run pytest tests/samples/ -m "foundry" working-directory: ./python - name: Move coverage file run: | diff --git a/.github/workflows/python-test-coverage.yml b/.github/workflows/python-test-coverage.yml index 59c8b4b4bf..08ffe47158 100644 --- a/.github/workflows/python-test-coverage.yml +++ b/.github/workflows/python-test-coverage.yml @@ -36,7 +36,7 @@ jobs: - name: Install the project run: uv sync --all-extras --dev - name: Run all tests with coverage report - run: uv run poe all-tests --cov-report=xml:python-coverage.xml -q --junitxml=pytest.xml + run: uv run poe all-tests -n logical --dist loadfile --dist worksteal --cov-report=xml:python-coverage.xml -q --junitxml=pytest.xml - name: Upload coverage report uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 6552b7829c..5981f27757 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -48,7 +48,7 @@ jobs: run: | echo "PACKAGE_NAME=main" >> $GITHUB_ENV - name: Test with pytest - main - run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test --junitxml=coverage.xml + run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test -n logical --dist loadfile --dist worksteal --junitxml=coverage.xml working-directory: ./python - name: Move coverage file - main run: | @@ -70,7 +70,7 @@ jobs: run: | echo "PACKAGE_NAME=azure" >> $GITHUB_ENV - name: Test with pytest - azure - run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test --junitxml=coverage.xml + run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test -n logical --dist loadfile --dist worksteal --junitxml=coverage.xml working-directory: ./python - name: Move coverage file - azure run: | @@ -92,7 +92,7 @@ jobs: run: | echo "PACKAGE_NAME=foundry" >> $GITHUB_ENV - name: Test with pytest - foundry - run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test --junitxml=coverage.xml + run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test -n logical --dist loadfile --dist worksteal --junitxml=coverage.xml working-directory: ./python - name: Move coverage file - foundry run: | @@ -114,7 +114,7 @@ jobs: run: | echo "PACKAGE_NAME=workflow" >> $GITHUB_ENV - name: Test with pytest - workflow - run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test --junitxml=coverage.xml + run: uv run poe --directory ./packages/${{ env.PACKAGE_NAME }} test -n logical --dist loadfile --dist worksteal --junitxml=coverage.xml working-directory: ./python - name: Move coverage file - workflow run: | diff --git a/python/packages/azure/tests/test_azure_assistants_client.py b/python/packages/azure/tests/test_azure_assistants_client.py index abc128151a..7bd2f6571b 100644 --- a/python/packages/azure/tests/test_azure_assistants_client.py +++ b/python/packages/azure/tests/test_azure_assistants_client.py @@ -16,7 +16,6 @@ from agent_framework import ( ChatResponseUpdate, HostedCodeInterpreterTool, TextContent, - ai_function, ) from agent_framework.exceptions import ServiceInitializationError from azure.identity import AzureCliCredential @@ -543,45 +542,6 @@ async def test_azure_assistants_client_agent_level_tool_persistence(): assert any(term in second_response.text.lower() for term in ["miami", "sunny", "72"]) -@skip_if_azure_integration_tests_disabled -async def test_azure_assistants_client_run_level_tool_isolation(): - """Test that run-level tools are isolated to specific runs and don't persist with Azure Assistants Client.""" - # Counter to track how many times the weather tool is called - call_count = 0 - - @ai_function - async def get_weather_with_counter(location: Annotated[str, "The location as a city name"]) -> str: - """Get the current weather in a given location.""" - nonlocal call_count - call_count += 1 - return f"The weather in {location} is sunny and 72°F." - - async with ChatAgent( - chat_client=AzureAssistantsClient(credential=AzureCliCredential()), - instructions="You are a helpful assistant.", - ) as agent: - # First run - use run-level tool - first_response = await agent.run( - "What's the weather like in Chicago?", - tools=[get_weather_with_counter], # Run-level tool - ) - - assert isinstance(first_response, AgentRunResponse) - assert first_response.text is not None - # Should use the run-level weather tool (call count should be 1) - assert call_count == 1 - assert any(term in first_response.text.lower() for term in ["chicago", "sunny", "72"]) - - # Second run - run-level tool should NOT persist (key isolation test) - second_response = await agent.run("What's the weather like in Miami?") - - assert isinstance(second_response, AgentRunResponse) - assert second_response.text is not None - # Should NOT use the weather tool since it was only run-level in previous call - # Call count should still be 1 (no additional calls) - assert call_count == 1 - - def test_azure_assistants_client_entra_id_authentication() -> None: """Test Entra ID authentication path with credential.""" mock_credential = MagicMock() diff --git a/python/packages/azure/tests/test_azure_chat_client.py b/python/packages/azure/tests/test_azure_chat_client.py index 60417581fb..b4669d1ec0 100644 --- a/python/packages/azure/tests/test_azure_chat_client.py +++ b/python/packages/azure/tests/test_azure_chat_client.py @@ -2,7 +2,6 @@ import json import os -from typing import Annotated from unittest.mock import AsyncMock, MagicMock, patch import openai @@ -834,42 +833,3 @@ async def test_azure_chat_client_agent_level_tool_persistence(): assert second_response.text is not None # Should use the agent-level weather tool again assert any(term in second_response.text.lower() for term in ["miami", "sunny", "72"]) - - -@skip_if_azure_integration_tests_disabled -async def test_azure_chat_client_run_level_tool_isolation(): - """Test that run-level tools are isolated to specific runs and don't persist with Azure Chat Client.""" - # Counter to track how many times the weather tool is called - call_count = 0 - - @ai_function - async def get_weather_with_counter(location: Annotated[str, "The location as a city name"]) -> str: - """Get the current weather in a given location.""" - nonlocal call_count - call_count += 1 - return f"The weather in {location} is sunny and 72°F." - - async with ChatAgent( - chat_client=AzureChatClient(credential=AzureCliCredential()), - instructions="You are a helpful assistant.", - ) as agent: - # First run - use run-level tool - first_response = await agent.run( - "What's the weather like in Chicago?", - tools=[get_weather_with_counter], # Run-level tool - ) - - assert isinstance(first_response, AgentRunResponse) - assert first_response.text is not None - # Should use the run-level weather tool (call count should be 1) - assert call_count == 1 - assert any(term in first_response.text.lower() for term in ["chicago", "sunny", "72"]) - - # Second run - run-level tool should NOT persist (key isolation test) - second_response = await agent.run("What's the weather like in Miami?") - - assert isinstance(second_response, AgentRunResponse) - assert second_response.text is not None - # Should NOT use the weather tool since it was only run-level in previous call - # Call count should still be 1 (no additional calls) - assert call_count == 1 diff --git a/python/packages/azure/tests/test_azure_responses_client.py b/python/packages/azure/tests/test_azure_responses_client.py index dcbc41e839..2e02989478 100644 --- a/python/packages/azure/tests/test_azure_responses_client.py +++ b/python/packages/azure/tests/test_azure_responses_client.py @@ -459,42 +459,3 @@ async def test_azure_responses_client_agent_level_tool_persistence(): assert second_response.text is not None # Should use the agent-level weather tool again assert any(term in second_response.text.lower() for term in ["miami", "sunny", "72"]) - - -@skip_if_azure_integration_tests_disabled -async def test_azure_responses_client_run_level_tool_isolation(): - """Test that run-level tools are isolated to specific runs and don't persist with Azure Responses Client.""" - # Counter to track how many times the weather tool is called - call_count = 0 - - @ai_function - async def get_weather_with_counter(location: Annotated[str, "The location as a city name"]) -> str: - """Get the current weather in a given location.""" - nonlocal call_count - call_count += 1 - return f"The weather in {location} is sunny and 72°F." - - async with ChatAgent( - chat_client=AzureResponsesClient(credential=AzureCliCredential()), - instructions="You are a helpful assistant.", - ) as agent: - # First run - use run-level tool - first_response = await agent.run( - "What's the weather like in Chicago?", - tools=[get_weather_with_counter], # Run-level tool - ) - - assert isinstance(first_response, AgentRunResponse) - assert first_response.text is not None - # Should use the run-level weather tool (call count should be 1) - assert call_count == 1 - assert any(term in first_response.text.lower() for term in ["chicago", "sunny", "72"]) - - # Second run - run-level tool should NOT persist (key isolation test) - second_response = await agent.run("What's the weather like in Miami?") - - assert isinstance(second_response, AgentRunResponse) - assert second_response.text is not None - # Should NOT use the weather tool since it was only run-level in previous call - # Call count should still be 1 (no additional calls) - assert call_count == 1 diff --git a/python/packages/foundry/tests/test_foundry_chat_client.py b/python/packages/foundry/tests/test_foundry_chat_client.py index 967b178ede..b77efe5954 100644 --- a/python/packages/foundry/tests/test_foundry_chat_client.py +++ b/python/packages/foundry/tests/test_foundry_chat_client.py @@ -22,7 +22,6 @@ from agent_framework import ( Role, TextContent, UriContent, - ai_function, ) from agent_framework import __version__ as AF_VERSION from agent_framework.exceptions import ServiceInitializationError @@ -933,42 +932,3 @@ async def test_foundry_chat_client_agent_level_tool_persistence(): assert second_response.text is not None # Should use the agent-level weather tool again assert any(term in second_response.text.lower() for term in ["miami", "sunny", "25"]) - - -@skip_if_foundry_integration_tests_disabled -async def test_foundry_chat_client_run_level_tool_isolation(): - """Test that run-level tools are isolated to specific runs and don't persist with FoundryChatClient.""" - # Counter to track how many times the weather tool is called - call_count = 0 - - @ai_function - async def get_weather_with_counter(location: Annotated[str, "The location as a city name"]) -> str: - """Get the current weather in a given location.""" - nonlocal call_count - call_count += 1 - return f"The weather in {location} is sunny and 25°C." - - async with ChatAgent( - chat_client=FoundryChatClient(async_credential=AzureCliCredential()), - instructions="You are a helpful assistant.", - ) as agent: - # First run - use run-level tool - first_response = await agent.run( - "What's the weather like in Chicago?", - tools=[get_weather_with_counter], # Run-level tool - ) - - assert isinstance(first_response, AgentRunResponse) - assert first_response.text is not None - # Should use the run-level weather tool (call count should be 1) - assert call_count == 1 - assert any(term in first_response.text.lower() for term in ["chicago", "sunny", "25"]) - - # Second run - run-level tool should NOT persist (key isolation test) - second_response = await agent.run("What's the weather like in Miami?") - - assert isinstance(second_response, AgentRunResponse) - assert second_response.text is not None - # Should NOT use the weather tool since it was only run-level in previous call - # Call count should still be 1 (no additional calls) - assert call_count == 1 diff --git a/python/packages/main/agent_framework/_clients.py b/python/packages/main/agent_framework/_clients.py index 4ad1119162..f80d1f5dc1 100644 --- a/python/packages/main/agent_framework/_clients.py +++ b/python/packages/main/agent_framework/_clients.py @@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any, Generic, Literal, Protocol, TypeVar, runt from pydantic import BaseModel from ._logging import get_logger +from ._mcp import MCPTool from ._pydantic import AFBaseModel from ._threads import ChatMessageStore from ._tools import AIFunction, ToolProtocol @@ -391,6 +392,25 @@ class BaseChatClient(AFBaseModel, ABC): return_messages.append(msg) return return_messages + @staticmethod + def _normalize_tools( + tools: ToolProtocol + | MutableMapping[str, Any] + | Callable[..., Any] + | list[ToolProtocol | MutableMapping[str, Any] | Callable[..., Any]] + | None = None, + ) -> list[ToolProtocol | dict[str, Any] | Callable[..., Any]]: + """Normalize the tools input to a list of tools.""" + final_tools: list[ToolProtocol | dict[str, Any] | Callable[..., Any]] = [] + if not tools: + return final_tools + for tool in tools if isinstance(tools, list) else [tools]: # type: ignore[reportUnknownType] + if isinstance(tool, MCPTool): + final_tools.extend(tool.functions) # type: ignore + continue + final_tools.append(tool) # type: ignore + return final_tools + # region Internal methods to be implemented by the derived classes @abstractmethod @@ -513,7 +533,7 @@ class BaseChatClient(AFBaseModel, ABC): temperature=temperature, top_p=top_p, tool_choice=tool_choice, - tools=tools, # type: ignore + tools=self._normalize_tools(tools), # type: ignore user=user, additional_properties=additional_properties or {}, ) @@ -592,7 +612,7 @@ class BaseChatClient(AFBaseModel, ABC): temperature=temperature, top_p=top_p, tool_choice=tool_choice, - tools=tools, # type: ignore + tools=self._normalize_tools(tools), # type: ignore user=user, additional_properties=additional_properties or {}, **kwargs, diff --git a/python/packages/main/agent_framework/_mcp.py b/python/packages/main/agent_framework/_mcp.py index 27a4c3d5af..180aecfc95 100644 --- a/python/packages/main/agent_framework/_mcp.py +++ b/python/packages/main/agent_framework/_mcp.py @@ -12,7 +12,6 @@ from typing import TYPE_CHECKING, Any from mcp import types from mcp.client.session import ClientSession -from mcp.client.sse import sse_client from mcp.client.stdio import StdioServerParameters, stdio_client from mcp.client.streamable_http import streamablehttp_client from mcp.client.websocket import websocket_client @@ -49,7 +48,6 @@ LOG_LEVEL_MAPPING: dict[types.LoggingLevel, int] = { } __all__ = [ - "MCPSseTools", "MCPStdioTool", "MCPStreamableHTTPTool", "MCPWebsocketTool", @@ -224,7 +222,7 @@ def _normalize_mcp_name(name: str) -> str: class MCPTool: - """Base class with the MCP logic.""" + """Main MCP class, to initialize use one of the subclasses.""" def __init__( self, @@ -567,82 +565,6 @@ class MCPStdioTool(MCPTool): return stdio_client(server=StdioServerParameters(**args)) -class MCPSseTools(MCPTool): - """MCP sse server configuration.""" - - def __init__( - self, - name: str, - url: str, - *, - load_tools: bool = True, - load_prompts: bool = True, - request_timeout: int | None = None, - session: ClientSession | None = None, - description: str | None = None, - additional_properties: dict[str, Any] | None = None, - headers: dict[str, Any] | None = None, - timeout: float | None = None, - sse_read_timeout: float | None = None, - chat_client: "ChatClientProtocol | None" = None, - **kwargs: Any, - ) -> None: - """Initialize the MCP sse plugin. - - The arguments are used to create a sse client. - see mcp.client.sse.sse_client for more details. - - Any extra arguments passed to the constructor will be passed to the - sse client constructor. - - Args: - name: The name of the plugin. - url: The URL of the MCP server. - load_tools: Whether to load tools from the MCP server. - load_prompts: Whether to load prompts from the MCP server. - request_timeout: The default timeout used for all requests. - session: The session to use for the MCP connection. - description: The description of the plugin. - additional_properties: Additional properties. - headers: The headers to send with the request. - timeout: The timeout for the request. - sse_read_timeout: The timeout for reading from the SSE stream. - chat_client: The chat client to use for sampling. - kwargs: Any extra arguments to pass to the sse client. - - """ - super().__init__( - name=name, - description=description, - additional_properties=additional_properties, - session=session, - chat_client=chat_client, - load_tools=load_tools, - load_prompts=load_prompts, - request_timeout=request_timeout, - ) - self.url = url - self.headers = headers or {} - self.timeout = timeout - self.sse_read_timeout = sse_read_timeout - self._client_kwargs = kwargs - - def get_mcp_client(self) -> _AsyncGeneratorContextManager[Any, None]: - """Get an MCP SSE client.""" - args: dict[str, Any] = { - "url": self.url, - } - if self.headers: - args["headers"] = self.headers - if self.timeout is not None: - args["timeout"] = self.timeout - if self.sse_read_timeout is not None: - args["sse_read_timeout"] = self.sse_read_timeout - if self._client_kwargs: - args.update(self._client_kwargs) - return sse_client(**args) - - class MCPStreamableHTTPTool(MCPTool): """MCP streamable http server configuration.""" diff --git a/python/packages/main/agent_framework/_tools.py b/python/packages/main/agent_framework/_tools.py index 5bd9566cb5..4b12835d43 100644 --- a/python/packages/main/agent_framework/_tools.py +++ b/python/packages/main/agent_framework/_tools.py @@ -1,7 +1,7 @@ # Copyright (c) Microsoft. All rights reserved. - import inspect -from collections.abc import Awaitable, Callable +import sys +from collections.abc import Awaitable, Callable, Collection from functools import wraps from time import perf_counter from typing import ( @@ -9,6 +9,7 @@ from typing import ( Annotated, Any, Generic, + Literal, Protocol, TypeVar, get_args, @@ -17,15 +18,21 @@ from typing import ( ) from opentelemetry import metrics, trace -from pydantic import BaseModel, Field, PrivateAttr, create_model +from pydantic import AnyUrl, BaseModel, Field, PrivateAttr, ValidationError, create_model, field_validator from ._logging import get_logger from ._pydantic import AFBaseModel +from .exceptions import ToolException from .telemetry import GenAIAttributes, start_as_current_span if TYPE_CHECKING: from ._types import Contents +if sys.version_info >= (3, 12): + from typing import TypedDict # pragma: no cover +else: + from typing_extensions import TypedDict # pragma: no cover + tracer: trace.Tracer = trace.get_tracer("agent_framework") meter: metrics.Meter = metrics.get_meter_provider().get_meter("agent_framework") logger = get_logger() @@ -34,6 +41,8 @@ __all__ = [ "AIFunction", "HostedCodeInterpreterTool", "HostedFileSearchTool", + "HostedMCPSpecificApproval", + "HostedMCPTool", "HostedWebSearchTool", "ToolProtocol", "ai_function", @@ -197,13 +206,88 @@ class HostedWebSearchTool(BaseTool): args: dict[str, Any] = { "name": "web_search", } + super().__init__(**args, **kwargs) + + +class HostedMCPSpecificApproval(TypedDict, total=False): + """Represents the `specific` mode for a hosted tool. + + When using this mode, the user must specify which tools always or never require approval. + This is represented as a dictionary with two optional keys: + - `always_require_approval`: A sequence of tool names that always require approval. + - `never_require_approval`: A sequence of tool names that never require approval. + + """ + + always_require_approval: Collection[str] | None + never_require_approval: Collection[str] | None + + +class HostedMCPTool(BaseTool): + """Represents a MCP tool that is managed and executed by the service.""" + + url: AnyUrl + approval_mode: Literal["always_require", "never_require"] | HostedMCPSpecificApproval | None = None + allowed_tools: set[str] | None = None + headers: dict[str, str] | None = None + + def __init__( + self, + *, + name: str, + description: str | None = None, + url: AnyUrl | str, + approval_mode: Literal["always_require", "never_require"] | HostedMCPSpecificApproval | None = None, + allowed_tools: Collection[str] | None = None, + headers: dict[str, str] | None = None, + additional_properties: dict[str, Any] | None = None, + **kwargs: Any, + ) -> None: + """Create a hosted MCP tool. + + Args: + name: The name of the tool. + description: A description of the tool. + url: The URL of the tool. + approval_mode: The approval mode for the tool. This can be: + - "always_require": The tool always requires approval before use. + - "never_require": The tool never requires approval before use. + - A dict with keys `always_require_approval` or `never_require_approval`, + followed by a sequence of strings with the names of the relevant tools. + allowed_tools: A list of tools that are allowed to use this tool. + headers: Headers to include in requests to the tool. + additional_properties: Additional properties to include in the tool definition. + **kwargs: Additional keyword arguments to pass to the base class. + """ + args: dict[str, Any] = { + "name": name, + "url": url, + } + if allowed_tools is not None: + args["allowed_tools"] = allowed_tools + if approval_mode is not None: + args["approval_mode"] = approval_mode + if headers is not None: + args["headers"] = headers if description is not None: args["description"] = description if additional_properties is not None: args["additional_properties"] = additional_properties - if "name" in kwargs: - raise ValueError("The 'name' argument is reserved for the HostedFileSearchTool and cannot be set.") - super().__init__(**args, **kwargs) + try: + super().__init__(**args, **kwargs) + except ValidationError as err: + raise ToolException(f"Error initializing HostedMCPTool: {err}", inner_exception=err) from err + + @field_validator("approval_mode") + def validate_approval_mode(cls, approval_mode: str | dict[str, Any] | None) -> str | dict[str, Any] | None: + """Validate the approval_mode field to ensure it is one of the accepted values.""" + if approval_mode is None or not isinstance(approval_mode, dict): + return approval_mode + # Validate that the dict has sets + for key, value in approval_mode.items(): + if not isinstance(value, set): + approval_mode[key] = set(value) # Convert to set if it's a list or other collection + return approval_mode class HostedFileSearchTool(BaseTool): diff --git a/python/packages/main/agent_framework/_types.py b/python/packages/main/agent_framework/_types.py index c80d01dc60..60108d982a 100644 --- a/python/packages/main/agent_framework/_types.py +++ b/python/packages/main/agent_framework/_types.py @@ -29,7 +29,7 @@ from pydantic import ( from ._logging import get_logger from ._pydantic import AFBaseModel from ._tools import ToolProtocol, ai_function -from .exceptions import AgentFrameworkException +from .exceptions import AdditionItemMismatch if sys.version_info >= (3, 11): from typing import Self # pragma: no cover @@ -55,6 +55,7 @@ KNOWN_MEDIA_TYPES = [ "application/pdf", "application/xml", "audio/mpeg", + "audio/mp3", "audio/ogg", "audio/wav", "image/apng", @@ -93,6 +94,8 @@ __all__ = [ "DataContent", "ErrorContent", "FinishReason", + "FunctionApprovalRequestContent", + "FunctionApprovalResponseContent", "FunctionCallContent", "FunctionResultContent", "GeneratedEmbeddings", @@ -224,7 +227,11 @@ def _process_update( is_new_message = False if ( not response.messages - or (update.message_id and response.messages[-1].message_id != update.message_id) + or ( + update.message_id + and response.messages[-1].message_id + and response.messages[-1].message_id != update.message_id + ) or (update.role and response.messages[-1].role != update.role) ): is_new_message = True @@ -249,7 +256,7 @@ def _process_update( ): try: message.contents[-1] += content - except AgentFrameworkException: + except AdditionItemMismatch: message.contents.append(content) elif isinstance(content, UsageContent): if response.usage_details is None: @@ -718,7 +725,7 @@ class DataContent(BaseContent): raise ValueError(f"Unknown media type: {media_type}") return uri - def has_top_level_media_type(self, top_level_media_type: str) -> bool: + def has_top_level_media_type(self, top_level_media_type: Literal["application", "audio", "image", "text"]) -> bool: return _has_top_level_media_type(self.media_type, top_level_media_type) @@ -776,11 +783,13 @@ class UriContent(BaseContent): **kwargs, ) - def has_top_level_media_type(self, top_level_media_type: str) -> bool: + def has_top_level_media_type(self, top_level_media_type: Literal["application", "audio", "image", "text"]) -> bool: return _has_top_level_media_type(self.media_type, top_level_media_type) -def _has_top_level_media_type(media_type: str | None, top_level_media_type: str) -> bool: +def _has_top_level_media_type( + media_type: str | None, top_level_media_type: Literal["application", "audio", "image", "text"] +) -> bool: if media_type is None: return False @@ -924,7 +933,7 @@ class FunctionCallContent(BaseContent): if not isinstance(other, FunctionCallContent): raise TypeError("Incompatible type") if other.call_id and self.call_id != other.call_id: - raise AgentFrameworkException("Incompatible function call contents") + raise AdditionItemMismatch if not self.arguments: arguments = other.arguments elif not other.arguments: @@ -1093,6 +1102,110 @@ class HostedVectorStoreContent(BaseContent): ) +class BaseUserInputRequest(BaseContent): + """Base class for all user requests.""" + + type: Literal["user_input_request"] = "user_input_request" # type: ignore[assignment] + id: Annotated[str, Field(..., min_length=1)] + + +class BaseUserInputResponse(BaseContent): + """Base class for all user responses.""" + + type: Literal["user_input_response"] = "user_input_response" # type: ignore[assignment] + id: Annotated[str, Field(..., min_length=1)] + + +class FunctionApprovalResponseContent(BaseUserInputResponse): + """Represents a response for user approval of a function call.""" + + type: Literal["function_approval_response"] = "function_approval_response" # type: ignore[assignment] + approved: bool + function_call: FunctionCallContent + + def __init__( + self, + approved: bool, + *, + id: str, + function_call: FunctionCallContent, + annotations: list[Annotations] | None = None, + additional_properties: dict[str, Any] | None = None, + raw_representation: Any | None = None, + **kwargs: Any, + ) -> None: + """Initializes a FunctionApprovalResponseContent instance. + + Args: + approved: Whether the function call was approved. + id: The unique identifier for the request. + function_call: The function call content to be approved. + annotations: Optional list of annotations for the request. + additional_properties: Optional additional properties for the request. + raw_representation: Optional raw representation of the request. + **kwargs: Additional keyword arguments. + """ + super().__init__( + approved=approved, # type: ignore[reportCallIssue] + id=id, # type: ignore[reportCallIssue] + function_call=function_call, # type: ignore[reportCallIssue] + annotations=annotations, + additional_properties=additional_properties, + raw_representation=raw_representation, + **kwargs, + ) + + +class FunctionApprovalRequestContent(BaseUserInputRequest): + """Represents a request for user approval of a function call.""" + + type: Literal["function_approval_request"] = "function_approval_request" # type: ignore[assignment] + function_call: FunctionCallContent + + def __init__( + self, + *, + id: str, + function_call: FunctionCallContent, + annotations: list[Annotations] | None = None, + additional_properties: dict[str, Any] | None = None, + raw_representation: Any | None = None, + **kwargs: Any, + ) -> None: + """Initializes a FunctionApprovalRequestContent instance. + + Args: + id: The unique identifier for the request. + function_call: The function call content to be approved. + annotations: Optional list of annotations for the request. + additional_properties: Optional additional properties for the request. + raw_representation: Optional raw representation of the request. + **kwargs: Additional keyword arguments. + """ + super().__init__( + id=id, # type: ignore[reportCallIssue] + function_call=function_call, # type: ignore[reportCallIssue] + annotations=annotations, + additional_properties=additional_properties, + raw_representation=raw_representation, + **kwargs, + ) + + def create_response(self, approved: bool) -> "FunctionApprovalResponseContent": + """Create a response for the function approval request.""" + return FunctionApprovalResponseContent( + approved, + id=self.id, + function_call=self.function_call, + additional_properties=self.additional_properties, + ) + + +UserInputRequestContents = Annotated[ + FunctionApprovalRequestContent, + Field(discriminator="type"), +] + Contents = Annotated[ TextContent | DataContent @@ -1103,7 +1216,9 @@ Contents = Annotated[ | ErrorContent | UsageContent | HostedFileContent - | HostedVectorStoreContent, + | HostedVectorStoreContent + | FunctionApprovalRequestContent + | FunctionApprovalResponseContent, Field(discriminator="type"), ] @@ -1957,6 +2072,13 @@ class AgentRunResponse(AFBaseModel): """Get the concatenated text of all messages.""" return "".join(msg.text for msg in self.messages) if self.messages else "" + @property + def user_input_requests(self) -> list[UserInputRequestContents]: + """Get all BaseUserInputRequest messages from the response.""" + return [ + content for msg in self.messages for content in msg.contents if isinstance(content, BaseUserInputRequest) + ] + @classmethod def from_agent_run_response_updates( cls: type[TAgentRunResponse], updates: Sequence["AgentRunResponseUpdate"] @@ -2007,6 +2129,11 @@ class AgentRunResponseUpdate(AFBaseModel): else "" ) + @property + def user_input_requests(self) -> list[UserInputRequestContents]: + """Get all BaseUserInputRequest messages from the response.""" + return [content for content in self.contents if isinstance(content, BaseUserInputRequest)] + def __str__(self) -> str: return self.text @@ -2082,9 +2209,3 @@ class TextToSpeechOptions(AFBaseModel): for key in merged_exclude: settings.pop(key, None) return settings - - -# endregion - - -# endregion diff --git a/python/packages/main/agent_framework/exceptions.py b/python/packages/main/agent_framework/exceptions.py index 66a2c14033..5e6a28b1e2 100644 --- a/python/packages/main/agent_framework/exceptions.py +++ b/python/packages/main/agent_framework/exceptions.py @@ -12,13 +12,15 @@ class AgentFrameworkException(Exception): Automatically logs the message as debug. """ - def __init__(self, message: str, inner_exception: Exception | None = None, *args: Any, **kwargs: Any): + def __init__(self, message: str, inner_exception: Exception | None = None, *args: Any): """Create an AgentFrameworkException. This emits a debug log, with the inner_exception if provided. """ logger.debug(message, exc_info=inner_exception) - super().__init__(message, *args, **kwargs) # type: ignore + if inner_exception: + super().__init__(message, inner_exception, *args) # type: ignore + super().__init__(message, *args) # type: ignore class AgentException(AgentFrameworkException): @@ -94,3 +96,14 @@ class ToolExecutionException(ToolException): """An error occurred while executing a tool.""" pass + + +class AdditionItemMismatch(AgentFrameworkException): + """An error occurred while adding two types.""" + + def __init__(self) -> None: + """Create an AdditionItemMismatch. + + Unlike the AgentFrameworkException, this does not log the message automatically, + """ + pass diff --git a/python/packages/main/agent_framework/openai/_responses_client.py b/python/packages/main/agent_framework/openai/_responses_client.py index 9abfc5772b..f684dcc31e 100644 --- a/python/packages/main/agent_framework/openai/_responses_client.py +++ b/python/packages/main/agent_framework/openai/_responses_client.py @@ -13,18 +13,12 @@ from openai.types.responses.parsed_response import ( ParsedResponse, ) from openai.types.responses.response import Response as OpenAIResponse -from openai.types.responses.response_completed_event import ResponseCompletedEvent -from openai.types.responses.response_content_part_added_event import ResponseContentPartAddedEvent -from openai.types.responses.response_function_call_arguments_delta_event import ResponseFunctionCallArgumentsDeltaEvent -from openai.types.responses.response_output_item_added_event import ResponseOutputItemAddedEvent -from openai.types.responses.response_output_refusal import ResponseOutputRefusal -from openai.types.responses.response_output_text import ResponseOutputText from openai.types.responses.response_stream_event import ResponseStreamEvent as OpenAIResponseStreamEvent -from openai.types.responses.response_text_delta_event import ResponseTextDeltaEvent from openai.types.responses.response_usage import ResponseUsage from openai.types.responses.tool_param import ( CodeInterpreter, CodeInterpreterContainerCodeInterpreterToolAuto, + Mcp, ToolParam, ) from openai.types.responses.web_search_tool_param import UserLocation as WebSearchUserLocation @@ -33,7 +27,14 @@ from pydantic import BaseModel, SecretStr, ValidationError from .._clients import BaseChatClient, use_tool_calling from .._logging import get_logger -from .._tools import AIFunction, HostedCodeInterpreterTool, HostedFileSearchTool, HostedWebSearchTool, ToolProtocol +from .._tools import ( + AIFunction, + HostedCodeInterpreterTool, + HostedFileSearchTool, + HostedMCPTool, + HostedWebSearchTool, + ToolProtocol, +) from .._types import ( ChatMessage, ChatOptions, @@ -42,6 +43,8 @@ from .._types import ( CitationAnnotation, Contents, DataContent, + FunctionApprovalRequestContent, + FunctionApprovalResponseContent, FunctionCallContent, FunctionResultContent, HostedFileContent, @@ -364,15 +367,41 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient): # region Prep methods - def _chat_to_response_tool_spec( + def _tools_to_response_tools( self, tools: list[ToolProtocol | MutableMapping[str, Any]] ) -> list[ToolParam | dict[str, Any]]: response_tools: list[ToolParam | dict[str, Any]] = [] for tool in tools: if isinstance(tool, ToolProtocol): match tool: + case HostedMCPTool(): + mcp: Mcp = { + "type": "mcp", + "server_label": tool.name.replace(" ", "_"), + "server_url": str(tool.url), + "server_description": tool.description, + "headers": tool.headers, + } + if tool.allowed_tools: + mcp["allowed_tools"] = list(tool.allowed_tools) + if tool.approval_mode: + match tool.approval_mode: + case str(): + mcp["require_approval"] = ( + "always" if tool.approval_mode == "always_require" else "never" + ) + case _: + if always_require_approvals := tool.approval_mode.get("always_require_approval"): + mcp["require_approval"] = { + "always": {"tool_names": list(always_require_approvals)} + } + if never_require_approvals := tool.approval_mode.get("never_require_approval"): + mcp["require_approval"] = { + "never": {"tool_names": list(never_require_approvals)} + } + response_tools.append(mcp) case HostedCodeInterpreterTool(): - tool_args: dict[str, Any] = {"type": "auto"} + tool_args: CodeInterpreterContainerCodeInterpreterToolAuto = {"type": "auto"} if tool.inputs: tool_args["file_ids"] = [] for tool_input in tool.inputs: @@ -383,7 +412,7 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient): response_tools.append( CodeInterpreter( type="code_interpreter", - container=CodeInterpreterContainerCodeInterpreterToolAuto(**tool_args), # type: ignore[typeddict-item] + container=tool_args, ) ) case AIFunction(): @@ -455,7 +484,7 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient): if chat_options.tools is None: options_dict.pop("parallel_tool_calls", None) else: - options_dict["tools"] = self._chat_to_response_tool_spec(chat_options.tools) + options_dict["tools"] = self._tools_to_response_tools(chat_options.tools) # other settings if "store" not in options_dict: options_dict["store"] = False @@ -496,6 +525,137 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient): # Flatten the list of lists into a single list return list(chain.from_iterable(list_of_list)) + def _openai_chat_message_parser( + self, + message: ChatMessage, + call_id_to_id: dict[str, str], + ) -> list[dict[str, Any]]: + """Parse a chat message into the openai format.""" + all_messages: list[dict[str, Any]] = [] + args: dict[str, Any] = { + "role": message.role.value if isinstance(message.role, Role) else message.role, + } + if message.additional_properties: + args["metadata"] = message.additional_properties + for content in message.contents: + match content: + case FunctionResultContent(): + new_args: dict[str, Any] = {} + new_args.update(self._openai_content_parser(message.role, content, call_id_to_id)) + all_messages.append(new_args) + case FunctionCallContent(): + function_call = self._openai_content_parser(message.role, content, call_id_to_id) + all_messages.append(function_call) # type: ignore + case FunctionApprovalResponseContent() | FunctionApprovalRequestContent(): + all_messages.append(self._openai_content_parser(message.role, content, call_id_to_id)) # type: ignore + case _: + if "content" not in args: + args["content"] = [] + args["content"].append(self._openai_content_parser(message.role, content, call_id_to_id)) # type: ignore + if "content" in args or "tool_calls" in args: + all_messages.append(args) + return all_messages + + def _openai_content_parser( + self, + role: Role, + content: Contents, + call_id_to_id: dict[str, str], + ) -> dict[str, Any]: + """Parse contents into the openai format.""" + match content: + case TextContent(): + return { + "type": "output_text" if role == Role.ASSISTANT else "input_text", + "text": content.text, + } + case TextReasoningContent(): + ret: dict[str, Any] = { + "type": "reasoning", + "summary": { + "type": "summary_text", + "text": content.text, + }, + } + if content.additional_properties is not None: + if status := content.additional_properties.get("status"): + ret["status"] = status + if reasoning_text := content.additional_properties.get("reasoning_text"): + ret["content"] = {"type": "reasoning_text", "text": reasoning_text} + if encrypted_content := content.additional_properties.get("encrypted_content"): + ret["encrypted_content"] = encrypted_content + return ret + case DataContent() | UriContent(): + if content.has_top_level_media_type("image"): + return { + "type": "input_image", + "image_url": content.uri, + "detail": content.additional_properties.get("detail", "auto") + if content.additional_properties + else "auto", + "file_id": content.additional_properties.get("file_id", None) + if content.additional_properties + else None, + } + if content.has_top_level_media_type("audio"): + if content.media_type and "wav" in content.media_type: + format = "wav" + elif content.media_type and "mp3" in content.media_type: + format = "mp3" + else: + logger.warning("Unsupported audio media type: %s", content.media_type) + return {} + return { + "type": "input_audio", + "input_audio": { + "data": content.uri, + "format": format, + }, + } + return {} + case FunctionCallContent(): + return { + "call_id": content.call_id, + "id": call_id_to_id[content.call_id], + "type": "function_call", + "name": content.name, + "arguments": content.arguments, + } + case FunctionResultContent(): + # call_id for the result needs to be the same as the call_id for the function call + args: dict[str, Any] = { + "call_id": content.call_id, + "id": call_id_to_id.get(content.call_id), + "type": "function_call_output", + } + if content.result: + args["output"] = prepare_function_call_results(content.result) + return args + case FunctionApprovalRequestContent(): + return { + "type": "mcp_approval_request", + "id": content.id, + "arguments": content.function_call.arguments, + "name": content.function_call.name, + "server_label": content.function_call.additional_properties.get("server_label") + if content.function_call.additional_properties + else None, + } + case FunctionApprovalResponseContent(): + return { + "type": "mcp_approval_response", + "approval_request_id": content.id, + "approve": content.approved, + } + case HostedFileContent(): + return { + "type": "input_file", + "file_id": content.file_id, + } + case _: # should catch UsageDetails and ErrorContent and HostedVectorStoreContent + logger.debug("Unsupported content type passed (type: %s)", type(content)) + return {} + # region Response creation methods def _create_response_content( @@ -533,7 +693,8 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient): match message_content.type: case "output_text": text_content = TextContent( - text=message_content.text, raw_representation=message_content + text=message_content.text, + raw_representation=message_content, # type: ignore[reportUnknownArgumentType] ) metadata.update(self._get_metadata_from_response(message_content)) if message_content.annotations: @@ -639,6 +800,19 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient): raw_representation=item, ) ) + case "mcp_approval_request": # ResponseOutputMcpApprovalRequest + contents.append( + FunctionApprovalRequestContent( + id=item.id, + function_call=FunctionCallContent( + call_id=item.id, + name=item.name, + arguments=item.arguments, + additional_properties={"server_label": item.server_label}, + raw_representation=item, + ), + ) + ) case "image_generation_call": # ResponseOutputImageGenerationCall if item.result: contents.append( @@ -649,7 +823,7 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient): ) # TODO(peterychang): Add support for other content types case _: - logger.debug("Unparsed content of type: %s: %s", item.type, item) + logger.debug("Unparsed output of type: %s: %s", item.type, item) response_message = ChatMessage(role="assistant", contents=contents) args: dict[str, Any] = { "response_id": response.id, @@ -677,35 +851,151 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient): ) -> ChatResponseUpdate: """Create a streaming chat message content object from a choice.""" metadata: dict[str, Any] = {} - items: list[Contents] = [] + contents: list[Contents] = [] conversation_id: str | None = None model = self.ai_model_id # TODO(peterychang): Add support for other content types - match event: - case ResponseContentPartAddedEvent(): - match event.part: - case ResponseOutputText(): - items.append(TextContent(text=event.part.text, raw_representation=event)) - metadata.update(self._get_metadata_from_response(event.part)) - case ResponseOutputRefusal(): - items.append(TextContent(text=event.part.refusal, raw_representation=event)) - case ResponseTextDeltaEvent(): - items.append(TextContent(text=event.delta, raw_representation=event)) + match event.type: + # types: + # ResponseAudioDeltaEvent, + # ResponseAudioDoneEvent, + # ResponseAudioTranscriptDeltaEvent, + # ResponseAudioTranscriptDoneEvent, + # ResponseCodeInterpreterCallCodeDeltaEvent, + # ResponseCodeInterpreterCallCodeDoneEvent, + # ResponseCodeInterpreterCallCompletedEvent, + # ResponseCodeInterpreterCallInProgressEvent, + # ResponseCodeInterpreterCallInterpretingEvent, + # ResponseCompletedEvent, + # ResponseContentPartAddedEvent, + # ResponseContentPartDoneEvent, + # ResponseCreatedEvent, + # ResponseErrorEvent, + # ResponseFileSearchCallCompletedEvent, + # ResponseFileSearchCallInProgressEvent, + # ResponseFileSearchCallSearchingEvent, + # ResponseFunctionCallArgumentsDeltaEvent, + # ResponseFunctionCallArgumentsDoneEvent, + # ResponseInProgressEvent, + # ResponseFailedEvent, + # ResponseIncompleteEvent, + # ResponseOutputItemAddedEvent, + # ResponseOutputItemDoneEvent, + # ResponseReasoningSummaryPartAddedEvent, + # ResponseReasoningSummaryPartDoneEvent, + # ResponseReasoningSummaryTextDeltaEvent, + # ResponseReasoningSummaryTextDoneEvent, + # ResponseReasoningTextDeltaEvent, + # ResponseReasoningTextDoneEvent, + # ResponseRefusalDeltaEvent, + # ResponseRefusalDoneEvent, + # ResponseTextDeltaEvent, + # ResponseTextDoneEvent, + # ResponseWebSearchCallCompletedEvent, + # ResponseWebSearchCallInProgressEvent, + # ResponseWebSearchCallSearchingEvent, + # ResponseImageGenCallCompletedEvent, + # ResponseImageGenCallGeneratingEvent, + # ResponseImageGenCallInProgressEvent, + # ResponseImageGenCallPartialImageEvent, + # ResponseMcpCallArgumentsDeltaEvent, + # ResponseMcpCallArgumentsDoneEvent, + # ResponseMcpCallCompletedEvent, + # ResponseMcpCallFailedEvent, + # ResponseMcpCallInProgressEvent, + # ResponseMcpListToolsCompletedEvent, + # ResponseMcpListToolsFailedEvent, + # ResponseMcpListToolsInProgressEvent, + # ResponseOutputTextAnnotationAddedEvent, + # ResponseQueuedEvent, + # ResponseCustomToolCallInputDeltaEvent, + # ResponseCustomToolCallInputDoneEvent, + case "response.content_part.added": + event_part = event.part + match event_part.type: + case "output_text": + contents.append(TextContent(text=event_part.text, raw_representation=event)) + metadata.update(self._get_metadata_from_response(event_part)) + case "refusal": + contents.append(TextContent(text=event_part.refusal, raw_representation=event)) + case "response.output_text.delta": + contents.append(TextContent(text=event.delta, raw_representation=event)) metadata.update(self._get_metadata_from_response(event)) - case ResponseCompletedEvent(): + case "response.completed": conversation_id = event.response.id if chat_options.store is True else None model = event.response.model if event.response.usage: usage = self._usage_details_from_openai(event.response.usage) if usage: - items.append(UsageContent(details=usage, raw_representation=event)) - case ResponseOutputItemAddedEvent(): - if event.item.type == "function_call": - function_call_ids[event.output_index] = (event.item.call_id, event.item.name) - case ResponseFunctionCallArgumentsDeltaEvent(): + contents.append(UsageContent(details=usage, raw_representation=event)) + case "response.output_item.added": + event_item = event.item + match event_item.type: + # types: + # ResponseOutputMessage, + # ResponseFileSearchToolCall, + # ResponseFunctionToolCall, + # ResponseFunctionWebSearch, + # ResponseComputerToolCall, + # ResponseReasoningItem, + # ImageGenerationCall, + # ResponseCodeInterpreterToolCall, + # LocalShellCall, + # McpCall, + # McpListTools, + # McpApprovalRequest, + # ResponseCustomToolCall, + case "function_call": + function_call_ids[event.output_index] = (event_item.call_id, event_item.name) + case "mcp_approval_request": + contents.append( + FunctionApprovalRequestContent( + id=event_item.id, + function_call=FunctionCallContent( + call_id=event_item.id, + name=event_item.name, + arguments=event_item.arguments, + additional_properties={"server_label": event_item.server_label}, + raw_representation=event_item, + ), + ) + ) + case "code_interpreter_call": # ResponseOutputCodeInterpreterCall + if event_item.outputs: + for code_output in event_item.outputs: + if code_output.type == "logs": + contents.append(TextContent(text=code_output.logs, raw_representation=event_item)) + if code_output.type == "image": + contents.append( + UriContent( + uri=code_output.url, + raw_representation=event_item, + # no more specific media type then this can be inferred + media_type="image", + ) + ) + elif event_item.code: + # fallback if no output was returned is the code: + contents.append(TextContent(text=event_item.code, raw_representation=event_item)) + case "reasoning": # ResponseOutputReasoning + if event_item.content: + for index, reasoning_content in enumerate(event_item.content): + additional_properties = None + if event_item.summary and index < len(event_item.summary): + additional_properties = {"summary": event_item.summary[index]} + contents.append( + TextReasoningContent( + text=reasoning_content.text, + raw_representation=reasoning_content, + additional_properties=additional_properties, + ) + ) + case _: + logger.debug("Unparsed event of type: %s: %s", event.type, event) + case "response.function_call_arguments.delta": call_id, name = function_call_ids.get(event.output_index, (None, None)) if call_id and name: - items.append( + contents.append( FunctionCallContent( call_id=call_id, name=name, @@ -715,10 +1005,10 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient): ) ) case _: - logger.debug("Unparsed event: %s", event) + logger.debug("Unparsed event of type: %s: %s", event.type, event) return ChatResponseUpdate( - contents=items, + contents=contents, conversation_id=conversation_id, role=Role.ASSISTANT, ai_model_id=model, @@ -738,69 +1028,6 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient): details["openai.reasoning_tokens"] = usage.output_tokens_details.reasoning_tokens return details - def _openai_chat_message_parser( - self, - message: ChatMessage, - call_id_to_id: dict[str, str], - ) -> list[dict[str, Any]]: - """Parse a chat message into the openai format.""" - all_messages: list[dict[str, Any]] = [] - args: dict[str, Any] = { - "role": message.role.value if isinstance(message.role, Role) else message.role, - } - if message.additional_properties: - args["metadata"] = message.additional_properties - for content in message.contents: - match content: - case FunctionResultContent(): - new_args: dict[str, Any] = {} - new_args.update(self._openai_content_parser(message.role, content, call_id_to_id)) - all_messages.append(new_args) - case FunctionCallContent(): - function_call = self._openai_content_parser(message.role, content, call_id_to_id) - all_messages.append(function_call) # type: ignore - case _: - if "content" not in args: - args["content"] = [] - args["content"].append(self._openai_content_parser(message.role, content, call_id_to_id)) # type: ignore - if "content" in args or "tool_calls" in args: - all_messages.append(args) - return all_messages - - def _openai_content_parser( - self, - role: Role, - content: Contents, - call_id_to_id: dict[str, str], - ) -> dict[str, Any]: - """Parse contents into the openai format.""" - match content: - case FunctionCallContent(): - return { - "call_id": content.call_id, - "id": call_id_to_id[content.call_id], - "type": "function_call", - "name": content.name, - "arguments": content.arguments, - } - case FunctionResultContent(): - # call_id for the result needs to be the same as the call_id for the function call - args: dict[str, Any] = { - "call_id": content.call_id, - "type": "function_call_output", - } - if content.result: - args["output"] = prepare_function_call_results(content.result) - return args - case TextContent(): - return { - "type": "output_text" if role == Role.ASSISTANT else "input_text", - "text": content.text, - } - # TODO(peterychang): We'll probably need to specialize the other content types as well - case _: - return content.model_dump(exclude_none=True) - def _get_metadata_from_response(self, output: Any) -> dict[str, Any]: """Get metadata from a chat choice.""" if logprobs := getattr(output, "logprobs", None): diff --git a/python/packages/main/tests/main/test_logging.py b/python/packages/main/tests/main/test_logging.py index 2f7e40f17a..6565834596 100644 --- a/python/packages/main/tests/main/test_logging.py +++ b/python/packages/main/tests/main/test_logging.py @@ -22,7 +22,7 @@ def test_get_logger_custom_name(): def test_get_logger_invalid_name(): """Test that an exception is raised for an invalid logger name.""" - with pytest.raises(AgentFrameworkException, match="Logger name must start with 'agent_framework'."): + with pytest.raises(AgentFrameworkException): get_logger("invalid_name") diff --git a/python/packages/main/tests/main/test_tools.py b/python/packages/main/tests/main/test_tools.py index a3a39b9613..537b8cc94e 100644 --- a/python/packages/main/tests/main/test_tools.py +++ b/python/packages/main/tests/main/test_tools.py @@ -1,14 +1,24 @@ # Copyright (c) Microsoft. All rights reserved. +from typing import Any from unittest.mock import Mock, patch import pytest from pydantic import BaseModel -from agent_framework import AIFunction, HostedCodeInterpreterTool, ToolProtocol, ai_function +from agent_framework import ( + AIFunction, + HostedCodeInterpreterTool, + HostedMCPTool, + ToolProtocol, + ai_function, +) from agent_framework._tools import _parse_inputs +from agent_framework.exceptions import ToolException from agent_framework.telemetry import GenAIAttributes +# region AIFunction and ai_function decorator tests + def test_ai_function_decorator(): """Test the ai_function decorator.""" @@ -291,7 +301,7 @@ async def test_ai_function_invoke_invalid_pydantic_args(): await invalid_args_test.invoke(arguments=wrong_args) -# Tests for HostedCodeInterpreterTool and _parse_inputs +# region HostedCodeInterpreterTool and _parse_inputs def test_hosted_code_interpreter_tool_default(): @@ -507,3 +517,104 @@ def test_hosted_code_interpreter_tool_with_unknown_input(): """Test HostedCodeInterpreterTool with single unknown input.""" with pytest.raises(ValueError, match="Unsupported input type"): HostedCodeInterpreterTool(inputs={"hosted_file": "file-single"}) + + +# region HostedMCPTool tests + + +def test_hosted_mcp_tool_with_other_fields(): + """Test creating a HostedMCPTool with a specific approval dict, headers and additional properties.""" + tool = HostedMCPTool( + name="mcp-tool", + url="https://mcp.example", + description="A test MCP tool", + headers={"x": "y"}, + additional_properties={"p": 1}, + ) + + assert tool.name == "mcp-tool" + # pydantic AnyUrl preserves as string-like + assert str(tool.url).startswith("https://") + assert tool.headers == {"x": "y"} + assert tool.additional_properties == {"p": 1} + assert tool.description == "A test MCP tool" + + +@pytest.mark.parametrize( + "approval_mode", + [ + "always_require", + "never_require", + { + "always_require_approval": {"toolA"}, + "never_require_approval": {"toolB"}, + }, + { + "always_require_approval": ["toolA"], + "never_require_approval": ("toolB",), + }, + ], + ids=["always_require", "never_require", "specific", "specific_with_parsing"], +) +def test_hosted_mcp_tool_with_approval_mode(approval_mode: str | dict[str, Any]): + """Test creating a HostedMCPTool with a specific approval dict, headers and additional properties.""" + tool = HostedMCPTool(name="mcp-tool", url="https://mcp.example", approval_mode=approval_mode) + + assert tool.name == "mcp-tool" + # pydantic AnyUrl preserves as string-like + assert str(tool.url).startswith("https://") + if not isinstance(approval_mode, dict): + assert tool.approval_mode == approval_mode + else: + # approval_mode parsed to sets + assert isinstance(tool.approval_mode["always_require_approval"], set) + assert isinstance(tool.approval_mode["never_require_approval"], set) + assert "toolA" in tool.approval_mode["always_require_approval"] + assert "toolB" in tool.approval_mode["never_require_approval"] + + +def test_hosted_mcp_tool_invalid_approval_mode_raises(): + """Invalid approval_mode string should raise ServiceInitializationError.""" + with pytest.raises(ToolException): + HostedMCPTool(name="bad", url="https://x", approval_mode="invalid_mode") + + +@pytest.mark.parametrize( + "tools", + [ + {"toolA", "toolB"}, + ("toolA", "toolB"), + ["toolA", "toolB"], + ["toolA", "toolB", "toolA"], + ], + ids=[ + "set", + "tuple", + "list", + "list_with_duplicates", + ], +) +def test_hosted_mcp_tool_with_allowed_tools(tools: list[str] | tuple[str, ...] | set[str]): + """Test creating a HostedMCPTool with a list of allowed tools.""" + tool = HostedMCPTool( + name="mcp-tool", + url="https://mcp.example", + allowed_tools=tools, + ) + + assert tool.name == "mcp-tool" + # pydantic AnyUrl preserves as string-like + assert str(tool.url).startswith("https://") + # approval_mode parsed to set + assert isinstance(tool.allowed_tools, set) + assert tool.allowed_tools == {"toolA", "toolB"} + + +def test_hosted_mcp_tool_with_dict_of_allowed_tools(): + """Test creating a HostedMCPTool with a dict of allowed tools.""" + with pytest.raises(ToolException): + HostedMCPTool( + name="mcp-tool", + url="https://mcp.example", + allowed_tools={"toolA": "Tool A", "toolC": "Tool C"}, + ) diff --git a/python/packages/main/tests/main/test_types.py b/python/packages/main/tests/main/test_types.py index 40fd8e9f0e..094d4ea494 100644 --- a/python/packages/main/tests/main/test_types.py +++ b/python/packages/main/tests/main/test_types.py @@ -21,6 +21,8 @@ from agent_framework import ( DataContent, ErrorContent, FinishReason, + FunctionApprovalRequestContent, + FunctionApprovalResponseContent, FunctionCallContent, FunctionResultContent, GeneratedEmbeddings, @@ -38,6 +40,7 @@ from agent_framework import ( UsageDetails, ai_function, ) +from agent_framework.exceptions import AdditionItemMismatch @fixture @@ -296,9 +299,8 @@ def test_function_call_content_add_merging_and_errors(): # incompatible call ids a = FunctionCallContent(call_id="1", name="f", arguments="abc") b = FunctionCallContent(call_id="2", name="f", arguments="def") - from agent_framework.exceptions import AgentFrameworkException - with raises(AgentFrameworkException): + with raises(AdditionItemMismatch): _ = a + b @@ -379,6 +381,42 @@ def test_usage_details_add_with_none_and_type_errors(): u += 42 # type: ignore[arg-type] +# region UserInputRequest and Response + + +def test_function_approval_request_and_response_creation(): + """Test creating a FunctionApprovalRequestContent and producing a response.""" + fc = FunctionCallContent(call_id="call-1", name="do_something", arguments={"a": 1}) + req = FunctionApprovalRequestContent(id="req-1", function_call=fc) + + assert req.type == "function_approval_request" + assert req.function_call == fc + assert req.id == "req-1" + assert isinstance(req, BaseContent) + + resp = req.create_response(True) + + assert isinstance(resp, FunctionApprovalResponseContent) + assert resp.approved is True + assert resp.function_call == fc + assert resp.id == "req-1" + + +def test_function_approval_serialization_roundtrip(): + fc = FunctionCallContent(call_id="c2", name="f", arguments='{"x":1}') + req = FunctionApprovalRequestContent(id="id-2", function_call=fc, additional_properties={"meta": 1}) + + dumped = req.model_dump() + loaded = FunctionApprovalRequestContent.model_validate(dumped) + assert loaded == req + + class TestModel(BaseModel): + content: Contents + + test_item = TestModel.model_validate({"content": dumped}) + assert isinstance(test_item.content, FunctionApprovalRequestContent) + + # region BaseContent Serialization diff --git a/python/packages/main/tests/openai/test_openai_assistants_client.py b/python/packages/main/tests/openai/test_openai_assistants_client.py index c78a45d1fb..76ce1ce87e 100644 --- a/python/packages/main/tests/openai/test_openai_assistants_client.py +++ b/python/packages/main/tests/openai/test_openai_assistants_client.py @@ -1251,42 +1251,3 @@ async def test_openai_assistants_client_agent_level_tool_persistence(): assert second_response.text is not None # Should use the agent-level weather tool again assert any(term in second_response.text.lower() for term in ["miami", "sunny", "72"]) - - -@skip_if_openai_integration_tests_disabled -async def test_openai_assistants_client_run_level_tool_isolation(): - """Test that run-level tools are isolated to specific runs and don't persist with OpenAI Assistants Client.""" - # Counter to track how many times the weather tool is called - call_count = 0 - - @ai_function - async def get_weather_with_counter(location: Annotated[str, "The location as a city name"]) -> str: - """Get the current weather in a given location.""" - nonlocal call_count - call_count += 1 - return f"The weather in {location} is sunny and 72°F." - - async with ChatAgent( - chat_client=OpenAIAssistantsClient(), - instructions="You are a helpful assistant.", - ) as agent: - # First run - use run-level tool - first_response = await agent.run( - "What's the weather like in Chicago?", - tools=[get_weather_with_counter], # Run-level tool - ) - - assert isinstance(first_response, AgentRunResponse) - assert first_response.text is not None - # Should use the run-level weather tool (call count should be 1) - assert call_count == 1 - assert any(term in first_response.text.lower() for term in ["chicago", "sunny", "72"]) - - # Second run - run-level tool should NOT persist (key isolation test) - second_response = await agent.run("What's the weather like in Miami?") - - assert isinstance(second_response, AgentRunResponse) - assert second_response.text is not None - # Should NOT use the weather tool since it was only run-level in previous call - # Call count should still be 1 (no additional calls) - assert call_count == 1 diff --git a/python/packages/main/tests/openai/test_openai_chat_client.py b/python/packages/main/tests/openai/test_openai_chat_client.py index 35e75095a1..f57af48207 100644 --- a/python/packages/main/tests/openai/test_openai_chat_client.py +++ b/python/packages/main/tests/openai/test_openai_chat_client.py @@ -339,7 +339,7 @@ async def test_openai_chat_client_web_search() -> None: tools=[HostedWebSearchTool(additional_properties=additional_properties)], tool_choice="auto", ) - assert "Seattle" in response.text + assert response.text is not None @skip_if_openai_integration_tests_disabled @@ -392,7 +392,7 @@ async def test_openai_chat_client_web_search_streaming() -> None: for content in chunk.contents: if isinstance(content, TextContent) and content.text: full_message += content.text - assert "Seattle" in full_message + assert full_message is not None @skip_if_openai_integration_tests_disabled diff --git a/python/packages/main/tests/openai/test_openai_responses_client.py b/python/packages/main/tests/openai/test_openai_responses_client.py index f9e7c18ab6..6b89329f4e 100644 --- a/python/packages/main/tests/openai/test_openai_responses_client.py +++ b/python/packages/main/tests/openai/test_openai_responses_client.py @@ -18,11 +18,14 @@ from agent_framework import ( ChatMessage, ChatResponse, ChatResponseUpdate, + FunctionApprovalRequestContent, + FunctionApprovalResponseContent, FunctionCallContent, FunctionResultContent, HostedCodeInterpreterTool, HostedFileContent, HostedFileSearchTool, + HostedMCPTool, HostedVectorStoreContent, HostedWebSearchTool, Role, @@ -49,7 +52,7 @@ class OutputStruct(BaseModel): """A structured output for testing purposes.""" location: str - weather: str + weather: str | None = None async def create_vector_store(client: OpenAIResponsesClient) -> tuple[str, HostedVectorStoreContent]: @@ -644,6 +647,156 @@ def test_response_content_creation_with_function_call() -> None: assert function_call.arguments == '{"location": "Seattle"}' +def test_tools_to_response_tools_with_hosted_mcp() -> None: + """Test that HostedMCPTool is converted to the correct response tool dict.""" + client = OpenAIResponsesClient(ai_model_id="test-model", api_key="test-key") + + tool = HostedMCPTool( + name="My MCP", + url="https://mcp.example", + description="An MCP server", + approval_mode={"always_require_approval": ["tool_a", "tool_b"]}, + allowed_tools={"tool_a", "tool_b"}, + headers={"X-Test": "yes"}, + additional_properties={"custom": "value"}, + ) + + resp_tools = client._tools_to_response_tools([tool]) + assert isinstance(resp_tools, list) + assert len(resp_tools) == 1 + mcp = resp_tools[0] + assert isinstance(mcp, dict) + assert mcp["type"] == "mcp" + assert mcp["server_label"] == "My_MCP" + # server_url may be normalized to include a trailing slash by the client + assert str(mcp["server_url"]).rstrip("/") == "https://mcp.example" + assert mcp["server_description"] == "An MCP server" + assert mcp["headers"]["X-Test"] == "yes" + assert set(mcp["allowed_tools"]) == {"tool_a", "tool_b"} + # approval mapping created from approval_mode dict + assert "require_approval" in mcp + + +def test_create_response_content_with_mcp_approval_request() -> None: + """Test that a non-streaming mcp_approval_request is parsed into FunctionApprovalRequestContent.""" + client = OpenAIResponsesClient(ai_model_id="test-model", api_key="test-key") + + mock_response = MagicMock() + mock_response.output_parsed = None + mock_response.metadata = {} + mock_response.usage = None + mock_response.id = "resp-id" + mock_response.model = "test-model" + mock_response.created_at = 1000000000 + + mock_item = MagicMock() + mock_item.type = "mcp_approval_request" + mock_item.id = "approval-1" + mock_item.name = "do_sensitive_action" + mock_item.arguments = {"arg": 1} + mock_item.server_label = "My_MCP" + + mock_response.output = [mock_item] + + response = client._create_response_content(mock_response, chat_options=ChatOptions()) # type: ignore + + assert isinstance(response.messages[0].contents[0], FunctionApprovalRequestContent) + req = response.messages[0].contents[0] + assert req.id == "approval-1" + assert req.function_call.name == "do_sensitive_action" + assert req.function_call.arguments == {"arg": 1} + assert req.function_call.additional_properties["server_label"] == "My_MCP" + + +def test_create_streaming_response_content_with_mcp_approval_request() -> None: + """Test that a streaming mcp_approval_request event is parsed into FunctionApprovalRequestContent.""" + client = OpenAIResponsesClient(ai_model_id="test-model", api_key="test-key") + chat_options = ChatOptions() + function_call_ids: dict[int, tuple[str, str]] = {} + + mock_event = MagicMock() + mock_event.type = "response.output_item.added" + mock_item = MagicMock() + mock_item.type = "mcp_approval_request" + mock_item.id = "approval-stream-1" + mock_item.name = "do_stream_action" + mock_item.arguments = {"x": 2} + mock_item.server_label = "My_MCP" + mock_event.item = mock_item + + update = client._create_streaming_response_content(mock_event, chat_options, function_call_ids) + assert any(isinstance(c, FunctionApprovalRequestContent) for c in update.contents) + fa = next(c for c in update.contents if isinstance(c, FunctionApprovalRequestContent)) + assert fa.id == "approval-stream-1" + assert fa.function_call.name == "do_stream_action" + + +def test_end_to_end_mcp_approval_flow() -> None: + """End-to-end mocked test: + model issues an mcp_approval_request, user approves, client sends mcp_approval_response. + """ + client = OpenAIResponsesClient(ai_model_id="test-model", api_key="test-key") + + # First mocked response: model issues an mcp_approval_request + mock_response1 = MagicMock() + mock_response1.output_parsed = None + mock_response1.metadata = {} + mock_response1.usage = None + mock_response1.id = "resp-1" + mock_response1.model = "test-model" + mock_response1.created_at = 1000000000 + + mock_item = MagicMock() + mock_item.type = "mcp_approval_request" + mock_item.id = "approval-1" + mock_item.name = "do_sensitive_action" + mock_item.arguments = {"arg": "value"} + mock_item.server_label = "My_MCP" + mock_response1.output = [mock_item] + + # Second mocked response: simple assistant acknowledgement after approval + mock_response2 = MagicMock() + mock_response2.output_parsed = None + mock_response2.metadata = {} + mock_response2.usage = None + mock_response2.id = "resp-2" + mock_response2.model = "test-model" + mock_response2.created_at = 1000000001 + mock_text_item = MagicMock() + mock_text_item.type = "message" + mock_text_content = MagicMock() + mock_text_content.type = "output_text" + mock_text_content.text = "Approved." + mock_text_item.content = [mock_text_content] + mock_response2.output = [mock_text_item] + + # Patch the create call to return the two mocked responses in sequence + with patch.object(client.client.responses, "create", side_effect=[mock_response1, mock_response2]) as mock_create: + # First call: get the approval request + response = asyncio.run(client.get_response(messages=[ChatMessage(role="user", text="Trigger approval")])) + assert isinstance(response.messages[0].contents[0], FunctionApprovalRequestContent) + req = response.messages[0].contents[0] + assert req.id == "approval-1" + + # Build a user approval and send it (include required function_call) + approval = FunctionApprovalResponseContent(approved=True, id=req.id, function_call=req.function_call) + approval_message = ChatMessage(role="user", contents=[approval]) + _ = asyncio.run(client.get_response(messages=[approval_message])) + + # Ensure two calls were made and the second includes the mcp_approval_response + assert mock_create.call_count == 2 + _, kwargs = mock_create.call_args_list[1] + sent_input = kwargs.get("input") + assert isinstance(sent_input, list) + found = False + for item in sent_input: + if isinstance(item, dict) and item.get("type") == "mcp_approval_response": + assert item["approval_request_id"] == "approval-1" + assert item["approve"] is True + found = True + assert found + + def test_usage_details_basic() -> None: """Test _usage_details_from_openai without cached or reasoning tokens.""" client = OpenAIResponsesClient(ai_model_id="test-model", api_key="test-key") @@ -775,9 +928,10 @@ async def test_openai_responses_client_response() -> None: assert response is not None assert isinstance(response, ChatResponse) - output = OutputStruct.model_validate_json(response.text) + output = response.value + assert output is not None, "Response value is None" assert "seattle" in output.location.lower() - assert "sunny" in output.weather.lower() + assert output.weather is not None @skip_if_openai_integration_tests_disabled @@ -839,17 +993,11 @@ async def test_openai_responses_client_streaming() -> None: messages.append(ChatMessage(role="user", text="who are Emily and David?")) # Test that the client can be used to get a response - response = openai_responses_client.get_streaming_response(messages=messages) + response = await ChatResponse.from_chat_response_generator( + openai_responses_client.get_streaming_response(messages=messages) + ) - full_message: str = "" - async for chunk in response: - assert chunk is not None - assert isinstance(chunk, ChatResponseUpdate) - for content in chunk.contents: - if isinstance(content, TextContent) and content.text: - full_message += content.text - - assert "scientists" in full_message + assert "scientists" in response.text messages.clear() messages.append(ChatMessage(role="user", text="The weather in Seattle is sunny")) @@ -859,17 +1007,16 @@ async def test_openai_responses_client_streaming() -> None: messages=messages, response_format=OutputStruct, ) - full_message = "" + chunks = [] async for chunk in response: assert chunk is not None assert isinstance(chunk, ChatResponseUpdate) - for content in chunk.contents: - if isinstance(content, TextContent) and content.text: - full_message += content.text - - output = OutputStruct.model_validate_json(full_message) + chunks.append(chunk) + full_message = ChatResponse.from_chat_response_updates(chunks, output_format_type=OutputStruct) + output = full_message.value + assert output is not None, "Response value is None" assert "seattle" in output.location.lower() - assert "sunny" in output.weather.lower() + assert output.weather is not None @skip_if_openai_integration_tests_disabled @@ -906,15 +1053,15 @@ async def test_openai_responses_client_streaming_tools() -> None: tool_choice="auto", response_format=OutputStruct, ) - full_message = "" + chunks = [] async for chunk in response: assert chunk is not None assert isinstance(chunk, ChatResponseUpdate) - for content in chunk.contents: - if isinstance(content, TextContent) and content.text: - full_message += content.text + chunks.append(chunk) - output = OutputStruct.model_validate_json(full_message) + full_message = ChatResponse.from_chat_response_updates(chunks, output_format_type=OutputStruct) + output = full_message.value + assert output is not None, "Response value is None" assert "seattle" in output.location.lower() assert "sunny" in output.weather.lower() @@ -955,7 +1102,7 @@ async def test_openai_responses_client_web_search() -> None: tools=[HostedWebSearchTool(additional_properties=additional_properties)], tool_choice="auto", ) - assert "Seattle" in response.text + assert response.text is not None @skip_if_openai_integration_tests_disabled @@ -1008,7 +1155,7 @@ async def test_openai_responses_client_web_search_streaming() -> None: for content in chunk.contents: if isinstance(content, TextContent) and content.text: full_message += content.text - assert "Seattle" in full_message + assert full_message is not None @skip_if_openai_integration_tests_disabled diff --git a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_hosted_mcp.py b/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_hosted_mcp.py new file mode 100644 index 0000000000..ee2140a72f --- /dev/null +++ b/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_hosted_mcp.py @@ -0,0 +1,224 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +from typing import TYPE_CHECKING, Any + +from agent_framework import ChatAgent, HostedMCPTool +from agent_framework.openai import OpenAIResponsesClient + +if TYPE_CHECKING: + from agent_framework import AgentProtocol, AgentThread + + +async def handle_approvals_without_thread(query: str, agent: "AgentProtocol"): + """When we don't have a thread, we need to ensure we return with the input, approval request and approval.""" + from agent_framework import ChatMessage + + result = await agent.run(query) + while len(result.user_input_requests) > 0: + new_inputs: list[Any] = [query] + for user_input_needed in result.user_input_requests: + print( + f"User Input Request for function from {agent.name}: {user_input_needed.function_call.name}" + f" with arguments: {user_input_needed.function_call.arguments}" + ) + new_inputs.append(ChatMessage(role="assistant", contents=[user_input_needed])) + user_approval = input("Approve function call? (y/n): ") + new_inputs.append( + ChatMessage(role="user", contents=[user_input_needed.create_response(user_approval.lower() == "y")]) + ) + + result = await agent.run(new_inputs) + return result + + +async def handle_approvals_with_thread(query: str, agent: "AgentProtocol", thread: "AgentThread"): + """Here we let the thread deal with the previous responses, and we just rerun with the approval.""" + from agent_framework import ChatMessage + + result = await agent.run(query, thread=thread, store=True) + while len(result.user_input_requests) > 0: + new_input: list[Any] = [] + for user_input_needed in result.user_input_requests: + print( + f"User Input Request for function from {agent.name}: {user_input_needed.function_call.name}" + f" with arguments: {user_input_needed.function_call.arguments}" + ) + user_approval = input("Approve function call? (y/n): ") + new_input.append( + ChatMessage( + role="user", + contents=[user_input_needed.create_response(user_approval.lower() == "y")], + ) + ) + result = await agent.run(new_input, thread=thread, store=True) + return result + + +async def handle_approvals_with_thread_streaming(query: str, agent: "AgentProtocol", thread: "AgentThread"): + """Here we let the thread deal with the previous responses, and we just rerun with the approval.""" + from agent_framework import ChatMessage + + new_input: list[ChatMessage] = [] + new_input_added = True + while new_input_added: + new_input_added = False + new_input.append(ChatMessage(role="user", text=query)) + async for update in agent.run_stream(new_input, thread=thread, store=True): + if update.user_input_requests: + for user_input_needed in update.user_input_requests: + print( + f"User Input Request for function from {agent.name}: {user_input_needed.function_call.name}" + f" with arguments: {user_input_needed.function_call.arguments}" + ) + user_approval = input("Approve function call? (y/n): ") + new_input.append( + ChatMessage( + role="user", contents=[user_input_needed.create_response(user_approval.lower() == "y")] + ) + ) + new_input_added = True + else: + yield update + + +async def run_hosted_mcp_without_thread_and_specific_approval() -> None: + """Example showing Mcp Tools with approvals without using a thread.""" + print("=== Mcp with approvals and without thread ===") + + # Tools are provided when creating the agent + # The agent can use these tools for any query during its lifetime + async with ChatAgent( + chat_client=OpenAIResponsesClient(), + name="DocsAgent", + instructions="You are a helpful assistant that can help with microsoft documentation questions.", + tools=HostedMCPTool( + name="Microsoft Learn MCP", + url="https://learn.microsoft.com/api/mcp", + # we don't require approval for microsoft_docs_search tool calls + # but we do for any other tool + approval_mode={"never_require_approval": ["microsoft_docs_search"]}, + ), + ) as agent: + # First query + query1 = "How to create an Azure storage account using az cli?" + print(f"User: {query1}") + result1 = await handle_approvals_without_thread(query1, agent) + print(f"{agent.name}: {result1}\n") + print("\n=======================================\n") + # Second query + query2 = "What is Microsoft Semantic Kernel?" + print(f"User: {query2}") + result2 = await handle_approvals_without_thread(query2, agent) + print(f"{agent.name}: {result2}\n") + + +async def run_hosted_mcp_without_approval() -> None: + """Example showing Mcp Tools without approvals.""" + print("=== Mcp without approvals ===") + + # Tools are provided when creating the agent + # The agent can use these tools for any query during its lifetime + async with ChatAgent( + chat_client=OpenAIResponsesClient(), + name="DocsAgent", + instructions="You are a helpful assistant that can help with microsoft documentation questions.", + tools=HostedMCPTool( + name="Microsoft Learn MCP", + url="https://learn.microsoft.com/api/mcp", + # we don't require approval for any function calls + # this means we will not see the approval messages, + # it is fully handled by the service and a final response is returned. + approval_mode="never_require", + ), + ) as agent: + # First query + query1 = "How to create an Azure storage account using az cli?" + print(f"User: {query1}") + result1 = await handle_approvals_without_thread(query1, agent) + print(f"{agent.name}: {result1}\n") + print("\n=======================================\n") + # Second query + query2 = "What is Microsoft Semantic Kernel?" + print(f"User: {query2}") + result2 = await handle_approvals_without_thread(query2, agent) + print(f"{agent.name}: {result2}\n") + + +async def run_hosted_mcp_with_thread() -> None: + """Example showing Mcp Tools with approvals using a thread.""" + print("=== Mcp with approvals and with thread ===") + + # Tools are provided when creating the agent + # The agent can use these tools for any query during its lifetime + async with ChatAgent( + chat_client=OpenAIResponsesClient(), + name="DocsAgent", + instructions="You are a helpful assistant that can help with microsoft documentation questions.", + tools=HostedMCPTool( + name="Microsoft Learn MCP", + url="https://learn.microsoft.com/api/mcp", + # we require approval for all function calls + approval_mode="always_require", + ), + ) as agent: + # First query + thread = agent.get_new_thread() + query1 = "How to create an Azure storage account using az cli?" + print(f"User: {query1}") + result1 = await handle_approvals_with_thread(query1, agent, thread) + print(f"{agent.name}: {result1}\n") + print("\n=======================================\n") + # Second query + query2 = "What is Microsoft Semantic Kernel?" + print(f"User: {query2}") + result2 = await handle_approvals_with_thread(query2, agent, thread) + print(f"{agent.name}: {result2}\n") + + +async def run_hosted_mcp_with_thread_streaming() -> None: + """Example showing Mcp Tools with approvals using a thread.""" + print("=== Mcp with approvals and with thread ===") + + # Tools are provided when creating the agent + # The agent can use these tools for any query during its lifetime + async with ChatAgent( + chat_client=OpenAIResponsesClient(), + name="DocsAgent", + instructions="You are a helpful assistant that can help with microsoft documentation questions.", + tools=HostedMCPTool( + name="Microsoft Learn MCP", + url="https://learn.microsoft.com/api/mcp", + # we require approval for all function calls + approval_mode="always_require", + ), + ) as agent: + # First query + thread = agent.get_new_thread() + query1 = "How to create an Azure storage account using az cli?" + print(f"User: {query1}") + print(f"{agent.name}: ", end="") + async for update in handle_approvals_with_thread_streaming(query1, agent, thread): + print(update, end="") + print("\n") + print("\n=======================================\n") + # Second query + query2 = "What is Microsoft Semantic Kernel?" + print(f"User: {query2}") + print(f"{agent.name}: ", end="") + async for update in handle_approvals_with_thread_streaming(query2, agent, thread): + print(update, end="") + print("\n") + + +async def main() -> None: + print("=== OpenAI Responses Client Agent with Hosted Mcp Tools Examples ===\n") + + await run_hosted_mcp_without_approval() + await run_hosted_mcp_without_thread_and_specific_approval() + await run_hosted_mcp_with_thread() + await run_hosted_mcp_with_thread_streaming() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/tests/samples/getting_started/test_agents.py b/python/tests/samples/getting_started/test_agents.py index c4a94f10ff..32ae175d4a 100644 --- a/python/tests/samples/getting_started/test_agents.py +++ b/python/tests/samples/getting_started/test_agents.py @@ -66,7 +66,13 @@ from samples.getting_started.agents.foundry.foundry_with_explicit_settings impor main as foundry_with_explicit_settings, ) from samples.getting_started.agents.foundry.foundry_with_function_tools import ( - main as foundry_with_function_tools, + mixed_tools_example as foundry_with_function_tools_mixed, +) +from samples.getting_started.agents.foundry.foundry_with_function_tools import ( + tools_on_agent_level as foundry_with_function_tools_agent, +) +from samples.getting_started.agents.foundry.foundry_with_function_tools import ( + tools_on_run_level as foundry_with_function_tools_run, ) from samples.getting_started.agents.foundry.foundry_with_local_mcp import ( main as foundry_with_local_mcp, @@ -323,7 +329,25 @@ agent_samples = [ ], ), param( - foundry_with_function_tools, + foundry_with_function_tools_agent, + [], # Non-interactive sample + id="foundry_with_function_tools", + marks=[ + pytest.mark.foundry, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + foundry_with_function_tools_run, + [], # Non-interactive sample + id="foundry_with_function_tools", + marks=[ + pytest.mark.foundry, + pytest.mark.skipif(os.getenv(RUN_SAMPLES_TESTS, None) is None, reason="Not running sample tests."), + ], + ), + param( + foundry_with_function_tools_mixed, [], # Non-interactive sample id="foundry_with_function_tools", marks=[ From 82ca4065cba912e0df2dc8d852d29aa397226307 Mon Sep 17 00:00:00 2001 From: Eduard van Valkenburg Date: Wed, 10 Sep 2025 16:52:42 +0200 Subject: [PATCH 24/32] Python: Improved telemetry setup (#421) * test with stack and simplified names * quick demo of agent decorator * moved builder to protocol to enhance functionality * undid chatclientAgent -> agent rename * one more * reverted AIAgent rename * final reverts * fixed foundry import * revert changes * streamlined otel and fcc decorators * cleanup of telemetry * further refinement * lots of updates * fixed typing * fix for mypy * added input and output atttributes * fix import * initial work on baking in otel * major update to telemetry * final fixes after rename * fix * fix test * updated tests * fix for tests * fixes for tests * updated based on comments * removed agent decorator * fix for Python: ServiceResponseException when using multiple tools Fixes #649 * addressed comments * fix tests * fix tests * fix tools tests * fix for conversation_id in assistants client * fix responses test * fix tests and mypy * updated test * foundry fix --------- Co-authored-by: Chris <66376200+crickman@users.noreply.github.com> --- python/.cspell.json | 1 + python/.env.example | 11 +- .../agent_framework_azure/_chat_client.py | 4 + .../_responses_client.py | 4 +- .../azure/agent_framework_azure/_shared.py | 2 +- .../agent_framework_foundry/_chat_client.py | 100 +- python/packages/foundry/pyproject.toml | 2 +- python/packages/foundry/tests/conftest.py | 1 + .../foundry/tests/test_foundry_chat_client.py | 6 +- .../packages/main/agent_framework/_agents.py | 16 +- .../packages/main/agent_framework/_clients.py | 264 +-- .../packages/main/agent_framework/_tools.py | 413 ++++- .../packages/main/agent_framework/_types.py | 2 +- .../main/agent_framework/exceptions.py | 41 +- .../openai/_assistants_client.py | 10 +- .../agent_framework/openai/_chat_client.py | 42 +- .../openai/_responses_client.py | 7 +- .../main/agent_framework/openai/_shared.py | 4 +- .../main/agent_framework/telemetry.py | 1422 ++++++++++------- python/packages/main/pyproject.toml | 3 + python/packages/main/tests/main/conftest.py | 240 ++- .../packages/main/tests/main/test_agents.py | 130 +- .../packages/main/tests/main/test_clients.py | 166 +- .../main/tests/main/test_telemetry.py | 732 ++------- python/packages/main/tests/main/test_tools.py | 113 +- python/packages/main/tests/openai/conftest.py | 25 + .../openai/test_openai_responses_client.py | 31 +- .../agent_framework_workflow/_telemetry.py | 4 +- .../packages/workflow/tests/test_tracing.py | 19 +- .../openai_responses_client_with_local_mcp.py | 1 - .../getting_started/telemetry/.env.example | 16 +- .../getting_started/telemetry/01-zero_code.py | 86 + .../telemetry/02a-generic_chat_client.py | 134 ++ .../telemetry/02b-foundry_chat_client.py | 88 + .../getting_started/telemetry/03a-agent.py | 59 + .../telemetry/03b-foundry_agent.py | 65 + .../getting_started/telemetry/04-workflow.py | 120 ++ .../getting_started/telemetry/README.md | 225 +-- .../getting_started/telemetry/agent.py | 190 --- .../getting_started/telemetry/interactive.py | 186 --- .../telemetry/manual_setup_console_output.py | 114 ++ .../getting_started/telemetry/scenarios.py | 249 --- .../getting_started/telemetry/workflow.py | 253 --- .../agents/azure_chat_agents_streaming.py | 2 +- python/uv.lock | 627 ++++++-- 45 files changed, 3246 insertions(+), 2984 deletions(-) create mode 100644 python/samples/getting_started/telemetry/01-zero_code.py create mode 100644 python/samples/getting_started/telemetry/02a-generic_chat_client.py create mode 100644 python/samples/getting_started/telemetry/02b-foundry_chat_client.py create mode 100644 python/samples/getting_started/telemetry/03a-agent.py create mode 100644 python/samples/getting_started/telemetry/03b-foundry_agent.py create mode 100644 python/samples/getting_started/telemetry/04-workflow.py delete mode 100644 python/samples/getting_started/telemetry/agent.py delete mode 100644 python/samples/getting_started/telemetry/interactive.py create mode 100644 python/samples/getting_started/telemetry/manual_setup_console_output.py delete mode 100644 python/samples/getting_started/telemetry/scenarios.py delete mode 100644 python/samples/getting_started/telemetry/workflow.py diff --git a/python/.cspell.json b/python/.cspell.json index ab7106ad7b..1b21f5263d 100644 --- a/python/.cspell.json +++ b/python/.cspell.json @@ -53,6 +53,7 @@ "nopep", "NOSQL", "ollama", + "otlp", "Onnx", "onyourdatatest", "OPENAI", diff --git a/python/.env.example b/python/.env.example index e8de35394a..ecbdef60a8 100644 --- a/python/.env.example +++ b/python/.env.example @@ -1,8 +1,17 @@ +# Foundry FOUNDRY_PROJECT_ENDPOINT="" FOUNDRY_MODEL_DEPLOYMENT_NAME="" +# OpenAI OPENAI_API_KEY="" OPENAI_CHAT_MODEL_ID="" +OPENAI_RESPONSES_MODEL_ID="" +# Azure OpenAI AZURE_OPENAI_ENDPOINT="" AZURE_OPENAI_CHAT_DEPLOYMENT_NAME="" AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME="" -OPENAI_RESPONSES_MODEL_ID="" +# Telemetry +AGENT_FRAMEWORK_MONITOR_CONNECTION_STRING="..." +AGENT_FRAMEWORK_OTLP_ENDPOINT="http://localhost:4317/" +AGENT_FRAMEWORK_ENABLE_OTEL=true +AGENT_FRAMEWORK_ENABLE_SENSITIVE_DATA=true +AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL=true diff --git a/python/packages/azure/agent_framework_azure/_chat_client.py b/python/packages/azure/agent_framework_azure/_chat_client.py index f140f281b2..11231353bf 100644 --- a/python/packages/azure/agent_framework_azure/_chat_client.py +++ b/python/packages/azure/agent_framework_azure/_chat_client.py @@ -11,9 +11,11 @@ from agent_framework import ( ChatResponseUpdate, CitationAnnotation, TextContent, + use_function_invocation, ) from agent_framework.exceptions import ServiceInitializationError from agent_framework.openai._chat_client import OpenAIBaseChatClient +from agent_framework.telemetry import use_telemetry from azure.core.credentials import TokenCredential from openai.lib.azure import AsyncAzureADTokenProvider, AsyncAzureOpenAI from openai.types.chat.chat_completion import Choice @@ -37,6 +39,8 @@ TChatResponse = TypeVar("TChatResponse", ChatResponse, ChatResponseUpdate) TAzureChatClient = TypeVar("TAzureChatClient", bound="AzureChatClient") +@use_function_invocation +@use_telemetry class AzureChatClient(AzureOpenAIConfigMixin, OpenAIBaseChatClient): """Azure Chat completion class.""" diff --git a/python/packages/azure/agent_framework_azure/_responses_client.py b/python/packages/azure/agent_framework_azure/_responses_client.py index de4d679e37..b1811280a6 100644 --- a/python/packages/azure/agent_framework_azure/_responses_client.py +++ b/python/packages/azure/agent_framework_azure/_responses_client.py @@ -4,7 +4,7 @@ from collections.abc import Mapping from typing import Any, TypeVar from urllib.parse import urljoin -from agent_framework import use_tool_calling +from agent_framework import use_function_invocation from agent_framework.exceptions import ServiceInitializationError from agent_framework.openai._responses_client import OpenAIBaseResponsesClient from agent_framework.telemetry import use_telemetry @@ -22,7 +22,7 @@ TAzureResponsesClient = TypeVar("TAzureResponsesClient", bound="AzureResponsesCl @use_telemetry -@use_tool_calling +@use_function_invocation class AzureResponsesClient(AzureOpenAIConfigMixin, OpenAIBaseResponsesClient): """Azure Responses completion class.""" diff --git a/python/packages/azure/agent_framework_azure/_shared.py b/python/packages/azure/agent_framework_azure/_shared.py index 6f3a6a72fd..4addf194b2 100644 --- a/python/packages/azure/agent_framework_azure/_shared.py +++ b/python/packages/azure/agent_framework_azure/_shared.py @@ -168,7 +168,7 @@ class AzureOpenAISettings(AFBaseSettings): class AzureOpenAIConfigMixin(OpenAIBase): """Internal class for configuring a connection to an Azure OpenAI service.""" - MODEL_PROVIDER_NAME: ClassVar[str] = "azure_openai" # type: ignore[reportIncompatibleVariableOverride, misc] + OTEL_PROVIDER_NAME: ClassVar[str] = "azure_openai" # type: ignore[reportIncompatibleVariableOverride, misc] @validate_call(config=ConfigDict(arbitrary_types_allowed=True)) def __init__( diff --git a/python/packages/foundry/agent_framework_foundry/_chat_client.py b/python/packages/foundry/agent_framework_foundry/_chat_client.py index 811daed8b8..7f375b885e 100644 --- a/python/packages/foundry/agent_framework_foundry/_chat_client.py +++ b/python/packages/foundry/agent_framework_foundry/_chat_client.py @@ -1,6 +1,5 @@ # Copyright (c) Microsoft. All rights reserved. -import contextlib import json import sys from collections.abc import AsyncIterable, MutableMapping, MutableSequence @@ -24,7 +23,7 @@ from agent_framework import ( UriContent, UsageContent, UsageDetails, - use_tool_calling, + use_function_invocation, ) from agent_framework._pydantic import AFBaseSettings from agent_framework.exceptions import ServiceInitializationError, ServiceResponseException @@ -98,17 +97,17 @@ TFoundryChatClient = TypeVar("TFoundryChatClient", bound="FoundryChatClient") HEADERS = prepend_agent_framework_to_user_agent() +@use_function_invocation @use_telemetry -@use_tool_calling class FoundryChatClient(BaseChatClient): """Azure AI Foundry Chat client.""" - MODEL_PROVIDER_NAME: ClassVar[str] = "azure_ai_foundry" # type: ignore[reportIncompatibleVariableOverride, misc] + OTEL_PROVIDER_NAME: ClassVar[str] = "azure.ai.foundry" # type: ignore[reportIncompatibleVariableOverride, misc] client: AIProjectClient = Field(...) credential: AsyncTokenCredential | None = Field(...) agent_id: str | None = Field(default=None) agent_name: str | None = Field(default=None) - ai_model_deployment_name: str | None = Field(default=None) + ai_model_id: str | None = Field(default=None) thread_id: str | None = Field(default=None) _should_delete_agent: bool = PrivateAttr(default=False) # Track whether we should delete the agent _should_close_client: bool = PrivateAttr(default=False) # Track whether we should close client connection @@ -140,6 +139,7 @@ class FoundryChatClient(BaseChatClient): project_endpoint: The Azure AI Foundry project endpoint URL. Used if client is not provided. model_deployment_name: The model deployment name to use for agent creation. async_credential: Azure async credential to use for authentication. + setup_tracing: Whether to setup tracing for the client. Defaults to True. env_file_path: Path to environment file for loading settings. env_file_encoding: Encoding of the environment file. **kwargs: Additional keyword arguments passed to the parent class. @@ -182,11 +182,25 @@ class FoundryChatClient(BaseChatClient): agent_id=agent_id, # type: ignore[reportCallIssue] thread_id=thread_id, # type: ignore[reportCallIssue] agent_name=foundry_settings.agent_name, # type: ignore[reportCallIssue] - ai_model_deployment_name=foundry_settings.model_deployment_name, # type: ignore[reportCallIssue] + ai_model_id=foundry_settings.model_deployment_name, # type: ignore[reportCallIssue] **kwargs, ) self._should_close_client = should_close_client + async def setup_foundry_telemetry(self, enable_live_metrics: bool = False) -> None: + """Call this method to setup tracing with Foundry. + + This will take the connection string from the project client. + It will override any connection string that is set in the environment variables. + It will disable any OTLP endpoint that might have been set. + """ + from agent_framework.telemetry import setup_telemetry + + setup_telemetry( + application_insights_connection_string=await self.client.telemetry.get_application_insights_connection_string(), # noqa: E501 + enable_live_metrics=enable_live_metrics, + ) + async def __aenter__(self) -> "Self": """Async context manager entry.""" return self @@ -241,7 +255,9 @@ class FoundryChatClient(BaseChatClient): # Get the thread ID thread_id: str | None = ( - chat_options.conversation_id if chat_options.conversation_id is not None else self.thread_id + chat_options.conversation_id + if chat_options.conversation_id is not None + else run_options.get("conversation_id", self.thread_id) ) if thread_id is None and tool_results is not None: @@ -265,12 +281,12 @@ class FoundryChatClient(BaseChatClient): """ # If no agent_id is provided, create a temporary agent if self.agent_id is None: - if not self.ai_model_deployment_name: + if not self.ai_model_id: raise ServiceInitializationError("Model deployment name is required for agent creation.") agent_name = self.agent_name args = { - "model": self.ai_model_deployment_name, + "model": self.ai_model_id, "name": agent_name, "headers": HEADERS, } @@ -323,6 +339,7 @@ class FoundryChatClient(BaseChatClient): final_thread_id = await self._prepare_thread(thread_id, thread_run, run_options) # Now create a new run and stream the results. + run_options.pop("conversation_id", None) stream = await self.client.agents.runs.stream( # type: ignore[reportUnknownMemberType] final_thread_id, agent_id=agent_id, @@ -353,21 +370,33 @@ class FoundryChatClient(BaseChatClient): self, thread_id: str | None, thread_run: ThreadRun | None, run_options: dict[str, Any] ) -> str: """Prepare the thread for a new run, creating or cleaning up as needed.""" - if thread_id is None: - # No thread ID was provided, so create a new thread. - thread = await self.client.agents.threads.create( - messages=run_options["additional_messages"], - tool_resources=run_options.get("tool_resources"), - metadata=run_options.get("metadata"), + if thread_id is not None: + if thread_run is not None: + # There was an active run; we need to cancel it before starting a new run. + await self.client.agents.runs.cancel(thread_id, thread_run.id, headers=HEADERS) + + return thread_id + + # No thread ID was provided, so create a new thread. + thread = await self.client.agents.threads.create( + tool_resources=run_options.get("tool_resources"), + metadata=run_options.get("metadata"), + headers=HEADERS, + ) + thread_id = thread.id + # workaround for: https://github.com/Azure/azure-sdk-for-python/issues/42805 + # this occurs when otel is enabled + # once fixed, in the function above, readd: + # `messages=run_options.pop("additional_messages")` + for msg in run_options.pop("additional_messages", []): + await self.client.agents.messages.create( + thread_id=thread_id, + role=msg.role, + content=msg.content, + metadata=msg.metadata, headers=HEADERS, ) - run_options["additional_messages"] = [] - return thread.id - - if thread_run is not None: - # There was an active run; we need to cancel it before starting a new run. - await self.client.agents.runs.cancel(thread_id, thread_run.id, headers=HEADERS) - + # and remove until here. return thread_id async def _process_stream_events( @@ -378,7 +407,7 @@ class FoundryChatClient(BaseChatClient): """Process events from the agent stream and yield ChatResponseUpdate objects.""" # Use 'async with' only if the stream supports async context management (main agent stream). # Tool output handlers only support async iteration, not context management. - if isinstance(stream, contextlib.AbstractAsyncContextManager): + if isinstance(stream, AsyncAgentRunStream): async with stream as response_stream: # type: ignore async for update in self._process_stream_events_from_iterator(response_stream, thread_id): yield update @@ -387,7 +416,7 @@ class FoundryChatClient(BaseChatClient): yield update async def _process_stream_events_from_iterator( - self, stream_iter: Any, thread_id: str + self, stream_iter: AsyncAgentEventHandler[Any], thread_id: str ) -> AsyncIterable[ChatResponseUpdate]: """Process events from the stream iterator and yield ChatResponseUpdate objects.""" response_id: str | None = None @@ -400,6 +429,7 @@ class FoundryChatClient(BaseChatClient): raw_representation=event_data, response_id=response_id, role=Role.ASSISTANT, + ai_model_id=event_data.model, ) elif event_type == AgentStreamEvent.THREAD_RUN_STEP_CREATED and isinstance(event_data, RunStep): response_id = event_data.run_id @@ -429,7 +459,7 @@ class FoundryChatClient(BaseChatClient): response_id=response_id, ) elif ( - event_type == AgentStreamEvent.THREAD_RUN_COMPLETED + event_type in [AgentStreamEvent.THREAD_RUN_COMPLETED, AgentStreamEvent.THREAD_RUN_STEP_COMPLETED] and isinstance(event_data, RunStep) and event_data.usage is not None ): @@ -468,16 +498,16 @@ class FoundryChatClient(BaseChatClient): """Create function call contents from a tool action event.""" contents: list[Contents] = [] - if isinstance(event_data.required_action, SubmitToolOutputsAction): + if isinstance(event_data, ThreadRun) and isinstance(event_data.required_action, SubmitToolOutputsAction): for tool_call in event_data.required_action.submit_tool_outputs.tool_calls: if isinstance(tool_call, RequiredFunctionToolCall): - call_id = json.dumps([response_id, tool_call.id]) - function_name = tool_call.function.name - function_arguments = json.loads(tool_call.function.arguments) contents.append( - FunctionCallContent(call_id=call_id, name=function_name, arguments=function_arguments) + FunctionCallContent( + call_id=f'["{response_id}", "{tool_call.id}"]', + name=tool_call.function.name, + arguments=tool_call.function.arguments, + ) ) - return contents async def _close_client_if_needed(self) -> None: @@ -632,3 +662,11 @@ class FoundryChatClient(BaseChatClient): # to update the agent name in the client. if agent_name and not self.agent_name: self.agent_name = agent_name + + def service_url(self) -> str: + """Get the service URL for the chat client. + + Returns: + The service URL for the chat client, or None if not set. + """ + return self.client._config.endpoint diff --git a/python/packages/foundry/pyproject.toml b/python/packages/foundry/pyproject.toml index 2d12831c69..1c1f18605f 100644 --- a/python/packages/foundry/pyproject.toml +++ b/python/packages/foundry/pyproject.toml @@ -26,7 +26,7 @@ dependencies = [ "agent-framework", "azure-ai-projects >= 1.0.0b11", "azure-ai-agents >= 1.1.0b1", - "aiohttp ~= 3.8" + "aiohttp ~= 3.8", ] [tool.uv] diff --git a/python/packages/foundry/tests/conftest.py b/python/packages/foundry/tests/conftest.py index cb204880ee..eb858f7a1b 100644 --- a/python/packages/foundry/tests/conftest.py +++ b/python/packages/foundry/tests/conftest.py @@ -62,6 +62,7 @@ def mock_ai_project_client() -> MagicMock: # Mock threads property mock_client.agents.threads = MagicMock() mock_client.agents.threads.create = AsyncMock() + mock_client.agents.messages.create = AsyncMock() # Mock runs property mock_client.agents.runs = MagicMock() diff --git a/python/packages/foundry/tests/test_foundry_chat_client.py b/python/packages/foundry/tests/test_foundry_chat_client.py index b77efe5954..8a7eec03b6 100644 --- a/python/packages/foundry/tests/test_foundry_chat_client.py +++ b/python/packages/foundry/tests/test_foundry_chat_client.py @@ -25,6 +25,7 @@ from agent_framework import ( ) from agent_framework import __version__ as AF_VERSION from agent_framework.exceptions import ServiceInitializationError +from agent_framework.foundry import FoundryChatClient, FoundrySettings from azure.ai.agents.models import ( RequiredFunctionToolCall, SubmitToolOutputsAction, @@ -34,8 +35,6 @@ from azure.core.credentials_async import AsyncTokenCredential from azure.identity.aio import AzureCliCredential from pydantic import Field, ValidationError -from agent_framework_foundry import FoundryChatClient, FoundrySettings - skip_if_foundry_integration_tests_disabled = pytest.mark.skipif( os.getenv("RUN_INTEGRATION_TESTS", "false").lower() != "true" or os.getenv("FOUNDRY_PROJECT_ENDPOINT", "") in ("", "https://test-project.cognitiveservices.azure.com/"), @@ -62,8 +61,7 @@ def create_test_foundry_chat_client( thread_id=thread_id, _should_delete_agent=should_delete_agent, agent_name=foundry_settings.agent_name, # type: ignore[reportCallIssue] - ai_model_deployment_name=foundry_settings.model_deployment_name, # type: - credential=None, + ai_model_id=foundry_settings.model_deployment_name, ) diff --git a/python/packages/main/agent_framework/_agents.py b/python/packages/main/agent_framework/_agents.py index bc22654079..2b63ca4ef0 100644 --- a/python/packages/main/agent_framework/_agents.py +++ b/python/packages/main/agent_framework/_agents.py @@ -9,11 +9,12 @@ from uuid import uuid4 from pydantic import BaseModel, Field, PrivateAttr -from ._clients import ChatClientProtocol +from ._clients import BaseChatClient, ChatClientProtocol +from ._logging import get_logger from ._mcp import MCPTool from ._pydantic import AFBaseModel from ._threads import AgentThread, ChatMessageStore, deserialize_thread_state, thread_on_new_messages -from ._tools import ToolProtocol +from ._tools import FUNCTION_INVOKING_CHAT_CLIENT_MARKER, ToolProtocol from ._types import ( AgentRunResponse, AgentRunResponseUpdate, @@ -32,6 +33,8 @@ if sys.version_info >= (3, 11): else: from typing_extensions import Self # pragma: no cover +logger = get_logger("agent_framework") + TThreadType = TypeVar("TThreadType", bound="AgentThread") __all__ = ["AgentProtocol", "BaseAgent", "ChatAgent"] @@ -248,6 +251,11 @@ class ChatAgent(BaseAgent): kwargs: any additional keyword arguments. Unused, can be used by subclasses of this Agent. """ + if not hasattr(chat_client, FUNCTION_INVOKING_CHAT_CLIENT_MARKER) and isinstance(chat_client, BaseChatClient): + logger.warning( + "The provided chat client does not support function invoking, this might limit agent capabilities." + ) + kwargs.update(additional_properties or {}) # We ignore the MCP Servers here and store them separately, @@ -317,8 +325,8 @@ class ChatAgent(BaseAgent): should check if there is already a agent name defined, and if not set it to this value. """ - if hasattr(self.chat_client, "_update_agent_name") and callable(self.chat_client._update_agent_name): # type: ignore[reportAttributeAccessIssue] - self.chat_client._update_agent_name(self.name) # type: ignore[reportAttributeAccessIssue] + if hasattr(self.chat_client, "_update_agent_name") and callable(self.chat_client._update_agent_name): # type: ignore[reportAttributeAccessIssue, attr-defined] + self.chat_client._update_agent_name(self.name) # type: ignore[reportAttributeAccessIssue, attr-defined] async def run( self, diff --git a/python/packages/main/agent_framework/_clients.py b/python/packages/main/agent_framework/_clients.py index f80d1f5dc1..2a6735fe0b 100644 --- a/python/packages/main/agent_framework/_clients.py +++ b/python/packages/main/agent_framework/_clients.py @@ -2,32 +2,29 @@ import asyncio from abc import ABC, abstractmethod -from collections.abc import AsyncIterable, Awaitable, Callable, MutableMapping, MutableSequence, Sequence -from functools import wraps +from collections.abc import AsyncIterable, Callable, MutableMapping, MutableSequence, Sequence from typing import TYPE_CHECKING, Any, Generic, Literal, Protocol, TypeVar, runtime_checkable -from pydantic import BaseModel +from pydantic import BaseModel, Field from ._logging import get_logger from ._mcp import MCPTool from ._pydantic import AFBaseModel from ._threads import ChatMessageStore -from ._tools import AIFunction, ToolProtocol +from ._tools import ToolProtocol from ._types import ( ChatMessage, ChatOptions, ChatResponse, ChatResponseUpdate, ChatToolMode, - Contents, - FunctionCallContent, - FunctionResultContent, GeneratedEmbeddings, ) if TYPE_CHECKING: from ._agents import ChatAgent + TInput = TypeVar("TInput", contravariant=True) TEmbedding = TypeVar("TEmbedding") TBaseChatClient = TypeVar("TBaseChatClient", bound="BaseChatClient") @@ -38,215 +35,8 @@ __all__ = [ "BaseChatClient", "ChatClientProtocol", "EmbeddingGenerator", - "use_tool_calling", ] -# region Tool Calling Functions and Decorators - - -async def _auto_invoke_function( - function_call_content: FunctionCallContent, - custom_args: dict[str, Any] | None = None, - *, - tool_map: dict[str, AIFunction[BaseModel, Any]], - sequence_index: int | None = None, - request_index: int | None = None, -) -> Contents: - """Invoke a function call requested by the agent, applying filters that are defined in the agent.""" - tool: AIFunction[BaseModel, Any] | None = tool_map.get(function_call_content.name) - if tool is None: - raise KeyError(f"No tool or function named '{function_call_content.name}'") - - parsed_args: dict[str, Any] = dict(function_call_content.parse_arguments() or {}) - - # Merge with user-supplied args; right-hand side dominates, so parsed args win on conflicts. - merged_args: dict[str, Any] = (custom_args or {}) | parsed_args - args = tool.input_model.model_validate(merged_args) - exception = None - try: - function_result = await tool.invoke(arguments=args, tool_call_id=function_call_content.call_id) - except Exception as ex: - exception = ex - function_result = None - return FunctionResultContent( - call_id=function_call_content.call_id, - exception=exception, - result=function_result, - ) - - -def _tool_call_non_streaming( - func: Callable[..., Awaitable["ChatResponse"]], -) -> Callable[..., Awaitable["ChatResponse"]]: - """Decorate the internal _inner_get_response method to enable tool calls.""" - - @wraps(func) - async def wrapper( - self: "BaseChatClient", - *, - messages: MutableSequence[ChatMessage], - chat_options: ChatOptions, - **kwargs: Any, - ) -> ChatResponse: - response: ChatResponse | None = None - fcc_messages: list[ChatMessage] = [] - for attempt_idx in range(getattr(self, "__maximum_iterations_per_request", 10)): - response = await func(self, messages=messages, chat_options=chat_options, **kwargs) - # if there are function calls, we will handle them first - function_results = { - it.call_id for it in response.messages[0].contents if isinstance(it, FunctionResultContent) - } - function_calls = [ - it - for it in response.messages[0].contents - if isinstance(it, FunctionCallContent) and it.call_id not in function_results - ] - if function_calls: - # Run all function calls concurrently - results = await asyncio.gather(*[ - _auto_invoke_function( - function_call, - custom_args=kwargs, - tool_map={t.name: t for t in chat_options.tools or [] if isinstance(t, AIFunction)}, # type: ignore[reportPrivateUsage] - sequence_index=seq_idx, - request_index=attempt_idx, - ) - for seq_idx, function_call in enumerate(function_calls) - ]) - # add a single ChatMessage to the response with the results - result_message = ChatMessage(role="tool", contents=results) - response.messages.append(result_message) - # response should contain 2 messages after this, - # one with function call contents - # and one with function result contents - # the amount and call_id's should match - # this runs in every but the first run - # we need to keep track of all function call messages - fcc_messages.extend(response.messages) - # and add them as additional context to the messages - if chat_options.store: - messages.clear() - messages.append(result_message) - else: - messages.extend(response.messages) - continue - # If we reach this point, it means there were no function calls to handle, - # we'll add the previous function call and responses - # to the front of the list, so that the final response is the last one - # TODO (eavanvalkenburg): control this behavior? - if fcc_messages: - for msg in reversed(fcc_messages): - response.messages.insert(0, msg) - return response - - # Failsafe: give up on tools, ask model for plain answer - chat_options.tool_choice = "none" - self._prepare_tool_choice(chat_options=chat_options) # type: ignore[reportPrivateUsage] - response = await func(self, messages=messages, chat_options=chat_options, **kwargs) - if fcc_messages: - for msg in reversed(fcc_messages): - response.messages.insert(0, msg) - return response - - return wrapper - - -def _tool_call_streaming( - func: Callable[..., AsyncIterable["ChatResponseUpdate"]], -) -> Callable[..., AsyncIterable["ChatResponseUpdate"]]: - """Decorate the internal _inner_get_response method to enable tool calls.""" - - @wraps(func) - async def wrapper( - self: "BaseChatClient", - *, - messages: MutableSequence[ChatMessage], - chat_options: ChatOptions, - **kwargs: Any, - ) -> AsyncIterable[ChatResponseUpdate]: - """Wrap the inner get streaming response method to handle tool calls.""" - for attempt_idx in range(getattr(self, "__maximum_iterations_per_request", 10)): - function_call_returned = False - all_messages: list[ChatResponseUpdate] = [] - async for update in func(self, messages=messages, chat_options=chat_options, **kwargs): - if update.contents and any(isinstance(item, FunctionCallContent) for item in update.contents): - all_messages.append(update) - function_call_returned = True - yield update - - if not function_call_returned: - return - - # There is one FunctionCallContent response stream in the messages, combining now to create - # the full completion depending on the prompt, the message may contain both function call - # content and others - response: ChatResponse = ChatResponse.from_chat_response_updates(all_messages) - # add the single assistant response message to the history - messages.append(response.messages[0]) - function_calls = [item for item in response.messages[0].contents if isinstance(item, FunctionCallContent)] - - # When conversation id is present, it means that messages are hosted on the server. - # In this case, we need to update ChatOptions with conversation id and also clear messages - if response.conversation_id is not None: - chat_options.conversation_id = response.conversation_id - messages = [] - - if function_calls: - # Run all function calls concurrently - results = await asyncio.gather(*[ - _auto_invoke_function( - function_call, - custom_args=kwargs, - tool_map={t.name: t for t in chat_options.tools or [] if isinstance(t, AIFunction)}, # type: ignore[reportPrivateUsage] - sequence_index=seq_idx, - request_index=attempt_idx, - ) - for seq_idx, function_call in enumerate(function_calls) - ]) - yield ChatResponseUpdate(contents=results, role="tool") - function_result_msg = ChatMessage(role="tool", contents=results) - response.messages.append(function_result_msg) - messages.append(function_result_msg) - continue - - # Failsafe: give up on tools, ask model for plain answer - chat_options.tool_choice = "none" - self._prepare_tool_choice(chat_options=chat_options) # type: ignore[reportPrivateUsage] - async for update in func(self, messages=messages, chat_options=chat_options, **kwargs): - yield update - - return wrapper - - -def use_tool_calling(cls: type[TBaseChatClient]) -> type[TBaseChatClient]: - """Class decorator that enables tool calling for a chat client. - - Remarks: - This only works on classes that derive from BaseChatClient - and the `_inner_get_response` - and `_inner_get_streaming_response` methods. - It also sets a `__maximum_iterations_per_request` attribute on the class. - if you want to expose this to end_users, do a version of this: - - @property - - def maximum_iterations_per_request(self): - return getattr(self, "__maximum_iterations_per_request", 10) - - @maximum_iterations_per_request.setter - - def maximum_iterations_per_request(self, value: int) -> None: - setattr(self, "__maximum_iterations_per_request", value) - - """ - setattr(cls, "__maximum_iterations_per_request", 10) - - if inner_response := getattr(cls, "_inner_get_response", None): - cls._inner_get_response = _tool_call_non_streaming(inner_response) # type: ignore - if inner_streaming_response := getattr(cls, "_inner_get_streaming_response", None): - cls._inner_get_streaming_response = _tool_call_streaming(inner_streaming_response) # type: ignore - return cls - # region ChatClientProtocol Protocol @@ -255,6 +45,11 @@ def use_tool_calling(cls: type[TBaseChatClient]) -> type[TBaseChatClient]: class ChatClientProtocol(Protocol): """A protocol for a chat client that can generate responses.""" + @property + def additional_properties(self) -> dict[str, Any]: + """Get additional properties associated with the client.""" + ... + async def get_response( self, messages: str | ChatMessage | list[str] | list[ChatMessage], @@ -371,26 +166,35 @@ class ChatClientProtocol(Protocol): ... +# region ChatClientBase + + +def prepare_messages(messages: str | ChatMessage | list[str] | list[ChatMessage]) -> list[ChatMessage]: + """Turn the allowed input into a list of chat messages.""" + if isinstance(messages, str): + return [ChatMessage(role="user", text=messages)] + if isinstance(messages, ChatMessage): + return [messages] + return_messages: list[ChatMessage] = [] + for msg in messages: + if isinstance(msg, str): + msg = ChatMessage(role="user", text=msg) + return_messages.append(msg) + return return_messages + + class BaseChatClient(AFBaseModel, ABC): """Base class for chat clients.""" - MODEL_PROVIDER_NAME: str = "unknown" + additional_properties: dict[str, Any] = Field(default_factory=dict) + OTEL_PROVIDER_NAME: str = "unknown" # This is used for OTel setup, should be overridden in subclasses - def _prepare_messages( + def prepare_messages( self, messages: str | ChatMessage | list[str] | list[ChatMessage] ) -> MutableSequence[ChatMessage]: """Turn the allowed input into a list of chat messages.""" - if isinstance(messages, str): - return [ChatMessage(role="user", text=messages)] - if isinstance(messages, ChatMessage): - return [messages] - return_messages: list[ChatMessage] = [] - for msg in messages: - if isinstance(msg, str): - msg = ChatMessage(role="user", text=msg) - return_messages.append(msg) - return return_messages + return prepare_messages(messages) @staticmethod def _normalize_tools( @@ -537,7 +341,7 @@ class BaseChatClient(AFBaseModel, ABC): user=user, additional_properties=additional_properties or {}, ) - prepped_messages = self._prepare_messages(messages) + prepped_messages = self.prepare_messages(messages) self._prepare_tool_choice(chat_options=chat_options) return await self._inner_get_response(messages=prepped_messages, chat_options=chat_options, **kwargs) @@ -617,7 +421,7 @@ class BaseChatClient(AFBaseModel, ABC): additional_properties=additional_properties or {}, **kwargs, ) - prepped_messages = self._prepare_messages(messages) + prepped_messages = self.prepare_messages(messages) self._prepare_tool_choice(chat_options=chat_options) async for update in self._inner_get_streaming_response( messages=prepped_messages, chat_options=chat_options, **kwargs @@ -640,13 +444,13 @@ class BaseChatClient(AFBaseModel, ABC): else: chat_options.tool_choice = chat_tool_mode.mode - def service_url(self) -> str | None: + def service_url(self) -> str: """Get the URL of the service. Override this in the subclass to return the proper URL. If the service does not have a URL, return None. """ - return None + return "Unknown" def create_agent( self, diff --git a/python/packages/main/agent_framework/_tools.py b/python/packages/main/agent_framework/_tools.py index 4b12835d43..b0751b2406 100644 --- a/python/packages/main/agent_framework/_tools.py +++ b/python/packages/main/agent_framework/_tools.py @@ -1,13 +1,16 @@ # Copyright (c) Microsoft. All rights reserved. + +import asyncio import inspect import sys -from collections.abc import Awaitable, Callable, Collection +from collections.abc import AsyncIterable, Awaitable, Callable, Collection, MutableMapping, Sequence from functools import wraps -from time import perf_counter +from time import perf_counter, time_ns from typing import ( TYPE_CHECKING, Annotated, Any, + Final, Generic, Literal, Protocol, @@ -17,27 +20,39 @@ from typing import ( runtime_checkable, ) -from opentelemetry import metrics, trace +from opentelemetry import metrics from pydantic import AnyUrl, BaseModel, Field, PrivateAttr, ValidationError, create_model, field_validator from ._logging import get_logger from ._pydantic import AFBaseModel -from .exceptions import ToolException -from .telemetry import GenAIAttributes, start_as_current_span +from .exceptions import ChatClientInitializationError, ToolException +from .telemetry import ( + OPERATION_DURATION_BUCKET_BOUNDARIES, + OtelAttr, + _capture_exception, # type: ignore + get_function_span, + meter, +) if TYPE_CHECKING: - from ._types import Contents + from ._clients import ChatClientProtocol + from ._types import ( + ChatMessage, + ChatResponse, + ChatResponseUpdate, + Contents, + FunctionCallContent, + ) if sys.version_info >= (3, 12): from typing import TypedDict # pragma: no cover else: from typing_extensions import TypedDict # pragma: no cover -tracer: trace.Tracer = trace.get_tracer("agent_framework") -meter: metrics.Meter = metrics.get_meter_provider().get_meter("agent_framework") logger = get_logger() __all__ = [ + "FUNCTION_INVOKING_CHAT_CLIENT_MARKER", "AIFunction", "HostedCodeInterpreterTool", "HostedFileSearchTool", @@ -46,9 +61,17 @@ __all__ = [ "HostedWebSearchTool", "ToolProtocol", "ai_function", + "use_function_invocation", ] +logger = get_logger() +FUNCTION_INVOKING_CHAT_CLIENT_MARKER: Final[str] = "__function_invoking_chat_client__" +DEFAULT_MAX_ITERATIONS: Final[int] = 10 +TChatClient = TypeVar("TChatClient", bound="ChatClientProtocol") +# region Helpers + + def _parse_inputs( inputs: "Contents | dict[str, Any] | str | list[Contents | dict[str, Any] | str] | None", ) -> list["Contents"]: @@ -91,6 +114,7 @@ def _parse_inputs( return parsed_inputs +# region Tools @runtime_checkable class ToolProtocol(Protocol): """Represents a generic tool that can be specified to an AI service. @@ -337,7 +361,7 @@ class HostedFileSearchTool(BaseTool): class AIFunction(BaseTool, Generic[ArgsT, ReturnT]): - """A ToolProtocol that is callable as code. + """A AITool that is callable as code. Args: name: The name of the function. @@ -351,9 +375,10 @@ class AIFunction(BaseTool, Generic[ArgsT, ReturnT]): input_model: type[ArgsT] _invocation_duration_histogram: metrics.Histogram = PrivateAttr( default_factory=lambda: meter.create_histogram( - GenAIAttributes.MEASUREMENT_FUNCTION_INVOCATION_DURATION.value, - unit="s", + name=OtelAttr.MEASUREMENT_FUNCTION_INVOCATION_DURATION, + unit=OtelAttr.DURATION_UNIT, description="Measures the duration of a function's execution", + explicit_bucket_boundaries_advisory=OPERATION_DURATION_BUCKET_BOUNDARIES, ) ) @@ -371,40 +396,60 @@ class AIFunction(BaseTool, Generic[ArgsT, ReturnT]): Args: arguments: A Pydantic model instance containing the arguments for the function. - kwargs: keyword arguments to pass to the function, will not be used if `args` is provided. + otel_settings: Optional model diagnostics settings to override the default settings. + kwargs: keyword arguments to pass to the function, will not be used if `arguments` is provided. """ + global OTEL_SETTINGS + from .telemetry import OTEL_SETTINGS, setup_telemetry + tool_call_id = kwargs.pop("tool_call_id", None) if arguments is not None: if not isinstance(arguments, self.input_model): raise TypeError(f"Expected {self.input_model.__name__}, got {type(arguments).__name__}") kwargs = arguments.model_dump(exclude_none=True) - logger.info(f"Function name: {self.name}") - logger.debug(f"Function arguments: {kwargs}") - with start_as_current_span( - tracer, self, metadata={"tool_call_id": tool_call_id, "kwargs": kwargs} - ) as current_span: - attributes: dict[str, Any] = { - GenAIAttributes.MEASUREMENT_FUNCTION_TAG_NAME.value: self.name, - GenAIAttributes.TOOL_CALL_ID.value: tool_call_id, + if not OTEL_SETTINGS.ENABLED: # type: ignore + logger.info(f"Function name: {self.name}") + logger.debug(f"Function arguments: {kwargs}") + res = self.__call__(**kwargs) + result = await res if inspect.isawaitable(res) else res + logger.info(f"Function {self.name} succeeded.") + logger.debug(f"Function result: {result or 'None'}") + return result # type: ignore[reportReturnType] + + setup_telemetry() + with get_function_span( + function=self, + tool_call_id=tool_call_id, + ) as span: + hist_attributes: dict[str, Any] = { + OtelAttr.MEASUREMENT_FUNCTION_TAG_NAME: self.name, + OtelAttr.TOOL_CALL_ID: tool_call_id or "unknown", } - starting_time_stamp = perf_counter() + logger.info(f"Function name: {self.name}") + if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED: # type: ignore + logger.debug(f"Function arguments: {kwargs}") + start_time_stamp = perf_counter() + end_time_stamp: float | None = None try: res = self.__call__(**kwargs) result = await res if inspect.isawaitable(res) else res - logger.info(f"Function {self.name} succeeded.") - logger.debug(f"Function result: {result or 'None'}") - return result # type: ignore[reportReturnType] + end_time_stamp = perf_counter() except Exception as exception: - attributes[GenAIAttributes.ERROR_TYPE.value] = type(exception).__name__ - current_span.record_exception(exception) - current_span.set_attribute(GenAIAttributes.ERROR_TYPE.value, type(exception).__name__) - current_span.set_status(trace.StatusCode.ERROR, description=str(exception)) + end_time_stamp = perf_counter() + hist_attributes[OtelAttr.ERROR_TYPE] = type(exception).__name__ + _capture_exception(span=span, exception=exception, timestamp=time_ns()) logger.error(f"Function failed. Error: {exception}") raise + else: + logger.info(f"Function {self.name} succeeded.") + if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED: # type: ignore + logger.debug(f"Function result: {result or 'None'}") + return result # type: ignore[reportReturnType] finally: - duration = perf_counter() - starting_time_stamp - self._invocation_duration_histogram.record(duration, attributes=attributes) - logger.info("Function completed. Duration: %fs", duration) + duration = (end_time_stamp or perf_counter()) - start_time_stamp + span.set_attribute(OtelAttr.MEASUREMENT_FUNCTION_INVOCATION_DURATION, duration) + self._invocation_duration_histogram.record(duration, attributes=hist_attributes) + logger.info("Function duration: %fs", duration) def parameters(self) -> dict[str, Any]: """Create the json schema of the parameters.""" @@ -422,6 +467,9 @@ class AIFunction(BaseTool, Generic[ArgsT, ReturnT]): } +# region AI Function Decorator + + def _parse_annotation(annotation: Any) -> Any: """Parse a type annotation and return the corresponding type. @@ -499,3 +547,306 @@ def ai_function( return wrapper(func) return decorator(func) if func else decorator # type: ignore[reportReturnType, return-value] + + +# region Function Invoking Chat Client + + +async def _auto_invoke_function( + function_call_content: "FunctionCallContent", + custom_args: dict[str, Any] | None = None, + *, + tool_map: dict[str, AIFunction[BaseModel, Any]], + sequence_index: int | None = None, + request_index: int | None = None, +) -> "Contents": + """Invoke a function call requested by the agent, applying filters that are defined in the agent.""" + from ._types import FunctionResultContent + + tool: AIFunction[BaseModel, Any] | None = tool_map.get(function_call_content.name) + if tool is None: + raise KeyError(f"No tool or function named '{function_call_content.name}'") + + parsed_args: dict[str, Any] = dict(function_call_content.parse_arguments() or {}) + + # Merge with user-supplied args; right-hand side dominates, so parsed args win on conflicts. + merged_args: dict[str, Any] = (custom_args or {}) | parsed_args + args = tool.input_model.model_validate(merged_args) + exception = None + try: + function_result = await tool.invoke( + arguments=args, + tool_call_id=function_call_content.call_id, + ) # type: ignore[arg-type] + except Exception as ex: + exception = ex + function_result = None + return FunctionResultContent( + call_id=function_call_content.call_id, + exception=exception, + result=function_result, + ) + + +def _get_tool_map( + tools: "ToolProtocol \ + | Callable[..., Any] \ + | MutableMapping[str, Any] \ + | list[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]]", +) -> dict[str, AIFunction[Any, Any]]: + ai_function_list: dict[str, AIFunction[Any, Any]] = {} + for tool in tools if isinstance(tools, list) else [tools]: + if isinstance(tool, AIFunction): + ai_function_list[tool.name] = tool + continue + if callable(tool): + # Convert to AITool if it's a function or callable + ai_tool = ai_function(tool) + ai_function_list[ai_tool.name] = ai_tool + return ai_function_list + + +async def execute_function_calls( + custom_args: dict[str, Any], + attempt_idx: int, + function_calls: Sequence["FunctionCallContent"], + tools: "ToolProtocol \ + | Callable[..., Any] \ + | MutableMapping[str, Any] \ + | list[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]]", +) -> list["Contents"]: + tool_map = _get_tool_map(tools) + # Run all function calls concurrently + return await asyncio.gather(*[ + _auto_invoke_function( + function_call_content=function_call, + custom_args=custom_args, + tool_map=tool_map, + sequence_index=seq_idx, + request_index=attempt_idx, + ) + for seq_idx, function_call in enumerate(function_calls) + ]) + + +def update_conversation_id(kwargs: dict[str, Any], conversation_id: str | None) -> None: + """Update kwargs with conversation id.""" + if conversation_id is None: + return + if "chat_options" in kwargs: + kwargs["chat_options"].conversation_id = conversation_id + else: + kwargs["conversation_id"] = conversation_id + + +def _handle_function_calls_response( + func: Callable[..., Awaitable["ChatResponse"]], + *, + max_iterations: int = 10, +) -> Callable[..., Awaitable["ChatResponse"]]: + """Decorate the get_response method to enable function calls. + + Args: + func: The get_response method to decorate. + max_iterations: The maximum number of function call iterations to perform. + + """ + + def decorator( + func: Callable[..., Awaitable["ChatResponse"]], + ) -> Callable[..., Awaitable["ChatResponse"]]: + """Inner decorator.""" + + @wraps(func) + async def function_invocation_wrapper( + self: "ChatClientProtocol", + messages: "str | ChatMessage | list[str] | list[ChatMessage]", + **kwargs: Any, + ) -> "ChatResponse": + from ._clients import prepare_messages + from ._types import ChatMessage, ChatOptions, FunctionCallContent, FunctionResultContent + + prepped_messages = prepare_messages(messages) + response: "ChatResponse | None" = None + fcc_messages: "list[ChatMessage]" = [] + for attempt_idx in range(max_iterations): + response = await func(self, messages=prepped_messages, **kwargs) + # if there are function calls, we will handle them first + function_results = { + it.call_id for it in response.messages[0].contents if isinstance(it, FunctionResultContent) + } + function_calls = [ + it + for it in response.messages[0].contents + if isinstance(it, FunctionCallContent) and it.call_id not in function_results + ] + + if response.conversation_id is not None: + update_conversation_id(kwargs, response.conversation_id) + prepped_messages = [] + + tools = kwargs.get("tools") + if not tools and (chat_options := kwargs.get("chat_options")) and isinstance(chat_options, ChatOptions): + tools = chat_options.tools + if function_calls and tools: + function_results = await execute_function_calls( + custom_args=kwargs, + attempt_idx=attempt_idx, + function_calls=function_calls, + tools=tools, # type: ignore + ) + # add a single ChatMessage to the response with the results + result_message = ChatMessage(role="tool", contents=function_results) # type: ignore[call-overload] + response.messages.append(result_message) + # response should contain 2 messages after this, + # one with function call contents + # and one with function result contents + # the amount and call_id's should match + # this runs in every but the first run + # we need to keep track of all function call messages + fcc_messages.extend(response.messages) + # and add them as additional context to the messages + if kwargs.get("store"): + prepped_messages.clear() + prepped_messages.append(result_message) + else: + prepped_messages.extend(response.messages) + continue + # If we reach this point, it means there were no function calls to handle, + # we'll add the previous function call and responses + # to the front of the list, so that the final response is the last one + # TODO (eavanvalkenburg): control this behavior? + if fcc_messages: + for msg in reversed(fcc_messages): + response.messages.insert(0, msg) + return response + + # Failsafe: give up on tools, ask model for plain answer + kwargs["tool_choice"] = "none" + response = await func(self, messages=prepped_messages, **kwargs) + if fcc_messages: + for msg in reversed(fcc_messages): + response.messages.insert(0, msg) + return response + + return function_invocation_wrapper # type: ignore + + return decorator(func) + + +def _handle_function_calls_streaming_response( + func: Callable[..., AsyncIterable["ChatResponseUpdate"]], + *, + max_iterations: int = 10, +) -> Callable[..., AsyncIterable["ChatResponseUpdate"]]: + """Decorate the get_streaming_response method to handle function calls. + + Args: + func: The get_streaming_response method to decorate. + max_iterations: The maximum number of function call iterations to perform. + + """ + + def decorator( + func: Callable[..., AsyncIterable["ChatResponseUpdate"]], + ) -> Callable[..., AsyncIterable["ChatResponseUpdate"]]: + """Inner decorator.""" + + @wraps(func) + async def streaming_function_invocation_wrapper( + self: "ChatClientProtocol", + messages: "str | ChatMessage | list[str] | list[ChatMessage]", + **kwargs: Any, + ) -> AsyncIterable["ChatResponseUpdate"]: + """Wrap the inner get streaming response method to handle tool calls.""" + from ._clients import prepare_messages + from ._types import ChatMessage, ChatOptions, ChatResponse, ChatResponseUpdate, FunctionCallContent + + prepped_messages = prepare_messages(messages) + for attempt_idx in range(max_iterations): + all_updates: list["ChatResponseUpdate"] = [] + async for update in func(self, messages=prepped_messages, **kwargs): + all_updates.append(update) + yield update + + # efficient check for FunctionCallContent in the updates + # if there is at least one, this stops and continuous + # if there are no FCC's then it returns + if not any(isinstance(item, FunctionCallContent) for upd in all_updates for item in upd.contents): + return + + # Now combining the updates to create the full response. + # Depending on the prompt, the message may contain both function call + # content and others + + response: "ChatResponse" = ChatResponse.from_chat_response_updates(all_updates) + # add the response message to the previous messages + prepped_messages.append(response.messages[0]) + # get the fccs + function_calls = [ + item for item in response.messages[0].contents if isinstance(item, FunctionCallContent) + ] + + # When conversation id is present, it means that messages are hosted on the server. + # In this case, we need to update kwargs with conversation id and also clear messages + if response.conversation_id is not None: + update_conversation_id(kwargs, response.conversation_id) + prepped_messages = [] + + tools = kwargs.get("tools") + if not tools and (chat_options := kwargs.get("chat_options")) and isinstance(chat_options, ChatOptions): + tools = chat_options.tools + + if function_calls and tools: + function_results = await execute_function_calls( + custom_args=kwargs, + attempt_idx=attempt_idx, + function_calls=function_calls, + tools=tools, # type: ignore[reportArgumentType] + ) + function_result_msg = ChatMessage(role="tool", contents=function_results) + yield ChatResponseUpdate(contents=function_results, role="tool") + response.messages.append(function_result_msg) + prepped_messages.append(function_result_msg) + continue + + # Failsafe: give up on tools, ask model for plain answer + kwargs["tool_choice"] = "none" + async for update in func(self, messages=prepped_messages, **kwargs): + yield update + + return streaming_function_invocation_wrapper + + return decorator(func) + + +def use_function_invocation( + chat_client: type[TChatClient], +) -> type[TChatClient]: + """Class decorator that enables tool calling for a chat client.""" + if getattr(chat_client, FUNCTION_INVOKING_CHAT_CLIENT_MARKER, False): + return chat_client + + max_iterations = DEFAULT_MAX_ITERATIONS + + try: + chat_client.get_response = _handle_function_calls_response( # type: ignore + func=chat_client.get_response, # type: ignore + max_iterations=max_iterations, + ) + except AttributeError as ex: + raise ChatClientInitializationError( + f"Chat client {chat_client.__name__} does not have a get_response method, cannot apply function invocation." + ) from ex + try: + chat_client.get_streaming_response = _handle_function_calls_streaming_response( # type: ignore + func=chat_client.get_streaming_response, + max_iterations=max_iterations, + ) + except AttributeError as ex: + raise ChatClientInitializationError( + f"Chat client {chat_client.__name__} does not have a get_streaming_response method, " + "cannot apply function invocation." + ) from ex + setattr(chat_client, FUNCTION_INVOKING_CHAT_CLIENT_MARKER, True) + return chat_client diff --git a/python/packages/main/agent_framework/_types.py b/python/packages/main/agent_framework/_types.py index 60108d982a..1a7e54efef 100644 --- a/python/packages/main/agent_framework/_types.py +++ b/python/packages/main/agent_framework/_types.py @@ -933,7 +933,7 @@ class FunctionCallContent(BaseContent): if not isinstance(other, FunctionCallContent): raise TypeError("Incompatible type") if other.call_id and self.call_id != other.call_id: - raise AdditionItemMismatch + raise AdditionItemMismatch("", log_level=None) if not self.arguments: arguments = other.arguments elif not other.arguments: diff --git a/python/packages/main/agent_framework/exceptions.py b/python/packages/main/agent_framework/exceptions.py index 5e6a28b1e2..242b83c995 100644 --- a/python/packages/main/agent_framework/exceptions.py +++ b/python/packages/main/agent_framework/exceptions.py @@ -1,7 +1,7 @@ # Copyright (c) Microsoft. All rights reserved. import logging -from typing import Any +from typing import Any, Literal logger = logging.getLogger("agent_framework") @@ -12,12 +12,20 @@ class AgentFrameworkException(Exception): Automatically logs the message as debug. """ - def __init__(self, message: str, inner_exception: Exception | None = None, *args: Any): + def __init__( + self, + message: str, + inner_exception: Exception | None = None, + log_level: Literal[0] | Literal[10] | Literal[20] | Literal[30] | Literal[40] | Literal[50] | None = 10, + *args: Any, + **kwargs: Any, + ): """Create an AgentFrameworkException. - This emits a debug log, with the inner_exception if provided. + This emits a debug log (by default), with the inner_exception if provided. """ - logger.debug(message, exc_info=inner_exception) + if log_level is not None: + logger.log(log_level, message, exc_info=inner_exception) if inner_exception: super().__init__(message, inner_exception, *args) # type: ignore super().__init__(message, *args) # type: ignore @@ -35,6 +43,24 @@ class AgentExecutionException(AgentException): pass +class AgentInitializationError(AgentException): + """An error occurred while initializing the agent.""" + + pass + + +class ChatClientException(AgentFrameworkException): + """An error occurred while dealing with a chat client.""" + + pass + + +class ChatClientInitializationError(ChatClientException): + """An error occurred while initializing the chat client.""" + + pass + + # region Service Exceptions @@ -101,9 +127,4 @@ class ToolExecutionException(ToolException): class AdditionItemMismatch(AgentFrameworkException): """An error occurred while adding two types.""" - def __init__(self) -> None: - """Create an AdditionItemMismatch. - - Unlike the AgentFrameworkException, this does not log the message automatically, - """ - pass + pass diff --git a/python/packages/main/agent_framework/openai/_assistants_client.py b/python/packages/main/agent_framework/openai/_assistants_client.py index da7a209112..ea81734481 100644 --- a/python/packages/main/agent_framework/openai/_assistants_client.py +++ b/python/packages/main/agent_framework/openai/_assistants_client.py @@ -20,8 +20,8 @@ from openai.types.beta.threads.run_submit_tool_outputs_params import ToolOutput from openai.types.beta.threads.runs import RunStep from pydantic import Field, PrivateAttr, SecretStr, ValidationError -from .._clients import BaseChatClient, use_tool_calling -from .._tools import AIFunction, HostedCodeInterpreterTool, HostedFileSearchTool +from .._clients import BaseChatClient +from .._tools import AIFunction, HostedCodeInterpreterTool, HostedFileSearchTool, use_function_invocation from .._types import ( ChatMessage, ChatOptions, @@ -50,8 +50,8 @@ else: __all__ = ["OpenAIAssistantsClient"] +@use_function_invocation @use_telemetry -@use_tool_calling class OpenAIAssistantsClient(OpenAIConfigMixin, BaseChatClient): """OpenAI Assistants client.""" @@ -166,7 +166,9 @@ class OpenAIAssistantsClient(OpenAIConfigMixin, BaseChatClient): # Get the thread ID thread_id: str | None = ( - chat_options.conversation_id if chat_options.conversation_id is not None else self.thread_id + chat_options.conversation_id + if chat_options.conversation_id is not None + else run_options.get("conversation_id", self.thread_id) ) if thread_id is None and tool_results is not None: diff --git a/python/packages/main/agent_framework/openai/_chat_client.py b/python/packages/main/agent_framework/openai/_chat_client.py index bd41ac48d3..aa98257804 100644 --- a/python/packages/main/agent_framework/openai/_chat_client.py +++ b/python/packages/main/agent_framework/openai/_chat_client.py @@ -1,6 +1,7 @@ # Copyright (c) Microsoft. All rights reserved. import json +import sys from collections.abc import AsyncIterable, Mapping, MutableMapping, MutableSequence, Sequence from datetime import datetime from itertools import chain @@ -15,9 +16,9 @@ from openai.types.chat.chat_completion_chunk import Choice as ChunkChoice from openai.types.chat.chat_completion_message_custom_tool_call import ChatCompletionMessageCustomToolCall from pydantic import BaseModel, SecretStr, ValidationError -from .._clients import BaseChatClient, use_tool_calling +from .._clients import BaseChatClient from .._logging import get_logger -from .._tools import AIFunction, HostedWebSearchTool, ToolProtocol +from .._tools import AIFunction, HostedWebSearchTool, ToolProtocol, use_function_invocation from .._types import ( ChatMessage, ChatOptions, @@ -41,14 +42,17 @@ from ..telemetry import use_telemetry from ._exceptions import OpenAIContentFilterException from ._shared import OpenAIBase, OpenAIConfigMixin, OpenAISettings, prepare_function_call_results +if sys.version_info >= (3, 12): + from typing import override # type: ignore # pragma: no cover +else: + from typing_extensions import override # type: ignore[import] # pragma: no cover + __all__ = ["OpenAIChatClient"] logger = get_logger("agent_framework.openai") # region Base Client -@use_telemetry -@use_tool_calling class OpenAIBaseChatClient(OpenAIBase, BaseChatClient): """OpenAI Chat completion class.""" @@ -233,11 +237,26 @@ class OpenAIBaseChatClient(OpenAIBase, BaseChatClient): ) def _usage_details_from_openai(self, usage: CompletionUsage) -> UsageDetails: - return UsageDetails( - prompt_tokens=usage.prompt_tokens, - completion_tokens=usage.completion_tokens, - total_tokens=usage.total_tokens, + details = UsageDetails( + input_token_count=usage.prompt_tokens, + output_token_count=usage.completion_tokens, + total_token_count=usage.total_tokens, ) + if usage.completion_tokens_details: + if tokens := usage.completion_tokens_details.accepted_prediction_tokens: + details["completion/accepted_prediction_tokens"] = tokens + if tokens := usage.completion_tokens_details.audio_tokens: + details["completion/audio_tokens"] = tokens + if tokens := usage.completion_tokens_details.reasoning_tokens: + details["completion/reasoning_tokens"] = tokens + if tokens := usage.completion_tokens_details.rejected_prediction_tokens: + details["completion/rejected_prediction_tokens"] = tokens + if usage.prompt_tokens_details: + if tokens := usage.prompt_tokens_details.audio_tokens: + details["prompt/audio_tokens"] = tokens + if tokens := usage.prompt_tokens_details.cached_tokens: + details["prompt/cached_tokens"] = tokens + return details def _parse_text_from_choice(self, choice: Choice | ChunkChoice) -> TextContent | None: """Parse the choice into a TextContent object.""" @@ -362,13 +381,14 @@ class OpenAIBaseChatClient(OpenAIBase, BaseChatClient): case _: return content.model_dump(exclude_none=True) - def service_url(self) -> str | None: + @override + def service_url(self) -> str: """Get the URL of the service. Override this in the subclass to return the proper URL. If the service does not have a URL, return None. """ - return str(self.client.base_url) if self.client else None + return str(self.client.base_url) if self.client else "Unknown" # region Public client @@ -376,6 +396,8 @@ class OpenAIBaseChatClient(OpenAIBase, BaseChatClient): TOpenAIChatClient = TypeVar("TOpenAIChatClient", bound="OpenAIChatClient") +@use_function_invocation +@use_telemetry class OpenAIChatClient(OpenAIConfigMixin, OpenAIBaseChatClient): """OpenAI Chat completion class.""" diff --git a/python/packages/main/agent_framework/openai/_responses_client.py b/python/packages/main/agent_framework/openai/_responses_client.py index f684dcc31e..61c40eaf79 100644 --- a/python/packages/main/agent_framework/openai/_responses_client.py +++ b/python/packages/main/agent_framework/openai/_responses_client.py @@ -25,7 +25,7 @@ from openai.types.responses.web_search_tool_param import UserLocation as WebSear from openai.types.responses.web_search_tool_param import WebSearchToolParam from pydantic import BaseModel, SecretStr, ValidationError -from .._clients import BaseChatClient, use_tool_calling +from .._clients import BaseChatClient from .._logging import get_logger from .._tools import ( AIFunction, @@ -34,6 +34,7 @@ from .._tools import ( HostedMCPTool, HostedWebSearchTool, ToolProtocol, + use_function_invocation, ) from .._types import ( ChatMessage, @@ -406,7 +407,7 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient): tool_args["file_ids"] = [] for tool_input in tool.inputs: if isinstance(tool_input, HostedFileContent): - tool_args["file_ids"].append(tool_input.file_id) + tool_args["file_ids"].append(tool_input.file_id) # type: ignore[attr-defined] if not tool_args["file_ids"]: tool_args.pop("file_ids") response_tools.append( @@ -1040,8 +1041,8 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient): TOpenAIResponsesClient = TypeVar("TOpenAIResponsesClient", bound="OpenAIResponsesClient") +@use_function_invocation @use_telemetry -@use_tool_calling class OpenAIResponsesClient(OpenAIConfigMixin, OpenAIBaseResponsesClient): """OpenAI Responses client class.""" diff --git a/python/packages/main/agent_framework/openai/_shared.py b/python/packages/main/agent_framework/openai/_shared.py index 5a7b5c5a75..de8ada2ca4 100644 --- a/python/packages/main/agent_framework/openai/_shared.py +++ b/python/packages/main/agent_framework/openai/_shared.py @@ -60,7 +60,7 @@ def prepare_function_call_results(content: Contents | Any | list[Contents | Any] results.extend(res) else: results.append(res) - return results[0] if len(results) == 1 else results + return results[0] if len(results) == 1 else json.dumps(results) if isinstance(content, BaseModel): return content.model_dump_json(exclude_none=True, exclude={"raw_representation", "additional_properties"}) # fallback @@ -127,7 +127,7 @@ class OpenAIBase(AFBaseModel): class OpenAIConfigMixin(OpenAIBase): """Internal class for configuring a connection to an OpenAI service.""" - MODEL_PROVIDER_NAME: ClassVar[str] = "openai" # type: ignore[reportIncompatibleVariableOverride, misc] + OTEL_PROVIDER_NAME: ClassVar[str] = "openai" # type: ignore[reportIncompatibleVariableOverride, misc] @validate_call(config=ConfigDict(arbitrary_types_allowed=True)) def __init__( diff --git a/python/packages/main/agent_framework/telemetry.py b/python/packages/main/agent_framework/telemetry.py index b52708fac3..604bed1388 100644 --- a/python/packages/main/agent_framework/telemetry.py +++ b/python/packages/main/agent_framework/telemetry.py @@ -1,157 +1,67 @@ # Copyright (c) Microsoft. All rights reserved. -import functools import json import logging import os -from collections.abc import AsyncIterable, Awaitable, Callable, MutableSequence +from collections.abc import AsyncIterable, Awaitable, Callable, Generator +from contextlib import contextmanager from enum import Enum +from functools import wraps +from time import perf_counter, time_ns from typing import TYPE_CHECKING, Any, ClassVar, Final, TypeVar -from opentelemetry import trace +from opentelemetry import metrics +from opentelemetry.semconv_ai import GenAISystem, Meters, SpanAttributes from opentelemetry.trace import Span, StatusCode, get_tracer, use_span +from opentelemetry.version import __version__ as otel_version +from pydantic import PrivateAttr from . import __version__ as version_info from ._logging import get_logger from ._pydantic import AFBaseSettings +from .exceptions import AgentInitializationError, ChatClientInitializationError if TYPE_CHECKING: # pragma: no cover + from opentelemetry.metrics import Histogram + from opentelemetry.sdk.resources import Resource from opentelemetry.util._decorator import _AgnosticContextManager # type: ignore[reportPrivateUsage] - from ._agents import AgentProtocol, ChatAgent - from ._clients import BaseChatClient + from ._agents import AgentProtocol + from ._clients import ChatClientProtocol from ._threads import AgentThread from ._tools import AIFunction from ._types import ( AgentRunResponse, AgentRunResponseUpdate, ChatMessage, - ChatOptions, ChatResponse, ChatResponseUpdate, + Contents, + FinishReason, ) -TBaseChatClient = TypeVar("TBaseChatClient", bound="BaseChatClient") -TChatClientAgent = TypeVar("TChatClientAgent", bound="ChatAgent") +TAgent = TypeVar("TAgent", bound="AgentProtocol") +TChatClient = TypeVar("TChatClient", bound="ChatClientProtocol") -tracer = get_tracer("agent_framework") logger = get_logger() __all__ = [ "AGENT_FRAMEWORK_USER_AGENT", "APP_INFO", + "OPEN_TELEMETRY_AGENT_MARKER", + "OPEN_TELEMETRY_CHAT_CLIENT_MARKER", + "OTEL_SETTINGS", "USER_AGENT_KEY", "prepend_agent_framework_to_user_agent", + "setup_telemetry", "use_agent_telemetry", "use_telemetry", ] - -# We're recording multiple events for the chat history, some of them are emitted within (hundreds of) -# nanoseconds of each other. The default timestamp resolution is not high enough to guarantee unique -# timestamps for each message. Also Azure Monitor truncates resolution to microseconds and some other -# backends truncate to milliseconds. -# -# But we need to give users a way to restore chat message order, so we're incrementing the timestamp -# by 1 microsecond for each message. -# -# This is a workaround, we'll find a generic and better solution - see -# https://github.com/open-telemetry/semantic-conventions/issues/1701 -class ChatMessageListTimestampFilter(logging.Filter): - """A filter to increment the timestamp of INFO logs by 1 microsecond.""" - - INDEX_KEY: ClassVar[str] = "CHAT_MESSAGE_INDEX" - - def filter(self, record: logging.LogRecord) -> bool: - """Increment the timestamp of INFO logs by 1 microsecond.""" - if hasattr(record, self.INDEX_KEY): - idx = getattr(record, self.INDEX_KEY) - record.created += idx * 1e-6 - return True - - -# Creates a tracer from the global tracer provider -logger.addFilter(ChatMessageListTimestampFilter()) - - -class GenAIAttributes(str, Enum): - """Enum to capture the attributes used in OpenTelemetry for Generative AI. - - Based on: https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/ - and https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-agent-spans/ - - Should always be used, with `.value` to get the string representation. - """ - - OPERATION = "gen_ai.operation.name" - SYSTEM = "gen_ai.system" - ERROR_TYPE = "error.type" - PORT = "server.port" - ADDRESS = "server.address" - SPAN_ID = "SpanId" - TRACE_ID = "TraceId" - # Request attributes - MODEL = "gen_ai.request.model" - SEED = "gen_ai.request.seed" - ENCODING_FORMATS = "gen_ai.request.encoding_formats" - FREQUENCY_PENALTY = "gen_ai.request.frequency_penalty" - MAX_TOKENS = "gen_ai.request.max_tokens" - PRESENCE_PENALTY = "gen_ai.request.presence_penalty" - STOP_SEQUENCES = "gen_ai.request.stop_sequences" - TEMPERATURE = "gen_ai.request.temperature" - TOP_K = "gen_ai.request.top_k" - TOP_P = "gen_ai.request.top_p" - CHOICE_COUNT = "gen_ai.request.choice.count" - # Response attributes - FINISH_REASONS = "gen_ai.response.finish_reasons" - RESPONSE_ID = "gen_ai.response.id" - RESPONSE_MODEL = "gen_ai.response.model" - # Usage attributes - INPUT_TOKENS = "gen_ai.usage.input_tokens" - OUTPUT_TOKENS = "gen_ai.usage.output_tokens" - # Tool attributes - TOOL_CALL_ID = "gen_ai.tool.call.id" - TOOL_DESCRIPTION = "gen_ai.tool.description" - TOOL_NAME = "gen_ai.tool.name" - AGENT_ID = "gen_ai.agent.id" - # Agent attributes - AGENT_NAME = "gen_ai.agent.name" - AGENT_DESCRIPTION = "gen_ai.agent.description" - CONVERSATION_ID = "gen_ai.conversation.id" - DATA_SOURCE_ID = "gen_ai.data_source.id" - OUTPUT_TYPE = "gen_ai.output.type" - - # Activity events - EVENT_NAME = "event.name" - SYSTEM_MESSAGE = "gen_ai.system.message" - USER_MESSAGE = "gen_ai.user.message" - ASSISTANT_MESSAGE = "gen_ai.assistant.message" - TOOL_MESSAGE = "gen_ai.tool.message" - CHOICE = "gen_ai.choice" - PROMPT = "gen_ai.prompt" - - # Operation names - CHAT_COMPLETION_OPERATION = "chat" - TOOL_EXECUTION_OPERATION = "execute_tool" - # Describes GenAI agent creation and is usually applicable when working with remote agent services. - AGENT_CREATE_OPERATION = "create_agent" - AGENT_INVOKE_OPERATION = "invoke_agent" - - # Agent Framework specific attributes - MEASUREMENT_FUNCTION_TAG_NAME = "agent_framework.function.name" - MEASUREMENT_FUNCTION_INVOCATION_DURATION = "agent_framework.function.invocation.duration" - AGENT_FRAMEWORK_GEN_AI_SYSTEM = "microsoft.agent_framework" - - -ROLE_EVENT_MAP = { - "system": GenAIAttributes.SYSTEM_MESSAGE.value, - "user": GenAIAttributes.USER_MESSAGE.value, - "assistant": GenAIAttributes.ASSISTANT_MESSAGE.value, - "tool": GenAIAttributes.TOOL_MESSAGE.value, -} -# Note that if this environment variable does not exist, telemetry is enabled. -TELEMETRY_DISABLED_ENV_VAR = "AZURE_TELEMETRY_DISABLED" -IS_TELEMETRY_ENABLED = os.environ.get(TELEMETRY_DISABLED_ENV_VAR, "false").lower() not in ["true", "1"] +# region User Agents +# Note that if this environment variable does not exist, user agent telemetry is enabled. +USER_AGENT_TELEMETRY_DISABLED_ENV_VAR = "AGENT_FRAMEWORK_USER_AGENT_DISABLED" +IS_TELEMETRY_ENABLED = os.environ.get(USER_AGENT_TELEMETRY_DISABLED_ENV_VAR, "false").lower() not in ["true", "1"] APP_INFO = ( { @@ -192,11 +102,260 @@ def prepend_agent_framework_to_user_agent(headers: dict[str, Any] | None = None) return headers +# region Otel + +tracer = get_tracer("agent_framework", otel_version) +meter = metrics.get_meter_provider().get_meter("agent_framework", otel_version) + +OTEL_METRICS: Final[str] = "__otel_metrics__" +OPEN_TELEMETRY_CHAT_CLIENT_MARKER: Final[str] = "__open_telemetry_chat_client__" +OPEN_TELEMETRY_AGENT_MARKER: Final[str] = "__open_telemetry_agent__" +TOKEN_USAGE_BUCKET_BOUNDARIES: Final[tuple[float, ...]] = ( + 1, + 4, + 16, + 64, + 256, + 1024, + 4096, + 16384, + 65536, + 262144, + 1048576, + 4194304, + 16777216, + 67108864, +) +OPERATION_DURATION_BUCKET_BOUNDARIES: Final[tuple[float, ...]] = ( + 0.01, + 0.02, + 0.04, + 0.08, + 0.16, + 0.32, + 0.64, + 1.28, + 2.56, + 5.12, + 10.24, + 20.48, + 40.96, + 81.92, +) + + +# We're recording multiple events for the chat history, some of them are emitted within (hundreds of) +# nanoseconds of each other. The default timestamp resolution is not high enough to guarantee unique +# timestamps for each message. Also Azure Monitor truncates resolution to microseconds and some other +# backends truncate to milliseconds. +# +# But we need to give users a way to restore chat message order, so we're incrementing the timestamp +# by 1 microsecond for each message. +# +# This is a workaround, we'll find a generic and better solution - see +# https://github.com/open-telemetry/semantic-conventions/issues/1701 +class ChatMessageListTimestampFilter(logging.Filter): + """A filter to increment the timestamp of INFO logs by 1 microsecond.""" + + INDEX_KEY: ClassVar[str] = "chat_message_index" + + def filter(self, record: logging.LogRecord) -> bool: + """Increment the timestamp of INFO logs by 1 microsecond.""" + if hasattr(record, self.INDEX_KEY): + idx = getattr(record, self.INDEX_KEY) + record.created += idx * 1e-6 + return True + + +logger.addFilter(ChatMessageListTimestampFilter()) + + +class OtelAttr(str, Enum): + """Enum to capture the attributes used in OpenTelemetry for Generative AI. + + Based on: https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/ + and https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-agent-spans/ + """ + + OPERATION = "gen_ai.operation.name" + PROVIDER_NAME = "gen_ai.provider.name" + ERROR_TYPE = "error.type" + PORT = "server.port" + ADDRESS = "server.address" + SPAN_ID = "SpanId" + TRACE_ID = "TraceId" + # Request attributes + SEED = "gen_ai.request.seed" + ENCODING_FORMATS = "gen_ai.request.encoding_formats" + FREQUENCY_PENALTY = "gen_ai.request.frequency_penalty" + PRESENCE_PENALTY = "gen_ai.request.presence_penalty" + STOP_SEQUENCES = "gen_ai.request.stop_sequences" + TOP_K = "gen_ai.request.top_k" + CHOICE_COUNT = "gen_ai.request.choice.count" + # Response attributes + FINISH_REASONS = "gen_ai.response.finish_reasons" + RESPONSE_ID = "gen_ai.response.id" + # Usage attributes + INPUT_TOKENS = "gen_ai.usage.input_tokens" + OUTPUT_TOKENS = "gen_ai.usage.output_tokens" + # Tool attributes + TOOL_CALL_ID = "gen_ai.tool.call.id" + TOOL_DESCRIPTION = "gen_ai.tool.description" + TOOL_NAME = "gen_ai.tool.name" + TOOL_TYPE = "gen_ai.tool.type" + # Agent attributes + AGENT_ID = "gen_ai.agent.id" + # Client attributes + # replaced TOKEN with T, because both ruff and bandit, + # complain about TOKEN being a potential secret + T_UNIT = "tokens" + T_TYPE = "gen_ai.token.type" + T_TYPE_INPUT = "input" + T_TYPE_OUTPUT = "output" + DURATION_UNIT = "s" + # Agent attributes + AGENT_NAME = "gen_ai.agent.name" + AGENT_DESCRIPTION = "gen_ai.agent.description" + CONVERSATION_ID = "gen_ai.conversation.id" + DATA_SOURCE_ID = "gen_ai.data_source.id" + OUTPUT_TYPE = "gen_ai.output.type" + INPUT_MESSAGES = "gen_ai.input.messages" + OUTPUT_MESSAGES = "gen_ai.output.messages" + SYSTEM_INSTRUCTIONS = "gen_ai.system_instructions" + + # Activity events + EVENT_NAME = "event.name" + SYSTEM_MESSAGE = "gen_ai.system.message" + USER_MESSAGE = "gen_ai.user.message" + ASSISTANT_MESSAGE = "gen_ai.assistant.message" + TOOL_MESSAGE = "gen_ai.tool.message" + CHOICE = "gen_ai.choice" + + # Operation names + CHAT_COMPLETION_OPERATION = "chat" + TOOL_EXECUTION_OPERATION = "execute_tool" + # Describes GenAI agent creation and is usually applicable when working with remote agent services. + AGENT_CREATE_OPERATION = "create_agent" + AGENT_INVOKE_OPERATION = "invoke_agent" + + # Agent Framework specific attributes + MEASUREMENT_FUNCTION_TAG_NAME = "agent_framework.function.name" + MEASUREMENT_FUNCTION_INVOCATION_DURATION = "agent_framework.function.invocation.duration" + AGENT_FRAMEWORK_GEN_AI_SYSTEM = "microsoft.agent_framework" + + def __repr__(self) -> str: + return self.value + + def __str__(self) -> str: + return self.value + + +ROLE_EVENT_MAP = { + "system": OtelAttr.SYSTEM_MESSAGE, + "user": OtelAttr.USER_MESSAGE, + "assistant": OtelAttr.ASSISTANT_MESSAGE, + "tool": OtelAttr.TOOL_MESSAGE, +} +FINISH_REASON_MAP = { + "stop": "stop", + "content_filter": "content_filter", + "tool_calls": "tool_call", + "length": "length", +} + + # region Telemetry utils -class ModelDiagnosticSettings(AFBaseSettings): - """Settings for model diagnostics. +def _get_exporters(endpoint: str | None = None, connection_string: str | None = None) -> dict[str, list[Any]]: + """Create the different exporters based on the connection string and endpoint.""" + from azure.monitor.opentelemetry.exporter import ( # pylint: disable=import-error,no-name-in-module + AzureMonitorLogExporter, + AzureMonitorMetricExporter, + AzureMonitorTraceExporter, + ) + from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter + from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter + from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter + + exporters: dict[str, Any] = {} + exporters.setdefault("log", []) + exporters.setdefault("trace", []) + exporters.setdefault("metric", []) + if endpoint: + exporters["log"].append(OTLPLogExporter(endpoint=endpoint)) + exporters["trace"].append(OTLPSpanExporter(endpoint=endpoint)) + exporters["metric"].append(OTLPMetricExporter(endpoint=endpoint)) + if connection_string: + exporters["log"].append(AzureMonitorLogExporter(connection_string=connection_string)) + exporters["trace"].append(AzureMonitorTraceExporter(connection_string=connection_string)) + exporters["metric"].append(AzureMonitorMetricExporter(connection_string=connection_string)) + return exporters + + +def _configure_tracing(exporters: dict[str, list[Any]], resource: "Resource") -> None: + from opentelemetry._events import set_event_logger_provider + from opentelemetry._logs import set_logger_provider + from opentelemetry.metrics import set_meter_provider + from opentelemetry.sdk._events import EventLoggerProvider + from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler + from opentelemetry.sdk._logs.export import BatchLogRecordProcessor + from opentelemetry.sdk.metrics import MeterProvider + from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader + from opentelemetry.sdk.metrics.view import DropAggregation, View + from opentelemetry.sdk.trace import TracerProvider + from opentelemetry.sdk.trace.export import BatchSpanProcessor + from opentelemetry.trace import set_tracer_provider + + # Tracing + tracer_provider = TracerProvider(resource=resource) + for exporter in exporters.get("trace", []): + tracer_provider.add_span_processor(BatchSpanProcessor(exporter)) + set_tracer_provider(tracer_provider) + + # Logging + logger_provider = LoggerProvider(resource=resource) + for exporter in exporters.get("log", []): + logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter)) + set_logger_provider(logger_provider) + logger = get_logger() + if not any(isinstance(handler, LoggingHandler) for handler in logger.handlers): + handler = LoggingHandler(logger_provider=logger_provider) + logger.addHandler(handler) + logger.setLevel(logging.NOTSET) + + # Events + event_logger_provider = EventLoggerProvider(logger_provider) + set_event_logger_provider(event_logger_provider) + + # metrics + + metric_readers = [ + PeriodicExportingMetricReader(exporter, export_interval_millis=5000) for exporter in exporters.get("metric", []) + ] + meter_provider = MeterProvider( + metric_readers=metric_readers, + resource=resource, + views=[ + # Dropping all instrument names except for those starting with "agent_framework" + View(instrument_name="*", aggregation=DropAggregation()), + View(instrument_name="agent_framework*"), + View(instrument_name="gen_ai*"), + ], + ) + # Sets the global default meter provider + set_meter_provider(meter_provider) + + +OTEL_ENABLED_ENV_VAR = "ENABLE_OTEL" +SENSITIVE_DATA_ENV_VAR = "ENABLE_SENSITIVE_DATA" +MONITOR_CONNECTION_STRING_ENV_VAR = "MONITOR_CONNECTION_STRING" +MONITOR_LIVE_METRICS_ENV_VAR = "MONITOR_LIVE_METRICS" +OTLP_ENDPOINT_ENV_VAR = "OTLP_ENDPOINT" + + +class OtelSettings(AFBaseSettings): + """Settings for Open Telemetry. The settings are first loaded from environment variables with the prefix 'AGENT_FRAMEWORK_GENAI_'. @@ -209,17 +368,27 @@ class ModelDiagnosticSettings(AFBaseSettings): Warning: Sensitive events should only be enabled on test and development environments. - Required settings for prefix 'AGENT_FRAMEWORK_GENAI_' are: - - enable_otel_diagnostics: bool - Enable OpenTelemetry diagnostics. Default is False. - (Env var AGENT_FRAMEWORK_GENAI_ENABLE_OTEL_DIAGNOSTICS) - - enable_otel_diagnostics_sensitive: bool - Enable OpenTelemetry sensitive events. Default is False. - (Env var AGENT_FRAMEWORK_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE) + Args: + enable_otel: Enable OpenTelemetry diagnostics. Default is False. + (Env var ENABLE_OTEL) + enable_sensitive_data: Enable OpenTelemetry sensitive events. Default is False. + (Env var ENABLE_SENSITIVE_DATA) + application_insights_connection_string: The Azure Monitor connection string. Default is None. + (Env var APPLICATION_INSIGHTS_CONNECTION_STRING) + application_insights_live_metrics: Enable Azure Monitor live metrics. Default is False. + (Env var APPLICATION_INSIGHTS_LIVE_METRICS) + otlp_endpoint: The OpenTelemetry Protocol (OTLP) endpoint. Default is None. + (Env var OTLP_ENDPOINT) """ - env_prefix: ClassVar[str] = "AGENT_FRAMEWORK_GENAI_" + env_prefix: ClassVar[str] = "" - enable_otel_diagnostics: bool = False - enable_otel_diagnostics_sensitive: bool = False + enable_otel: bool = False + enable_sensitive_data: bool = False + application_insights_connection_string: str | None = None + application_insights_live_metrics: bool = False + otlp_endpoint: str | None = None + _executed_setup: bool = PrivateAttr(default=False) @property def ENABLED(self) -> bool: @@ -227,292 +396,336 @@ class ModelDiagnosticSettings(AFBaseSettings): Model diagnostics are enabled if either diagnostic is enabled or diagnostic with sensitive events is enabled. """ - return self.enable_otel_diagnostics or self.enable_otel_diagnostics_sensitive + return self.enable_otel or self.enable_sensitive_data @property - def SENSITIVE_EVENTS_ENABLED(self) -> bool: + def SENSITIVE_DATA_ENABLED(self) -> bool: """Check if sensitive events are enabled. Sensitive events are enabled if the diagnostic with sensitive events is enabled. """ - return self.enable_otel_diagnostics_sensitive + return self.enable_sensitive_data + + @property + def is_setup(self) -> bool: + """Check if the setup has been executed.""" + return self._executed_setup + + def setup_telemetry(self) -> None: + """Setup telemetry based on the settings. + + If both connection_string and otlp_endpoint both will be used. + """ + if not self.ENABLED or self._executed_setup: + return + + if not self.application_insights_connection_string and not self.otlp_endpoint: + logger.warning("Telemetry is enabled but no connection string or OTLP endpoint is provided.") + return + if self.application_insights_connection_string and self.otlp_endpoint: + logger.warning("Both connection string and OTLP endpoint are provided. Azure Monitor will be used.") + + from opentelemetry.sdk.resources import Resource + from opentelemetry.semconv.attributes import service_attributes + + resource = Resource.create({service_attributes.SERVICE_NAME: "agent_framework"}) + global_logger = logging.getLogger() + global_logger.setLevel(logging.NOTSET) + if self.application_insights_connection_string: + from azure.monitor.opentelemetry import configure_azure_monitor + + configure_azure_monitor( + connection_string=self.application_insights_connection_string, + logger_name="agent_framework", + resource=resource, + enable_live_metrics=self.application_insights_live_metrics, + ) + if self.otlp_endpoint: + exporters = _get_exporters(endpoint=self.otlp_endpoint) + _configure_tracing(exporters, resource) + + self._executed_setup = True -MODEL_DIAGNOSTICS_SETTINGS = ModelDiagnosticSettings() +global OTEL_SETTINGS +OTEL_SETTINGS: OtelSettings = OtelSettings() -def start_as_current_span( - tracer: trace.Tracer, - function: "AIFunction[Any, Any]", - metadata: dict[str, Any] | None = None, -) -> "_AgnosticContextManager[Span]": - """Starts a span for the given function using the provided tracer. +def setup_telemetry( + enable_otel: bool | None = None, + enable_sensitive_data: bool | None = None, + otlp_endpoint: str | None = None, + application_insights_connection_string: str | None = None, + enable_live_metrics: bool | None = None, +) -> None: + """Setup telemetry with optionally provided settings. + + All of these values can be set through environment variables or you can pass them here, + in the case where both are present, the provided value takes precedence. + + If you have both connection_string and otlp_endpoint, the connection_string will be used. Args: - tracer: The OpenTelemetry tracer to use. - function: The function for which to start the span. - metadata: Optional metadata to include in the span attributes. + enable_otel: Enable OpenTelemetry diagnostics. Default is False. + enable_sensitive_data: Enable OpenTelemetry sensitive events. Default is False. + otlp_endpoint: The OpenTelemetry Protocol (OTLP) endpoint. Default is None. + application_insights_connection_string: The Azure Monitor connection string. Default is None. + enable_live_metrics: Enable Azure Monitor live metrics. Default is False. - Returns: - trace.Span: The started span as a context manager. """ - attributes = { - GenAIAttributes.OPERATION.value: GenAIAttributes.TOOL_EXECUTION_OPERATION.value, - GenAIAttributes.TOOL_NAME.value: function.name, - } + global OTEL_SETTINGS + if enable_otel is not None: + OTEL_SETTINGS.enable_otel = enable_otel + if enable_sensitive_data is not None: + OTEL_SETTINGS.enable_sensitive_data = enable_sensitive_data + if otlp_endpoint is not None: + OTEL_SETTINGS.otlp_endpoint = otlp_endpoint + if application_insights_connection_string is not None: + OTEL_SETTINGS.application_insights_connection_string = application_insights_connection_string + if enable_live_metrics is not None: + OTEL_SETTINGS.application_insights_live_metrics = enable_live_metrics + OTEL_SETTINGS.setup_telemetry() - tool_call_id = metadata.get("tool_call_id", None) if metadata else None - if tool_call_id: - attributes[GenAIAttributes.TOOL_CALL_ID.value] = tool_call_id - if function.description: - attributes[GenAIAttributes.TOOL_DESCRIPTION.value] = function.description - return tracer.start_as_current_span( - f"{GenAIAttributes.TOOL_EXECUTION_OPERATION.value} {function.name}", attributes=attributes +# region Chat Client Telemetry + + +def _get_duration_histogram() -> "Histogram": + return meter.create_histogram( + name=Meters.LLM_OPERATION_DURATION, + unit=OtelAttr.DURATION_UNIT, + description="Captures the duration of operations of function-invoking chat clients", + explicit_bucket_boundaries_advisory=OPERATION_DURATION_BUCKET_BOUNDARIES, ) -def _set_error(span: Span, error: Exception) -> None: - """Set an error for spans.""" - span.set_attribute(GenAIAttributes.ERROR_TYPE.value, str(type(error))) - span.set_status(StatusCode.ERROR, repr(error)) +def _get_token_usage_histogram() -> "Histogram": + return meter.create_histogram( + name=Meters.LLM_TOKEN_USAGE, + unit=OtelAttr.T_UNIT, + description="Captures the token usage of chat clients", + explicit_bucket_boundaries_advisory=TOKEN_USAGE_BUCKET_BOUNDARIES, + ) # region ChatClientProtocol -def _trace_chat_get_response( - completion_func: Callable[..., Awaitable["ChatResponse"]], +def _trace_get_response( + func: Callable[..., Awaitable["ChatResponse"]], + *, + provider_name: str = "unknown", ) -> Callable[..., Awaitable["ChatResponse"]]: """Decorator to trace chat completion activities. Args: - completion_func: The function to trace. + func: The function to trace. + provider_name: The model provider name. """ - @functools.wraps(completion_func) - async def wrap_inner_get_response( - self: "BaseChatClient", - *, - messages: MutableSequence["ChatMessage"], - chat_options: "ChatOptions", - **kwargs: Any, - ) -> "ChatResponse": - if not MODEL_DIAGNOSTICS_SETTINGS.ENABLED: - # If model diagnostics are not enabled, just return the completion - return await completion_func( - self, - messages=messages, - chat_options=chat_options, + def decorator(func: Callable[..., Awaitable["ChatResponse"]]) -> Callable[..., Awaitable["ChatResponse"]]: + """Inner decorator.""" + + @wraps(func) + async def trace_get_response( + self: "ChatClientProtocol", + messages: "str | ChatMessage | list[str] | list[ChatMessage]", + **kwargs: Any, + ) -> "ChatResponse": + global OTEL_SETTINGS + if not OTEL_SETTINGS.ENABLED: + # If model diagnostics are not enabled, just return the completion + return await func( + self, + messages=messages, + **kwargs, + ) + setup_telemetry() + if "token_usage_histogram" not in self.additional_properties: + self.additional_properties["token_usage_histogram"] = _get_token_usage_histogram() + if "operation_duration_histogram" not in self.additional_properties: + self.additional_properties["operation_duration_histogram"] = _get_duration_histogram() + model_id = str(kwargs.get("ai_model_id") or getattr(self, "ai_model_id", "unknown")) + service_url = str( + service_url_func() + if (service_url_func := getattr(self, "service_url", None)) and callable(service_url_func) + else "unknown" + ) + attributes = _get_span_attributes( + operation_name=OtelAttr.CHAT_COMPLETION_OPERATION, + provider_name=provider_name, + model_id=model_id, + service_url=service_url, **kwargs, ) + with _get_span(attributes=attributes, span_name_attribute=SpanAttributes.LLM_REQUEST_MODEL) as span: + if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and messages: + _capture_messages(span=span, provider_name=provider_name, messages=messages) + start_time_stamp = perf_counter() + end_time_stamp: float | None = None + try: + response = await func(self, messages=messages, **kwargs) + end_time_stamp = perf_counter() + except Exception as exception: + end_time_stamp = perf_counter() + _capture_exception(span=span, exception=exception, timestamp=time_ns()) + raise + else: + duration = (end_time_stamp or perf_counter()) - start_time_stamp + attributes = _get_response_attributes(attributes, response, duration=duration) + _capture_response( + span=span, + attributes=attributes, + token_usage_histogram=self.additional_properties["token_usage_histogram"], + operation_duration_histogram=self.additional_properties["operation_duration_histogram"], + ) + if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and response.messages: + _capture_messages( + span=span, + provider_name=provider_name, + messages=response.messages, + finish_reason=response.finish_reason, + output=True, + ) + return response - with use_span( - _get_chat_response_span( - GenAIAttributes.CHAT_COMPLETION_OPERATION.value, - getattr(self, "ai_model_id", chat_options.ai_model_id or "unknown"), - self.MODEL_PROVIDER_NAME, - self.service_url() if hasattr(self, "service_url") else None, - chat_options, - ), - end_on_exit=True, - ) as current_span: - _set_chat_response_input(self.MODEL_PROVIDER_NAME, messages) - try: - response = await completion_func(self, messages=messages, chat_options=chat_options, **kwargs) - _set_chat_response_output(current_span, response, self.MODEL_PROVIDER_NAME) - return response - except Exception as exception: - _set_error(current_span, exception) - raise + return trace_get_response - # Mark the wrapper decorator as a chat completion decorator - wrap_inner_get_response.__model_diagnostics_chat_client__ = True # type: ignore - - return wrap_inner_get_response + return decorator(func) -def _trace_chat_get_streaming_response( - completion_func: Callable[..., AsyncIterable["ChatResponseUpdate"]], +def _trace_get_streaming_response( + func: Callable[..., AsyncIterable["ChatResponseUpdate"]], + *, + provider_name: str = "unknown", ) -> Callable[..., AsyncIterable["ChatResponseUpdate"]]: """Decorator to trace streaming chat completion activities. Args: - completion_func: The function to trace. + func: The function to trace. + provider_name: The model provider name. """ - @functools.wraps(completion_func) - async def wrap_inner_get_streaming_response( - self: "BaseChatClient", *, messages: MutableSequence["ChatMessage"], chat_options: "ChatOptions", **kwargs: Any - ) -> AsyncIterable["ChatResponseUpdate"]: - if not MODEL_DIAGNOSTICS_SETTINGS.ENABLED: - # If model diagnostics are not enabled, just return the completion - async for streaming_chat_message_contents in completion_func( - self, messages=messages, chat_options=chat_options, **kwargs - ): - yield streaming_chat_message_contents - return + def decorator( + func: Callable[..., AsyncIterable["ChatResponseUpdate"]], + ) -> Callable[..., AsyncIterable["ChatResponseUpdate"]]: + """Inner decorator.""" - from ._types import ChatResponse + @wraps(func) + async def trace_get_streaming_response( + self: "ChatClientProtocol", messages: "str | ChatMessage | list[str] | list[ChatMessage]", **kwargs: Any + ) -> AsyncIterable["ChatResponseUpdate"]: + global OTEL_SETTINGS + if not OTEL_SETTINGS.ENABLED: + # If model diagnostics are not enabled, just return the completion + async for update in func(self, messages=messages, **kwargs): + yield update + return + setup_telemetry() + if "token_usage_histogram" not in self.additional_properties: + self.additional_properties["token_usage_histogram"] = _get_token_usage_histogram() + if "operation_duration_histogram" not in self.additional_properties: + self.additional_properties["operation_duration_histogram"] = _get_duration_histogram() - all_updates: list["ChatResponseUpdate"] = [] + model_id = kwargs.get("ai_model_id") or getattr(self, "ai_model_id", None) + service_url = str( + service_url_func() + if (service_url_func := getattr(self, "service_url", None)) and callable(service_url_func) + else "unknown" + ) + attributes = _get_span_attributes( + operation_name=OtelAttr.CHAT_COMPLETION_OPERATION, + provider_name=provider_name, + model_id=model_id, + service_url=service_url, + **kwargs, + ) + all_updates: list["ChatResponseUpdate"] = [] + with _get_span(attributes=attributes, span_name_attribute=SpanAttributes.LLM_REQUEST_MODEL) as span: + if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and messages: + _capture_messages( + span=span, + provider_name=provider_name, + messages=messages, + ) + start_time_stamp = perf_counter() + end_time_stamp: float | None = None + try: + async for update in func(self, messages=messages, **kwargs): + all_updates.append(update) + yield update + end_time_stamp = perf_counter() + except Exception as exception: + end_time_stamp = perf_counter() + _capture_exception(span=span, exception=exception, timestamp=time_ns()) + raise + else: + duration = (end_time_stamp or perf_counter()) - start_time_stamp + from ._types import ChatResponse - with use_span( - _get_chat_response_span( - GenAIAttributes.CHAT_COMPLETION_OPERATION.value, - getattr(self, "ai_model_id", chat_options.ai_model_id or "unknown"), - self.MODEL_PROVIDER_NAME, - self.service_url() if hasattr(self, "service_url") else None, - chat_options, - ), - end_on_exit=True, - ) as current_span: - _set_chat_response_input(self.MODEL_PROVIDER_NAME, messages) - try: - async for response in completion_func(self, messages=messages, chat_options=chat_options, **kwargs): - all_updates.append(response) - yield response + response = ChatResponse.from_chat_response_updates(all_updates) + attributes = _get_response_attributes(attributes, response, duration=duration) + _capture_response( + span=span, + attributes=attributes, + token_usage_histogram=self.additional_properties["token_usage_histogram"], + operation_duration_histogram=self.additional_properties["operation_duration_histogram"], + ) - all_messages_flattened = ChatResponse.from_chat_response_updates(all_updates) - _set_chat_response_output(current_span, all_messages_flattened, self.MODEL_PROVIDER_NAME) - except Exception as exception: - _set_error(current_span, exception) - raise + if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and response.messages: + _capture_messages( + span=span, + provider_name=provider_name, + messages=response.messages, + finish_reason=response.finish_reason, + output=True, + ) - # Mark the wrapper decorator as a streaming chat completion decorator - wrap_inner_get_streaming_response.__model_diagnostics_streaming_chat_completion__ = True # type: ignore - return wrap_inner_get_streaming_response + return trace_get_streaming_response + + return decorator(func) -def use_telemetry(cls: type[TBaseChatClient]) -> type[TBaseChatClient]: +def use_telemetry( + chat_client: type[TChatClient], +) -> type[TChatClient]: """Class decorator that enables telemetry for a chat client. - Remarks: - This only works on classes that derive from BaseChatClient - and the _inner_get_response - and _inner_get_streaming_response methods. - It also relies on the presence of the MODEL_PROVIDER_NAME class variable. - ``` + This needs to be applied on the class itself, not a instance of it. + + To set the proper provider name, the chat client class should have a class variable + OTEL_PROVIDER_NAME. """ - if inner_response := getattr(cls, "_inner_get_response", None): - cls._inner_get_response = _trace_chat_get_response(inner_response) # type: ignore - if inner_streaming_response := getattr(cls, "_inner_get_streaming_response", None): - cls._inner_get_streaming_response = _trace_chat_get_streaming_response(inner_streaming_response) # type: ignore - return cls + if getattr(chat_client, OPEN_TELEMETRY_CHAT_CLIENT_MARKER, False): + # Already decorated + return chat_client + provider_name = str(getattr(chat_client, "OTEL_PROVIDER_NAME", "unknown")) -def _get_chat_response_span( - operation_name: str, - model_name: str, - model_provider: str, - service_url: str | None, - chat_options: "ChatOptions", -) -> Span: - """Start a text or chat completion span for a given model. - - Note that `start_span` doesn't make the span the current span. - Use `use_span` to make it the current span as a context manager. - """ - span = tracer.start_span(f"{operation_name} {model_name}") - - # Set attributes on the span - span.set_attributes({ - GenAIAttributes.OPERATION.value: operation_name, - GenAIAttributes.SYSTEM.value: model_provider, - GenAIAttributes.MODEL.value: model_name, - GenAIAttributes.CHOICE_COUNT.value: 1, - }) - - if service_url: - span.set_attribute(GenAIAttributes.ADDRESS.value, service_url) - - if chat_options.seed is not None: - span.set_attribute(GenAIAttributes.SEED.value, chat_options.seed) - if chat_options.frequency_penalty is not None: - span.set_attribute(GenAIAttributes.FREQUENCY_PENALTY.value, chat_options.frequency_penalty) - if chat_options.max_tokens is not None: - span.set_attribute(GenAIAttributes.MAX_TOKENS.value, chat_options.max_tokens) - if chat_options.stop is not None: - span.set_attribute(GenAIAttributes.STOP_SEQUENCES.value, chat_options.stop) - if chat_options.temperature is not None: - span.set_attribute(GenAIAttributes.TEMPERATURE.value, chat_options.temperature) - if chat_options.top_p is not None: - span.set_attribute(GenAIAttributes.TOP_P.value, chat_options.top_p) - if chat_options.presence_penalty is not None: - span.set_attribute(GenAIAttributes.PRESENCE_PENALTY.value, chat_options.presence_penalty) - if "top_k" in chat_options.additional_properties: - span.set_attribute(GenAIAttributes.TOP_K.value, chat_options.additional_properties["top_k"]) - if "encoding_formats" in chat_options.additional_properties: - span.set_attribute( - GenAIAttributes.ENCODING_FORMATS.value, chat_options.additional_properties["encoding_formats"] + if provider_name not in GenAISystem.__members__: + # that list is not complete, so just logging, no consequences. + logger.debug( + f"The provider name '{provider_name}' is not recognized. " + f"Consider using one of the following: {', '.join(GenAISystem.__members__.keys())}" ) - return span + try: + chat_client.get_response = _trace_get_response(chat_client.get_response, provider_name=provider_name) # type: ignore + except AttributeError as exc: + raise ChatClientInitializationError( + f"The chat client {chat_client.__name__} does not have a get_response method.", exc + ) from exc + try: + chat_client.get_streaming_response = _trace_get_streaming_response( # type: ignore + chat_client.get_streaming_response, provider_name=provider_name + ) + except AttributeError as exc: + raise ChatClientInitializationError( + f"The chat client {chat_client.__name__} does not have a get_streaming_response method.", exc + ) from exc + setattr(chat_client, OPEN_TELEMETRY_CHAT_CLIENT_MARKER, True) -def _set_chat_response_input( - model_provider: str, - messages: MutableSequence["ChatMessage"], -) -> None: - """Set the input for a chat response. - - The logs will be associated to the current span. - """ - if MODEL_DIAGNOSTICS_SETTINGS.SENSITIVE_EVENTS_ENABLED: - for idx, message in enumerate(messages): - event_name = ROLE_EVENT_MAP.get(message.role.value) - logger.info( - message.model_dump_json(exclude_none=True), - extra={ - GenAIAttributes.EVENT_NAME.value: event_name, - GenAIAttributes.SYSTEM.value: model_provider, - ChatMessageListTimestampFilter.INDEX_KEY: idx, - }, - ) - - -def _set_chat_response_output( - current_span: Span, - response: "ChatResponse", - model_provider: str, -) -> None: - """Set the response for a given span.""" - first_completion = response.messages[0] - - # Set the response ID - response_id = ( - first_completion.additional_properties.get("id") if first_completion.additional_properties is not None else None - ) - if response_id: - current_span.set_attribute(GenAIAttributes.RESPONSE_ID.value, response_id) - - # Set the finish reason - finish_reason = response.finish_reason - if finish_reason: - current_span.set_attribute(GenAIAttributes.FINISH_REASONS.value, [finish_reason.value]) - - # Set usage attributes - - usage = response.usage_details - if usage: - if usage.input_token_count: - current_span.set_attribute(GenAIAttributes.INPUT_TOKENS.value, usage.input_token_count) - if usage.output_token_count: - current_span.set_attribute(GenAIAttributes.OUTPUT_TOKENS.value, usage.output_token_count) - - # Set the completion event - if MODEL_DIAGNOSTICS_SETTINGS.SENSITIVE_EVENTS_ENABLED: - for completion in response.messages: - full_response: dict[str, Any] = { - "message": completion.model_dump(exclude_none=True), - } - full_response["index"] = response.response_id - logger.info( - json.dumps(full_response), - extra={ - GenAIAttributes.EVENT_NAME.value: GenAIAttributes.CHOICE.value, - GenAIAttributes.SYSTEM.value: model_provider, - }, - ) + return chat_client # region Agent @@ -520,75 +733,91 @@ def _set_chat_response_output( def _trace_agent_run( run_func: Callable[..., Awaitable["AgentRunResponse"]], + provider_name: str, ) -> Callable[..., Awaitable["AgentRunResponse"]]: """Decorator to trace chat completion activities. Args: run_func: The function to trace. + provider_name: The system name used for Open Telemetry. """ - @functools.wraps(run_func) - async def wrap_run( - self: "ChatAgent", + @wraps(run_func) + async def trace_run( + self: "AgentProtocol", messages: "str | ChatMessage | list[str] | list[ChatMessage] | None" = None, *, thread: "AgentThread | None" = None, **kwargs: Any, ) -> "AgentRunResponse": - if not MODEL_DIAGNOSTICS_SETTINGS.ENABLED: - # If model diagnostics are not enabled, just return the completion - return await run_func( - self, - messages=messages, - thread=thread, - **kwargs, - ) + global OTEL_SETTINGS - with use_span( - _get_agent_run_span( - operation_name=GenAIAttributes.AGENT_INVOKE_OPERATION.value, - agent=self, - system=self.AGENT_SYSTEM_NAME, - thread=thread, - **kwargs, - ), - end_on_exit=True, - ) as current_span: - _set_agent_run_input(self.AGENT_SYSTEM_NAME, messages) + if not OTEL_SETTINGS.ENABLED: + # If model diagnostics are not enabled, just return the completion + return await run_func(self, messages=messages, thread=thread, **kwargs) + setup_telemetry() + attributes = _get_span_attributes( + operation_name=OtelAttr.AGENT_INVOKE_OPERATION, + provider_name=provider_name, + agent_id=self.id, + agent_name=self.display_name, + agent_description=self.description, + thread_id=thread.service_thread_id if thread else None, + **kwargs, + ) + with _get_span(attributes=attributes, span_name_attribute=OtelAttr.AGENT_NAME) as span: + if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and messages: + _capture_messages( + span=span, + provider_name=provider_name, + messages=messages, + system_instructions=getattr(self, "instructions", None), + ) try: response = await run_func(self, messages=messages, thread=thread, **kwargs) - _set_agent_run_output(current_span, response, self.AGENT_SYSTEM_NAME) - return response except Exception as exception: - _set_error(current_span, exception) + _capture_exception(span=span, exception=exception, timestamp=time_ns()) raise + else: + attributes = _get_response_attributes(attributes, response) + _capture_response(span=span, attributes=attributes) + if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and response.messages: + _capture_messages( + span=span, + provider_name=provider_name, + messages=response.messages, + output=True, + ) + return response - # Mark the wrapper decorator as a agent run decorator - wrap_run.__model_diagnostics_agent_run__ = True # type: ignore - - return wrap_run + return trace_run def _trace_agent_run_stream( - run_func: Callable[..., AsyncIterable["AgentRunResponseUpdate"]], + run_streaming_func: Callable[..., AsyncIterable["AgentRunResponseUpdate"]], + provider_name: str, ) -> Callable[..., AsyncIterable["AgentRunResponseUpdate"]]: """Decorator to trace streaming agent run activities. Args: - run_func: The function to trace. + agent: The agent that is wrapped. + run_streaming_func: The function to trace. + provider_name: The system name used for Open Telemetry. """ - @functools.wraps(run_func) - async def wrap_run_stream( - self: "ChatAgent", + @wraps(run_streaming_func) + async def trace_run_streaming( + self: "AgentProtocol", messages: "str | ChatMessage | list[str] | list[ChatMessage] | None" = None, *, thread: "AgentThread | None" = None, **kwargs: Any, ) -> AsyncIterable["AgentRunResponseUpdate"]: - if not MODEL_DIAGNOSTICS_SETTINGS.ENABLED: + global OTEL_SETTINGS + + if not OTEL_SETTINGS.ENABLED: # If model diagnostics are not enabled, just return the completion - async for streaming_agent_response in run_func(self, messages=messages, thread=thread, **kwargs): + async for streaming_agent_response in run_streaming_func(self, messages=messages, thread=thread, **kwargs): yield streaming_agent_response return @@ -596,167 +825,280 @@ def _trace_agent_run_stream( all_updates: list["AgentRunResponseUpdate"] = [] - with use_span( - _get_agent_run_span( - operation_name=GenAIAttributes.AGENT_INVOKE_OPERATION.value, - agent=self, - system=self.AGENT_SYSTEM_NAME, - thread=thread, - **kwargs, - ), - end_on_exit=True, - ) as current_span: - _set_agent_run_input(self.AGENT_SYSTEM_NAME, messages) + setup_telemetry() + attributes = _get_span_attributes( + operation_name=OtelAttr.AGENT_INVOKE_OPERATION, + provider_name=provider_name, + agent_id=self.id, + agent_name=self.display_name, + agent_description=self.description, + thread_id=thread.service_thread_id if thread else None, + **kwargs, + ) + with _get_span(attributes=attributes, span_name_attribute=OtelAttr.AGENT_NAME) as span: + if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and messages: + _capture_messages( + span=span, + provider_name=provider_name, + messages=messages, + system_instructions=getattr(self, "instructions", None), + ) try: - async for response in run_func(self, messages=messages, thread=thread, **kwargs): - all_updates.append(response) - yield response - - all_messages_flattened = AgentRunResponse.from_agent_run_response_updates(all_updates) - _set_agent_run_output(current_span, all_messages_flattened, self.AGENT_SYSTEM_NAME) + async for update in run_streaming_func(self, messages=messages, thread=thread, **kwargs): + all_updates.append(update) + yield update except Exception as exception: - _set_error(current_span, exception) + _capture_exception(span=span, exception=exception, timestamp=time_ns()) raise - - # Mark the wrapper decorator as a streaming agent run decorator - wrap_run_stream.__model_diagnostics_streaming_agent_run__ = True # type: ignore - return wrap_run_stream - - -def use_agent_telemetry(cls: type[TChatClientAgent]) -> type[TChatClientAgent]: - """Class decorator that enables telemetry for an agent.""" - if run := getattr(cls, "run", None): - cls.run = _trace_agent_run(run) # type: ignore - if run_stream := getattr(cls, "run_stream", None): - cls.run_stream = _trace_agent_run_stream(run_stream) # type: ignore - return cls - - -def _get_agent_run_span( - *, - operation_name: str, - agent: "AgentProtocol", - system: str, - thread: "AgentThread | None", - **kwargs: Any, -) -> Span: - """Start a text or chat completion span for a given model. - - Note that `start_span` doesn't make the span the current span. - Use `use_span` to make it the current span as a context manager. - - Should follow: https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-agent-spans/#invoke-agent-span - """ - span = tracer.start_span(f"{operation_name} {agent.display_name}") - - # Set attributes on the span - span.set_attributes({ - GenAIAttributes.OPERATION.value: operation_name, - GenAIAttributes.SYSTEM.value: system, - GenAIAttributes.CHOICE_COUNT.value: 1, - GenAIAttributes.AGENT_ID.value: agent.id, - }) - if agent.name: - span.set_attribute(GenAIAttributes.AGENT_NAME.value, agent.name) - if agent.description: - span.set_attribute(GenAIAttributes.AGENT_DESCRIPTION.value, agent.description) - if thread and thread.service_thread_id: - span.set_attribute(GenAIAttributes.CONVERSATION_ID.value, thread.service_thread_id) - if "model" in kwargs: - span.set_attribute(GenAIAttributes.MODEL.value, kwargs["model"]) - if "seed" in kwargs: - span.set_attribute(GenAIAttributes.SEED.value, kwargs["seed"]) - if "frequency_penalty" in kwargs: - span.set_attribute(GenAIAttributes.FREQUENCY_PENALTY.value, kwargs["frequency_penalty"]) - if "presence_penalty" in kwargs: - span.set_attribute(GenAIAttributes.PRESENCE_PENALTY.value, kwargs["presence_penalty"]) - if "max_tokens" in kwargs: - span.set_attribute(GenAIAttributes.MAX_TOKENS.value, kwargs["max_tokens"]) - if "stop" in kwargs: - span.set_attribute(GenAIAttributes.STOP_SEQUENCES.value, kwargs["stop"]) - if "temperature" in kwargs: - span.set_attribute(GenAIAttributes.TEMPERATURE.value, kwargs["temperature"]) - if "top_p" in kwargs: - span.set_attribute(GenAIAttributes.TOP_P.value, kwargs["top_p"]) - if "top_k" in kwargs: - span.set_attribute(GenAIAttributes.TOP_K.value, kwargs["top_k"]) - if "encoding_formats" in kwargs: - span.set_attribute(GenAIAttributes.ENCODING_FORMATS.value, kwargs["encoding_formats"]) - return span - - -def _set_agent_run_input( - system: str, - messages: "str | ChatMessage | list[str] | list[ChatMessage] | list[str | ChatMessage] | None" = None, -) -> None: - """Set the input for a chat response. - - The logs will be associated to the current span. - """ - if messages and MODEL_DIAGNOSTICS_SETTINGS.SENSITIVE_EVENTS_ENABLED: - if not isinstance(messages, list): - messages = [messages] - for idx, message in enumerate(messages): - if isinstance(message, str): - logger.info( - message, - extra={ - # assume user message - GenAIAttributes.EVENT_NAME.value: GenAIAttributes.USER_MESSAGE.value, - GenAIAttributes.SYSTEM.value: system, - ChatMessageListTimestampFilter.INDEX_KEY: idx, - }, - ) else: - logger.info( - message.model_dump_json(exclude_none=True), - extra={ - GenAIAttributes.EVENT_NAME.value: ROLE_EVENT_MAP.get(message.role.value), - GenAIAttributes.SYSTEM.value: system, - ChatMessageListTimestampFilter.INDEX_KEY: idx, - }, - ) + response = AgentRunResponse.from_agent_run_response_updates(all_updates) + attributes = _get_response_attributes(attributes, response) + _capture_response(span=span, attributes=attributes) + if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and response.messages: + _capture_messages( + span=span, + provider_name=provider_name, + messages=response.messages, + output=True, + ) + + return trace_run_streaming -def _set_agent_run_output( - current_span: Span, - response: "AgentRunResponse", - model_provider: str, -) -> None: - """Set the agent response for a given span.""" - first_completion = response.messages[0] +def use_agent_telemetry( + agent: type[TAgent], +) -> type[TAgent]: + """Class decorator that enables telemetry for an agent.""" + provider_name = str(getattr(agent, "AGENT_SYSTEM_NAME", "Unknown")) + try: + agent.run = _trace_agent_run(agent.run, provider_name) # type: ignore + except AttributeError as exc: + raise AgentInitializationError(f"The agent {agent.__name__} does not have a run method.", exc) from exc + try: + agent.run_stream = _trace_agent_run_stream(agent.run_stream, provider_name) # type: ignore + except AttributeError as exc: + raise AgentInitializationError(f"The agent {agent.__name__} does not have a run_stream method.", exc) from exc + setattr(agent, OPEN_TELEMETRY_AGENT_MARKER, True) + return agent - # Set the response ID - response_id = ( - first_completion.additional_properties.get("id") if first_completion.additional_properties is not None else None + +# region Otel Helpers + + +def get_function_span( + function: "AIFunction[Any, Any]", + tool_call_id: str | None = None, +) -> "_AgnosticContextManager[Span]": + """Starts a span for the given function. + + Args: + function: The function for which to start the span. + tool_call_id: The id of the tool_call that was requested. + + Returns: + trace.Span: The started span as a context manager. + """ + attributes: dict[str, str] = { + OtelAttr.OPERATION: OtelAttr.TOOL_EXECUTION_OPERATION, + OtelAttr.TOOL_NAME: function.name, + OtelAttr.TOOL_CALL_ID: tool_call_id or "unknown", + OtelAttr.TOOL_TYPE: "function", + } + if function.description: + attributes[OtelAttr.TOOL_DESCRIPTION] = function.description + + return tracer.start_as_current_span( + name=f"{OtelAttr.TOOL_EXECUTION_OPERATION} {function.name}", + attributes=attributes, + set_status_on_exception=False, + end_on_exit=True, + record_exception=False, ) - if response_id: - current_span.set_attribute(GenAIAttributes.RESPONSE_ID.value, response_id) - # Set the finish reason - finish_reason = getattr(response.raw_representation, "finish_reason", None) if response.raw_representation else None + +@contextmanager +def _get_span( + attributes: dict[str, Any], + span_name_attribute: str, +) -> Generator[Span, Any, Any]: + """Start a span for a agent run.""" + span = tracer.start_span(f"{attributes[OtelAttr.OPERATION]} {attributes[span_name_attribute]}") + span.set_attributes(attributes) + with use_span( + span=span, + end_on_exit=True, + record_exception=False, + set_status_on_exception=False, + ) as current_span: + yield current_span + + +def _get_span_attributes(**kwargs: Any) -> dict[str, Any]: + """Get the span attributes from a kwargs dictionary.""" + attributes: dict[str, Any] = {} + if operation_name := kwargs.get("operation_name"): + attributes[OtelAttr.OPERATION] = operation_name + if choice_count := kwargs.get("choice_count", 1): + attributes[OtelAttr.CHOICE_COUNT] = choice_count + if operation_name := kwargs.get("operation_name"): + attributes[OtelAttr.OPERATION] = operation_name + if system_name := kwargs.get("system_name"): + attributes[SpanAttributes.LLM_SYSTEM] = system_name + if provider_name := kwargs.get("provider_name"): + attributes[OtelAttr.PROVIDER_NAME] = provider_name + attributes[SpanAttributes.LLM_REQUEST_MODEL] = kwargs.get("model_id", "unknown") + if service_url := kwargs.get("service_url"): + attributes[OtelAttr.ADDRESS] = service_url + if conversation_id := kwargs.get("conversation_id"): + attributes[OtelAttr.CONVERSATION_ID] = conversation_id + if seed := kwargs.get("seed"): + attributes[OtelAttr.SEED] = seed + if frequency_penalty := kwargs.get("frequency_penalty"): + attributes[OtelAttr.FREQUENCY_PENALTY] = frequency_penalty + if max_tokens := kwargs.get("max_tokens"): + attributes[SpanAttributes.LLM_REQUEST_MAX_TOKENS] = max_tokens + if stop := kwargs.get("stop"): + attributes[OtelAttr.STOP_SEQUENCES] = stop + if temperature := kwargs.get("temperature"): + attributes[SpanAttributes.LLM_REQUEST_TEMPERATURE] = temperature + if top_p := kwargs.get("top_p"): + attributes[SpanAttributes.LLM_REQUEST_TOP_P] = top_p + if presence_penalty := kwargs.get("presence_penalty"): + attributes[OtelAttr.PRESENCE_PENALTY] = presence_penalty + if top_k := kwargs.get("top_k"): + attributes[OtelAttr.TOP_K] = top_k + if encoding_formats := kwargs.get("encoding_formats"): + attributes[OtelAttr.ENCODING_FORMATS] = json.dumps( + encoding_formats if isinstance(encoding_formats, list) else [encoding_formats] + ) + if error := kwargs.get("error"): + attributes[OtelAttr.ERROR_TYPE] = type(error).__name__ + # agent attributes + if agent_id := kwargs.get("agent_id"): + attributes[OtelAttr.AGENT_ID] = agent_id + if agent_name := kwargs.get("agent_name"): + attributes[OtelAttr.AGENT_NAME] = agent_name + if agent_description := kwargs.get("agent_description"): + attributes[OtelAttr.AGENT_DESCRIPTION] = agent_description + if thread_id := kwargs.get("thread_id"): + # override if thread is set + attributes[OtelAttr.CONVERSATION_ID] = thread_id + return attributes + + +def _capture_exception(span: Span, exception: Exception, timestamp: int | None = None) -> None: + """Set an error for spans.""" + span.set_attribute(OtelAttr.ERROR_TYPE, type(exception).__name__) + span.record_exception(exception=exception, timestamp=timestamp) + span.set_status(status=StatusCode.ERROR, description=repr(exception)) + + +def _capture_messages( + span: Span, + provider_name: str, + messages: "str | ChatMessage | list[str] | list[ChatMessage]", + system_instructions: str | list[str] | None = None, + output: bool = False, + finish_reason: "FinishReason | None" = None, +) -> None: + """Log messages with extra information.""" + from ._clients import prepare_messages + + prepped = prepare_messages(messages) + for index, message in enumerate(prepped): + logger.info( + message.model_dump_json(exclude_none=True), + extra={ + OtelAttr.EVENT_NAME: OtelAttr.CHOICE if output else ROLE_EVENT_MAP.get(message.role.value), + OtelAttr.PROVIDER_NAME: provider_name, + ChatMessageListTimestampFilter.INDEX_KEY: index, + }, + ) + otel_messages = [_to_otel_message(message) for message in prepped] if finish_reason: - current_span.set_attribute(GenAIAttributes.FINISH_REASONS.value, [finish_reason.value]) + otel_messages[-1]["finish_reason"] = FINISH_REASON_MAP[finish_reason.value] + span.set_attribute(OtelAttr.OUTPUT_MESSAGES if output else OtelAttr.INPUT_MESSAGES, json.dumps(otel_messages)) + if system_instructions: + if not isinstance(system_instructions, list): + system_instructions = [system_instructions] + otel_sys_instructions = [{"type": "text", "content": instruction} for instruction in system_instructions] + span.set_attribute(OtelAttr.SYSTEM_INSTRUCTIONS, json.dumps(otel_sys_instructions)) - # Set usage attributes - usage = response.usage_details - if usage: + +def _to_otel_message(message: "ChatMessage") -> dict[str, Any]: + """Create a otel representation of a message.""" + return {"role": message.role.value, "parts": [_to_otel_part(content) for content in message.contents]} + + +def _to_otel_part(content: "Contents") -> dict[str, Any] | None: + """Create a otel representation of a Content.""" + match content.type: + case "text": + return {"type": "text", "content": content.text} + case "function_call": + return {"type": "tool_call", "id": content.call_id, "name": content.name, "arguments": content.arguments} + case "function_result": + return {"type": "tool_call_response", "id": content.call_id, "response": content.result} + case _: + # GenericPart in otel output messages json spec. + # just required type, and arbitrary other fields. + return content.model_dump(exclude_none=True) + return None + + +def _get_response_attributes( + attributes: dict[str, Any], + response: "ChatResponse | AgentRunResponse", + duration: float | None = None, +) -> dict[str, Any]: + """Get the response attributes from a response.""" + if response.response_id: + attributes[OtelAttr.RESPONSE_ID] = response.response_id + finish_reason = getattr(response, "finish_reason", None) + if not finish_reason: + finish_reason = ( + getattr(response.raw_representation, "finish_reason", None) if response.raw_representation else None + ) + if finish_reason: + attributes[OtelAttr.FINISH_REASONS] = json.dumps([finish_reason.value]) + if ai_model_id := getattr(response, "ai_model_id", None): + attributes[SpanAttributes.LLM_RESPONSE_MODEL] = ai_model_id + if usage := response.usage_details: if usage.input_token_count: - current_span.set_attribute(GenAIAttributes.INPUT_TOKENS.value, usage.input_token_count) + attributes[OtelAttr.INPUT_TOKENS] = usage.input_token_count if usage.output_token_count: - current_span.set_attribute(GenAIAttributes.OUTPUT_TOKENS.value, usage.output_token_count) + attributes[OtelAttr.OUTPUT_TOKENS] = usage.output_token_count + if duration: + attributes[Meters.LLM_OPERATION_DURATION] = duration + return attributes - # Set the completion event - if MODEL_DIAGNOSTICS_SETTINGS.SENSITIVE_EVENTS_ENABLED: - for msg in response.messages: - full_response: dict[str, Any] = { - "message": msg.model_dump(exclude_none=True), - } - full_response["index"] = response.response_id - logger.info( - json.dumps(full_response), - extra={ - GenAIAttributes.EVENT_NAME.value: GenAIAttributes.CHOICE.value, - GenAIAttributes.SYSTEM.value: model_provider, - }, - ) + +GEN_AI_METRIC_ATTRIBUTES = ( + OtelAttr.OPERATION, + OtelAttr.PROVIDER_NAME, + SpanAttributes.LLM_REQUEST_MODEL, + SpanAttributes.LLM_RESPONSE_MODEL, + OtelAttr.ADDRESS, + OtelAttr.PORT, +) + + +def _capture_response( + span: Span, + attributes: dict[str, Any], + operation_duration_histogram: "Histogram | None" = None, + token_usage_histogram: "Histogram | None" = None, +) -> None: + """Set the response for a given span.""" + span.set_attributes(attributes) + attrs: dict[str, Any] = {k: v for k, v in attributes.items() if k in GEN_AI_METRIC_ATTRIBUTES} + if token_usage_histogram and (input_tokens := attributes.get(OtelAttr.INPUT_TOKENS)): + token_usage_histogram.record( + input_tokens, attributes={**attrs, SpanAttributes.LLM_TOKEN_TYPE: OtelAttr.T_TYPE_INPUT} + ) + if token_usage_histogram and (output_tokens := attributes.get(OtelAttr.OUTPUT_TOKENS)): + token_usage_histogram.record(output_tokens, {**attrs, SpanAttributes.LLM_TOKEN_TYPE: OtelAttr.T_TYPE_OUTPUT}) + if operation_duration_histogram and (duration := attributes.get(Meters.LLM_OPERATION_DURATION)): + if OtelAttr.ERROR_TYPE in attributes: + attrs[OtelAttr.ERROR_TYPE] = attributes[OtelAttr.ERROR_TYPE] + operation_duration_histogram.record(duration, attributes=attrs) diff --git a/python/packages/main/pyproject.toml b/python/packages/main/pyproject.toml index b8fcf2f966..28746c286a 100644 --- a/python/packages/main/pyproject.toml +++ b/python/packages/main/pyproject.toml @@ -30,6 +30,9 @@ dependencies = [ "opentelemetry-api ~= 1.24", "opentelemetry-sdk ~= 1.24", "mcp>=1.12", + "azure-monitor-opentelemetry>=1.7.0", + "azure-monitor-opentelemetry-exporter>=1.0.0b41", + "opentelemetry-exporter-otlp-proto-grpc>=1.36.0", ] [project.optional-dependencies] diff --git a/python/packages/main/tests/main/conftest.py b/python/packages/main/tests/main/conftest.py index 7801989167..3391a3266e 100644 --- a/python/packages/main/tests/main/conftest.py +++ b/python/packages/main/tests/main/conftest.py @@ -1,11 +1,64 @@ # Copyright (c) Microsoft. All rights reserved. -from typing import Any -from pydantic import BaseModel +import asyncio +import logging +import sys +from collections.abc import AsyncIterable, MutableSequence +from typing import Any +from unittest.mock import patch +from uuid import uuid4 + +from pydantic import BaseModel, Field from pytest import fixture -from agent_framework import ChatMessage, ToolProtocol, ai_function -from agent_framework.telemetry import ModelDiagnosticSettings +from agent_framework import ( + AgentProtocol, + AgentRunResponse, + AgentRunResponseUpdate, + AgentThread, + BaseChatClient, + ChatMessage, + ChatOptions, + ChatResponse, + ChatResponseUpdate, + Role, + TextContent, + ToolProtocol, + ai_function, + use_function_invocation, +) +from agent_framework.telemetry import OtelSettings, setup_telemetry + +if sys.version_info >= (3, 12): + from typing import override # type: ignore +else: + from typing_extensions import override # type: ignore[import] +# region Chat History + +logger = logging.getLogger(__name__) + + +@fixture +def enable_otel(request: Any) -> bool: + """Fixture that returns a boolean indicating if Otel is enabled.""" + return request.param if hasattr(request, "param") else True + + +@fixture +def enable_sensitive_data(request: Any) -> bool: + """Fixture that returns a boolean indicating if sensitive data is enabled.""" + return request.param if hasattr(request, "param") else False + + +@fixture +def otel_settings(enable_otel: bool, enable_sensitive_data: bool) -> OtelSettings: + """Fixture to set environment variables for OtelSettings.""" + + from agent_framework.telemetry import OTEL_SETTINGS + + setup_telemetry(enable_otel=enable_otel, enable_sensitive_data=enable_sensitive_data) + + return OTEL_SETTINGS @fixture(scope="function") @@ -13,13 +66,16 @@ def chat_history() -> list[ChatMessage]: return [] +# region Tools + + @fixture def ai_tool() -> ToolProtocol: """Returns a generic ToolProtocol.""" class GenericTool(BaseModel): name: str - description: str | None = None + description: str additional_properties: dict[str, Any] | None = None def parameters(self) -> dict[str, Any]: @@ -43,17 +99,165 @@ def ai_function_tool() -> ToolProtocol: return simple_function +# region Chat Clients +class MockChatClient: + """Simple implementation of a chat client.""" + + def __init__(self) -> None: + self.additional_properties: dict[str, Any] = {} + + async def get_response( + self, + messages: str | ChatMessage | list[str] | list[ChatMessage], + **kwargs: Any, + ) -> ChatResponse: + logger.debug(f"Running custom chat client, with: {messages=}, {kwargs=}") + return ChatResponse(messages=ChatMessage(role="assistant", text="test response")) + + async def get_streaming_response( + self, + messages: str | ChatMessage | list[str] | list[ChatMessage], + **kwargs: Any, + ) -> AsyncIterable[ChatResponseUpdate]: + logger.debug(f"Running custom chat client stream, with: {messages=}, {kwargs=}") + yield ChatResponseUpdate(text=TextContent(text="test streaming response "), role="assistant") + yield ChatResponseUpdate(contents=[TextContent(text="another update")], role="assistant") + + +class MockBaseChatClient(BaseChatClient): + """Mock implementation of the BaseChatClient.""" + + run_responses: list[ChatResponse] = Field(default_factory=list) + streaming_responses: list[list[ChatResponseUpdate]] = Field(default_factory=list) + + @override + async def _inner_get_response( + self, + *, + messages: MutableSequence[ChatMessage], + chat_options: ChatOptions, + **kwargs: Any, + ) -> ChatResponse: + """Send a chat request to the AI service. + + Args: + messages: The chat messages to send. + chat_options: The options for the request. + kwargs: Any additional keyword arguments. + + Returns: + The chat response contents representing the response(s). + """ + logger.debug(f"Running base chat client inner, with: {messages=}, {chat_options=}, {kwargs=}") + if not self.run_responses: + return ChatResponse(messages=ChatMessage(role="assistant", text=f"test response - {messages[0].text}")) + if chat_options.tool_choice == "none": + return ChatResponse( + messages=ChatMessage(role="assistant", text="I broke out of the function invocation loop...") + ) + return self.run_responses.pop(0) + + @override + async def _inner_get_streaming_response( + self, + *, + messages: MutableSequence[ChatMessage], + chat_options: ChatOptions, + **kwargs: Any, + ) -> AsyncIterable[ChatResponseUpdate]: + logger.debug(f"Running base chat client inner stream, with: {messages=}, {chat_options=}, {kwargs=}") + if not self.streaming_responses: + yield ChatResponseUpdate(text=f"update - {messages[0].text}", role="assistant") + return + if chat_options.tool_choice == "none": + yield ChatResponseUpdate(text="I broke out of the function invocation loop...", role="assistant") + return + response = self.streaming_responses.pop(0) + for update in response: + yield update + await asyncio.sleep(0) + + @fixture -def model_diagnostic_settings(monkeypatch, request) -> ModelDiagnosticSettings: - """Fixture to set environment variables for ModelDiagnosticSettings.""" - enabled = getattr(request, "param", (None, None))[0] - sensitive = getattr(request, "param", (None, None))[1] - if enabled is None: - monkeypatch.delenv("AGENT_FRAMEWORK_GENAI_ENABLE_OTEL_DIAGNOSTICS", raising=False) - else: - monkeypatch.setenv("AGENT_FRAMEWORK_GENAI_ENABLE_OTEL_DIAGNOSTICS", str(enabled).lower()) - if sensitive is None: - monkeypatch.delenv("AGENT_FRAMEWORK_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE", raising=False) - else: - monkeypatch.setenv("AGENT_FRAMEWORK_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE", str(sensitive).lower()) - return ModelDiagnosticSettings(env_file_path="test.env") +def enable_function_calling(request: Any) -> bool: + return request.param if hasattr(request, "param") else True + + +@fixture +def max_iterations(request: Any) -> int: + return request.param if hasattr(request, "param") else 2 + + +@fixture +def chat_client(enable_function_calling: bool, max_iterations: int) -> MockChatClient: + if enable_function_calling: + with patch("agent_framework._tools.DEFAULT_MAX_ITERATIONS", max_iterations): + return use_function_invocation(MockChatClient)() + return MockChatClient() + + +@fixture +def chat_client_base(enable_function_calling: bool, max_iterations: int) -> MockBaseChatClient: + if enable_function_calling: + with patch("agent_framework._tools.DEFAULT_MAX_ITERATIONS", max_iterations): + return use_function_invocation(MockBaseChatClient)() + return MockBaseChatClient() + + +# region Agents +class MockAgentThread(AgentThread): + pass + + +# Mock Agent implementation for testing +class MockAgent(AgentProtocol): + @property + def id(self) -> str: + return str(uuid4()) + + @property + def name(self) -> str | None: + """Returns the name of the agent.""" + return "Name" + + @property + def display_name(self) -> str: + """Returns the name of the agent.""" + return "Display Name" + + @property + def description(self) -> str | None: + return "Description" + + async def run( + self, + messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None, + *, + thread: AgentThread | None = None, + **kwargs: Any, + ) -> AgentRunResponse: + logger.debug(f"Running mock agent, with: {messages=}, {thread=}, {kwargs=}") + return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, contents=[TextContent("Response")])]) + + async def run_stream( + self, + messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None, + *, + thread: AgentThread | None = None, + **kwargs: Any, + ) -> AsyncIterable[AgentRunResponseUpdate]: + logger.debug(f"Running mock agent stream, with: {messages=}, {thread=}, {kwargs=}") + yield AgentRunResponseUpdate(contents=[TextContent("Response")]) + + def get_new_thread(self) -> AgentThread: + return MockAgentThread() + + +@fixture +def agent_thread() -> AgentThread: + return MockAgentThread() + + +@fixture +def agent() -> AgentProtocol: + return MockAgent() diff --git a/python/packages/main/tests/main/test_agents.py b/python/packages/main/tests/main/test_agents.py index ab48a8ad45..7af3252708 100644 --- a/python/packages/main/tests/main/test_agents.py +++ b/python/packages/main/tests/main/test_agents.py @@ -1,122 +1,27 @@ # Copyright (c) Microsoft. All rights reserved. -from collections.abc import AsyncIterable, MutableSequence -from typing import Any +from collections.abc import AsyncIterable from uuid import uuid4 -from pytest import fixture, raises +from pytest import raises from agent_framework import ( AgentProtocol, AgentRunResponse, AgentRunResponseUpdate, AgentThread, - BaseChatClient, ChatAgent, ChatClientProtocol, ChatMessage, ChatMessageList, - ChatOptions, ChatResponse, - ChatResponseUpdate, + HostedCodeInterpreterTool, Role, TextContent, ) from agent_framework.exceptions import AgentExecutionException -# Mock AgentThread implementation for testing -class MockAgentThread(AgentThread): - pass - - -# Mock Agent implementation for testing -class MockAgent(AgentProtocol): - @property - def id(self) -> str: - return str(uuid4()) - - @property - def name(self) -> str | None: - """Returns the name of the agent.""" - return "Name" - - @property - def display_name(self) -> str: - """Returns the name of the agent.""" - return "Display Name" - - @property - def description(self) -> str | None: - return "Description" - - async def run( - self, - messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None, - *, - thread: AgentThread | None = None, - **kwargs: Any, - ) -> AgentRunResponse: - return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, contents=[TextContent("Response")])]) - - async def run_stream( - self, - messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None, - *, - thread: AgentThread | None = None, - **kwargs: Any, - ) -> AsyncIterable[AgentRunResponseUpdate]: - yield AgentRunResponseUpdate(contents=[TextContent("Response")]) - - def get_new_thread(self) -> AgentThread: - return MockAgentThread() - - -# Mock ChatClientProtocol implementation for testing -class MockChatClient(BaseChatClient): - _mock_response: ChatResponse | None = None - - def __init__(self, mock_response: ChatResponse | None = None) -> None: - self._mock_response = mock_response - - async def _inner_get_response( - self, - *, - messages: MutableSequence[ChatMessage], - chat_options: ChatOptions, - **kwargs: Any, - ) -> ChatResponse: - return ( - self._mock_response - if self._mock_response - else ChatResponse(messages=ChatMessage(role=Role.ASSISTANT, text="test response")) - ) - - async def _inner_get_streaming_response( - self, - *, - messages: MutableSequence[ChatMessage], - chat_options: ChatOptions, - **kwargs: Any, - ) -> AsyncIterable[ChatResponseUpdate]: - yield ChatResponseUpdate(role=Role.ASSISTANT, text=TextContent(text="test streaming response")) - - -@fixture -def agent_thread() -> AgentThread: - return MockAgentThread() - - -@fixture -def agent() -> AgentProtocol: - return MockAgent() - - -@fixture -def chat_client() -> BaseChatClient: - return MockChatClient() - - def test_agent_thread_type(agent_thread: AgentThread) -> None: assert isinstance(agent_thread, AgentThread) @@ -178,7 +83,7 @@ async def test_chat_client_agent_run_streaming(chat_client: ChatClientProtocol) result = await AgentRunResponse.from_agent_response_generator(agent.run_stream("Hello")) - assert result.text == "test streaming response" + assert result.text == "test streaming response another update" async def test_chat_client_agent_get_new_thread(chat_client: ChatClientProtocol) -> None: @@ -203,14 +108,16 @@ async def test_chat_client_agent_prepare_thread_and_messages(chat_client: ChatCl assert result_messages[1].text == "Test" -async def test_chat_client_agent_update_thread_id() -> None: - chat_client = MockChatClient( - mock_response=ChatResponse( - messages=[ChatMessage(role=Role.ASSISTANT, contents=[TextContent("test response")])], - conversation_id="123", - ) +async def test_chat_client_agent_update_thread_id(chat_client_base: ChatClientProtocol) -> None: + mock_response = ChatResponse( + messages=[ChatMessage(role=Role.ASSISTANT, contents=[TextContent("test response")])], + conversation_id="123", + ) + chat_client_base.run_responses = [mock_response] + agent = ChatAgent( + chat_client=chat_client_base, + tools=HostedCodeInterpreterTool(), ) - agent = ChatAgent(chat_client=chat_client) thread = agent.get_new_thread() result = await agent.run("Hello", thread=thread) @@ -263,15 +170,16 @@ async def test_chat_client_agent_author_name_as_agent_name(chat_client: ChatClie assert result.messages[0].author_name == "TestAgent" -async def test_chat_client_agent_author_name_is_used_from_response() -> None: - chat_client = MockChatClient( - mock_response=ChatResponse( +async def test_chat_client_agent_author_name_is_used_from_response(chat_client_base: ChatClientProtocol) -> None: + chat_client_base.run_responses = [ + ChatResponse( messages=[ ChatMessage(role=Role.ASSISTANT, contents=[TextContent("test response")], author_name="TestAuthor") ] ) - ) - agent = ChatAgent(chat_client=chat_client) + ] + + agent = ChatAgent(chat_client=chat_client_base, tools=HostedCodeInterpreterTool()) result = await agent.run("Hello") assert result.text == "test response" diff --git a/python/packages/main/tests/main/test_clients.py b/python/packages/main/tests/main/test_clients.py index 5d415bec91..d733362bc9 100644 --- a/python/packages/main/tests/main/test_clients.py +++ b/python/packages/main/tests/main/test_clients.py @@ -1,18 +1,15 @@ # Copyright (c) Microsoft. All rights reserved. -import asyncio import sys -from collections.abc import AsyncIterable, MutableSequence, Sequence +from collections.abc import Sequence from typing import Any -from pydantic import Field from pytest import fixture from agent_framework import ( BaseChatClient, ChatClientProtocol, ChatMessage, - ChatOptions, ChatResponse, ChatResponseUpdate, EmbeddingGenerator, @@ -22,81 +19,12 @@ from agent_framework import ( Role, TextContent, ai_function, - use_tool_calling, ) if sys.version_info >= (3, 12): - from typing import override # type: ignore + pass # type: ignore else: - from typing_extensions import override # type: ignore[import] - - -class MockChatClient: - """Simple implementation of a chat client.""" - - async def get_response( - self, - messages: ChatMessage | Sequence[ChatMessage], - **kwargs: Any, - ) -> ChatResponse: - # Implement the method - - return ChatResponse(messages=ChatMessage(role="assistant", text="test response")) - - async def get_streaming_response( - self, - messages: ChatMessage | Sequence[ChatMessage], - **kwargs: Any, - ) -> AsyncIterable[ChatResponseUpdate]: - # Implement the method - yield ChatResponseUpdate(text=TextContent(text="test streaming response"), role="assistant") - yield ChatResponseUpdate(contents=[TextContent(text="another update")], role="assistant") - - -@use_tool_calling -class MockBaseChatClient(BaseChatClient): - """Mock implementation of the BaseChatClient.""" - - run_responses: list[ChatResponse] = Field(default_factory=list) - streaming_responses: list[list[ChatResponseUpdate]] = Field(default_factory=list) - - @override - async def _inner_get_response( - self, - *, - messages: MutableSequence[ChatMessage], - chat_options: ChatOptions, - **kwargs: Any, - ) -> ChatResponse: - """Send a chat request to the AI service. - - Args: - messages: The chat messages to send. - chat_options: The options for the request. - kwargs: Any additional keyword arguments. - - Returns: - The chat response contents representing the response(s). - """ - if not self.run_responses or chat_options.tool_choice == "none": - return ChatResponse(messages=ChatMessage(role="assistant", text=f"test response - {messages[0].text}")) - return self.run_responses.pop(0) - - @override - async def _inner_get_streaming_response( - self, - *, - messages: MutableSequence[ChatMessage], - chat_options: ChatOptions, - **kwargs: Any, - ) -> AsyncIterable[ChatResponseUpdate]: - if not self.streaming_responses or chat_options.tool_choice == "none": - yield ChatResponseUpdate(text=f"update - {messages[0].text}", role="assistant") - return - response = self.streaming_responses.pop(0) - for update in response: - yield update - await asyncio.sleep(0) + pass # type: ignore[import] class MockEmbeddingGenerator: @@ -114,35 +42,25 @@ class MockEmbeddingGenerator: return embeddings -@fixture -def chat_client() -> MockChatClient: - return MockChatClient() - - -@fixture -def chat_client_base() -> MockBaseChatClient: - return MockBaseChatClient() - - @fixture def embedding_generator() -> MockEmbeddingGenerator: gen: EmbeddingGenerator[str, list[float]] = MockEmbeddingGenerator() return gen -def test_chat_client_type(chat_client: MockChatClient): +def test_chat_client_type(chat_client: ChatClientProtocol): assert isinstance(chat_client, ChatClientProtocol) -async def test_chat_client_get_response(chat_client: MockChatClient): +async def test_chat_client_get_response(chat_client: ChatClientProtocol): response = await chat_client.get_response(ChatMessage(role="user", text="Hello")) assert response.text == "test response" assert response.messages[0].role == Role.ASSISTANT -async def test_chat_client_get_streaming_response(chat_client: MockChatClient): +async def test_chat_client_get_streaming_response(chat_client: ChatClientProtocol): async for update in chat_client.get_streaming_response(ChatMessage(role="user", text="Hello")): - assert update.text == "test streaming response" or update.text == "another update" + assert update.text == "test streaming response " or update.text == "another update" assert update.role == Role.ASSISTANT @@ -158,23 +76,23 @@ async def test_embedding_generator_generate(embedding_generator: MockEmbeddingGe assert len(emb) == 5 -def test_base_client(chat_client_base: MockBaseChatClient): +def test_base_client(chat_client_base: ChatClientProtocol): assert isinstance(chat_client_base, BaseChatClient) assert isinstance(chat_client_base, ChatClientProtocol) -async def test_base_client_get_response(chat_client_base: MockBaseChatClient): +async def test_base_client_get_response(chat_client_base: ChatClientProtocol): response = await chat_client_base.get_response(ChatMessage(role="user", text="Hello")) assert response.messages[0].role == Role.ASSISTANT assert response.messages[0].text == "test response - Hello" -async def test_base_client_get_streaming_response(chat_client_base: MockBaseChatClient): +async def test_base_client_get_streaming_response(chat_client_base: ChatClientProtocol): async for update in chat_client_base.get_streaming_response(ChatMessage(role="user", text="Hello")): assert update.text == "update - Hello" or update.text == "another update" -async def test_base_client_with_function_calling(chat_client_base: MockBaseChatClient): +async def test_base_client_with_function_calling(chat_client_base: ChatClientProtocol): exec_counter = 0 @ai_function(name="test_function") @@ -208,8 +126,7 @@ async def test_base_client_with_function_calling(chat_client_base: MockBaseChatC assert response.messages[2].text == "done" -async def test_base_client_with_function_calling_disabled(chat_client_base: MockBaseChatClient): - chat_client_base.__maximum_iterations_per_request = 0 +async def test_base_client_with_function_calling_resets(chat_client_base: ChatClientProtocol): exec_counter = 0 @ai_function(name="test_function") @@ -225,16 +142,32 @@ async def test_base_client_with_function_calling_disabled(chat_client_base: Mock contents=[FunctionCallContent(call_id="1", name="test_function", arguments='{"arg1": "value1"}')], ) ), + ChatResponse( + messages=ChatMessage( + role="assistant", + contents=[FunctionCallContent(call_id="2", name="test_function", arguments='{"arg1": "value1"}')], + ) + ), ChatResponse(messages=ChatMessage(role="assistant", text="done")), ] response = await chat_client_base.get_response("hello", tool_choice="auto", tools=[ai_func]) - assert exec_counter == 0 - assert len(response.messages) == 1 + assert exec_counter == 2 + assert len(response.messages) == 5 assert response.messages[0].role == Role.ASSISTANT - assert response.messages[0].text == "test response - hello" + assert response.messages[1].role == Role.TOOL + assert response.messages[2].role == Role.ASSISTANT + assert response.messages[3].role == Role.TOOL + assert response.messages[4].role == Role.ASSISTANT + assert isinstance(response.messages[0].contents[0], FunctionCallContent) + assert isinstance(response.messages[1].contents[0], FunctionResultContent) + assert isinstance(response.messages[2].contents[0], FunctionCallContent) + assert isinstance(response.messages[3].contents[0], FunctionResultContent) + # after these two responses, it would try another regular call, but since max_iterations is 1, it stops and calls + assert isinstance(response.messages[4].contents[0], TextContent) + assert response.text == "I broke out of the function invocation loop..." -async def test_base_client_with_streaming_function_calling(chat_client_base: MockBaseChatClient): +async def test_base_client_with_streaming_function_calling(chat_client_base: ChatClientProtocol): exec_counter = 0 @ai_function(name="test_function") @@ -270,38 +203,3 @@ async def test_base_client_with_streaming_function_calling(chat_client_base: Moc assert updates[2].contents[0].call_id == "1" assert updates[3].text == "Processed value1" assert exec_counter == 1 - - -async def test_base_client_with_streaming_function_calling_disabled(chat_client_base: MockBaseChatClient): - chat_client_base.__maximum_iterations_per_request = 0 - exec_counter = 0 - - @ai_function(name="test_function") - def ai_func(arg1: str) -> str: - nonlocal exec_counter - exec_counter += 1 - return f"Processed {arg1}" - - chat_client_base.streaming_responses = [ - [ - ChatResponseUpdate( - contents=[FunctionCallContent(call_id="1", name="test_function", arguments='{"arg1":')], - role="assistant", - ), - ChatResponseUpdate( - contents=[FunctionCallContent(call_id="1", name="test_function", arguments='"value1"}')], - role="assistant", - ), - ], - [ - ChatResponseUpdate( - contents=[TextContent(text="Processed value1")], - role="assistant", - ) - ], - ] - updates = [] - async for update in chat_client_base.get_streaming_response("hello", tool_choice="auto", tools=[ai_func]): - updates.append(update) - assert len(updates) == 1 - assert exec_counter == 0 diff --git a/python/packages/main/tests/main/test_telemetry.py b/python/packages/main/tests/main/test_telemetry.py index 8936decfcc..597961488e 100644 --- a/python/packages/main/tests/main/test_telemetry.py +++ b/python/packages/main/tests/main/test_telemetry.py @@ -1,14 +1,19 @@ # Copyright (c) Microsoft. All rights reserved. import logging -from collections.abc import AsyncIterable, MutableSequence +from collections.abc import MutableSequence from typing import Any -from unittest.mock import Mock, patch +from unittest.mock import MagicMock, Mock, patch import pytest +from opentelemetry.semconv_ai import SpanAttributes from opentelemetry.trace import StatusCode from agent_framework import ( + AgentProtocol, + AgentRunResponse, + AgentThread, + BaseChatClient, ChatMessage, ChatOptions, ChatResponse, @@ -16,15 +21,19 @@ from agent_framework import ( Role, UsageDetails, ) +from agent_framework.exceptions import AgentInitializationError, ChatClientInitializationError from agent_framework.telemetry import ( AGENT_FRAMEWORK_USER_AGENT, + OPEN_TELEMETRY_AGENT_MARKER, + OPEN_TELEMETRY_CHAT_CLIENT_MARKER, ROLE_EVENT_MAP, - TELEMETRY_DISABLED_ENV_VAR, USER_AGENT_KEY, + USER_AGENT_TELEMETRY_DISABLED_ENV_VAR, ChatMessageListTimestampFilter, - GenAIAttributes, + OtelAttr, + get_function_span, prepend_agent_framework_to_user_agent, - start_as_current_span, + use_agent_telemetry, use_telemetry, ) @@ -33,7 +42,7 @@ from agent_framework.telemetry import ( def test_telemetry_disabled_env_var(): """Test that the telemetry disabled environment variable is correctly defined.""" - assert TELEMETRY_DISABLED_ENV_VAR == "AZURE_TELEMETRY_DISABLED" + assert USER_AGENT_TELEMETRY_DISABLED_ENV_VAR == "AGENT_FRAMEWORK_USER_AGENT_DISABLED" def test_user_agent_key(): @@ -78,20 +87,20 @@ def test_app_info_when_telemetry_disabled(): def test_role_event_map(): """Test that ROLE_EVENT_MAP contains expected mappings.""" - assert ROLE_EVENT_MAP["system"] == GenAIAttributes.SYSTEM_MESSAGE.value - assert ROLE_EVENT_MAP["user"] == GenAIAttributes.USER_MESSAGE.value - assert ROLE_EVENT_MAP["assistant"] == GenAIAttributes.ASSISTANT_MESSAGE.value - assert ROLE_EVENT_MAP["tool"] == GenAIAttributes.TOOL_MESSAGE.value + assert ROLE_EVENT_MAP["system"] == OtelAttr.SYSTEM_MESSAGE + assert ROLE_EVENT_MAP["user"] == OtelAttr.USER_MESSAGE + assert ROLE_EVENT_MAP["assistant"] == OtelAttr.ASSISTANT_MESSAGE + assert ROLE_EVENT_MAP["tool"] == OtelAttr.TOOL_MESSAGE def test_enum_values(): - """Test that GenAIAttributes enum has expected values.""" - assert GenAIAttributes.OPERATION.value == "gen_ai.operation.name" - assert GenAIAttributes.SYSTEM.value == "gen_ai.system" - assert GenAIAttributes.MODEL.value == "gen_ai.request.model" - assert GenAIAttributes.CHAT_COMPLETION_OPERATION.value == "chat" - assert GenAIAttributes.TOOL_EXECUTION_OPERATION.value == "execute_tool" - assert GenAIAttributes.AGENT_INVOKE_OPERATION.value == "invoke_agent" + """Test that OtelAttr enum has expected values.""" + assert OtelAttr.OPERATION == "gen_ai.operation.name" + assert SpanAttributes.LLM_SYSTEM == "gen_ai.system" + assert SpanAttributes.LLM_REQUEST_MODEL == "gen_ai.request.model" + assert OtelAttr.CHAT_COMPLETION_OPERATION == "chat" + assert OtelAttr.TOOL_EXECUTION_OPERATION == "execute_tool" + assert OtelAttr.AGENT_INVOKE_OPERATION == "invoke_agent" # region Test prepend_agent_framework_to_user_agent @@ -135,47 +144,6 @@ def test_modifies_original_dict(): assert "User-Agent" in headers -# region ModelDiagnosticSettings tests - - -@pytest.mark.parametrize("model_diagnostic_settings", [(None, None)], indirect=True) -def test_default_values(model_diagnostic_settings): - """Test default values for ModelDiagnosticSettings.""" - assert not model_diagnostic_settings.ENABLED - assert not model_diagnostic_settings.SENSITIVE_EVENTS_ENABLED - - -@pytest.mark.parametrize("model_diagnostic_settings", [(False, False)], indirect=True) -def test_disabled(model_diagnostic_settings): - """Test default values for ModelDiagnosticSettings.""" - assert not model_diagnostic_settings.ENABLED - assert not model_diagnostic_settings.SENSITIVE_EVENTS_ENABLED - - -@pytest.mark.parametrize("model_diagnostic_settings", [(True, False)], indirect=True) -def test_non_sensitive_events_enabled(model_diagnostic_settings): - """Test loading model_diagnostic_settings from environment variables.""" - assert model_diagnostic_settings.ENABLED - assert not model_diagnostic_settings.SENSITIVE_EVENTS_ENABLED - - -@pytest.mark.parametrize("model_diagnostic_settings", [(True, True)], indirect=True) -def test_sensitive_events_enabled(model_diagnostic_settings): - """Test loading model_diagnostic_settings from environment variables.""" - assert model_diagnostic_settings.ENABLED - assert model_diagnostic_settings.SENSITIVE_EVENTS_ENABLED - - -@pytest.mark.parametrize("model_diagnostic_settings", [(False, True)], indirect=True) -def test_sensitive_events_enabled_only(model_diagnostic_settings): - """Test loading sensitive events setting from environment. - - But when sensitive events are enabled, diagnostics are also enabled. - """ - assert model_diagnostic_settings.ENABLED - assert model_diagnostic_settings.SENSITIVE_EVENTS_ENABLED - - # region Test ChatMessageListTimestampFilter @@ -213,88 +181,74 @@ def test_filter_with_index_key(): def test_index_key_constant(): """Test that INDEX_KEY constant is correctly defined.""" - assert ChatMessageListTimestampFilter.INDEX_KEY == "CHAT_MESSAGE_INDEX" + assert ChatMessageListTimestampFilter.INDEX_KEY == "chat_message_index" -# region Test start_as_current_span +# region Test get_function_span def test_start_span_basic(): """Test starting a span with basic function info.""" mock_tracer = Mock() - mock_span = Mock() - mock_tracer.start_as_current_span.return_value = mock_span + with patch("agent_framework.telemetry.tracer", mock_tracer): + mock_span = Mock() + mock_tracer.start_as_current_span.return_value = mock_span - # Create a mock function - mock_function = Mock() - mock_function.name = "test_function" - mock_function.description = "Test function description" + # Create a mock function + mock_function = Mock() + mock_function.name = "test_function" + mock_function.description = "Test function description" - result = start_as_current_span(mock_tracer, mock_function) + result = get_function_span(mock_function) - assert result == mock_span - mock_tracer.start_as_current_span.assert_called_once() + assert result == mock_span + mock_tracer.start_as_current_span.assert_called_once() - call_args = mock_tracer.start_as_current_span.call_args - assert call_args[0][0] == "execute_tool test_function" + call_args = mock_tracer.start_as_current_span.call_args + assert call_args[1]["name"] == "execute_tool test_function" - attributes = call_args[1]["attributes"] - assert attributes[GenAIAttributes.OPERATION.value] == GenAIAttributes.TOOL_EXECUTION_OPERATION.value - assert attributes[GenAIAttributes.TOOL_NAME.value] == "test_function" - assert attributes[GenAIAttributes.TOOL_DESCRIPTION.value] == "Test function description" + attributes = call_args[1]["attributes"] + assert attributes[OtelAttr.OPERATION.value] == OtelAttr.TOOL_EXECUTION_OPERATION + assert attributes[OtelAttr.TOOL_NAME] == "test_function" + assert attributes[OtelAttr.TOOL_DESCRIPTION] == "Test function description" -def test_start_span_with_metadata(): - """Test starting a span with metadata containing tool_call_id.""" +def test_start_span_with_tool_call_id(): + """Test starting a span with tool_call_id.""" mock_tracer = Mock() - mock_span = Mock() - mock_tracer.start_as_current_span.return_value = mock_span + with patch("agent_framework.telemetry.tracer", mock_tracer): + mock_span = Mock() + mock_tracer.start_as_current_span.return_value = mock_span - mock_function = Mock() - mock_function.name = "test_function" - mock_function.description = "Test function" + mock_function = Mock() + mock_function.name = "test_function" + mock_function.description = "Test function" - metadata = {"tool_call_id": "test_call_123"} + tool_call_id = "test_call_123" - _ = start_as_current_span(mock_tracer, mock_function, metadata) + _ = get_function_span(mock_function, tool_call_id) - call_args = mock_tracer.start_as_current_span.call_args - attributes = call_args[1]["attributes"] - assert attributes[GenAIAttributes.TOOL_CALL_ID.value] == "test_call_123" + call_args = mock_tracer.start_as_current_span.call_args + attributes = call_args[1]["attributes"] + assert attributes[OtelAttr.TOOL_CALL_ID] == "test_call_123" def test_start_span_without_description(): """Test starting a span when function has no description.""" mock_tracer = Mock() - mock_span = Mock() - mock_tracer.start_as_current_span.return_value = mock_span + with patch("agent_framework.telemetry.tracer", mock_tracer): + mock_span = Mock() + mock_tracer.start_as_current_span.return_value = mock_span - mock_function = Mock() - mock_function.name = "test_function" - mock_function.description = None + mock_function = Mock() + mock_function.name = "test_function" + mock_function.description = None - start_as_current_span(mock_tracer, mock_function) + get_function_span(mock_function) - call_args = mock_tracer.start_as_current_span.call_args - attributes = call_args[1]["attributes"] - assert GenAIAttributes.TOOL_DESCRIPTION.value not in attributes - - -def test_start_span_empty_metadata(): - """Test starting a span with empty metadata.""" - mock_tracer = Mock() - mock_span = Mock() - mock_tracer.start_as_current_span.return_value = mock_span - - mock_function = Mock() - mock_function.name = "test_function" - mock_function.description = "Test function" - - start_as_current_span(mock_tracer, mock_function, {}) - - call_args = mock_tracer.start_as_current_span.call_args - attributes = call_args[1]["attributes"] - assert GenAIAttributes.TOOL_CALL_ID.value not in attributes + call_args = mock_tracer.start_as_current_span.call_args + attributes = call_args[1]["attributes"] + assert OtelAttr.TOOL_DESCRIPTION not in attributes # region Test use_telemetry decorator @@ -305,12 +259,10 @@ def test_decorator_with_valid_class(): # Create a mock class with the required methods class MockChatClient: - MODEL_PROVIDER_NAME = "test_provider" - - async def _inner_get_response(self, *, messages, chat_options, **kwargs): + async def get_response(self, messages, **kwargs): return Mock() - async def _inner_get_streaming_response(self, *, messages, chat_options, **kwargs): + async def get_streaming_response(self, messages, **kwargs): async def gen(): yield Mock() @@ -318,39 +270,31 @@ def test_decorator_with_valid_class(): # Apply the decorator decorated_class = use_telemetry(MockChatClient) - - # Check that the methods were wrapped - assert hasattr(decorated_class._inner_get_response, "__model_diagnostics_chat_client__") - assert hasattr(decorated_class._inner_get_streaming_response, "__model_diagnostics_streaming_chat_completion__") + assert hasattr(decorated_class, OPEN_TELEMETRY_CHAT_CLIENT_MARKER) def test_decorator_with_missing_methods(): """Test that decorator handles classes missing required methods gracefully.""" class MockChatClient: - MODEL_PROVIDER_NAME = "test_provider" + OTEL_PROVIDER_NAME = "test_provider" # Apply the decorator - should not raise an error - decorated_class = use_telemetry(MockChatClient) - - # Class should be returned unchanged - assert decorated_class is MockChatClient + with pytest.raises(ChatClientInitializationError): + use_telemetry(MockChatClient) def test_decorator_with_partial_methods(): """Test decorator when only one method is present.""" class MockChatClient: - MODEL_PROVIDER_NAME = "test_provider" + OTEL_PROVIDER_NAME = "test_provider" - async def _inner_get_response(self, *, messages, chat_options, **kwargs): + async def get_response(self, messages, **kwargs): return Mock() - decorated_class = use_telemetry(MockChatClient) - - # Only the present method should be wrapped - assert hasattr(decorated_class._inner_get_response, "__model_diagnostics_chat_client__") - assert not hasattr(decorated_class, "_inner_get_streaming_response") + with pytest.raises(ChatClientInitializationError): + use_telemetry(MockChatClient) # region Test telemetry decorator with mock client @@ -360,12 +304,7 @@ def test_decorator_with_partial_methods(): def mock_chat_client(): """Create a mock chat client for testing.""" - class MockChatClient: - MODEL_PROVIDER_NAME = "test_provider" - - def __init__(self): - self.ai_model_id = "test-model" - + class MockChatClient(BaseChatClient): def service_url(self): return "https://test.example.com" @@ -384,223 +323,77 @@ def mock_chat_client(): yield ChatResponseUpdate(text="Hello", role=Role.ASSISTANT) yield ChatResponseUpdate(text=" world", role=Role.ASSISTANT) - return MockChatClient() + return MockChatClient -@pytest.mark.parametrize("model_diagnostic_settings", [(False, False)], indirect=True) -async def test_telemetry_disabled_bypasses_instrumentation(mock_chat_client, model_diagnostic_settings): - """Test that when diagnostics are disabled, telemetry is bypassed.""" - decorated_class = use_telemetry(type(mock_chat_client)) - client = decorated_class() - - messages = [ChatMessage(role=Role.USER, text="Test message")] - chat_options = ChatOptions() - - with ( - patch("agent_framework.telemetry.MODEL_DIAGNOSTICS_SETTINGS", model_diagnostic_settings), - patch("agent_framework.telemetry.use_span") as mock_use_span, - ): - # This should not create any spans - response = await client._inner_get_response(messages=messages, chat_options=chat_options) - assert response is not None - mock_use_span.assert_not_called() - - -@pytest.mark.parametrize("model_diagnostic_settings", [(True, True)], indirect=True) -async def test_instrumentation_enabled(mock_chat_client, model_diagnostic_settings): +@pytest.mark.parametrize("enable_sensitive_data", [True, False], indirect=True) +async def test_instrumentation_enabled(mock_chat_client, otel_settings): """Test that when diagnostics are enabled, telemetry is applied.""" - decorated_class = use_telemetry(type(mock_chat_client)) - client = decorated_class() + client = use_telemetry(mock_chat_client)() messages = [ChatMessage(role=Role.USER, text="Test message")] chat_options = ChatOptions() with ( - patch("agent_framework.telemetry.MODEL_DIAGNOSTICS_SETTINGS", model_diagnostic_settings), - patch("agent_framework.telemetry.use_span") as mock_use_span, - patch("agent_framework.telemetry.logger") as mock_logger, + patch("agent_framework.telemetry._get_span") as mock_response_span, + patch("agent_framework.telemetry._capture_messages") as mock_log_messages, ): - response = await client._inner_get_response(messages=messages, chat_options=chat_options) + response = await client.get_response(messages=messages, chat_options=chat_options) assert response is not None - mock_use_span.assert_called_once() - # Check that logger.info was called (telemetry logs input/output) - assert mock_logger.info.call_count == 2 + mock_response_span.assert_called_once() + + # Check that log messages was called only if sensitive events are enabled + assert mock_log_messages.call_count == (2 if otel_settings.enable_sensitive_data else 0) -@pytest.mark.parametrize("model_diagnostic_settings", [(True, False)], indirect=True) -async def test_streaming_response_with_diagnostics_enabled_via_decorator(mock_chat_client, model_diagnostic_settings): +@pytest.mark.parametrize("enable_sensitive_data", [True, False], indirect=True) +async def test_streaming_response_with_otel(mock_chat_client, otel_settings): """Test streaming telemetry through the use_telemetry decorator.""" - decorated_class = use_telemetry(type(mock_chat_client)) - client = decorated_class() + client = use_telemetry(mock_chat_client)() messages = [ChatMessage(role=Role.USER, text="Test")] chat_options = ChatOptions() with ( - patch("agent_framework.telemetry.MODEL_DIAGNOSTICS_SETTINGS", model_diagnostic_settings), - patch("agent_framework.telemetry.use_span") as mock_use_span, - patch("agent_framework.telemetry._get_chat_response_span") as mock_get_span, - patch("agent_framework.telemetry._set_chat_response_input") as mock_set_input, - patch("agent_framework.telemetry._set_chat_response_output") as mock_set_output, + patch("agent_framework.telemetry._get_span") as mock_response_span, + patch("agent_framework.telemetry._capture_messages") as mock_log_messages, + patch("agent_framework.telemetry._capture_response") as mock_set_output, ): - mock_span = Mock() - mock_use_span.return_value.__enter__.return_value = mock_span - mock_use_span.return_value.__exit__.return_value = None - - # We can't easily mock ChatResponse.from_chat_response_updates since it's imported locally, - # but we can verify telemetry calls were made - # Collect all yielded updates updates = [] - async for update in client._inner_get_streaming_response(messages=messages, chat_options=chat_options): + async for update in client.get_streaming_response(messages=messages, chat_options=chat_options): updates.append(update) - # Verify we got the expected updates + # Verify we got the expected updates, this shouldn't be dependent on otel assert len(updates) == 2 # Verify telemetry calls were made - mock_get_span.assert_called_once() - mock_set_input.assert_called_once_with("test_provider", messages) - mock_set_output.assert_called_once() + mock_response_span.assert_called_once() + if otel_settings.enable_sensitive_data: + mock_log_messages.assert_called() + assert mock_log_messages.call_count == 2 # One for input, one for output + else: + mock_log_messages.assert_not_called() - -@pytest.mark.parametrize("model_diagnostic_settings", [(True, False)], indirect=True) -async def test_streaming_response_with_exception_via_decorator(mock_chat_client, model_diagnostic_settings): - """Test streaming telemetry exception handling through decorator.""" - - async def _inner_get_streaming_response( - self, *, messages: MutableSequence[ChatMessage], chat_options: ChatOptions, **kwargs: Any - ) -> AsyncIterable[ChatResponseUpdate]: - yield ChatResponseUpdate(text="Partial", role=Role.ASSISTANT) - raise ValueError("Test streaming error") - - type(mock_chat_client)._inner_get_streaming_response = _inner_get_streaming_response - - decorated_class = use_telemetry(type(mock_chat_client)) - client = decorated_class() - - messages = [ChatMessage(role=Role.USER, text="Test")] - chat_options = ChatOptions() - - with ( - patch("agent_framework.telemetry.MODEL_DIAGNOSTICS_SETTINGS", model_diagnostic_settings), - patch("agent_framework.telemetry.use_span") as mock_use_span, - patch("agent_framework.telemetry._get_chat_response_span"), - patch("agent_framework.telemetry._set_chat_response_input"), - patch("agent_framework.telemetry._set_error") as mock_set_error, - ): - mock_span = Mock() - mock_use_span.return_value.__enter__.return_value = mock_span - mock_use_span.return_value.__exit__.return_value = None - - # Should raise the exception and call error handler - with pytest.raises(ValueError, match="Test streaming error"): - async for _ in client._inner_get_streaming_response(messages=messages, chat_options=chat_options): - pass - - # Verify error was recorded - mock_set_error.assert_called_once() - assert isinstance(mock_set_error.call_args[0][1], ValueError) - - -@pytest.mark.parametrize("model_diagnostic_settings", [(False, False)], indirect=True) -async def test_streaming_response_diagnostics_disabled_via_decorator(model_diagnostic_settings): - """Test streaming response when diagnostics are disabled.""" - from agent_framework import ChatResponseUpdate - - class MockStreamingClientNoDiagnostics: - MODEL_PROVIDER_NAME = "test_provider" - - async def _inner_get_streaming_response( - self, *, messages: MutableSequence[ChatMessage], chat_options: ChatOptions, **kwargs: Any - ) -> AsyncIterable[ChatResponseUpdate]: - yield ChatResponseUpdate(text="Test", role=Role.ASSISTANT) - - decorated_class = use_telemetry(MockStreamingClientNoDiagnostics) - client = decorated_class() - - messages = [ChatMessage(role=Role.USER, text="Test")] - chat_options = ChatOptions() - - with ( - patch("agent_framework.telemetry.MODEL_DIAGNOSTICS_SETTINGS", model_diagnostic_settings), - patch("agent_framework.telemetry._get_chat_response_span") as mock_get_span, - ): - # Should not create spans when diagnostics are disabled - updates = [] - async for update in client._inner_get_streaming_response(messages=messages, chat_options=chat_options): - updates.append(update) - - assert len(updates) == 1 - # Should not have called telemetry functions - mock_get_span.assert_not_called() - - -# region Test empty streaming response handling - - -@pytest.mark.parametrize("model_diagnostic_settings", [(True, False)], indirect=True) -async def test_empty_streaming_response_via_decorator(model_diagnostic_settings): - """Test streaming wrapper with empty response.""" - - class MockEmptyStreamingClient: - MODEL_PROVIDER_NAME = "test_provider" - - def __init__(self): - self.ai_model_id = "test_model" - - def service_url(self) -> str: - return "https://test.com" - - async def _inner_get_streaming_response( - self, *, messages: MutableSequence[ChatMessage], chat_options: ChatOptions, **kwargs: Any - ) -> AsyncIterable[ChatResponseUpdate]: - # Return empty stream - return - yield # This will never be reached - - decorated_class = use_telemetry(MockEmptyStreamingClient) - client = decorated_class() - - messages = [ChatMessage(role=Role.USER, text="Test")] - chat_options = ChatOptions() - - with ( - patch("agent_framework.telemetry.MODEL_DIAGNOSTICS_SETTINGS", model_diagnostic_settings), - patch("agent_framework.telemetry.use_span") as mock_use_span, - patch("agent_framework.telemetry._get_chat_response_span"), - patch("agent_framework.telemetry._set_chat_response_input"), - patch("agent_framework.telemetry._set_chat_response_output") as mock_set_output, - ): - mock_span = Mock() - mock_use_span.return_value.__enter__.return_value = mock_span - mock_use_span.return_value.__exit__.return_value = None - - # Should handle empty stream gracefully - updates = [] - async for update in client._inner_get_streaming_response(messages=messages, chat_options=chat_options): - updates.append(update) - - assert len(updates) == 0 - # Should still call telemetry mock_set_output.assert_called_once() def test_start_as_current_span_with_none_metadata(): - """Test start_as_current_span with None metadata.""" + """Test get_function_span with None metadata.""" mock_tracer = Mock() - mock_span = Mock() - mock_tracer.start_as_current_span.return_value = mock_span + with patch("agent_framework.telemetry.tracer", mock_tracer): + mock_span = Mock() + mock_tracer.start_as_current_span.return_value = mock_span - mock_function = Mock() - mock_function.name = "test_function" - mock_function.description = "Test description" + mock_function = Mock() + mock_function.name = "test_function" + mock_function.description = "Test description" - result = start_as_current_span(mock_tracer, mock_function, None) + result = get_function_span(mock_function, None) - assert result == mock_span - call_args = mock_tracer.start_as_current_span.call_args - attributes = call_args[1]["attributes"] - assert GenAIAttributes.TOOL_CALL_ID.value not in attributes + assert result == mock_span + call_args = mock_tracer.start_as_current_span.call_args + attributes = call_args[1]["attributes"] + assert attributes[OtelAttr.TOOL_CALL_ID] == "unknown" def test_prepend_user_agent_with_none_value(): @@ -618,7 +411,6 @@ def test_prepend_user_agent_with_none_value(): def test_agent_decorator_with_valid_class(): """Test that agent decorator works with a valid ChatAgent-like class.""" - from agent_framework.telemetry import use_agent_telemetry # Create a mock class with the required methods class MockChatClientAgent: @@ -639,33 +431,31 @@ def test_agent_decorator_with_valid_class(): return gen() + def get_new_thread(self) -> AgentThread: + return AgentThread() + # Apply the decorator decorated_class = use_agent_telemetry(MockChatClientAgent) - # Check that the methods were wrapped - assert hasattr(decorated_class.run, "__model_diagnostics_agent_run__") - assert hasattr(decorated_class.run_stream, "__model_diagnostics_streaming_agent_run__") + assert hasattr(decorated_class, OPEN_TELEMETRY_AGENT_MARKER) def test_agent_decorator_with_missing_methods(): """Test that agent decorator handles classes missing required methods gracefully.""" - from agent_framework.telemetry import use_agent_telemetry - class MockChatClientAgent: + class MockAgent: AGENT_SYSTEM_NAME = "test_agent_system" # Apply the decorator - should not raise an error - decorated_class = use_agent_telemetry(MockChatClientAgent) - - # Class should be returned unchanged - assert decorated_class is MockChatClientAgent + with pytest.raises(AgentInitializationError): + use_agent_telemetry(MockAgent) def test_agent_decorator_with_partial_methods(): """Test agent decorator when only one method is present.""" from agent_framework.telemetry import use_agent_telemetry - class MockChatClientAgent: + class MockAgent: AGENT_SYSTEM_NAME = "test_agent_system" def __init__(self): @@ -676,11 +466,8 @@ def test_agent_decorator_with_partial_methods(): async def run(self, messages=None, *, thread=None, **kwargs): return Mock() - decorated_class = use_agent_telemetry(MockChatClientAgent) - - # Only the present method should be wrapped - assert hasattr(decorated_class.run, "__model_diagnostics_agent_run__") - assert not hasattr(decorated_class, "run_stream") + with pytest.raises(AgentInitializationError): + use_agent_telemetry(MockAgent) # region Test agent telemetry decorator with mock agent @@ -689,7 +476,6 @@ def test_agent_decorator_with_partial_methods(): @pytest.fixture def mock_chat_client_agent(): """Create a mock chat client agent for testing.""" - from agent_framework import AgentRunResponse, ChatMessage, Role, UsageDetails class MockChatClientAgent: AGENT_SYSTEM_NAME = "test_agent_system" @@ -714,37 +500,16 @@ def mock_chat_client_agent(): yield AgentRunResponseUpdate(text="Hello", role=Role.ASSISTANT) yield AgentRunResponseUpdate(text=" from agent", role=Role.ASSISTANT) - return MockChatClientAgent() + return MockChatClientAgent -@pytest.mark.parametrize("model_diagnostic_settings", [(False, False)], indirect=True) -async def test_agent_telemetry_disabled_bypasses_instrumentation(mock_chat_client_agent, model_diagnostic_settings): - """Test that when agent diagnostics are disabled, telemetry is bypassed.""" - from agent_framework.telemetry import use_agent_telemetry - - decorated_class = use_agent_telemetry(type(mock_chat_client_agent)) - agent = decorated_class() - - with ( - patch("agent_framework.telemetry.MODEL_DIAGNOSTICS_SETTINGS", model_diagnostic_settings), - patch("agent_framework.telemetry.use_span") as mock_use_span, - ): - # This should not create any spans - response = await agent.run("Test message") - assert response is not None - mock_use_span.assert_not_called() - - -@pytest.mark.parametrize("model_diagnostic_settings", [(True, True)], indirect=True) -async def test_agent_instrumentation_enabled(mock_chat_client_agent, model_diagnostic_settings): +@pytest.mark.parametrize("enable_sensitive_data", [True, False], indirect=True) +async def test_agent_instrumentation_enabled(mock_chat_client_agent: AgentProtocol, otel_settings): """Test that when agent diagnostics are enabled, telemetry is applied.""" - from agent_framework.telemetry import use_agent_telemetry - decorated_class = use_agent_telemetry(type(mock_chat_client_agent)) - agent = decorated_class() + agent = use_agent_telemetry(mock_chat_client_agent)() with ( - patch("agent_framework.telemetry.MODEL_DIAGNOSTICS_SETTINGS", model_diagnostic_settings), patch("agent_framework.telemetry.use_span") as mock_use_span, patch("agent_framework.telemetry.logger") as mock_logger, ): @@ -752,30 +517,21 @@ async def test_agent_instrumentation_enabled(mock_chat_client_agent, model_diagn assert response is not None mock_use_span.assert_called_once() # Check that logger.info was called (telemetry logs input/output) - assert mock_logger.info.call_count == 2 + assert mock_logger.info.call_count == (2 if otel_settings.enable_sensitive_data else 0) -@pytest.mark.parametrize("model_diagnostic_settings", [(True, False)], indirect=True) +@pytest.mark.parametrize("enable_sensitive_data", [True, False], indirect=True) async def test_agent_streaming_response_with_diagnostics_enabled_via_decorator( - mock_chat_client_agent, model_diagnostic_settings + mock_chat_client_agent: AgentProtocol, otel_settings ): """Test agent streaming telemetry through the use_agent_telemetry decorator.""" - from agent_framework.telemetry import use_agent_telemetry - - decorated_class = use_agent_telemetry(type(mock_chat_client_agent)) - agent = decorated_class() + agent = use_agent_telemetry(mock_chat_client_agent)() with ( - patch("agent_framework.telemetry.MODEL_DIAGNOSTICS_SETTINGS", model_diagnostic_settings), - patch("agent_framework.telemetry.use_span") as mock_use_span, - patch("agent_framework.telemetry._get_agent_run_span") as mock_get_span, - patch("agent_framework.telemetry._set_agent_run_input") as mock_set_input, - patch("agent_framework.telemetry._set_agent_run_output") as mock_set_output, + patch("agent_framework.telemetry._get_span") as mock_get_span, + patch("agent_framework.telemetry._capture_messages") as mock_capture_messages, + patch("agent_framework.telemetry._capture_response") as mock_capture_response, ): - mock_span = Mock() - mock_use_span.return_value.__enter__.return_value = mock_span - mock_use_span.return_value.__exit__.return_value = None - # Collect all yielded updates updates = [] async for update in agent.run_stream("Test message"): @@ -786,219 +542,39 @@ async def test_agent_streaming_response_with_diagnostics_enabled_via_decorator( # Verify telemetry calls were made mock_get_span.assert_called_once() - mock_set_input.assert_called_once_with("test_agent_system", "Test message") - mock_set_output.assert_called_once() + mock_capture_response.assert_called_once() + if otel_settings.enable_sensitive_data: + mock_capture_messages.assert_called() + else: + mock_capture_messages.assert_not_called() -@pytest.mark.parametrize("model_diagnostic_settings", [(True, False)], indirect=True) -async def test_agent_streaming_response_with_exception_via_decorator(mock_chat_client_agent, model_diagnostic_settings): - """Test agent streaming telemetry exception handling through decorator.""" - from agent_framework.telemetry import use_agent_telemetry - - async def run_stream(self, messages=None, *, thread=None, **kwargs): - from agent_framework import AgentRunResponseUpdate, Role - - yield AgentRunResponseUpdate(text="Partial", role=Role.ASSISTANT) - raise ValueError("Test agent streaming error") - - type(mock_chat_client_agent).run_stream = run_stream - - decorated_class = use_agent_telemetry(type(mock_chat_client_agent)) - agent = decorated_class() - - with ( - patch("agent_framework.telemetry.MODEL_DIAGNOSTICS_SETTINGS", model_diagnostic_settings), - patch("agent_framework.telemetry.use_span") as mock_use_span, - patch("agent_framework.telemetry._get_agent_run_span"), - patch("agent_framework.telemetry._set_agent_run_input"), - patch("agent_framework.telemetry._set_error") as mock_set_error, - ): - mock_span = Mock() - mock_use_span.return_value.__enter__.return_value = mock_span - mock_use_span.return_value.__exit__.return_value = None - - # Should raise the exception and call error handler - with pytest.raises(ValueError, match="Test agent streaming error"): - async for _ in agent.run_stream("Test message"): - pass - - # Verify error was recorded - mock_set_error.assert_called_once() - assert isinstance(mock_set_error.call_args[0][1], ValueError) - - -@pytest.mark.parametrize("model_diagnostic_settings", [(False, False)], indirect=True) -async def test_agent_streaming_response_diagnostics_disabled_via_decorator(model_diagnostic_settings): - """Test agent streaming response when diagnostics are disabled.""" - from agent_framework import AgentRunResponseUpdate, Role - from agent_framework.telemetry import use_agent_telemetry - - class MockStreamingAgentNoDiagnostics: - AGENT_SYSTEM_NAME = "test_agent_system" - - def __init__(self): - self.id = "test_agent_id" - self.name = "test_agent" - self.display_name = "Test Agent" - - async def run_stream(self, messages=None, *, thread=None, **kwargs): - yield AgentRunResponseUpdate(text="Test", role=Role.ASSISTANT) - - decorated_class = use_agent_telemetry(MockStreamingAgentNoDiagnostics) - agent = decorated_class() - - with ( - patch("agent_framework.telemetry.MODEL_DIAGNOSTICS_SETTINGS", model_diagnostic_settings), - patch("agent_framework.telemetry._get_agent_run_span") as mock_get_span, - ): - # Should not create spans when diagnostics are disabled - updates = [] - async for update in agent.run_stream("Test message"): - updates.append(update) - - assert len(updates) == 1 - # Should not have called telemetry functions - mock_get_span.assert_not_called() - - -@pytest.mark.parametrize("model_diagnostic_settings", [(True, False)], indirect=True) -async def test_agent_empty_streaming_response_via_decorator(model_diagnostic_settings): - """Test agent streaming wrapper with empty response.""" - from agent_framework.telemetry import use_agent_telemetry - - class MockEmptyStreamingAgent: - AGENT_SYSTEM_NAME = "test_agent_system" - - def __init__(self): - self.id = "test_agent_id" - self.name = "test_agent" - self.display_name = "Test Agent" - - async def run_stream(self, messages=None, *, thread=None, **kwargs): - # Return empty stream - return - yield # This will never be reached - - decorated_class = use_agent_telemetry(MockEmptyStreamingAgent) - agent = decorated_class() - - with ( - patch("agent_framework.telemetry.MODEL_DIAGNOSTICS_SETTINGS", model_diagnostic_settings), - patch("agent_framework.telemetry.use_span") as mock_use_span, - patch("agent_framework.telemetry._get_agent_run_span"), - patch("agent_framework.telemetry._set_agent_run_input"), - patch("agent_framework.telemetry._set_agent_run_output") as mock_set_output, - ): - mock_span = Mock() - mock_use_span.return_value.__enter__.return_value = mock_span - mock_use_span.return_value.__exit__.return_value = None - - # Should handle empty stream gracefully - updates = [] - async for update in agent.run_stream("Test message"): - updates.append(update) - - assert len(updates) == 0 - # Should still call telemetry - mock_set_output.assert_called_once() - - -@pytest.mark.parametrize("model_diagnostic_settings", [(True, True)], indirect=True) -async def test_agent_run_with_thread_and_kwargs(mock_chat_client_agent, model_diagnostic_settings): - """Test agent run with thread and additional kwargs.""" - from agent_framework.telemetry import use_agent_telemetry - - decorated_class = use_agent_telemetry(type(mock_chat_client_agent)) - agent = decorated_class() - - # Mock thread - mock_thread = Mock() - mock_thread.id = "test_thread_id" - - with ( - patch("agent_framework.telemetry.MODEL_DIAGNOSTICS_SETTINGS", model_diagnostic_settings), - patch("agent_framework.telemetry.use_span") as mock_use_span, - patch("agent_framework.telemetry._get_agent_run_span") as mock_get_span, - ): - mock_span = Mock() - mock_use_span.return_value.__enter__.return_value = mock_span - mock_use_span.return_value.__exit__.return_value = None - - # Test with thread and additional kwargs - response = await agent.run( - "Test message", thread=mock_thread, temperature=0.7, max_tokens=100, model="test-model" - ) - assert response is not None - - # Verify the span was created with the correct parameters - mock_get_span.assert_called_once() - call_kwargs = mock_get_span.call_args[1] - assert call_kwargs["agent"] == agent - assert call_kwargs["thread"] == mock_thread - assert call_kwargs["temperature"] == 0.7 - assert call_kwargs["max_tokens"] == 100 - assert call_kwargs["model"] == "test-model" - - -@pytest.mark.parametrize("model_diagnostic_settings", [(True, False)], indirect=True) -async def test_agent_run_with_list_messages(mock_chat_client_agent, model_diagnostic_settings): - """Test agent run with list of messages.""" - from agent_framework import ChatMessage, Role - from agent_framework.telemetry import use_agent_telemetry - - decorated_class = use_agent_telemetry(type(mock_chat_client_agent)) - agent = decorated_class() - - messages = [ - ChatMessage(role=Role.USER, text="First message"), - ChatMessage(role=Role.ASSISTANT, text="Response"), - ChatMessage(role=Role.USER, text="Second message"), - ] - - with ( - patch("agent_framework.telemetry.MODEL_DIAGNOSTICS_SETTINGS", model_diagnostic_settings), - patch("agent_framework.telemetry.use_span") as mock_use_span, - patch("agent_framework.telemetry._set_agent_run_input") as mock_set_input, - ): - mock_span = Mock() - mock_use_span.return_value.__enter__.return_value = mock_span - mock_use_span.return_value.__exit__.return_value = None - - response = await agent.run(messages) - assert response is not None - - # Verify input was set with the list of messages - mock_set_input.assert_called_once_with("test_agent_system", messages) - - -@pytest.mark.parametrize("model_diagnostic_settings", [(True, False)], indirect=True) -async def test_agent_run_with_exception_handling(mock_chat_client_agent, model_diagnostic_settings): +async def test_agent_run_with_exception_handling(mock_chat_client_agent: AgentProtocol): """Test agent run with exception handling.""" - from agent_framework.telemetry import use_agent_telemetry async def run_with_error(self, messages=None, *, thread=None, **kwargs): raise RuntimeError("Agent run error") - type(mock_chat_client_agent).run = run_with_error + mock_chat_client_agent.run = run_with_error - decorated_class = use_agent_telemetry(type(mock_chat_client_agent)) - agent = decorated_class() + agent = use_agent_telemetry(mock_chat_client_agent)() + + from opentelemetry.trace import Span with ( - patch("agent_framework.telemetry.MODEL_DIAGNOSTICS_SETTINGS", model_diagnostic_settings), - patch("agent_framework.telemetry.use_span") as mock_use_span, + patch("agent_framework.telemetry._get_span") as mock_get_span, ): - mock_span = Mock() - mock_use_span.return_value.__enter__.return_value = mock_span - mock_use_span.return_value.__exit__.return_value = None - + mock_span = MagicMock(spec=Span) + # Ensure the patched context manager returns mock_span when entered + mock_get_span.return_value.__enter__.return_value = mock_span # Should raise the exception and call error handler with pytest.raises(RuntimeError, match="Agent run error"): await agent.run("Test message") # Verify error was recorded # Check that both error attributes were set on the span - mock_span.set_attribute.assert_called_once_with( - GenAIAttributes.ERROR_TYPE.value, str(type(RuntimeError("Agent run error"))) + mock_span.set_attribute.assert_called_with(OtelAttr.ERROR_TYPE, "RuntimeError") + mock_span.record_exception.assert_called_once() + mock_span.set_status.assert_called_once_with( + status=StatusCode.ERROR, description=repr(RuntimeError("Agent run error")) ) - mock_span.set_status.assert_called_once_with(StatusCode.ERROR, repr(RuntimeError("Agent run error"))) diff --git a/python/packages/main/tests/main/test_tools.py b/python/packages/main/tests/main/test_tools.py index 537b8cc94e..11daff6716 100644 --- a/python/packages/main/tests/main/test_tools.py +++ b/python/packages/main/tests/main/test_tools.py @@ -15,7 +15,7 @@ from agent_framework import ( ) from agent_framework._tools import _parse_inputs from agent_framework.exceptions import ToolException -from agent_framework.telemetry import GenAIAttributes +from agent_framework.telemetry import OtelAttr # region AIFunction and ai_function decorator tests @@ -83,19 +83,23 @@ async def test_ai_function_decorator_with_async(): assert (await async_test_tool(1, 2)) == 3 -# Telemetry tests for AIFunction -async def test_ai_function_invoke_telemetry_enabled(): +@pytest.mark.parametrize("otel_settings", [(True, True)], indirect=True) +async def test_ai_function_invoke_telemetry_enabled(otel_settings): """Test the ai_function invoke method with telemetry enabled.""" - @ai_function(name="telemetry_test_tool", description="A test tool for telemetry") + @ai_function( + name="telemetry_test_tool", + description="A test tool for telemetry", + additional_properties={"otel_settings": otel_settings}, + ) def telemetry_test_tool(x: int, y: int) -> int: """A function that adds two numbers for telemetry testing.""" return x + y # Mock the tracer and span with ( - patch("agent_framework._tools.tracer") as mock_tracer, - patch("agent_framework._tools.start_as_current_span") as mock_start_span, + patch("agent_framework.telemetry.tracer"), + patch("agent_framework._tools.get_function_span") as mock_start_span, ): mock_span = Mock() mock_context_manager = Mock() @@ -114,23 +118,26 @@ async def test_ai_function_invoke_telemetry_enabled(): assert result == 3 # Verify telemetry calls - mock_start_span.assert_called_once_with( - mock_tracer, telemetry_test_tool, metadata={"tool_call_id": "test_call_id", "kwargs": {"x": 1, "y": 2}} - ) + mock_start_span.assert_called_once_with(function=telemetry_test_tool, tool_call_id="test_call_id") # Verify histogram was called with correct attributes mock_histogram.record.assert_called_once() call_args = mock_histogram.record.call_args assert call_args[0][0] > 0 # duration should be positive attributes = call_args[1]["attributes"] - assert attributes[GenAIAttributes.MEASUREMENT_FUNCTION_TAG_NAME.value] == "telemetry_test_tool" - assert attributes[GenAIAttributes.TOOL_CALL_ID.value] == "test_call_id" + assert attributes[OtelAttr.MEASUREMENT_FUNCTION_TAG_NAME] == "telemetry_test_tool" + assert attributes[OtelAttr.TOOL_CALL_ID] == "test_call_id" -async def test_ai_function_invoke_telemetry_with_pydantic_args(): +@pytest.mark.parametrize("otel_settings", [(True, True)], indirect=True) +async def test_ai_function_invoke_telemetry_with_pydantic_args(otel_settings): """Test the ai_function invoke method with Pydantic model arguments.""" - @ai_function(name="pydantic_test_tool", description="A test tool with Pydantic args") + @ai_function( + name="pydantic_test_tool", + description="A test tool with Pydantic args", + additional_properties={"otel_settings": otel_settings}, + ) def pydantic_test_tool(x: int, y: int) -> int: """A function that adds two numbers using Pydantic args.""" return x + y @@ -139,8 +146,8 @@ async def test_ai_function_invoke_telemetry_with_pydantic_args(): args_model = pydantic_test_tool.input_model(x=5, y=10) with ( - patch("agent_framework._tools.tracer") as mock_tracer, - patch("agent_framework._tools.start_as_current_span") as mock_start_span, + patch("agent_framework.telemetry.tracer"), + patch("agent_framework._tools.get_function_span") as mock_start_span, ): mock_span = Mock() mock_context_manager = Mock() @@ -159,21 +166,27 @@ async def test_ai_function_invoke_telemetry_with_pydantic_args(): # Verify telemetry calls mock_start_span.assert_called_once_with( - mock_tracer, pydantic_test_tool, metadata={"tool_call_id": "pydantic_call", "kwargs": {"x": 5, "y": 10}} + function=pydantic_test_tool, + tool_call_id="pydantic_call", ) -async def test_ai_function_invoke_telemetry_with_exception(): +@pytest.mark.parametrize("otel_settings", [(True, True)], indirect=True) +async def test_ai_function_invoke_telemetry_with_exception(otel_settings): """Test the ai_function invoke method with telemetry when an exception occurs.""" - @ai_function(name="exception_test_tool", description="A test tool that raises an exception") + @ai_function( + name="exception_test_tool", + description="A test tool that raises an exception", + additional_properties={"otel_settings": otel_settings}, + ) def exception_test_tool(x: int, y: int) -> int: """A function that raises an exception for telemetry testing.""" raise ValueError("Test exception for telemetry") with ( - patch("agent_framework._tools.tracer"), - patch("agent_framework._tools.start_as_current_span") as mock_start_span, + patch("agent_framework.telemetry.tracer"), + patch("agent_framework._tools.get_function_span") as mock_start_span, ): mock_span = Mock() mock_context_manager = Mock() @@ -200,20 +213,25 @@ async def test_ai_function_invoke_telemetry_with_exception(): mock_histogram.record.assert_called_once() call_args = mock_histogram.record.call_args attributes = call_args[1]["attributes"] - assert attributes[GenAIAttributes.ERROR_TYPE.value] == "ValueError" + assert attributes[OtelAttr.ERROR_TYPE] == ValueError.__name__ -async def test_ai_function_invoke_telemetry_async_function(): +@pytest.mark.parametrize("otel_settings", [(True, True)], indirect=True) +async def test_ai_function_invoke_telemetry_async_function(otel_settings): """Test the ai_function invoke method with telemetry on async function.""" - @ai_function(name="async_telemetry_test", description="An async test tool for telemetry") + @ai_function( + name="async_telemetry_test", + description="An async test tool for telemetry", + additional_properties={"otel_settings": otel_settings}, + ) async def async_telemetry_test(x: int, y: int) -> int: """An async function for telemetry testing.""" return x * y with ( - patch("agent_framework._tools.tracer") as mock_tracer, - patch("agent_framework._tools.start_as_current_span") as mock_start_span, + patch("agent_framework.telemetry.tracer"), + patch("agent_framework._tools.get_function_span") as mock_start_span, ): mock_span = Mock() mock_context_manager = Mock() @@ -231,54 +249,13 @@ async def test_ai_function_invoke_telemetry_async_function(): assert result == 12 # Verify telemetry calls - mock_start_span.assert_called_once_with( - mock_tracer, async_telemetry_test, metadata={"tool_call_id": "async_call", "kwargs": {"x": 3, "y": 4}} - ) + mock_start_span.assert_called_once_with(function=async_telemetry_test, tool_call_id="async_call") # Verify histogram recording mock_histogram.record.assert_called_once() call_args = mock_histogram.record.call_args attributes = call_args[1]["attributes"] - assert attributes[GenAIAttributes.MEASUREMENT_FUNCTION_TAG_NAME.value] == "async_telemetry_test" - - -async def test_ai_function_invoke_telemetry_no_tool_call_id(): - """Test the ai_function invoke method with telemetry when no tool_call_id is provided.""" - - @ai_function(name="no_id_test_tool", description="A test tool without tool_call_id") - def no_id_test_tool(x: int) -> int: - """A function for testing without tool_call_id.""" - return x * 2 - - with ( - patch("agent_framework._tools.tracer") as mock_tracer, - patch("agent_framework._tools.start_as_current_span") as mock_start_span, - ): - mock_span = Mock() - mock_context_manager = Mock() - mock_context_manager.__enter__ = Mock(return_value=mock_span) - mock_context_manager.__exit__ = Mock(return_value=None) - mock_start_span.return_value = mock_context_manager - - mock_histogram = Mock() - no_id_test_tool._invocation_duration_histogram = mock_histogram - - # Call invoke without tool_call_id - result = await no_id_test_tool.invoke(x=5) - - # Verify result - assert result == 10 - - # Verify telemetry calls - mock_start_span.assert_called_once_with( - mock_tracer, no_id_test_tool, metadata={"tool_call_id": None, "kwargs": {"x": 5}} - ) - - # Verify histogram attributes - mock_histogram.record.assert_called_once() - call_args = mock_histogram.record.call_args - attributes = call_args[1]["attributes"] - assert attributes[GenAIAttributes.TOOL_CALL_ID.value] is None + assert attributes[OtelAttr.MEASUREMENT_FUNCTION_TAG_NAME] == "async_telemetry_test" async def test_ai_function_invoke_invalid_pydantic_args(): diff --git a/python/packages/main/tests/openai/conftest.py b/python/packages/main/tests/openai/conftest.py index beb20ead82..7bedccd52d 100644 --- a/python/packages/main/tests/openai/conftest.py +++ b/python/packages/main/tests/openai/conftest.py @@ -3,6 +3,8 @@ from typing import Any from pytest import fixture +from agent_framework.telemetry import OtelSettings, setup_telemetry + # region Connector Settings fixtures @fixture @@ -49,3 +51,26 @@ def openai_unit_test_env(monkeypatch, exclude_list, override_env_param_dict): # monkeypatch.setenv(key, value) # type: ignore return env_vars + + +@fixture +def enable_otel(request: Any) -> bool: + """Fixture that returns a boolean indicating if Otel is enabled.""" + return request.param if hasattr(request, "param") else True + + +@fixture +def enable_sensitive_data(request: Any) -> bool: + """Fixture that returns a boolean indicating if sensitive data is enabled.""" + return request.param if hasattr(request, "param") else False + + +@fixture +def otel_settings(enable_otel: bool, enable_sensitive_data: bool) -> OtelSettings: + """Fixture to set environment variables for OtelSettings.""" + + from agent_framework.telemetry import OTEL_SETTINGS + + setup_telemetry(enable_otel=enable_otel, enable_sensitive_data=enable_sensitive_data) + + return OTEL_SETTINGS diff --git a/python/packages/main/tests/openai/test_openai_responses_client.py b/python/packages/main/tests/openai/test_openai_responses_client.py index 6b89329f4e..4f2846a981 100644 --- a/python/packages/main/tests/openai/test_openai_responses_client.py +++ b/python/packages/main/tests/openai/test_openai_responses_client.py @@ -373,7 +373,7 @@ def test_chat_message_parsing_with_function_calls() -> None: asyncio.run(client.get_response(messages=messages)) -def test_response_format_parse_path() -> None: +async def test_response_format_parse_path() -> None: """Test get_response response_format parsing path.""" client = OpenAIResponsesClient(ai_model_id="test-model", api_key="test-key") @@ -386,19 +386,18 @@ def test_response_format_parse_path() -> None: mock_parsed_response.metadata = {} mock_parsed_response.output_parsed = None mock_parsed_response.usage = None + mock_parsed_response.finish_reason = None with patch.object(client.client.responses, "parse", return_value=mock_parsed_response): - response = asyncio.run( - client.get_response( - messages=[ChatMessage(role="user", text="Test message")], response_format=OutputStruct, store=True - ) + response = await client.get_response( + messages=[ChatMessage(role="user", text="Test message")], response_format=OutputStruct, store=True ) assert response.conversation_id == "parsed_response_123" assert response.ai_model_id == "test-model" -def test_bad_request_error_non_content_filter() -> None: +async def test_bad_request_error_non_content_filter() -> None: """Test get_response BadRequestError without content_filter.""" client = OpenAIResponsesClient(ai_model_id="test-model", api_key="test-key") @@ -412,10 +411,8 @@ def test_bad_request_error_non_content_filter() -> None: with patch.object(client.client.responses, "parse", side_effect=mock_error): with pytest.raises(ServiceResponseException) as exc_info: - asyncio.run( - client.get_response( - messages=[ChatMessage(role="user", text="Test message")], response_format=OutputStruct - ) + await client.get_response( + messages=[ChatMessage(role="user", text="Test message")], response_format=OutputStruct ) assert "failed to complete the prompt" in str(exc_info.value) @@ -440,11 +437,13 @@ async def test_streaming_content_filter_exception_handling() -> None: break -def test_get_streaming_response_with_all_parameters() -> None: +@skip_if_openai_integration_tests_disabled +async def test_get_streaming_response_with_all_parameters() -> None: """Test get_streaming_response with all possible parameters.""" client = OpenAIResponsesClient(ai_model_id="test-model", api_key="test-key") - async def run_streaming_test(): + # Should fail due to invalid API key + with pytest.raises(ServiceResponseException): response = client.get_streaming_response( messages=[ChatMessage(role="user", text="Test streaming")], include=["file_search_call.results"], @@ -471,10 +470,6 @@ def test_get_streaming_response_with_all_parameters() -> None: async for _ in response: break - # Should fail due to invalid API key - with pytest.raises(ServiceResponseException): - asyncio.run(run_streaming_test()) - def test_response_content_creation_with_annotations() -> None: """Test _create_response_content with different annotation types.""" @@ -731,7 +726,9 @@ def test_create_streaming_response_content_with_mcp_approval_request() -> None: assert fa.function_call.name == "do_stream_action" -def test_end_to_end_mcp_approval_flow() -> None: +@pytest.mark.parametrize("enable_otel", [False], indirect=True) +@pytest.mark.parametrize("enable_sensitive_data", [False], indirect=True) +def test_end_to_end_mcp_approval_flow(otel_settings) -> None: """End-to-end mocked test: model issues an mcp_approval_request, user approves, client sends mcp_approval_response. """ diff --git a/python/packages/workflow/agent_framework_workflow/_telemetry.py b/python/packages/workflow/agent_framework_workflow/_telemetry.py index 625e81aa67..682a90b040 100644 --- a/python/packages/workflow/agent_framework_workflow/_telemetry.py +++ b/python/packages/workflow/agent_framework_workflow/_telemetry.py @@ -35,11 +35,11 @@ class WorkflowDiagnosticSettings(AFBaseSettings): """Settings for workflow tracing diagnostics.""" env_prefix: ClassVar[str] = "AGENT_FRAMEWORK_WORKFLOW_" - enable_otel_diagnostics: bool = False + enable_otel: bool = False @property def ENABLED(self) -> bool: - return self.enable_otel_diagnostics + return self.enable_otel class WorkflowTracer: diff --git a/python/packages/workflow/tests/test_tracing.py b/python/packages/workflow/tests/test_tracing.py index 021012f6fd..d42132402a 100644 --- a/python/packages/workflow/tests/test_tracing.py +++ b/python/packages/workflow/tests/test_tracing.py @@ -22,8 +22,8 @@ from agent_framework_workflow._workflow_context import WorkflowContext @pytest.fixture def tracing_enabled() -> Generator[None, None, None]: """Enable tracing for tests.""" - original_value = os.environ.get("AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL_DIAGNOSTICS") - os.environ["AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL_DIAGNOSTICS"] = "true" + original_value = os.environ.get("AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL") + os.environ["AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL"] = "true" # Force reload the settings to pick up the environment variable from agent_framework_workflow._telemetry import WorkflowDiagnosticSettings @@ -34,9 +34,9 @@ def tracing_enabled() -> Generator[None, None, None]: # Restore original value if original_value is None: - os.environ.pop("AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL_DIAGNOSTICS", None) + os.environ.pop("AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL", None) else: - os.environ["AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL_DIAGNOSTICS"] = original_value + os.environ["AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL"] = original_value # Reload settings again workflow_tracer.settings = WorkflowDiagnosticSettings() @@ -142,7 +142,6 @@ class FanInAggregator(Executor): return self._processed_messages -@pytest.mark.asyncio async def test_workflow_tracer_configuration() -> None: """Test that workflow tracer can be enabled and disabled.""" # Test disabled by default @@ -150,8 +149,8 @@ async def test_workflow_tracer_configuration() -> None: assert not tracer.enabled # Test enabled with environment variable - original_value = os.environ.get("AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL_DIAGNOSTICS") - os.environ["AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL_DIAGNOSTICS"] = "true" + original_value = os.environ.get("AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL") + os.environ["AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL"] = "true" # Force reload the settings to pick up the environment variable from agent_framework_workflow._telemetry import WorkflowDiagnosticSettings @@ -162,15 +161,14 @@ async def test_workflow_tracer_configuration() -> None: # Restore original value if original_value is None: - os.environ.pop("AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL_DIAGNOSTICS", None) + os.environ.pop("AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL", None) else: - os.environ["AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL_DIAGNOSTICS"] = original_value + os.environ["AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL"] = original_value # Reload settings again tracer.settings = WorkflowDiagnosticSettings() -@pytest.mark.asyncio async def test_span_creation_and_attributes(tracing_enabled: Any, span_exporter: InMemorySpanExporter) -> None: """Test creation and attributes of all span types (workflow, processing, sending).""" # Create a mock workflow object @@ -228,7 +226,6 @@ async def test_span_creation_and_attributes(tracing_enabled: Any, span_exporter: assert sending_span.attributes.get("message.destination_executor_id") == "target-789" -@pytest.mark.asyncio async def test_trace_context_handling(tracing_enabled: Any, span_exporter: InMemorySpanExporter) -> None: """Test trace context propagation and handling in messages and executors.""" shared_state = SharedState() diff --git a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_local_mcp.py b/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_local_mcp.py index 11302318de..a3935b87a5 100644 --- a/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_local_mcp.py +++ b/python/samples/getting_started/agents/openai_responses_client/openai_responses_client_with_local_mcp.py @@ -13,7 +13,6 @@ async def streaming_with_mcp(show_raw_stream: bool = False) -> None: through the raw_representation. You can view this, by setting the show_raw_stream parameter to True. """ print("=== Tools Defined on Agent Level ===") - # Tools are provided when creating the agent # The agent can use these tools for any query during its lifetime async with ChatAgent( diff --git a/python/samples/getting_started/telemetry/.env.example b/python/samples/getting_started/telemetry/.env.example index 5e8bced445..3c1632a83a 100644 --- a/python/samples/getting_started/telemetry/.env.example +++ b/python/samples/getting_started/telemetry/.env.example @@ -1,5 +1,13 @@ -CONNECTION_STRING="..." +# Connector environment variables +# Foundry +# see ../../../env.example for details +# OpenAI +# see ../../../env.example for details + +# Otel specific variables +APPLICATION_INSIGHTS_CONNECTION_STRING="..." +APPLICATION_INSIGHTS_LIVE_METRICS=true OTLP_ENDPOINT="http://localhost:4317/" -AGENT_FRAMEWORK_GENAI_ENABLE_OTEL_DIAGNOSTICS=true -AGENT_FRAMEWORK_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE=true -AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL_DIAGNOSTICS=true +ENABLE_OTEL=true +ENABLE_SENSITIVE_DATA=true +WORKFLOW_ENABLE_OTEL=true diff --git a/python/samples/getting_started/telemetry/01-zero_code.py b/python/samples/getting_started/telemetry/01-zero_code.py new file mode 100644 index 0000000000..6fd6e06e79 --- /dev/null +++ b/python/samples/getting_started/telemetry/01-zero_code.py @@ -0,0 +1,86 @@ +# Copyright (c) Microsoft. All rights reserved. +# type: ignore +import asyncio +import os +from random import randint +from typing import TYPE_CHECKING, Annotated + +from agent_framework.openai import OpenAIResponsesClient +from pydantic import Field + +if TYPE_CHECKING: + from agent_framework import ChatClientProtocol + + +""" +This is the simplest sample of using the Agent Framework with telemetry. +Since it does not create a tracer or span in the script's code, we can let the Agent Framework SDK handle everything. +If the environment variables are set correctly, +the SDK will automatically initialize telemetry and collect traces and logs. +""" + + +if "AGENT_FRAMEWORK_ENABLE_OTEL" not in os.environ: + print("Set AGENT_FRAMEWORK_ENABLE_OTEL to enable telemetry with a OTLP endpoint.") +if "AGENT_FRAMEWORK_OTLP_ENDPOINT" not in os.environ and "AGENT_FRAMEWORK_MONITOR_CONNECTION_STRING" not in os.environ: + print("Set AGENT_FRAMEWORK_OTLP_ENDPOINT or AGENT_FRAMEWORK_MONITOR_CONNECTION_STRING to enable telemetry.") + + +async def get_weather( + location: Annotated[str, Field(description="The location to get the weather for.")], +) -> str: + """Get the weather for a given location.""" + await asyncio.sleep(randint(0, 10) / 10.0) # Simulate a network call + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +async def run_chat_client(client: "ChatClientProtocol", stream: bool = False) -> None: + """Run an AI service. + + This function runs an AI service and prints the output. + Telemetry will be collected for the service execution behind the scenes, + and the traces will be sent to the configured telemetry backend. + + The telemetry will include information about the AI service execution. + + Args: + stream: Whether to use streaming for the plugin + + Remarks: + When function calling is outside the open telemetry loop + each of the call to the model is handled as a seperate span, + while when the open telemetry is put last, a single span + is shown, which might include one or more rounds of function calling. + + So for the scenario below, you should see the following: + + 2 spans with gen_ai.operation.name=chat + The first has finish_reason "tool_calls" + The second has finish_reason "stop" + 2 spans with gen_ai.operation.name=execute_tool + + """ + message = "What's the weather in Amsterdam and in Paris?" + print(f"User: {message}") + if stream: + print("Assistant: ", end="") + async for chunk in client.get_streaming_response(message, tools=get_weather): + if str(chunk): + print(str(chunk), end="") + print("") + else: + response = await client.get_response(message, tools=get_weather) + print(f"Assistant: {response}") + + +async def main() -> None: + client = OpenAIResponsesClient() + + # Scenarios where telemetry is collected in the SDK, from the most basic to the most complex. + await run_chat_client(client, stream=True) + await run_chat_client(client, stream=False) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/getting_started/telemetry/02a-generic_chat_client.py b/python/samples/getting_started/telemetry/02a-generic_chat_client.py new file mode 100644 index 0000000000..6fb85a440a --- /dev/null +++ b/python/samples/getting_started/telemetry/02a-generic_chat_client.py @@ -0,0 +1,134 @@ +# Copyright (c) Microsoft. All rights reserved. +# type: ignore +import argparse +import asyncio +from contextlib import suppress +from random import randint +from typing import TYPE_CHECKING, Annotated, Literal + +from agent_framework import __version__, ai_function +from agent_framework.openai import OpenAIResponsesClient +from agent_framework.telemetry import setup_telemetry +from opentelemetry import trace +from opentelemetry.trace import SpanKind +from opentelemetry.trace.span import format_trace_id +from pydantic import Field + +if TYPE_CHECKING: + from agent_framework import ChatClientProtocol + +""" +This sample, show how you can get telemetry from a chat client and tool. +it explicitly calls the `setup_telemetry` function to set up telemetry in order to include the overall spans, +those are defined in the main and run_* functions. +""" + + +# Define the scenarios that can be run +SCENARIOS = ["chat_client", "chat_client_stream", "ai_function", "all"] + + +async def get_weather( + location: Annotated[str, Field(description="The location to get the weather for.")], +) -> str: + """Get the weather for a given location.""" + await asyncio.sleep(randint(0, 10) / 10.0) # Simulate a network call + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +async def run_chat_client(client: "ChatClientProtocol", stream: bool = False) -> None: + """Run an AI service. + + This function runs an AI service and prints the output. + Telemetry will be collected for the service execution behind the scenes, + and the traces will be sent to the configured telemetry backend. + + The telemetry will include information about the AI service execution. + + Args: + client: The chat client to use. + stream: Whether to use streaming for the response + + Remarks: + For the scenario below, you should see the following: + 1 Client span, with 4 children: + 2 Internal span with gen_ai.operation.name=chat + The first has finish_reason "tool_calls" + The second has finish_reason "stop" + 2 Internal span with gen_ai.operation.name=execute_tool + + """ + scenario_name = "Chat Client Stream" if stream else "Chat Client" + + tracer = trace.get_tracer("agent_framework", __version__) + with tracer.start_as_current_span(name=f"Scenario: {scenario_name}", kind=SpanKind.CLIENT): + print("Running scenario:", scenario_name) + message = "What's the weather in Amsterdam and in Paris?" + print(f"User: {message}") + if stream: + print("Assistant: ", end="") + async for chunk in client.get_streaming_response(message, tools=get_weather): + if str(chunk): + print(str(chunk), end="") + print("") + else: + response = await client.get_response(message, tools=get_weather) + print(f"Assistant: {response}") + + +async def run_ai_function() -> None: + """Run a AI function. + + This function runs a AI function and prints the output. + Telemetry will be collected for the function execution behind the scenes, + and the traces will be sent to the configured telemetry backend. + + The telemetry will include information about the AI function execution + and the AI service execution. + """ + + tracer = trace.get_tracer("agent_framework", __version__) + with tracer.start_as_current_span("Scenario: AI Function", kind=SpanKind.CLIENT): + print("Running scenario: AI Function") + func = ai_function(get_weather) + weather = await func.invoke(location="Amsterdam") + print(f"Weather in Amsterdam:\n{weather}") + + +async def main(scenario: Literal["chat_client", "chat_client_stream", "ai_function", "all"] = "all"): + """Run the selected scenario(s).""" + + setup_telemetry() + + tracer = trace.get_tracer("My application", __version__) + with tracer.start_as_current_span("Sample Scenario's", kind=SpanKind.CLIENT) as current_span: + print(f"Trace ID: {format_trace_id(current_span.get_span_context().trace_id)}") + + client = OpenAIResponsesClient() + + # Scenarios where telemetry is collected in the SDK, from the most basic to the most complex. + if scenario == "chat_client_stream" or scenario == "all": + with suppress(Exception): + await run_chat_client(client, stream=True) + if scenario == "chat_client" or scenario == "all": + with suppress(Exception): + await run_chat_client(client, stream=False) + if scenario == "ai_function" or scenario == "all": + with suppress(Exception): + await run_ai_function() + + +if __name__ == "__main__": + arg_parser = argparse.ArgumentParser() + + arg_parser.add_argument( + "--scenario", + type=str, + choices=SCENARIOS, + default="all", + help="The scenario to run. Default is all.", + ) + + args = arg_parser.parse_args() + asyncio.run(main(args.scenario)) diff --git a/python/samples/getting_started/telemetry/02b-foundry_chat_client.py b/python/samples/getting_started/telemetry/02b-foundry_chat_client.py new file mode 100644 index 0000000000..fa4706a016 --- /dev/null +++ b/python/samples/getting_started/telemetry/02b-foundry_chat_client.py @@ -0,0 +1,88 @@ +# Copyright (c) Microsoft. All rights reserved. +# type: ignore +import asyncio +import os +from random import randint +from typing import Annotated + +from agent_framework import HostedCodeInterpreterTool +from agent_framework.telemetry import setup_telemetry +from agent_framework_foundry import FoundryChatClient +from azure.ai.projects.aio import AIProjectClient +from azure.identity.aio import AzureCliCredential +from opentelemetry import trace +from opentelemetry.trace import SpanKind +from opentelemetry.trace.span import format_trace_id +from pydantic import Field + +""" +This sample, shows you can leverage the built-in telemetry in Foundry. +It uses the Foundry client to setup the telemetry, this calls +out to Foundry for a telemetry connection strings, +and then call the setup_telemetry function in the agent framework. +If you want to compare with the trace sent to a generic OTLP endpoint, +switch the `use_foundry_telemetry` variable to False. +""" + + +# ANSI color codes for printing in blue and resetting after each print +BLUE = "\x1b[34m" +RESET = "\x1b[0m" + + +async def get_weather( + location: Annotated[str, Field(description="The location to get the weather for.")], +) -> str: + """Get the weather for a given location.""" + await asyncio.sleep(randint(0, 10) / 10.0) # Simulate a network call + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +async def main() -> None: + """Run an AI service. + + This function runs an AI service and prints the output. + Telemetry will be collected for the service execution behind the scenes, + and the traces will be sent to the configured telemetry backend. + + The telemetry will include information about the AI service execution. + + In foundry you will also see specific operations happening that are called by the Foundry implementation, + such as `create_agent`. + """ + use_foundry_telemetry = True + questions = [ + "What's the weather in Amsterdam and in Paris?", + "Why is the sky blue?", + "Tell me about AI.", + "Can you write a python function that adds two numbers? and use it to add 8483 and 5692?", + ] + async with ( + AzureCliCredential() as credential, + AIProjectClient(endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], credential=credential) as project, + FoundryChatClient(client=project, setup_tracing=False) as client, + ): + if use_foundry_telemetry: + await client.setup_foundry_telemetry(enable_live_metrics=True) + else: + setup_telemetry() + + tracer = trace.get_tracer("agent_framework") + with tracer.start_as_current_span(name="Foundry Telemetry from Agent Framework", kind=SpanKind.CLIENT) as span: + for question in questions: + print(f"{BLUE}User: {question}{RESET}") + print(f"{BLUE}Assistant: {RESET}", end="") + async for chunk in client.get_streaming_response( + question, tools=[get_weather, HostedCodeInterpreterTool()] + ): + if str(chunk): + print(f"{BLUE}{str(chunk)}{RESET}", end="") + print(f"{BLUE}{RESET}") + + print(f"{BLUE}Done{RESET}") + print(f"{BLUE}Operation ID: {format_trace_id(span.get_span_context().trace_id)}{RESET}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/getting_started/telemetry/03a-agent.py b/python/samples/getting_started/telemetry/03a-agent.py new file mode 100644 index 0000000000..4bdb111321 --- /dev/null +++ b/python/samples/getting_started/telemetry/03a-agent.py @@ -0,0 +1,59 @@ +# Copyright (c) Microsoft. All rights reserved. +# type: ignore +import asyncio +from random import randint +from typing import Annotated + +from agent_framework import ChatAgent +from agent_framework.openai import OpenAIChatClient +from agent_framework.telemetry import setup_telemetry +from opentelemetry import trace +from opentelemetry.trace import SpanKind +from pydantic import Field + +""" +This sample shows you can can setup telemetry with a agent. +The agent invoke is a additional Semantic Convention that now +will wrap the calls made by the underlying chat client and tools. +""" + + +async def get_weather( + location: Annotated[str, Field(description="The location to get the weather for.")], +) -> str: + """Get the weather for a given location.""" + await asyncio.sleep(randint(0, 10) / 10.0) # Simulate a network call + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +async def main(): + # Set up the telemetry + setup_telemetry() + + questions = ["What's the weather in Amsterdam?", "and in Paris, and which is better?", "Why is the sky blue?"] + + tracer = trace.get_tracer("agent_framework") + with tracer.start_as_current_span("Scenario: Agent Chat", kind=SpanKind.CLIENT): + print("Running scenario: Agent Chat") + print("Welcome to the chat, type 'exit' to quit.") + agent = ChatAgent( + chat_client=OpenAIChatClient(), + tools=get_weather, + name="WeatherAgent", + instructions="You are a weather assistant.", + ) + thread = agent.get_new_thread() + for question in questions: + print(f"User: {question}") + print(f"{agent.display_name}: ", end="") + async for update in agent.run_stream( + question, + thread=thread, + ): + if update.text: + print(update.text, end="") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/getting_started/telemetry/03b-foundry_agent.py b/python/samples/getting_started/telemetry/03b-foundry_agent.py new file mode 100644 index 0000000000..4c5bc414a3 --- /dev/null +++ b/python/samples/getting_started/telemetry/03b-foundry_agent.py @@ -0,0 +1,65 @@ +# Copyright (c) Microsoft. All rights reserved. +# type: ignore +import asyncio +import os +from random import randint +from typing import Annotated + +from agent_framework import ChatAgent +from agent_framework_foundry import FoundryChatClient +from azure.ai.projects.aio import AIProjectClient +from azure.identity.aio import AzureCliCredential +from opentelemetry import trace +from opentelemetry.trace import SpanKind +from pydantic import Field + +""" +This sample shows you can can setup telemetry with a agent from Foundry. +We once again call the `setup_foundry_telemetry` method to set up telemetry in order to include the overall spans. +""" + + +async def get_weather( + location: Annotated[str, Field(description="The location to get the weather for.")], +) -> str: + """Get the weather for a given location.""" + await asyncio.sleep(randint(0, 10) / 10.0) # Simulate a network call + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +async def main(): + # Set up the providers + # This must be done before any other telemetry calls + questions = ["What's the weather in Amsterdam?", "and in Paris, and which is better?", "Why is the sky blue?"] + async with ( + AzureCliCredential() as credential, + AIProjectClient(endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], credential=credential) as project, + # this calls `setup_foundry_telemetry` through the context manager + FoundryChatClient(client=project) as client, + ): + await client.setup_foundry_telemetry(enable_live_metrics=True) + tracer = trace.get_tracer("agent_framework") + with tracer.start_as_current_span("Single Agent Chat", kind=SpanKind.CLIENT): + print("Running Single Agent Chat") + print("Welcome to the chat, type 'exit' to quit.") + agent = ChatAgent( + chat_client=client, + tools=get_weather, + name="WeatherAgent", + instructions="You are a weather assistant.", + ) + thread = agent.get_new_thread() + for question in questions: + print(f"User: {question}") + print(f"{agent.display_name}: ", end="") + async for update in agent.run_stream( + question, + thread=thread, + ): + if update.text: + print(update.text, end="") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/getting_started/telemetry/04-workflow.py b/python/samples/getting_started/telemetry/04-workflow.py new file mode 100644 index 0000000000..e07df885e2 --- /dev/null +++ b/python/samples/getting_started/telemetry/04-workflow.py @@ -0,0 +1,120 @@ +# Copyright (c) Microsoft. All rights reserved. +# type: ignore +import asyncio +from typing import Any + +from agent_framework.telemetry import setup_telemetry +from agent_framework.workflow import ( + Executor, + WorkflowBuilder, + WorkflowCompletedEvent, + WorkflowContext, + handler, +) +from opentelemetry import trace +from opentelemetry.trace import SpanKind +from opentelemetry.trace.span import format_trace_id + +"""Telemetry sample demonstrating OpenTelemetry integration with Agent Framework workflows. + +This sample runs a simple sequential workflow with telemetry collection, +showing telemetry collection for workflow execution, executor processing, +and message publishing between executors. +""" + + +# Executors for sequential workflow +class UpperCaseExecutor(Executor): + """An executor that converts text to uppercase.""" + + @handler + async def to_upper_case(self, text: str, ctx: WorkflowContext[str]) -> None: + """Execute the task by converting the input string to uppercase.""" + print(f"UpperCaseExecutor: Processing '{text}'") + result = text.upper() + print(f"UpperCaseExecutor: Result '{result}'") + + # Send the result to the next executor in the workflow. + await ctx.send_message(result) + + +class ReverseTextExecutor(Executor): + """An executor that reverses text.""" + + @handler + async def reverse_text(self, text: str, ctx: WorkflowContext[Any]) -> None: + """Execute the task by reversing the input string.""" + print(f"ReverseTextExecutor: Processing '{text}'") + result = text[::-1] + print(f"ReverseTextExecutor: Result '{result}'") + + # Send the result with a workflow completion event. + await ctx.add_event(WorkflowCompletedEvent(result)) + + +async def run_sequential_workflow() -> None: + """Run a simple sequential workflow demonstrating telemetry collection. + + This workflow processes a string through two executors in sequence: + 1. UpperCaseExecutor converts the input to uppercase + 2. ReverseTextExecutor reverses the string and completes the workflow + + Telemetry data collected includes: + - Overall workflow execution spans + - Individual executor processing spans + - Message publishing between executors + - Workflow completion events + """ + + tracer = trace.get_tracer(__name__) + with tracer.start_as_current_span("Scenario: Sequential Workflow", kind=SpanKind.CLIENT) as current_span: + print("Running scenario: Sequential Workflow") + try: + # Step 1: Create the executors. + upper_case_executor = UpperCaseExecutor(id="upper_case_executor") + reverse_text_executor = ReverseTextExecutor(id="reverse_text_executor") + + # Step 2: Build the workflow with the defined edges. + workflow = ( + WorkflowBuilder() + .add_edge(upper_case_executor, reverse_text_executor) + .set_start_executor(upper_case_executor) + .build() + ) + + # Step 3: Run the workflow with an initial message. + input_text = "hello world" + print(f"Starting workflow with input: '{input_text}'") + + completion_event = None + async for event in workflow.run_stream(input_text): + print(f"Event: {event}") + if isinstance(event, WorkflowCompletedEvent): + # The WorkflowCompletedEvent contains the final result. + completion_event = event + + if completion_event: + print(f"Workflow completed with result: '{completion_event.data}'") + else: + print("Workflow completed without a completion event") + + except Exception as e: + current_span.record_exception(e) + print(f"Error running workflow: {e}") + + +async def main(): + """Run the telemetry sample with a simple sequential workflow.""" + + setup_telemetry() + + tracer = trace.get_tracer("agent_framework") + with tracer.start_as_current_span("Sequential Workflow Scenario", kind=SpanKind.CLIENT) as current_span: + print(f"Trace ID: {format_trace_id(current_span.get_span_context().trace_id)}") + + # Run the sequential workflow scenario + await run_sequential_workflow() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/getting_started/telemetry/README.md b/python/samples/getting_started/telemetry/README.md index 5d947cda17..b574aa4251 100644 --- a/python/samples/getting_started/telemetry/README.md +++ b/python/samples/getting_started/telemetry/README.md @@ -1,8 +1,8 @@ # Agent Framework Python Telemetry -This sample project shows how a Python application can be configured to send Agent Framework telemetry to the Application Performance Management (APM) vendors of your choice. +This sample folder shows how a Python application can be configured to send Agent Framework telemetry to the Application Performance Management (APM) vendors of your choice. -In this sample, we provide options to send telemetry to [Application Insights](https://learn.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview), [Aspire Dashboard](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/overview?tabs=bash), and console output. +In this sample, we provide options to send telemetry to [Application Insights](https://learn.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview) and [Aspire Dashboard](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/overview?tabs=bash). > **Quick Start**: For local development without Azure setup, you can use the [Aspire Dashboard](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/standalone) which runs locally via Docker and provides an excellent telemetry viewing experience for OpenTelemetry data. @@ -24,6 +24,7 @@ The Agent Framework Python SDK is designed to efficiently generate comprehensive ### Required resources 2. OpenAI or [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/create-resource?pivots=web-portal) +2. [Foundry project](https://ai.azure.com/doc/azure/ai-foundry/what-is-azure-ai-foundry) ### Optional resources @@ -31,61 +32,51 @@ The Agent Framework Python SDK is designed to efficiently generate comprehensive 2. [Aspire Dashboard](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/standalone-for-python?tabs=flask%2Cwindows#start-the-aspire-dashboard) ### Dependencies +No additional dependencies are required to enable telemetry. The necessary packages are included as part of the `agent-framework` package. Unless you want to use a different APM vendor, in which case you will need to install the appropriate OpenTelemetry exporter package. -You will also need to install the following dependencies to your virtual environment to run this sample: +### Environment variables +The following environment variables can be set to configure telemetry, the first two set the basic configuration: -```bash -# For Azure ApplicationInsights/AzureMonitor -uv pip install azure-monitor-opentelemetry azure-monitor-opentelemetry-exporter -# For OTLP endpoint -uv pip install opentelemetry-exporter-otlp-proto-grpc -``` +- AGENT_FRAMEWORK_ENABLE_OTEL=true +- AGENT_FRAMEWORK_ENABLE_SENSITIVE_DATA=true -## Running the sample +Next we need to know where to send the telemetry, for that you can use either a OTLP endpoint or a connection string for Application Insights: +- AGENT_FRAMEWORK_OTLP_ENDPOINT="" +or +- AGENT_FRAMEWORK_MONITOR_CONNECTION_STRING="" +Finally, you can enable live metrics streaming to Application Insights: +- AGENT_FRAMEWORK_MONITOR_LIVE_METRICS=true + +> IMPORTANT - If both OTLP endpoint and connection string are set, the connection string will take precedence and there will be no trace to the OTLP endpoint. + +## Samples +This folder contains different samples demonstrating how to use telemetry in various scenarios. + +### [01 - zero_code](./01-zero_code.py): +A simple example showing how to enable telemetry in a zero-touch scenario. When the above environment variables are set, telemetry will be automatically enabled, however since you do not define any overarching tracer, you will only see the spans for the specific calls to the chat client and tools. + +### [02a](./02a-generic_chat_client.py) and [02b](./02b-foundry_chat_client.py) Chat Clients: +These two samples show how to first setup the telemetry by manually importing the `setup_telemetry` function from the `agent_framework.telemetry` module and calling it. After this is done, the trace that get's created will live in the same context as the chat client calls, allowing you to see the end-to-end flow of your application. For Foundry, there is a method in the Foundry project client to get the telemetry url for your project, the `.setup_foundry_telemetry()` method in the `FoundryChatClient` class will use this url to configure telemetry and you then do not have to import and call `setup_telemetry()` manually. +Because of the way OpenTelemetry works, you can only call `setup_telemetry()` once per application run, so make sure you do that in the right place. + +### [03a](./03a-generic_agent.py) and [03b](./03b-foundry_agent.py) Agents: +These two samples show how to setup telemetry when using the Agent Framework's agent abstraction layer. They are similar to the chat client samples, but also show how to create an agent and invoke it. The same rules apply for setting up telemetry, you can either call `setup_telemetry()` manually, or use the `setup_foundry_telemetry()` method in the `FoundryChatClient` class. + +### [04 - workflow](./04-workflow.py) Workflow: +This sample shows how to setup telemetry when using the Agent Framework's workflow execution engine. It demonstrates a simple workflow scenario with telemetry. + + +## Running the samples 1. Open a terminal and navigate to this folder: `python/samples/getting_started/telemetry/`. This is necessary for the `.env` file to be read correctly. 2. Create a `.env` file if one doesn't already exist in this folder. Please refer to the [example file](./.env.example). > Note that `CONNECTION_STRING` and `SAMPLE_OTLP_ENDPOINT` are optional. If you don't configure them, everything will get outputted to the console. - > Set `AGENT_FRAMEWORK_GENAI_ENABLE_OTEL_DIAGNOSTICS=true` to enable basic telemetry and `AGENT_FRAMEWORK_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE=true` to include sensitive information like prompts and responses. - > Set `AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL_DIAGNOSTICS=true` to enable workflow telemetry for the workflow samples. + > Set `AGENT_FRAMEWORK_ENABLE_OTEL=true` to enable basic telemetry and `AGENT_FRAMEWORK_ENABLE_SENSITIVE_DATA=true` to include sensitive information like prompts and responses. > Sensitive information should only be enabled in a development or test environment. It is not recommended to enable this in production environments as it may expose sensitive data. -3. Activate your python virtual environment, and then run `python scenarios.py`, `python interactive.py`, `python agent.py`, or `python workflow.py`. - + > Set `AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL=true` to enable workflow telemetry for the workflow samples. +3. Activate your python virtual environment, and then run `python 01-zero_code.py` or others. > This will output the Operation/Trace ID, which can be used later for filtering. -### Scenarios - -This sample includes multiple applications demonstrating Agent Framework telemetry: - -#### scenarios.py - -Organized into specific scenarios where the framework will generate useful telemetry data: - -- `chat_client`: This is when a chat client is invoked directly (i.e. not streaming) with a weather tool function. **Information about the call to the underlying model and tool usage will be recorded**. -- `chat_client_stream`: This is when a chat client is invoked with streaming enabled and a weather tool function. **Information about the streaming call to the underlying model and tool usage will be recorded**. -- `ai_function`: This is when an AI function (`get_weather`) is invoked directly. **Information about the AI function and the call to the underlying model will be recorded**. - -By default, running `python scenarios.py` will run all three scenarios. To run individual scenarios, use the `--scenario` command line argument. For example, `python scenarios.py --scenario chat_client`. For more information, please run `python scenarios.py -h`. - -#### interactive.py - -An interactive chat application that demonstrates telemetry collection in a conversational context. This sample includes the same `get_weather` tool function and allows for multi-turn conversations. Run `python interactive.py` and start chatting. Type 'exit' to quit the application. This sample only logs at the `WARNING` level, so you will not see as much telemetry data as in the `scenarios.py` sample. - -#### agent.py - -A sample demonstrating Agent Framework telemetry collection for agent-based workflows. This shows how telemetry is captured when using the Agent Framework's agent abstraction layer, including agent initialization, message processing, and tool execution within an agent context. - -By default, running `python agent.py` will run all agent scenarios. To run individual scenarios, use the `--scenario` command line argument. For example, `python agent.py --scenario basic`. For more information, please run `python agent.py -h`. - -#### workflow.py - -A sample demonstrating workflow telemetry collection for the Agent Framework's workflow execution engine. This includes two scenarios: - -- `sequential`: A simple sequential workflow that processes text through two connected executors (uppercase conversion followed by text reversal). **Information about workflow execution, executor processing, and message passing between executors will be recorded**. -- `sub_workflow`: A more complex scenario demonstrating sub-workflow patterns with a parent workflow orchestrating multiple text processing tasks via sub-workflows. **Information about parent workflow execution, sub-workflow invocation, and cross-workflow communication will be recorded**. - -By default, running `python workflow.py` will run all workflow scenarios. To run individual scenarios, use the `--scenario` command line argument. For example, `python workflow.py --scenario sequential`. For more information, please run `python workflow.py -h`. - ## Application Insights/Azure Monitor ### Logs and traces @@ -151,13 +142,13 @@ This will start the dashboard with: Make sure your `.env` file includes the OTLP endpoint: ```bash -OTLP_ENDPOINT=http://localhost:4317 +AGENT_FRAMEWORK_OTLP_ENDPOINT=http://localhost:4317 ``` Or set it as an environment variable when running your samples: ```bash -OTLP_ENDPOINT=http://localhost:4317 python scenarios.py +AGENT_FRAMEWORK_ENABLE_OTEL=true AGENT_FRAMEWORK_OTLP_ENDPOINT=http://localhost:4317 python 01-zero_code.py ``` ### Viewing telemetry data @@ -170,138 +161,4 @@ Once your sample finishes running, navigate to in a web You won't have to deploy an Application Insights resource or install Docker to run Aspire Dashboard if you choose to inspect telemetry data in a console. However, it is difficult to navigate through all the spans and logs produced, so **this method is only recommended when you are just getting started**. -We recommend you to get started with the `chat_client` scenario as this generates the least amount of telemetry data. Below is similar to what you will see when you run `python scenarios.py --scenario chat_client`: - -```Json -{ - "name": "chat.completions gpt-4o", - "context": { - "trace_id": "0xbda1d9efcd65435653d18fa37aef7dd3", - "span_id": "0xcd443e1917510385", - "trace_state": "[]" - }, - "kind": "SpanKind.INTERNAL", - "parent_id": "0xeca0a2ca7b7a8191", - "start_time": "2024-09-09T23:13:14.625156Z", - "end_time": "2024-09-09T23:13:17.311909Z", - "status": { - "status_code": "UNSET" - }, - "attributes": { - "gen_ai.operation.name": "chat.completions", - "gen_ai.system": "openai", - "gen_ai.request.model": "gpt-4o", - "gen_ai.response.id": "chatcmpl-A5hrG13nhtFsOgx4ziuoskjNscHtT", - "gen_ai.response.finish_reason": "FinishReason.STOP", - "gen_ai.response.prompt_tokens": 16, - "gen_ai.response.completion_tokens": 28 - }, - "events": [ - { - "name": "gen_ai.content.prompt", - "timestamp": "2024-09-09T23:13:14.625156Z", - "attributes": { - "gen_ai.prompt": "[{\"role\": \"user\", \"content\": \"Why is the sky blue in one sentence?\"}]" - } - }, - { - "name": "gen_ai.content.completion", - "timestamp": "2024-09-09T23:13:17.311909Z", - "attributes": { - "gen_ai.completion": "[{\"role\": \"assistant\", \"content\": \"The sky appears blue because molecules in the Earth's atmosphere scatter shorter wavelengths of sunlight, such as blue, more effectively than longer wavelengths like red.\"}]" - } - } - ], - "links": [], - "resource": { - "attributes": { - "telemetry.sdk.language": "python", - "telemetry.sdk.name": "opentelemetry", - "telemetry.sdk.version": "1.26.0", - "service.name": "TelemetryExample" - }, - "schema_url": "" - } -} -{ - "name": "Scenario: Chat Client", - "context": { - "trace_id": "0xbda1d9efcd65435653d18fa37aef7dd3", - "span_id": "0xeca0a2ca7b7a8191", - "trace_state": "[]" - }, - "kind": "SpanKind.INTERNAL", - "parent_id": "0x48af7ad55f2f64b5", - "start_time": "2024-09-09T23:13:14.625156Z", - "end_time": "2024-09-09T23:13:17.312910Z", - "status": { - "status_code": "UNSET" - }, - "attributes": {}, - "events": [], - "links": [], - "resource": { - "attributes": { - "telemetry.sdk.language": "python", - "telemetry.sdk.name": "opentelemetry", - "telemetry.sdk.version": "1.26.0", - "service.name": "TelemetryExample" - }, - "schema_url": "" - } -} -{ - "name": "Scenario's", - "context": { - "trace_id": "0xbda1d9efcd65435653d18fa37aef7dd3", - "span_id": "0x48af7ad55f2f64b5", - "trace_state": "[]" - }, - "kind": "SpanKind.INTERNAL", - "parent_id": null, - "start_time": "2024-09-09T23:13:13.840481Z", - "end_time": "2024-09-09T23:13:17.312910Z", - "status": { - "status_code": "UNSET" - }, - "attributes": {}, - "events": [], - "links": [], - "resource": { - "attributes": { - "telemetry.sdk.language": "python", - "telemetry.sdk.name": "opentelemetry", - "telemetry.sdk.version": "1.26.0", - "service.name": "TelemetryExample" - }, - "schema_url": "" - } -} -{ - "body": "Agent Framework usage: CompletionUsage(completion_tokens=28, prompt_tokens=16, total_tokens=44)", - "severity_number": "", - "severity_text": "INFO", - "attributes": { - "code.filepath": "/path/to/agent_framework/openai/chat_client.py", - "code.function": "store_usage", - "code.lineno": 81 - }, - "dropped_attributes": 0, - "timestamp": "2024-09-09T23:13:17.311909Z", - "observed_timestamp": "2024-09-09T23:13:17.311909Z", - "trace_id": "0xbda1d9efcd65435653d18fa37aef7dd3", - "span_id": "0xcd443e1917510385", - "trace_flags": 1, - "resource": { - "attributes": { - "telemetry.sdk.language": "python", - "telemetry.sdk.name": "opentelemetry", - "telemetry.sdk.version": "1.26.0", - "service.name": "TelemetryExample" - }, - "schema_url": "" - } -} -``` - -In the output, you will find three spans: `Scenario's`, `Scenario: Chat Client`, and `chat.completions gpt-4o`, each representing a different layer in the sample. In particular, `chat.completions gpt-4o` is generated by the chat client. Inside it, you will find information about the call, such as the timestamp of the operation, the response id and the finish reason. You will also find sensitive information such as the prompt and response to and from the model (only if you have `AGENT_FRAMEWORK__GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE` set to true). If you use Application Insights or Aspire Dashboard, these information will be available to you in an interactive UI. +Use the guides from OpenTelemetry to setup exporters for [the console](https://opentelemetry.io/docs/languages/python/getting-started/), or use [manual_setup_console_output](./manual_setup_console_output.py) as a reference, just know that there are a lot of options you can setup and this is not a comprehensive example. diff --git a/python/samples/getting_started/telemetry/agent.py b/python/samples/getting_started/telemetry/agent.py deleted file mode 100644 index 6f9d59b8dd..0000000000 --- a/python/samples/getting_started/telemetry/agent.py +++ /dev/null @@ -1,190 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. -# type: ignore -import asyncio -import logging -from random import randint -from typing import Annotated - -from agent_framework import ChatAgent -from agent_framework.openai import OpenAIChatClient -from azure.monitor.opentelemetry import configure_azure_monitor -from opentelemetry import trace -from opentelemetry._logs import set_logger_provider -from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter -from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter -from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter -from opentelemetry.metrics import set_meter_provider -from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler -from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogExporter -from opentelemetry.sdk.metrics import MeterProvider -from opentelemetry.sdk.metrics.export import ConsoleMetricExporter, PeriodicExportingMetricReader -from opentelemetry.sdk.metrics.view import DropAggregation, View -from opentelemetry.sdk.resources import Resource -from opentelemetry.sdk.trace import TracerProvider -from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter -from opentelemetry.semconv.attributes import service_attributes -from opentelemetry.trace import SpanKind, set_tracer_provider -from pydantic import Field -from pydantic_settings import BaseSettings - - -class TelemetrySampleSettings(BaseSettings): - """Settings for the telemetry sample application. - - Optional settings are: - - connection_string: str - The connection string for the Application Insights resource. - This value can be found in the Overview section when examining - your resource from the Azure portal. - (Env var CONNECTION_STRING) - - otlp_endpoint: str - The OTLP endpoint to send telemetry data to. - Depending on the exporter used, you may find this value in different places. - (Env var OTLP_ENDPOINT) - - If no connection string or OTLP endpoint is provided, the telemetry data will be - exported to the console. - """ - - connection_string: str | None = None - otlp_endpoint: str | None = None - - -# Load settings -settings = TelemetrySampleSettings() - -# Create a resource to represent the service/sample -resource = Resource.create({service_attributes.SERVICE_NAME: "TelemetryExample"}) - -# Define the scenarios that can be run -SCENARIOS = ["ai_service", "kernel_function", "auto_function_invocation", "all"] - -if settings.connection_string: - configure_azure_monitor( - connection_string=settings.connection_string, enable_live_metrics=True, logger_name="agent_framework" - ) - - -def set_up_logging(): - class LogFilter(logging.Filter): - """A filter to not process records from several subpackages.""" - - # These are the namespaces that we want to exclude from logging for the purposes of this demo. - namespaces_to_exclude: list[str] = [ - "httpx", - "openai", - ] - - def filter(self, record): - return not any([record.name.startswith(namespace) for namespace in self.namespaces_to_exclude]) - - exporters = [] - if settings.otlp_endpoint: - exporters.append(OTLPLogExporter(endpoint=settings.otlp_endpoint)) - if not exporters: - exporters.append(ConsoleLogExporter()) - - # Create and set a global logger provider for the application. - logger_provider = LoggerProvider(resource=resource) - # Log processors are initialized with an exporter which is responsible - # for sending the telemetry data to a particular backend. - for log_exporter in exporters: - logger_provider.add_log_record_processor(BatchLogRecordProcessor(log_exporter)) - # Sets the global default logger provider - set_logger_provider(logger_provider) - - # Create a logging handler to write logging records, in OTLP format, to the exporter. - handler = LoggingHandler() - handler.addFilter(LogFilter()) - # Attach the handler to the root logger. `getLogger()` with no arguments returns the root logger. - # Events from all child loggers will be processed by this handler. - logger = logging.getLogger() - logger.addHandler(handler) - # Set the logging level to INFO. - logger.setLevel(logging.INFO) - - -def set_up_tracing(): - exporters = [] - if settings.otlp_endpoint: - exporters.append(OTLPSpanExporter(endpoint=settings.otlp_endpoint)) - if not exporters: - exporters.append(ConsoleSpanExporter()) - - # Initialize a trace provider for the application. This is a factory for creating tracers. - tracer_provider = TracerProvider(resource=resource) - # Span processors are initialized with an exporter which is responsible - # for sending the telemetry data to a particular backend. - for exporter in exporters: - tracer_provider.add_span_processor(BatchSpanProcessor(exporter)) - # Sets the global default tracer provider - set_tracer_provider(tracer_provider) - - -def set_up_metrics(): - exporters = [] - if settings.otlp_endpoint: - exporters.append(OTLPMetricExporter(endpoint=settings.otlp_endpoint)) - if not exporters: - exporters.append(ConsoleMetricExporter()) - - # Initialize a metric provider for the application. This is a factory for creating meters. - metric_readers = [ - PeriodicExportingMetricReader(metric_exporter, export_interval_millis=5000) for metric_exporter in exporters - ] - meter_provider = MeterProvider( - metric_readers=metric_readers, - resource=resource, - views=[ - # Dropping all instrument names except for those starting with "agent_framework" - View(instrument_name="*", aggregation=DropAggregation()), - View(instrument_name="agent_framework*"), - ], - ) - # Sets the global default meter provider - set_meter_provider(meter_provider) - - -async def get_weather( - location: Annotated[str, Field(description="The location to get the weather for.")], -) -> str: - """Get the weather for a given location.""" - await asyncio.sleep(randint(0, 10) / 10.0) # Simulate a network call - conditions = ["sunny", "cloudy", "rainy", "stormy"] - return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." - - -async def main(): - # Set up the providers - # This must be done before any other telemetry calls - set_up_logging() - set_up_tracing() - set_up_metrics() - - tracer = trace.get_tracer("agent_framework") - with tracer.start_as_current_span("Scenario: Agent Chat", kind=SpanKind.CLIENT) as current_span: - print("Running scenario: Agent Chat") - print("Welcome to the chat, type 'exit' to quit.") - agent = ChatAgent( - chat_client=OpenAIChatClient(), - tools=get_weather, - name="WeatherAgent", - instructions="You are a weather assistant.", - ) - thread = agent.get_new_thread() - message = input("User: ") - try: - while message.lower() != "exit": - print(f"{agent.display_name}: ", end="") - async for update in agent.run_stream( - message, - thread=thread, - ): - if update.text: - print(update.text, end="") - message = input("\nUser: ") - except Exception as e: - current_span.record_exception(e) - print(f"\nError running interactive chat: {e}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/python/samples/getting_started/telemetry/interactive.py b/python/samples/getting_started/telemetry/interactive.py deleted file mode 100644 index 88dd294cde..0000000000 --- a/python/samples/getting_started/telemetry/interactive.py +++ /dev/null @@ -1,186 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. -# type: ignore -import asyncio -import logging -from random import randint -from typing import Annotated - -from agent_framework import ChatMessage, ChatResponse, ChatResponseUpdate -from agent_framework.openai import OpenAIChatClient -from azure.monitor.opentelemetry import configure_azure_monitor -from opentelemetry import trace -from opentelemetry._logs import set_logger_provider -from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter -from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter -from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter -from opentelemetry.metrics import set_meter_provider -from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler -from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogExporter -from opentelemetry.sdk.metrics import MeterProvider -from opentelemetry.sdk.metrics.export import ConsoleMetricExporter, PeriodicExportingMetricReader -from opentelemetry.sdk.metrics.view import DropAggregation, View -from opentelemetry.sdk.resources import Resource -from opentelemetry.sdk.trace import TracerProvider -from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter -from opentelemetry.semconv.attributes import service_attributes -from opentelemetry.trace import SpanKind, set_tracer_provider -from pydantic import Field -from pydantic_settings import BaseSettings - - -class TelemetrySampleSettings(BaseSettings): - """Settings for the telemetry sample application. - - Optional settings are: - - connection_string: str - The connection string for the Application Insights resource. - This value can be found in the Overview section when examining - your resource from the Azure portal. - (Env var CONNECTION_STRING) - - otlp_endpoint: str - The OTLP endpoint to send telemetry data to. - Depending on the exporter used, you may find this value in different places. - (Env var OTLP_ENDPOINT) - - If no connection string or OTLP endpoint is provided, the telemetry data will be - exported to the console. - """ - - connection_string: str | None = None - otlp_endpoint: str | None = None - - -# Load settings -settings = TelemetrySampleSettings() - -# Create a resource to represent the service/sample -resource = Resource.create({service_attributes.SERVICE_NAME: "TelemetryExample"}) - -# Define the scenarios that can be run -SCENARIOS = ["ai_service", "kernel_function", "auto_function_invocation", "all"] - -if settings.connection_string: - configure_azure_monitor( - connection_string=settings.connection_string, enable_live_metrics=True, logger_name="agent_framework" - ) - - -def set_up_logging(): - class LogFilter(logging.Filter): - """A filter to not process records from several subpackages.""" - - # These are the namespaces that we want to exclude from logging for the purposes of this demo. - namespaces_to_exclude: list[str] = [ - "httpx", - "openai", - ] - - def filter(self, record): - return not any([record.name.startswith(namespace) for namespace in self.namespaces_to_exclude]) - - exporters = [] - if settings.otlp_endpoint: - exporters.append(OTLPLogExporter(endpoint=settings.otlp_endpoint)) - if not exporters: - exporters.append(ConsoleLogExporter()) - - # Create and set a global logger provider for the application. - logger_provider = LoggerProvider(resource=resource) - # Log processors are initialized with an exporter which is responsible - # for sending the telemetry data to a particular backend. - for log_exporter in exporters: - logger_provider.add_log_record_processor(BatchLogRecordProcessor(log_exporter)) - # Sets the global default logger provider - set_logger_provider(logger_provider) - - # Create a logging handler to write logging records, in OTLP format, to the exporter. - handler = LoggingHandler() - handler.addFilter(LogFilter()) - # Attach the handler to the root logger. `getLogger()` with no arguments returns the root logger. - # Events from all child loggers will be processed by this handler. - logger = logging.getLogger() - logger.addHandler(handler) - # Set the logging level to WARNING, this will not log detailed events to the logger. - logger.setLevel(logging.WARNING) - - -def set_up_tracing(): - exporters = [] - if settings.otlp_endpoint: - exporters.append(OTLPSpanExporter(endpoint=settings.otlp_endpoint)) - if not exporters: - exporters.append(ConsoleSpanExporter()) - - # Initialize a trace provider for the application. This is a factory for creating tracers. - tracer_provider = TracerProvider(resource=resource) - # Span processors are initialized with an exporter which is responsible - # for sending the telemetry data to a particular backend. - for exporter in exporters: - tracer_provider.add_span_processor(BatchSpanProcessor(exporter)) - # Sets the global default tracer provider - set_tracer_provider(tracer_provider) - - -def set_up_metrics(): - exporters = [] - if settings.otlp_endpoint: - exporters.append(OTLPMetricExporter(endpoint=settings.otlp_endpoint)) - if not exporters: - exporters.append(ConsoleMetricExporter()) - - # Initialize a metric provider for the application. This is a factory for creating meters. - metric_readers = [ - PeriodicExportingMetricReader(metric_exporter, export_interval_millis=5000) for metric_exporter in exporters - ] - meter_provider = MeterProvider( - metric_readers=metric_readers, - resource=resource, - views=[ - # Dropping all instrument names except for those starting with "agent_framework" - View(instrument_name="*", aggregation=DropAggregation()), - View(instrument_name="agent_framework*"), - ], - ) - # Sets the global default meter provider - set_meter_provider(meter_provider) - - -def get_weather( - location: Annotated[str, Field(description="The location to get the weather for.")], -) -> str: - """Get the weather for a given location.""" - conditions = ["sunny", "cloudy", "rainy", "stormy"] - return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." - - -async def main(): - # Set up the providers - # This must be done before any other telemetry calls - set_up_logging() - set_up_tracing() - set_up_metrics() - - tracer = trace.get_tracer("agent_framework") - with tracer.start_as_current_span("Scenario: Interactive Chat", kind=SpanKind.CLIENT) as current_span: - print("Running scenario: Interactive Chat") - print("Welcome to the chat, type 'exit' to quit.") - client = OpenAIChatClient() - messages: list[ChatMessage] = [] - message = input("User: ") - try: - while message.lower() != "exit": - messages.append(ChatMessage(role="user", text=message)) - print("Assistant: ", end="") - updates: list[ChatResponseUpdate] = [] - async for update in client.get_streaming_response(messages, tools=get_weather): - updates.append(update) - if update.text: - print(update.text, end="") - print("") - messages.extend(ChatResponse.from_chat_response_updates(updates).messages) - message = input("User: ") - except Exception as e: - current_span.record_exception(e) - print(f"\nError running interactive chat: {e}") - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/python/samples/getting_started/telemetry/manual_setup_console_output.py b/python/samples/getting_started/telemetry/manual_setup_console_output.py new file mode 100644 index 0000000000..2a74e67f5a --- /dev/null +++ b/python/samples/getting_started/telemetry/manual_setup_console_output.py @@ -0,0 +1,114 @@ +# Copyright (c) Microsoft. All rights reserved. +# type: ignore +import asyncio +import logging +from random import randint +from typing import Annotated + +from agent_framework.openai import OpenAIChatClient +from opentelemetry._logs import set_logger_provider +from opentelemetry.metrics import set_meter_provider +from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler +from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogExporter +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics.export import ConsoleMetricExporter, PeriodicExportingMetricReader +from opentelemetry.sdk.resources import Resource +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter +from opentelemetry.semconv.resource import ResourceAttributes +from opentelemetry.trace import set_tracer_provider +from pydantic import Field + +""" +This sample shows how to manually set up OpenTelemetry to log to the console. +And this can also be used as a reference for more complex telemetry setups. +""" + +resource = Resource.create({ResourceAttributes.SERVICE_NAME: "ManualSetup"}) + + +def setup_console_telemetry(): + # Create and set a global logger provider for the application. + logger_provider = LoggerProvider(resource=resource) + # Log processors are initialized with an exporter which is responsible + logger_provider.add_log_record_processor(BatchLogRecordProcessor(ConsoleLogExporter())) + # Sets the global default logger provider + set_logger_provider(logger_provider) + + # Create a logging handler to write logging records, in OTLP format, to the exporter. + handler = LoggingHandler() + # Attach the handler to the root logger. `getLogger()` with no arguments returns the root logger. + # Events from all child loggers will be processed by this handler. + logger = logging.getLogger() + logger.addHandler(handler) + # Set the logging level to NOTSET to allow all records to be processed by the handler. + logger.setLevel(logging.NOTSET) + # Initialize a trace provider for the application. This is a factory for creating tracers. + tracer_provider = TracerProvider(resource=resource) + # Span processors are initialized with an exporter which is responsible + # for sending the telemetry data to a particular backend. + tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter())) + # Sets the global default tracer provider + set_tracer_provider(tracer_provider) + # Initialize a metric provider for the application. This is a factory for creating meters. + meter_provider = MeterProvider( + metric_readers=[PeriodicExportingMetricReader(ConsoleMetricExporter(), export_interval_millis=5000)], + resource=resource, + ) + # Sets the global default meter provider + set_meter_provider(meter_provider) + + +async def get_weather( + location: Annotated[str, Field(description="The location to get the weather for.")], +) -> str: + """Get the weather for a given location.""" + await asyncio.sleep(randint(0, 10) / 10.0) # Simulate a network call + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +async def run_chat_client() -> None: + """Run an AI service. + + This function runs an AI service and prints the output. + Telemetry will be collected for the service execution behind the scenes, + and the traces will be sent to the configured telemetry backend. + + The telemetry will include information about the AI service execution. + + Args: + stream: Whether to use streaming for the plugin + + Remarks: + When function calling is outside the open telemetry loop + each of the call to the model is handled as a seperate span, + while when the open telemetry is put last, a single span + is shown, which might include one or more rounds of function calling. + + So for the scenario below, you should see the following: + + 2 spans with gen_ai.operation.name=chat + The first has finish_reason "tool_calls" + The second has finish_reason "stop" + 2 spans with gen_ai.operation.name=execute_tool + + """ + client = OpenAIChatClient() + message = "What's the weather in Amsterdam and in Paris?" + print(f"User: {message}") + print("Assistant: ", end="") + async for chunk in client.get_streaming_response(message, tools=get_weather): + if str(chunk): + print(str(chunk), end="") + print("") + + +async def main(): + """Run the selected scenario(s).""" + setup_console_telemetry() + await run_chat_client() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/getting_started/telemetry/scenarios.py b/python/samples/getting_started/telemetry/scenarios.py deleted file mode 100644 index 044260d8ca..0000000000 --- a/python/samples/getting_started/telemetry/scenarios.py +++ /dev/null @@ -1,249 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. -# type: ignore -import argparse -import asyncio -import logging -from random import randint -from typing import Annotated, Literal - -from agent_framework import ai_function -from agent_framework.openai import OpenAIChatClient -from azure.monitor.opentelemetry import configure_azure_monitor -from opentelemetry import trace -from opentelemetry._logs import set_logger_provider -from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter -from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter -from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter -from opentelemetry.metrics import set_meter_provider -from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler -from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogExporter -from opentelemetry.sdk.metrics import MeterProvider -from opentelemetry.sdk.metrics.export import ConsoleMetricExporter, PeriodicExportingMetricReader -from opentelemetry.sdk.metrics.view import DropAggregation, View -from opentelemetry.sdk.resources import Resource -from opentelemetry.sdk.trace import TracerProvider -from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter -from opentelemetry.semconv.attributes import service_attributes -from opentelemetry.trace import SpanKind, set_tracer_provider -from opentelemetry.trace.span import format_trace_id -from pydantic import Field -from pydantic_settings import BaseSettings - - -class TelemetrySampleSettings(BaseSettings): - """Settings for the telemetry sample application. - - Optional settings are: - - connection_string: str - The connection string for the Application Insights resource. - This value can be found in the Overview section when examining - your resource from the Azure portal. - (Env var CONNECTION_STRING) - - otlp_endpoint: str - The OTLP endpoint to send telemetry data to. - Depending on the exporter used, you may find this value in different places. - (Env var OTLP_ENDPOINT) - - If no connection string or OTLP endpoint is provided, the telemetry data will be - exported to the console. - """ - - connection_string: str | None = None - otlp_endpoint: str | None = None - - -# Load settings -settings = TelemetrySampleSettings() - -# Create a resource to represent the service/sample -resource = Resource.create({service_attributes.SERVICE_NAME: "TelemetryExample"}) - -# Define the scenarios that can be run -SCENARIOS = ["chat_client", "chat_client_stream", "ai_function", "all"] - -if settings.connection_string: - configure_azure_monitor( - connection_string=settings.connection_string, - enable_live_metrics=True, - logger_name="agent_framework", - ) - - -def set_up_logging(): - class LogFilter(logging.Filter): - """A filter to not process records from several subpackages.""" - - # These are the namespaces that we want to exclude from logging for the purposes of this demo. - namespaces_to_exclude: list[str] = [ - "httpx", - "openai", - ] - - def filter(self, record): - return not any([record.name.startswith(namespace) for namespace in self.namespaces_to_exclude]) - - exporters = [] - if settings.otlp_endpoint: - exporters.append(OTLPLogExporter(endpoint=settings.otlp_endpoint)) - if not exporters: - exporters.append(ConsoleLogExporter()) - - # Create and set a global logger provider for the application. - logger_provider = LoggerProvider(resource=resource) - # Log processors are initialized with an exporter which is responsible - # for sending the telemetry data to a particular backend. - for log_exporter in exporters: - logger_provider.add_log_record_processor(BatchLogRecordProcessor(log_exporter)) - # Sets the global default logger provider - set_logger_provider(logger_provider) - - # Create a logging handler to write logging records, in OTLP format, to the exporter. - handler = LoggingHandler() - handler.addFilter(LogFilter()) - # Attach the handler to the root logger. `getLogger()` with no arguments returns the root logger. - # Events from all child loggers will be processed by this handler. - logger = logging.getLogger() - logger.addHandler(handler) - # Set the logging level to NOTSET to allow all records to be processed by the handler. - logger.setLevel(logging.NOTSET) - - -def set_up_tracing(): - exporters = [] - if settings.otlp_endpoint: - exporters.append(OTLPSpanExporter(endpoint=settings.otlp_endpoint)) - if not exporters: - exporters.append(ConsoleSpanExporter()) - - # Initialize a trace provider for the application. This is a factory for creating tracers. - tracer_provider = TracerProvider(resource=resource) - # Span processors are initialized with an exporter which is responsible - # for sending the telemetry data to a particular backend. - for exporter in exporters: - tracer_provider.add_span_processor(BatchSpanProcessor(exporter)) - # Sets the global default tracer provider - set_tracer_provider(tracer_provider) - - -def set_up_metrics(): - exporters = [] - if settings.otlp_endpoint: - exporters.append(OTLPMetricExporter(endpoint=settings.otlp_endpoint)) - if not exporters: - exporters.append(ConsoleMetricExporter()) - - # Initialize a metric provider for the application. This is a factory for creating meters. - metric_readers = [ - PeriodicExportingMetricReader(metric_exporter, export_interval_millis=5000) for metric_exporter in exporters - ] - meter_provider = MeterProvider( - metric_readers=metric_readers, - resource=resource, - views=[ - # Dropping all instrument names except for those starting with "agent_framework" - View(instrument_name="*", aggregation=DropAggregation()), - View(instrument_name="agent_framework*"), - ], - ) - # Sets the global default meter provider - set_meter_provider(meter_provider) - - -def get_weather( - location: Annotated[str, Field(description="The location to get the weather for.")], -) -> str: - """Get the weather for a given location.""" - conditions = ["sunny", "cloudy", "rainy", "stormy"] - return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." - - -async def run_chat_client(stream: bool = False) -> None: - """Run an AI service. - - This function runs an AI service and prints the output. - Telemetry will be collected for the service execution behind the scenes, - and the traces will be sent to the configured telemetry backend. - - The telemetry will include information about the AI service execution. - - Args: - stream (bool): Whether to use streaming for the plugin - """ - - tracer = trace.get_tracer(__name__) - with tracer.start_as_current_span( - "Scenario: Chat Client Stream" if stream else "Scenario: Chat Client", kind=SpanKind.CLIENT - ) as current_span: - print("Running scenario: Chat Client" if not stream else "Running scenario: Chat Client Stream") - try: - client = OpenAIChatClient() - message = "What's the weather in Amsterdam and in Paris?" - print(f"User: {message}") - if stream: - print("Assistant: ", end="") - async for chunk in client.get_streaming_response(message, tools=get_weather): - if str(chunk): - print(str(chunk), end="") - print("") - else: - response = await client.get_response(message, tools=get_weather) - print(f"Assistant: {response}") - except Exception as e: - current_span.record_exception(e) - print(f"Error running AI service: {e}") - - -async def run_ai_function() -> None: - """Run a AI function. - - This function runs a AI function and prints the output. - Telemetry will be collected for the function execution behind the scenes, - and the traces will be sent to the configured telemetry backend. - - The telemetry will include information about the AI function execution - and the AI service execution. - """ - - tracer = trace.get_tracer(__name__) - with tracer.start_as_current_span("Scenario: AI Function", kind=SpanKind.CLIENT) as current_span: - print("Running scenario: AI Function") - try: - func = ai_function(get_weather) - weather = await func.invoke(location="Amsterdam") - print(f"Weather in Amsterdam:\n{weather}") - except Exception as e: - current_span.record_exception(e) - print(f"Error running kernel plugin: {e}") - - -async def main(scenario: Literal["chat_client", "chat_client_stream", "ai_function", "all"] = "all"): - # Set up the providers - # This must be done before any other telemetry calls - set_up_logging() - set_up_tracing() - set_up_metrics() - - tracer = trace.get_tracer("agent_framework") - with tracer.start_as_current_span("Scenario's", kind=SpanKind.CLIENT) as current_span: - print(f"Trace ID: {format_trace_id(current_span.get_span_context().trace_id)}") - - # Scenarios where telemetry is collected in the SDK, from the most basic to the most complex. - if scenario == "chat_client" or scenario == "all": - await run_chat_client(stream=False) - if scenario == "chat_client_stream" or scenario == "all": - await run_chat_client(stream=True) - if scenario == "ai_function" or scenario == "all": - await run_ai_function() - - -if __name__ == "__main__": - arg_parser = argparse.ArgumentParser() - - arg_parser.add_argument( - "--scenario", - type=str, - choices=SCENARIOS, - default="all", - help="The scenario to run. Default is all.", - ) - - args = arg_parser.parse_args() - asyncio.run(main(args.scenario)) diff --git a/python/samples/getting_started/telemetry/workflow.py b/python/samples/getting_started/telemetry/workflow.py deleted file mode 100644 index 830e2794cb..0000000000 --- a/python/samples/getting_started/telemetry/workflow.py +++ /dev/null @@ -1,253 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. -# type: ignore -import asyncio -import logging -from typing import Any - -from agent_framework.workflow import ( - Executor, - WorkflowBuilder, - WorkflowCompletedEvent, - WorkflowContext, - handler, -) -from azure.monitor.opentelemetry import configure_azure_monitor -from opentelemetry import trace -from opentelemetry._logs import set_logger_provider -from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter -from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter -from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter -from opentelemetry.metrics import set_meter_provider -from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler -from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogExporter -from opentelemetry.sdk.metrics import MeterProvider -from opentelemetry.sdk.metrics.export import ConsoleMetricExporter, PeriodicExportingMetricReader -from opentelemetry.sdk.metrics.view import DropAggregation, View -from opentelemetry.sdk.resources import Resource -from opentelemetry.sdk.trace import TracerProvider -from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter -from opentelemetry.semconv.attributes import service_attributes -from opentelemetry.trace import SpanKind, set_tracer_provider -from opentelemetry.trace.span import format_trace_id -from pydantic_settings import BaseSettings - -"""Telemetry sample demonstrating OpenTelemetry integration with Agent Framework workflows. - -This sample runs a simple sequential workflow with telemetry collection, -showing telemetry collection for workflow execution, executor processing, -and message publishing between executors. -""" - - -class TelemetrySampleSettings(BaseSettings): - """Settings for the telemetry sample application. - - Optional settings are: - - connection_string: str - The connection string for the Application Insights resource. - This value can be found in the Overview section when examining - your resource from the Azure portal. - (Env var CONNECTION_STRING) - - otlp_endpoint: str - The OTLP endpoint to send telemetry data to. - Depending on the exporter used, you may find this value in different places. - (Env var OTLP_ENDPOINT) - - If no connection string or OTLP endpoint is provided, the telemetry data will be - exported to the console. - """ - - connection_string: str | None = None - otlp_endpoint: str | None = None - - -# Load settings -settings = TelemetrySampleSettings() - -# Create a resource to represent the service/sample -resource = Resource.create({service_attributes.SERVICE_NAME: "WorkflowTelemetryExample"}) - -if settings.connection_string: - configure_azure_monitor( - connection_string=settings.connection_string, - enable_live_metrics=True, - logger_name="agent_framework", - ) - - -def set_up_logging(): - class LogFilter(logging.Filter): - """A filter to not process records from several subpackages.""" - - # These are the namespaces that we want to exclude from logging for the purposes of this demo. - namespaces_to_exclude: list[str] = [ - "httpx", - "openai", - ] - - def filter(self, record): - return not any([record.name.startswith(namespace) for namespace in self.namespaces_to_exclude]) - - exporters = [] - if settings.otlp_endpoint: - exporters.append(OTLPLogExporter(endpoint=settings.otlp_endpoint)) - if not exporters: - exporters.append(ConsoleLogExporter()) - - # Create and set a global logger provider for the application. - logger_provider = LoggerProvider(resource=resource) - # Log processors are initialized with an exporter which is responsible - # for sending the telemetry data to a particular backend. - for log_exporter in exporters: - logger_provider.add_log_record_processor(BatchLogRecordProcessor(log_exporter)) - # Sets the global default logger provider - set_logger_provider(logger_provider) - - # Create a logging handler to write logging records, in OTLP format, to the exporter. - handler = LoggingHandler() - handler.addFilter(LogFilter()) - # Attach the handler to the root logger. `getLogger()` with no arguments returns the root logger. - # Events from all child loggers will be processed by this handler. - logger = logging.getLogger() - logger.addHandler(handler) - # Set the logging level to NOTSET to allow all records to be processed by the handler. - logger.setLevel(logging.NOTSET) - - -def set_up_tracing(): - exporters = [] - if settings.otlp_endpoint: - exporters.append(OTLPSpanExporter(endpoint=settings.otlp_endpoint)) - if not exporters: - exporters.append(ConsoleSpanExporter()) - - # Initialize a trace provider for the application. This is a factory for creating tracers. - tracer_provider = TracerProvider(resource=resource) - # Span processors are initialized with an exporter which is responsible - # for sending the telemetry data to a particular backend. - for exporter in exporters: - tracer_provider.add_span_processor(BatchSpanProcessor(exporter)) - # Sets the global default tracer provider - set_tracer_provider(tracer_provider) - - -def set_up_metrics(): - exporters = [] - if settings.otlp_endpoint: - exporters.append(OTLPMetricExporter(endpoint=settings.otlp_endpoint)) - if not exporters: - exporters.append(ConsoleMetricExporter()) - - # Initialize a metric provider for the application. This is a factory for creating meters. - metric_readers = [ - PeriodicExportingMetricReader(metric_exporter, export_interval_millis=5000) for metric_exporter in exporters - ] - meter_provider = MeterProvider( - metric_readers=metric_readers, - resource=resource, - views=[ - # Dropping all instrument names except for those starting with "agent_framework" - View(instrument_name="*", aggregation=DropAggregation()), - View(instrument_name="agent_framework*"), - ], - ) - # Sets the global default meter provider - set_meter_provider(meter_provider) - - -# Executors for sequential workflow -class UpperCaseExecutor(Executor): - """An executor that converts text to uppercase.""" - - @handler - async def to_upper_case(self, text: str, ctx: WorkflowContext[str]) -> None: - """Execute the task by converting the input string to uppercase.""" - print(f"UpperCaseExecutor: Processing '{text}'") - result = text.upper() - print(f"UpperCaseExecutor: Result '{result}'") - - # Send the result to the next executor in the workflow. - await ctx.send_message(result) - - -class ReverseTextExecutor(Executor): - """An executor that reverses text.""" - - @handler - async def reverse_text(self, text: str, ctx: WorkflowContext[Any]) -> None: - """Execute the task by reversing the input string.""" - print(f"ReverseTextExecutor: Processing '{text}'") - result = text[::-1] - print(f"ReverseTextExecutor: Result '{result}'") - - # Send the result with a workflow completion event. - await ctx.add_event(WorkflowCompletedEvent(result)) - - -async def run_sequential_workflow() -> None: - """Run a simple sequential workflow demonstrating telemetry collection. - - This workflow processes a string through two executors in sequence: - 1. UpperCaseExecutor converts the input to uppercase - 2. ReverseTextExecutor reverses the string and completes the workflow - - Telemetry data collected includes: - - Overall workflow execution spans - - Individual executor processing spans - - Message publishing between executors - - Workflow completion events - """ - - tracer = trace.get_tracer(__name__) - with tracer.start_as_current_span("Scenario: Sequential Workflow", kind=SpanKind.CLIENT) as current_span: - print("Running scenario: Sequential Workflow") - try: - # Step 1: Create the executors. - upper_case_executor = UpperCaseExecutor(id="upper_case_executor") - reverse_text_executor = ReverseTextExecutor(id="reverse_text_executor") - - # Step 2: Build the workflow with the defined edges. - workflow = ( - WorkflowBuilder() - .add_edge(upper_case_executor, reverse_text_executor) - .set_start_executor(upper_case_executor) - .build() - ) - - # Step 3: Run the workflow with an initial message. - input_text = "hello world" - print(f"Starting workflow with input: '{input_text}'") - - completion_event = None - async for event in workflow.run_stream(input_text): - print(f"Event: {event}") - if isinstance(event, WorkflowCompletedEvent): - # The WorkflowCompletedEvent contains the final result. - completion_event = event - - if completion_event: - print(f"Workflow completed with result: '{completion_event.data}'") - else: - print("Workflow completed without a completion event") - - except Exception as e: - current_span.record_exception(e) - print(f"Error running workflow: {e}") - - -async def main(): - """Run the telemetry sample with a simple sequential workflow.""" - # Set up the providers - # This must be done before any other telemetry calls - set_up_logging() - set_up_tracing() - set_up_metrics() - - tracer = trace.get_tracer("agent_framework") - with tracer.start_as_current_span("Sequential Workflow Scenario", kind=SpanKind.CLIENT) as current_span: - print(f"Trace ID: {format_trace_id(current_span.get_span_context().trace_id)}") - - # Run the sequential workflow scenario - await run_sequential_workflow() - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/python/samples/getting_started/workflow/agents/azure_chat_agents_streaming.py b/python/samples/getting_started/workflow/agents/azure_chat_agents_streaming.py index 97d563215a..4f39385a92 100644 --- a/python/samples/getting_started/workflow/agents/azure_chat_agents_streaming.py +++ b/python/samples/getting_started/workflow/agents/azure_chat_agents_streaming.py @@ -64,7 +64,7 @@ async def main(): # AgentRunUpdateEvent contains incremental text deltas from the underlying agent. # Print a prefix when the executor changes, then append updates on the same line. eid = event.executor_id - if eid != last_executor_id: + if eid != last_executor_id: # type: ignore[reportUnnecessaryComparison] if last_executor_id is not None: print() print(f"{eid}:", end=" ", flush=True) diff --git a/python/uv.lock b/python/uv.lock index 1f995f10f7..a987125e3b 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -2,11 +2,14 @@ version = 1 revision = 3 requires-python = ">=3.10" resolution-markers = [ - "python_full_version >= '3.11' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and sys_platform == 'darwin'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'darwin'", "python_full_version < '3.11' and sys_platform == 'darwin'", - "python_full_version >= '3.11' and sys_platform == 'linux'", + "python_full_version >= '3.13' and sys_platform == 'linux'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'linux'", "python_full_version < '3.11' and sys_platform == 'linux'", - "python_full_version >= '3.11' and sys_platform == 'win32'", + "python_full_version >= '3.13' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'win32'", "python_full_version < '3.11' and sys_platform == 'win32'", ] supported-markers = [ @@ -42,9 +45,12 @@ name = "agent-framework" version = "0.1.0b1" source = { editable = "packages/main" } dependencies = [ + { name = "azure-monitor-opentelemetry", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-monitor-opentelemetry-exporter", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "mcp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-exporter-otlp-proto-grpc", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "pydantic-settings", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -71,9 +77,12 @@ requires-dist = [ { name = "agent-framework-foundry", marker = "extra == 'foundry'", editable = "packages/foundry" }, { name = "agent-framework-runtime", marker = "extra == 'runtime'", editable = "packages/runtime" }, { name = "agent-framework-workflow", marker = "extra == 'workflow'", editable = "packages/workflow" }, + { name = "azure-monitor-opentelemetry", specifier = ">=1.7.0" }, + { name = "azure-monitor-opentelemetry-exporter", specifier = ">=1.0.0b41" }, { name = "mcp", specifier = ">=1.12" }, { name = "openai", specifier = ">=1.103.0" }, { name = "opentelemetry-api", specifier = "~=1.24" }, + { name = "opentelemetry-exporter-otlp-proto-grpc", specifier = ">=1.36.0" }, { name = "opentelemetry-sdk", specifier = "~=1.24" }, { name = "pydantic", specifier = ">=2.11.7" }, { name = "pydantic-settings", specifier = ">=2.10.1" }, @@ -392,6 +401,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, ] +[[package]] +name = "asgiref" +version = "3.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/90/61/0aa957eec22ff70b830b22ff91f825e70e1ef732c06666a805730f28b36b/asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142", size = 36870, upload-time = "2025-07-08T09:07:43.344Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/3c/0464dcada90d5da0e71018c04a140ad6349558afb30b3051b4264cc5b965/asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c", size = 23790, upload-time = "2025-07-08T09:07:41.548Z" }, +] + [[package]] name = "asttokens" version = "3.0.0" @@ -477,6 +498,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/78/bf94897361fdd650850f0f2e405b2293e2f12808239046232bdedf554301/azure_core-1.35.0-py3-none-any.whl", hash = "sha256:8db78c72868a58f3de8991eb4d22c4d368fae226dac1002998d6c50437e7dad1", size = 210708, upload-time = "2025-07-03T00:55:25.238Z" }, ] +[[package]] +name = "azure-core-tracing-opentelemetry" +version = "1.0.0b12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/7f/5de13a331a5f2919417819cc37dcf7c897018f02f83aa82b733e6629a6a6/azure_core_tracing_opentelemetry-1.0.0b12.tar.gz", hash = "sha256:bb454142440bae11fd9d68c7c1d67ae38a1756ce808c5e4d736730a7b4b04144", size = 26010, upload-time = "2025-03-21T00:18:37.346Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/5e/97a471f66935e7f89f521d0e11ae49c7f0871ca38f5c319dccae2155c8d8/azure_core_tracing_opentelemetry-1.0.0b12-py3-none-any.whl", hash = "sha256:38fd42709f1cc4bbc4f2797008b1c30a6a01617e49910c05daa3a0d0c65053ac", size = 11962, upload-time = "2025-03-21T00:18:38.581Z" }, +] + [[package]] name = "azure-identity" version = "1.24.0" @@ -493,6 +527,47 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a9/74/17428cb429e8d52f6d0d69ed685f4760a545cb0156594963a9337b53b6c9/azure_identity-1.24.0-py3-none-any.whl", hash = "sha256:9e04997cde0ab02ed66422c74748548e620b7b29361c72ce622acab0267ff7c4", size = 187890, upload-time = "2025-08-07T22:27:38.033Z" }, ] +[[package]] +name = "azure-monitor-opentelemetry" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-core-tracing-opentelemetry", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-monitor-opentelemetry-exporter", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation-django", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation-fastapi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation-flask", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation-psycopg2", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation-requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation-urllib", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation-urllib3", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-resource-detector-azure", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/77/be4ae57398fe54fdd97af90df32173f68f37593dc56610c7b04c1643da96/azure_monitor_opentelemetry-1.7.0.tar.gz", hash = "sha256:eba75e793a95d50f6e5bc35dd2781744e2c1a5cc801b530b688f649423f2ee00", size = 51735, upload-time = "2025-08-21T15:52:58.563Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/bd/b898a883f379d2b4f9bcb9473d4daac24160854d947f17219a7b9211ab34/azure_monitor_opentelemetry-1.7.0-py3-none-any.whl", hash = "sha256:937c60e9706f75c77b221979a273a27e811cc6529d6887099f53916719c66dd3", size = 26316, upload-time = "2025-08-21T15:53:00.153Z" }, +] + +[[package]] +name = "azure-monitor-opentelemetry-exporter" +version = "1.0.0b41" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "azure-identity", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "fixedint", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "msrest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "psutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6c/c3/2f18eaed17a40982ad04953ad0fa5b1a2dbc5a5c98b6d3ef68c1d7d285ae/azure_monitor_opentelemetry_exporter-1.0.0b41.tar.gz", hash = "sha256:b363e6f89c0dee16d02782a310a60d626e4c081ef49d533ff5225a40cbab12cc", size = 206710, upload-time = "2025-07-31T22:37:28.378Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/d4/8cc989c4eedcefb74dccdd5af2faf51e0237163f538d2b258eef7e35f33d/azure_monitor_opentelemetry_exporter-1.0.0b41-py2.py3-none-any.whl", hash = "sha256:cbba629cca53e0e33416c61e08ebaabe833e740cfbfd7f2e9151821f92c66a51", size = 162631, upload-time = "2025-07-31T22:37:29.809Z" }, +] + [[package]] name = "azure-storage-blob" version = "12.26.0" @@ -800,49 +875,49 @@ toml = [ [[package]] name = "cryptography" -version = "45.0.6" +version = "45.0.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "(platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_python_implementation != 'PyPy' and sys_platform == 'linux') or (platform_python_implementation != 'PyPy' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/0d/d13399c94234ee8f3df384819dc67e0c5ce215fb751d567a55a1f4b028c7/cryptography-45.0.6.tar.gz", hash = "sha256:5c966c732cf6e4a276ce83b6e4c729edda2df6929083a952cc7da973c539c719", size = 744949, upload-time = "2025-08-05T23:59:27.93Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/35/c495bffc2056f2dadb32434f1feedd79abde2a7f8363e1974afa9c33c7e2/cryptography-45.0.7.tar.gz", hash = "sha256:4b1654dfc64ea479c242508eb8c724044f1e964a47d1d1cacc5132292d851971", size = 744980, upload-time = "2025-09-01T11:15:03.146Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/29/2793d178d0eda1ca4a09a7c4e09a5185e75738cc6d526433e8663b460ea6/cryptography-45.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:048e7ad9e08cf4c0ab07ff7f36cc3115924e22e2266e034450a890d9e312dd74", size = 7042702, upload-time = "2025-08-05T23:58:23.464Z" }, - { url = "https://files.pythonhosted.org/packages/b3/b6/cabd07410f222f32c8d55486c464f432808abaa1f12af9afcbe8f2f19030/cryptography-45.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:44647c5d796f5fc042bbc6d61307d04bf29bccb74d188f18051b635f20a9c75f", size = 4206483, upload-time = "2025-08-05T23:58:27.132Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9e/f9c7d36a38b1cfeb1cc74849aabe9bf817990f7603ff6eb485e0d70e0b27/cryptography-45.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e40b80ecf35ec265c452eea0ba94c9587ca763e739b8e559c128d23bff7ebbbf", size = 4429679, upload-time = "2025-08-05T23:58:29.152Z" }, - { url = "https://files.pythonhosted.org/packages/9c/2a/4434c17eb32ef30b254b9e8b9830cee4e516f08b47fdd291c5b1255b8101/cryptography-45.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:00e8724bdad672d75e6f069b27970883179bd472cd24a63f6e620ca7e41cc0c5", size = 4210553, upload-time = "2025-08-05T23:58:30.596Z" }, - { url = "https://files.pythonhosted.org/packages/ef/1d/09a5df8e0c4b7970f5d1f3aff1b640df6d4be28a64cae970d56c6cf1c772/cryptography-45.0.6-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a3085d1b319d35296176af31c90338eeb2ddac8104661df79f80e1d9787b8b2", size = 3894499, upload-time = "2025-08-05T23:58:32.03Z" }, - { url = "https://files.pythonhosted.org/packages/79/62/120842ab20d9150a9d3a6bdc07fe2870384e82f5266d41c53b08a3a96b34/cryptography-45.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1b7fa6a1c1188c7ee32e47590d16a5a0646270921f8020efc9a511648e1b2e08", size = 4458484, upload-time = "2025-08-05T23:58:33.526Z" }, - { url = "https://files.pythonhosted.org/packages/fd/80/1bc3634d45ddfed0871bfba52cf8f1ad724761662a0c792b97a951fb1b30/cryptography-45.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:275ba5cc0d9e320cd70f8e7b96d9e59903c815ca579ab96c1e37278d231fc402", size = 4210281, upload-time = "2025-08-05T23:58:35.445Z" }, - { url = "https://files.pythonhosted.org/packages/7d/fe/ffb12c2d83d0ee625f124880a1f023b5878f79da92e64c37962bbbe35f3f/cryptography-45.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f4028f29a9f38a2025abedb2e409973709c660d44319c61762202206ed577c42", size = 4456890, upload-time = "2025-08-05T23:58:36.923Z" }, - { url = "https://files.pythonhosted.org/packages/8c/8e/b3f3fe0dc82c77a0deb5f493b23311e09193f2268b77196ec0f7a36e3f3e/cryptography-45.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ee411a1b977f40bd075392c80c10b58025ee5c6b47a822a33c1198598a7a5f05", size = 4333247, upload-time = "2025-08-05T23:58:38.781Z" }, - { url = "https://files.pythonhosted.org/packages/b3/a6/c3ef2ab9e334da27a1d7b56af4a2417d77e7806b2e0f90d6267ce120d2e4/cryptography-45.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e2a21a8eda2d86bb604934b6b37691585bd095c1f788530c1fcefc53a82b3453", size = 4565045, upload-time = "2025-08-05T23:58:40.415Z" }, - { url = "https://files.pythonhosted.org/packages/31/c3/77722446b13fa71dddd820a5faab4ce6db49e7e0bf8312ef4192a3f78e2f/cryptography-45.0.6-cp311-abi3-win32.whl", hash = "sha256:d063341378d7ee9c91f9d23b431a3502fc8bfacd54ef0a27baa72a0843b29159", size = 2928923, upload-time = "2025-08-05T23:58:41.919Z" }, - { url = "https://files.pythonhosted.org/packages/38/63/a025c3225188a811b82932a4dcc8457a26c3729d81578ccecbcce2cb784e/cryptography-45.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:833dc32dfc1e39b7376a87b9a6a4288a10aae234631268486558920029b086ec", size = 3403805, upload-time = "2025-08-05T23:58:43.792Z" }, - { url = "https://files.pythonhosted.org/packages/5b/af/bcfbea93a30809f126d51c074ee0fac5bd9d57d068edf56c2a73abedbea4/cryptography-45.0.6-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:3436128a60a5e5490603ab2adbabc8763613f638513ffa7d311c900a8349a2a0", size = 7020111, upload-time = "2025-08-05T23:58:45.316Z" }, - { url = "https://files.pythonhosted.org/packages/98/c6/ea5173689e014f1a8470899cd5beeb358e22bb3cf5a876060f9d1ca78af4/cryptography-45.0.6-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0d9ef57b6768d9fa58e92f4947cea96ade1233c0e236db22ba44748ffedca394", size = 4198169, upload-time = "2025-08-05T23:58:47.121Z" }, - { url = "https://files.pythonhosted.org/packages/ba/73/b12995edc0c7e2311ffb57ebd3b351f6b268fed37d93bfc6f9856e01c473/cryptography-45.0.6-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea3c42f2016a5bbf71825537c2ad753f2870191134933196bee408aac397b3d9", size = 4421273, upload-time = "2025-08-05T23:58:48.557Z" }, - { url = "https://files.pythonhosted.org/packages/f7/6e/286894f6f71926bc0da67408c853dd9ba953f662dcb70993a59fd499f111/cryptography-45.0.6-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:20ae4906a13716139d6d762ceb3e0e7e110f7955f3bc3876e3a07f5daadec5f3", size = 4199211, upload-time = "2025-08-05T23:58:50.139Z" }, - { url = "https://files.pythonhosted.org/packages/de/34/a7f55e39b9623c5cb571d77a6a90387fe557908ffc44f6872f26ca8ae270/cryptography-45.0.6-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dac5ec199038b8e131365e2324c03d20e97fe214af051d20c49db129844e8b3", size = 3883732, upload-time = "2025-08-05T23:58:52.253Z" }, - { url = "https://files.pythonhosted.org/packages/f9/b9/c6d32edbcba0cd9f5df90f29ed46a65c4631c4fbe11187feb9169c6ff506/cryptography-45.0.6-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:18f878a34b90d688982e43f4b700408b478102dd58b3e39de21b5ebf6509c301", size = 4450655, upload-time = "2025-08-05T23:58:53.848Z" }, - { url = "https://files.pythonhosted.org/packages/77/2d/09b097adfdee0227cfd4c699b3375a842080f065bab9014248933497c3f9/cryptography-45.0.6-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5bd6020c80c5b2b2242d6c48487d7b85700f5e0038e67b29d706f98440d66eb5", size = 4198956, upload-time = "2025-08-05T23:58:55.209Z" }, - { url = "https://files.pythonhosted.org/packages/55/66/061ec6689207d54effdff535bbdf85cc380d32dd5377173085812565cf38/cryptography-45.0.6-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:eccddbd986e43014263eda489abbddfbc287af5cddfd690477993dbb31e31016", size = 4449859, upload-time = "2025-08-05T23:58:56.639Z" }, - { url = "https://files.pythonhosted.org/packages/41/ff/e7d5a2ad2d035e5a2af116e1a3adb4d8fcd0be92a18032917a089c6e5028/cryptography-45.0.6-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:550ae02148206beb722cfe4ef0933f9352bab26b087af00e48fdfb9ade35c5b3", size = 4320254, upload-time = "2025-08-05T23:58:58.833Z" }, - { url = "https://files.pythonhosted.org/packages/82/27/092d311af22095d288f4db89fcaebadfb2f28944f3d790a4cf51fe5ddaeb/cryptography-45.0.6-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5b64e668fc3528e77efa51ca70fadcd6610e8ab231e3e06ae2bab3b31c2b8ed9", size = 4554815, upload-time = "2025-08-05T23:59:00.283Z" }, - { url = "https://files.pythonhosted.org/packages/7e/01/aa2f4940262d588a8fdf4edabe4cda45854d00ebc6eaac12568b3a491a16/cryptography-45.0.6-cp37-abi3-win32.whl", hash = "sha256:780c40fb751c7d2b0c6786ceee6b6f871e86e8718a8ff4bc35073ac353c7cd02", size = 2912147, upload-time = "2025-08-05T23:59:01.716Z" }, - { url = "https://files.pythonhosted.org/packages/0a/bc/16e0276078c2de3ceef6b5a34b965f4436215efac45313df90d55f0ba2d2/cryptography-45.0.6-cp37-abi3-win_amd64.whl", hash = "sha256:20d15aed3ee522faac1a39fbfdfee25d17b1284bafd808e1640a74846d7c4d1b", size = 3390459, upload-time = "2025-08-05T23:59:03.358Z" }, - { url = "https://files.pythonhosted.org/packages/56/d2/4482d97c948c029be08cb29854a91bd2ae8da7eb9c4152461f1244dcea70/cryptography-45.0.6-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:705bb7c7ecc3d79a50f236adda12ca331c8e7ecfbea51edd931ce5a7a7c4f012", size = 3576812, upload-time = "2025-08-05T23:59:04.833Z" }, - { url = "https://files.pythonhosted.org/packages/ec/24/55fc238fcaa122855442604b8badb2d442367dfbd5a7ca4bb0bd346e263a/cryptography-45.0.6-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:826b46dae41a1155a0c0e66fafba43d0ede1dc16570b95e40c4d83bfcf0a451d", size = 4141694, upload-time = "2025-08-05T23:59:06.66Z" }, - { url = "https://files.pythonhosted.org/packages/f9/7e/3ea4fa6fbe51baf3903806a0241c666b04c73d2358a3ecce09ebee8b9622/cryptography-45.0.6-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:cc4d66f5dc4dc37b89cfef1bd5044387f7a1f6f0abb490815628501909332d5d", size = 4375010, upload-time = "2025-08-05T23:59:08.14Z" }, - { url = "https://files.pythonhosted.org/packages/50/42/ec5a892d82d2a2c29f80fc19ced4ba669bca29f032faf6989609cff1f8dc/cryptography-45.0.6-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:f68f833a9d445cc49f01097d95c83a850795921b3f7cc6488731e69bde3288da", size = 4141377, upload-time = "2025-08-05T23:59:09.584Z" }, - { url = "https://files.pythonhosted.org/packages/e7/d7/246c4c973a22b9c2931999da953a2c19cae7c66b9154c2d62ffed811225e/cryptography-45.0.6-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:3b5bf5267e98661b9b888a9250d05b063220dfa917a8203744454573c7eb79db", size = 4374609, upload-time = "2025-08-05T23:59:11.923Z" }, - { url = "https://files.pythonhosted.org/packages/78/6d/c49ccf243f0a1b0781c2a8de8123ee552f0c8a417c6367a24d2ecb7c11b3/cryptography-45.0.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2384f2ab18d9be88a6e4f8972923405e2dbb8d3e16c6b43f15ca491d7831bd18", size = 3322156, upload-time = "2025-08-05T23:59:13.597Z" }, - { url = "https://files.pythonhosted.org/packages/61/69/c252de4ec047ba2f567ecb53149410219577d408c2aea9c989acae7eafce/cryptography-45.0.6-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fc022c1fa5acff6def2fc6d7819bbbd31ccddfe67d075331a65d9cfb28a20983", size = 3584669, upload-time = "2025-08-05T23:59:15.431Z" }, - { url = "https://files.pythonhosted.org/packages/e3/fe/deea71e9f310a31fe0a6bfee670955152128d309ea2d1c79e2a5ae0f0401/cryptography-45.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3de77e4df42ac8d4e4d6cdb342d989803ad37707cf8f3fbf7b088c9cbdd46427", size = 4153022, upload-time = "2025-08-05T23:59:16.954Z" }, - { url = "https://files.pythonhosted.org/packages/60/45/a77452f5e49cb580feedba6606d66ae7b82c128947aa754533b3d1bd44b0/cryptography-45.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:599c8d7df950aa68baa7e98f7b73f4f414c9f02d0e8104a30c0182a07732638b", size = 4386802, upload-time = "2025-08-05T23:59:18.55Z" }, - { url = "https://files.pythonhosted.org/packages/a3/b9/a2f747d2acd5e3075fdf5c145c7c3568895daaa38b3b0c960ef830db6cdc/cryptography-45.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:31a2b9a10530a1cb04ffd6aa1cd4d3be9ed49f7d77a4dafe198f3b382f41545c", size = 4152706, upload-time = "2025-08-05T23:59:20.044Z" }, - { url = "https://files.pythonhosted.org/packages/81/ec/381b3e8d0685a3f3f304a382aa3dfce36af2d76467da0fd4bb21ddccc7b2/cryptography-45.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:e5b3dda1b00fb41da3af4c5ef3f922a200e33ee5ba0f0bc9ecf0b0c173958385", size = 4386740, upload-time = "2025-08-05T23:59:21.525Z" }, - { url = "https://files.pythonhosted.org/packages/0a/76/cf8d69da8d0b5ecb0db406f24a63a3f69ba5e791a11b782aeeefef27ccbb/cryptography-45.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:629127cfdcdc6806dfe234734d7cb8ac54edaf572148274fa377a7d3405b0043", size = 3331874, upload-time = "2025-08-05T23:59:23.017Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/925c0ac74362172ae4516000fe877912e33b5983df735ff290c653de4913/cryptography-45.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3be4f21c6245930688bd9e162829480de027f8bf962ede33d4f8ba7d67a00cee", size = 7041105, upload-time = "2025-09-01T11:13:59.684Z" }, + { url = "https://files.pythonhosted.org/packages/fc/63/43641c5acce3a6105cf8bd5baeceeb1846bb63067d26dae3e5db59f1513a/cryptography-45.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:67285f8a611b0ebc0857ced2081e30302909f571a46bfa7a3cc0ad303fe015c6", size = 4205799, upload-time = "2025-09-01T11:14:02.517Z" }, + { url = "https://files.pythonhosted.org/packages/bc/29/c238dd9107f10bfde09a4d1c52fd38828b1aa353ced11f358b5dd2507d24/cryptography-45.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:577470e39e60a6cd7780793202e63536026d9b8641de011ed9d8174da9ca5339", size = 4430504, upload-time = "2025-09-01T11:14:04.522Z" }, + { url = "https://files.pythonhosted.org/packages/62/62/24203e7cbcc9bd7c94739428cd30680b18ae6b18377ae66075c8e4771b1b/cryptography-45.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4bd3e5c4b9682bc112d634f2c6ccc6736ed3635fc3319ac2bb11d768cc5a00d8", size = 4209542, upload-time = "2025-09-01T11:14:06.309Z" }, + { url = "https://files.pythonhosted.org/packages/cd/e3/e7de4771a08620eef2389b86cd87a2c50326827dea5528feb70595439ce4/cryptography-45.0.7-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:465ccac9d70115cd4de7186e60cfe989de73f7bb23e8a7aa45af18f7412e75bf", size = 3889244, upload-time = "2025-09-01T11:14:08.152Z" }, + { url = "https://files.pythonhosted.org/packages/96/b8/bca71059e79a0bb2f8e4ec61d9c205fbe97876318566cde3b5092529faa9/cryptography-45.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:16ede8a4f7929b4b7ff3642eba2bf79aa1d71f24ab6ee443935c0d269b6bc513", size = 4461975, upload-time = "2025-09-01T11:14:09.755Z" }, + { url = "https://files.pythonhosted.org/packages/58/67/3f5b26937fe1218c40e95ef4ff8d23c8dc05aa950d54200cc7ea5fb58d28/cryptography-45.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8978132287a9d3ad6b54fcd1e08548033cc09dc6aacacb6c004c73c3eb5d3ac3", size = 4209082, upload-time = "2025-09-01T11:14:11.229Z" }, + { url = "https://files.pythonhosted.org/packages/0e/e4/b3e68a4ac363406a56cf7b741eeb80d05284d8c60ee1a55cdc7587e2a553/cryptography-45.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b6a0e535baec27b528cb07a119f321ac024592388c5681a5ced167ae98e9fff3", size = 4460397, upload-time = "2025-09-01T11:14:12.924Z" }, + { url = "https://files.pythonhosted.org/packages/22/49/2c93f3cd4e3efc8cb22b02678c1fad691cff9dd71bb889e030d100acbfe0/cryptography-45.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a24ee598d10befaec178efdff6054bc4d7e883f615bfbcd08126a0f4931c83a6", size = 4337244, upload-time = "2025-09-01T11:14:14.431Z" }, + { url = "https://files.pythonhosted.org/packages/04/19/030f400de0bccccc09aa262706d90f2ec23d56bc4eb4f4e8268d0ddf3fb8/cryptography-45.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa26fa54c0a9384c27fcdc905a2fb7d60ac6e47d14bc2692145f2b3b1e2cfdbd", size = 4568862, upload-time = "2025-09-01T11:14:16.185Z" }, + { url = "https://files.pythonhosted.org/packages/29/56/3034a3a353efa65116fa20eb3c990a8c9f0d3db4085429040a7eef9ada5f/cryptography-45.0.7-cp311-abi3-win32.whl", hash = "sha256:bef32a5e327bd8e5af915d3416ffefdbe65ed975b646b3805be81b23580b57b8", size = 2936578, upload-time = "2025-09-01T11:14:17.638Z" }, + { url = "https://files.pythonhosted.org/packages/b3/61/0ab90f421c6194705a99d0fa9f6ee2045d916e4455fdbb095a9c2c9a520f/cryptography-45.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:3808e6b2e5f0b46d981c24d79648e5c25c35e59902ea4391a0dcb3e667bf7443", size = 3405400, upload-time = "2025-09-01T11:14:18.958Z" }, + { url = "https://files.pythonhosted.org/packages/63/e8/c436233ddf19c5f15b25ace33979a9dd2e7aa1a59209a0ee8554179f1cc0/cryptography-45.0.7-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bfb4c801f65dd61cedfc61a83732327fafbac55a47282e6f26f073ca7a41c3b2", size = 7021824, upload-time = "2025-09-01T11:14:20.954Z" }, + { url = "https://files.pythonhosted.org/packages/bc/4c/8f57f2500d0ccd2675c5d0cc462095adf3faa8c52294ba085c036befb901/cryptography-45.0.7-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:81823935e2f8d476707e85a78a405953a03ef7b7b4f55f93f7c2d9680e5e0691", size = 4202233, upload-time = "2025-09-01T11:14:22.454Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ac/59b7790b4ccaed739fc44775ce4645c9b8ce54cbec53edf16c74fd80cb2b/cryptography-45.0.7-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3994c809c17fc570c2af12c9b840d7cea85a9fd3e5c0e0491f4fa3c029216d59", size = 4423075, upload-time = "2025-09-01T11:14:24.287Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/d4f07ea21434bf891faa088a6ac15d6d98093a66e75e30ad08e88aa2b9ba/cryptography-45.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dad43797959a74103cb59c5dac71409f9c27d34c8a05921341fb64ea8ccb1dd4", size = 4204517, upload-time = "2025-09-01T11:14:25.679Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ac/924a723299848b4c741c1059752c7cfe09473b6fd77d2920398fc26bfb53/cryptography-45.0.7-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ce7a453385e4c4693985b4a4a3533e041558851eae061a58a5405363b098fcd3", size = 3882893, upload-time = "2025-09-01T11:14:27.1Z" }, + { url = "https://files.pythonhosted.org/packages/83/dc/4dab2ff0a871cc2d81d3ae6d780991c0192b259c35e4d83fe1de18b20c70/cryptography-45.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b04f85ac3a90c227b6e5890acb0edbaf3140938dbecf07bff618bf3638578cf1", size = 4450132, upload-time = "2025-09-01T11:14:28.58Z" }, + { url = "https://files.pythonhosted.org/packages/12/dd/b2882b65db8fc944585d7fb00d67cf84a9cef4e77d9ba8f69082e911d0de/cryptography-45.0.7-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:48c41a44ef8b8c2e80ca4527ee81daa4c527df3ecbc9423c41a420a9559d0e27", size = 4204086, upload-time = "2025-09-01T11:14:30.572Z" }, + { url = "https://files.pythonhosted.org/packages/5d/fa/1d5745d878048699b8eb87c984d4ccc5da4f5008dfd3ad7a94040caca23a/cryptography-45.0.7-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f3df7b3d0f91b88b2106031fd995802a2e9ae13e02c36c1fc075b43f420f3a17", size = 4449383, upload-time = "2025-09-01T11:14:32.046Z" }, + { url = "https://files.pythonhosted.org/packages/36/8b/fc61f87931bc030598e1876c45b936867bb72777eac693e905ab89832670/cryptography-45.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd342f085542f6eb894ca00ef70236ea46070c8a13824c6bde0dfdcd36065b9b", size = 4332186, upload-time = "2025-09-01T11:14:33.95Z" }, + { url = "https://files.pythonhosted.org/packages/0b/11/09700ddad7443ccb11d674efdbe9a832b4455dc1f16566d9bd3834922ce5/cryptography-45.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1993a1bb7e4eccfb922b6cd414f072e08ff5816702a0bdb8941c247a6b1b287c", size = 4561639, upload-time = "2025-09-01T11:14:35.343Z" }, + { url = "https://files.pythonhosted.org/packages/71/ed/8f4c1337e9d3b94d8e50ae0b08ad0304a5709d483bfcadfcc77a23dbcb52/cryptography-45.0.7-cp37-abi3-win32.whl", hash = "sha256:18fcf70f243fe07252dcb1b268a687f2358025ce32f9f88028ca5c364b123ef5", size = 2926552, upload-time = "2025-09-01T11:14:36.929Z" }, + { url = "https://files.pythonhosted.org/packages/bc/ff/026513ecad58dacd45d1d24ebe52b852165a26e287177de1d545325c0c25/cryptography-45.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:7285a89df4900ed3bfaad5679b1e668cb4b38a8de1ccbfc84b05f34512da0a90", size = 3392742, upload-time = "2025-09-01T11:14:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/13/3e/e42f1528ca1ea82256b835191eab1be014e0f9f934b60d98b0be8a38ed70/cryptography-45.0.7-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:de58755d723e86175756f463f2f0bddd45cc36fbd62601228a3f8761c9f58252", size = 3572442, upload-time = "2025-09-01T11:14:39.836Z" }, + { url = "https://files.pythonhosted.org/packages/59/aa/e947693ab08674a2663ed2534cd8d345cf17bf6a1facf99273e8ec8986dc/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a20e442e917889d1a6b3c570c9e3fa2fdc398c20868abcea268ea33c024c4083", size = 4142233, upload-time = "2025-09-01T11:14:41.305Z" }, + { url = "https://files.pythonhosted.org/packages/24/06/09b6f6a2fc43474a32b8fe259038eef1500ee3d3c141599b57ac6c57612c/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:258e0dff86d1d891169b5af222d362468a9570e2532923088658aa866eb11130", size = 4376202, upload-time = "2025-09-01T11:14:43.047Z" }, + { url = "https://files.pythonhosted.org/packages/00/f2/c166af87e95ce6ae6d38471a7e039d3a0549c2d55d74e059680162052824/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d97cf502abe2ab9eff8bd5e4aca274da8d06dd3ef08b759a8d6143f4ad65d4b4", size = 4141900, upload-time = "2025-09-01T11:14:45.089Z" }, + { url = "https://files.pythonhosted.org/packages/16/b9/e96e0b6cb86eae27ea51fa8a3151535a18e66fe7c451fa90f7f89c85f541/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:c987dad82e8c65ebc985f5dae5e74a3beda9d0a2a4daf8a1115f3772b59e5141", size = 4375562, upload-time = "2025-09-01T11:14:47.166Z" }, + { url = "https://files.pythonhosted.org/packages/36/d0/36e8ee39274e9d77baf7d0dafda680cba6e52f3936b846f0d56d64fec915/cryptography-45.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c13b1e3afd29a5b3b2656257f14669ca8fa8d7956d509926f0b130b600b50ab7", size = 3322781, upload-time = "2025-09-01T11:14:48.747Z" }, + { url = "https://files.pythonhosted.org/packages/99/4e/49199a4c82946938a3e05d2e8ad9482484ba48bbc1e809e3d506c686d051/cryptography-45.0.7-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a862753b36620af6fc54209264f92c716367f2f0ff4624952276a6bbd18cbde", size = 3584634, upload-time = "2025-09-01T11:14:50.593Z" }, + { url = "https://files.pythonhosted.org/packages/16/ce/5f6ff59ea9c7779dba51b84871c19962529bdcc12e1a6ea172664916c550/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:06ce84dc14df0bf6ea84666f958e6080cdb6fe1231be2a51f3fc1267d9f3fb34", size = 4149533, upload-time = "2025-09-01T11:14:52.091Z" }, + { url = "https://files.pythonhosted.org/packages/ce/13/b3cfbd257ac96da4b88b46372e662009b7a16833bfc5da33bb97dd5631ae/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d0c5c6bac22b177bf8da7435d9d27a6834ee130309749d162b26c3105c0795a9", size = 4385557, upload-time = "2025-09-01T11:14:53.551Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c5/8c59d6b7c7b439ba4fc8d0cab868027fd095f215031bc123c3a070962912/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:2f641b64acc00811da98df63df7d59fd4706c0df449da71cb7ac39a0732b40ae", size = 4149023, upload-time = "2025-09-01T11:14:55.022Z" }, + { url = "https://files.pythonhosted.org/packages/55/32/05385c86d6ca9ab0b4d5bb442d2e3d85e727939a11f3e163fc776ce5eb40/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:f5414a788ecc6ee6bc58560e85ca624258a55ca434884445440a810796ea0e0b", size = 4385722, upload-time = "2025-09-01T11:14:57.319Z" }, + { url = "https://files.pythonhosted.org/packages/23/87/7ce86f3fa14bc11a5a48c30d8103c26e09b6465f8d8e9d74cf7a0714f043/cryptography-45.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:1f3d56f73595376f4244646dd5c5870c14c196949807be39e79e7bd9bac3da63", size = 3332908, upload-time = "2025-09-01T11:14:58.78Z" }, ] [[package]] @@ -938,11 +1013,11 @@ wheels = [ [[package]] name = "executing" -version = "2.2.0" +version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693, upload-time = "2025-01-22T15:41:29.403Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702, upload-time = "2025-01-22T15:41:25.929Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, ] [[package]] @@ -963,6 +1038,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, ] +[[package]] +name = "fixedint" +version = "0.1.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/32/c6/b1b9b3f69915d51909ef6ebe6352e286ec3d6f2077278af83ec6e3cc569c/fixedint-0.1.6.tar.gz", hash = "sha256:703005d090499d41ce7ce2ee7eae8f7a5589a81acdc6b79f1728a56495f2c799", size = 12750, upload-time = "2020-06-20T22:14:16.544Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/6d/8f5307d26ce700a89e5a67d1e1ad15eff977211f9ed3ae90d7b0d67f4e66/fixedint-0.1.6-py3-none-any.whl", hash = "sha256:b8cf9f913735d2904deadda7a6daa9f57100599da1de57a7448ea1be75ae8c9c", size = 12702, upload-time = "2020-06-20T22:14:15.454Z" }, +] + [[package]] name = "frozenlist" version = "1.7.0" @@ -1057,6 +1141,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e", size = 13106, upload-time = "2025-06-09T23:02:34.204Z" }, ] +[[package]] +name = "googleapis-common-protos" +version = "1.70.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/24/33db22342cf4a2ea27c9955e6713140fedd51e8b141b5ce5260897020f1a/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257", size = 145903, upload-time = "2025-04-14T10:17:02.924Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/f1/62a193f0227cf15a920390abe675f386dec35f7ae3ffe6da582d3ade42c7/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8", size = 294530, upload-time = "2025-04-14T10:17:01.271Z" }, +] + [[package]] name = "graphviz" version = "0.21" @@ -1117,6 +1213,54 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425, upload-time = "2025-08-07T13:32:27.59Z" }, ] +[[package]] +name = "grpcio" +version = "1.74.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/b4/35feb8f7cab7239c5b94bd2db71abb3d6adb5f335ad8f131abb6060840b6/grpcio-1.74.0.tar.gz", hash = "sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1", size = 12756048, upload-time = "2025-07-24T18:54:23.039Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/54/68e51a90797ad7afc5b0a7881426c337f6a9168ebab73c3210b76aa7c90d/grpcio-1.74.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:85bd5cdf4ed7b2d6438871adf6afff9af7096486fcf51818a81b77ef4dd30907", size = 5481935, upload-time = "2025-07-24T18:52:43.756Z" }, + { url = "https://files.pythonhosted.org/packages/32/2a/af817c7e9843929e93e54d09c9aee2555c2e8d81b93102a9426b36e91833/grpcio-1.74.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:68c8ebcca945efff9d86d8d6d7bfb0841cf0071024417e2d7f45c5e46b5b08eb", size = 10986796, upload-time = "2025-07-24T18:52:47.219Z" }, + { url = "https://files.pythonhosted.org/packages/d5/94/d67756638d7bb07750b07d0826c68e414124574b53840ba1ff777abcd388/grpcio-1.74.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:e154d230dc1bbbd78ad2fdc3039fa50ad7ffcf438e4eb2fa30bce223a70c7486", size = 5983663, upload-time = "2025-07-24T18:52:49.463Z" }, + { url = "https://files.pythonhosted.org/packages/35/f5/c5e4853bf42148fea8532d49e919426585b73eafcf379a712934652a8de9/grpcio-1.74.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8978003816c7b9eabe217f88c78bc26adc8f9304bf6a594b02e5a49b2ef9c11", size = 6653765, upload-time = "2025-07-24T18:52:51.094Z" }, + { url = "https://files.pythonhosted.org/packages/fd/75/a1991dd64b331d199935e096cc9daa3415ee5ccbe9f909aa48eded7bba34/grpcio-1.74.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3d7bd6e3929fd2ea7fbc3f562e4987229ead70c9ae5f01501a46701e08f1ad9", size = 6215172, upload-time = "2025-07-24T18:52:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/01/a4/7cef3dbb3b073d0ce34fd507efc44ac4c9442a0ef9fba4fb3f5c551efef5/grpcio-1.74.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:136b53c91ac1d02c8c24201bfdeb56f8b3ac3278668cbb8e0ba49c88069e1bdc", size = 6329142, upload-time = "2025-07-24T18:52:54.927Z" }, + { url = "https://files.pythonhosted.org/packages/bf/d3/587920f882b46e835ad96014087054655312400e2f1f1446419e5179a383/grpcio-1.74.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fe0f540750a13fd8e5da4b3eaba91a785eea8dca5ccd2bc2ffe978caa403090e", size = 7018632, upload-time = "2025-07-24T18:52:56.523Z" }, + { url = "https://files.pythonhosted.org/packages/1f/95/c70a3b15a0bc83334b507e3d2ae20ee8fa38d419b8758a4d838f5c2a7d32/grpcio-1.74.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4e4181bfc24413d1e3a37a0b7889bea68d973d4b45dd2bc68bb766c140718f82", size = 6509641, upload-time = "2025-07-24T18:52:58.495Z" }, + { url = "https://files.pythonhosted.org/packages/4b/06/2e7042d06247d668ae69ea6998eca33f475fd4e2855f94dcb2aa5daef334/grpcio-1.74.0-cp310-cp310-win32.whl", hash = "sha256:1733969040989f7acc3d94c22f55b4a9501a30f6aaacdbccfaba0a3ffb255ab7", size = 3817478, upload-time = "2025-07-24T18:53:00.128Z" }, + { url = "https://files.pythonhosted.org/packages/93/20/e02b9dcca3ee91124060b65bbf5b8e1af80b3b76a30f694b44b964ab4d71/grpcio-1.74.0-cp310-cp310-win_amd64.whl", hash = "sha256:9e912d3c993a29df6c627459af58975b2e5c897d93287939b9d5065f000249b5", size = 4493971, upload-time = "2025-07-24T18:53:02.068Z" }, + { url = "https://files.pythonhosted.org/packages/e7/77/b2f06db9f240a5abeddd23a0e49eae2b6ac54d85f0e5267784ce02269c3b/grpcio-1.74.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:69e1a8180868a2576f02356565f16635b99088da7df3d45aaa7e24e73a054e31", size = 5487368, upload-time = "2025-07-24T18:53:03.548Z" }, + { url = "https://files.pythonhosted.org/packages/48/99/0ac8678a819c28d9a370a663007581744a9f2a844e32f0fa95e1ddda5b9e/grpcio-1.74.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8efe72fde5500f47aca1ef59495cb59c885afe04ac89dd11d810f2de87d935d4", size = 10999804, upload-time = "2025-07-24T18:53:05.095Z" }, + { url = "https://files.pythonhosted.org/packages/45/c6/a2d586300d9e14ad72e8dc211c7aecb45fe9846a51e558c5bca0c9102c7f/grpcio-1.74.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a8f0302f9ac4e9923f98d8e243939a6fb627cd048f5cd38595c97e38020dffce", size = 5987667, upload-time = "2025-07-24T18:53:07.157Z" }, + { url = "https://files.pythonhosted.org/packages/c9/57/5f338bf56a7f22584e68d669632e521f0de460bb3749d54533fc3d0fca4f/grpcio-1.74.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f609a39f62a6f6f05c7512746798282546358a37ea93c1fcbadf8b2fed162e3", size = 6655612, upload-time = "2025-07-24T18:53:09.244Z" }, + { url = "https://files.pythonhosted.org/packages/82/ea/a4820c4c44c8b35b1903a6c72a5bdccec92d0840cf5c858c498c66786ba5/grpcio-1.74.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c98e0b7434a7fa4e3e63f250456eaef52499fba5ae661c58cc5b5477d11e7182", size = 6219544, upload-time = "2025-07-24T18:53:11.221Z" }, + { url = "https://files.pythonhosted.org/packages/a4/17/0537630a921365928f5abb6d14c79ba4dcb3e662e0dbeede8af4138d9dcf/grpcio-1.74.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:662456c4513e298db6d7bd9c3b8df6f75f8752f0ba01fb653e252ed4a59b5a5d", size = 6334863, upload-time = "2025-07-24T18:53:12.925Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a6/85ca6cb9af3f13e1320d0a806658dca432ff88149d5972df1f7b51e87127/grpcio-1.74.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3d14e3c4d65e19d8430a4e28ceb71ace4728776fd6c3ce34016947474479683f", size = 7019320, upload-time = "2025-07-24T18:53:15.002Z" }, + { url = "https://files.pythonhosted.org/packages/4f/a7/fe2beab970a1e25d2eff108b3cf4f7d9a53c185106377a3d1989216eba45/grpcio-1.74.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bf949792cee20d2078323a9b02bacbbae002b9e3b9e2433f2741c15bdeba1c4", size = 6514228, upload-time = "2025-07-24T18:53:16.999Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c2/2f9c945c8a248cebc3ccda1b7a1bf1775b9d7d59e444dbb18c0014e23da6/grpcio-1.74.0-cp311-cp311-win32.whl", hash = "sha256:55b453812fa7c7ce2f5c88be3018fb4a490519b6ce80788d5913f3f9d7da8c7b", size = 3817216, upload-time = "2025-07-24T18:53:20.564Z" }, + { url = "https://files.pythonhosted.org/packages/ff/d1/a9cf9c94b55becda2199299a12b9feef0c79946b0d9d34c989de6d12d05d/grpcio-1.74.0-cp311-cp311-win_amd64.whl", hash = "sha256:86ad489db097141a907c559988c29718719aa3e13370d40e20506f11b4de0d11", size = 4495380, upload-time = "2025-07-24T18:53:22.058Z" }, + { url = "https://files.pythonhosted.org/packages/4c/5d/e504d5d5c4469823504f65687d6c8fb97b7f7bf0b34873b7598f1df24630/grpcio-1.74.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8533e6e9c5bd630ca98062e3a1326249e6ada07d05acf191a77bc33f8948f3d8", size = 5445551, upload-time = "2025-07-24T18:53:23.641Z" }, + { url = "https://files.pythonhosted.org/packages/43/01/730e37056f96f2f6ce9f17999af1556df62ee8dab7fa48bceeaab5fd3008/grpcio-1.74.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:2918948864fec2a11721d91568effffbe0a02b23ecd57f281391d986847982f6", size = 10979810, upload-time = "2025-07-24T18:53:25.349Z" }, + { url = "https://files.pythonhosted.org/packages/79/3d/09fd100473ea5c47083889ca47ffd356576173ec134312f6aa0e13111dee/grpcio-1.74.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:60d2d48b0580e70d2e1954d0d19fa3c2e60dd7cbed826aca104fff518310d1c5", size = 5941946, upload-time = "2025-07-24T18:53:27.387Z" }, + { url = "https://files.pythonhosted.org/packages/8a/99/12d2cca0a63c874c6d3d195629dcd85cdf5d6f98a30d8db44271f8a97b93/grpcio-1.74.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3601274bc0523f6dc07666c0e01682c94472402ac2fd1226fd96e079863bfa49", size = 6621763, upload-time = "2025-07-24T18:53:29.193Z" }, + { url = "https://files.pythonhosted.org/packages/9d/2c/930b0e7a2f1029bbc193443c7bc4dc2a46fedb0203c8793dcd97081f1520/grpcio-1.74.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:176d60a5168d7948539def20b2a3adcce67d72454d9ae05969a2e73f3a0feee7", size = 6180664, upload-time = "2025-07-24T18:53:30.823Z" }, + { url = "https://files.pythonhosted.org/packages/db/d5/ff8a2442180ad0867717e670f5ec42bfd8d38b92158ad6bcd864e6d4b1ed/grpcio-1.74.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e759f9e8bc908aaae0412642afe5416c9f983a80499448fcc7fab8692ae044c3", size = 6301083, upload-time = "2025-07-24T18:53:32.454Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ba/b361d390451a37ca118e4ec7dccec690422e05bc85fba2ec72b06cefec9f/grpcio-1.74.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9e7c4389771855a92934b2846bd807fc25a3dfa820fd912fe6bd8136026b2707", size = 6994132, upload-time = "2025-07-24T18:53:34.506Z" }, + { url = "https://files.pythonhosted.org/packages/3b/0c/3a5fa47d2437a44ced74141795ac0251bbddeae74bf81df3447edd767d27/grpcio-1.74.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cce634b10aeab37010449124814b05a62fb5f18928ca878f1bf4750d1f0c815b", size = 6489616, upload-time = "2025-07-24T18:53:36.217Z" }, + { url = "https://files.pythonhosted.org/packages/ae/95/ab64703b436d99dc5217228babc76047d60e9ad14df129e307b5fec81fd0/grpcio-1.74.0-cp312-cp312-win32.whl", hash = "sha256:885912559974df35d92219e2dc98f51a16a48395f37b92865ad45186f294096c", size = 3807083, upload-time = "2025-07-24T18:53:37.911Z" }, + { url = "https://files.pythonhosted.org/packages/84/59/900aa2445891fc47a33f7d2f76e00ca5d6ae6584b20d19af9c06fa09bf9a/grpcio-1.74.0-cp312-cp312-win_amd64.whl", hash = "sha256:42f8fee287427b94be63d916c90399ed310ed10aadbf9e2e5538b3e497d269bc", size = 4490123, upload-time = "2025-07-24T18:53:39.528Z" }, + { url = "https://files.pythonhosted.org/packages/d4/d8/1004a5f468715221450e66b051c839c2ce9a985aa3ee427422061fcbb6aa/grpcio-1.74.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:2bc2d7d8d184e2362b53905cb1708c84cb16354771c04b490485fa07ce3a1d89", size = 5449488, upload-time = "2025-07-24T18:53:41.174Z" }, + { url = "https://files.pythonhosted.org/packages/94/0e/33731a03f63740d7743dced423846c831d8e6da808fcd02821a4416df7fa/grpcio-1.74.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:c14e803037e572c177ba54a3e090d6eb12efd795d49327c5ee2b3bddb836bf01", size = 10974059, upload-time = "2025-07-24T18:53:43.066Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c6/3d2c14d87771a421205bdca991467cfe473ee4c6a1231c1ede5248c62ab8/grpcio-1.74.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f6ec94f0e50eb8fa1744a731088b966427575e40c2944a980049798b127a687e", size = 5945647, upload-time = "2025-07-24T18:53:45.269Z" }, + { url = "https://files.pythonhosted.org/packages/c5/83/5a354c8aaff58594eef7fffebae41a0f8995a6258bbc6809b800c33d4c13/grpcio-1.74.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:566b9395b90cc3d0d0c6404bc8572c7c18786ede549cdb540ae27b58afe0fb91", size = 6626101, upload-time = "2025-07-24T18:53:47.015Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ca/4fdc7bf59bf6994aa45cbd4ef1055cd65e2884de6113dbd49f75498ddb08/grpcio-1.74.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1ea6176d7dfd5b941ea01c2ec34de9531ba494d541fe2057c904e601879f249", size = 6182562, upload-time = "2025-07-24T18:53:48.967Z" }, + { url = "https://files.pythonhosted.org/packages/fd/48/2869e5b2c1922583686f7ae674937986807c2f676d08be70d0a541316270/grpcio-1.74.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:64229c1e9cea079420527fa8ac45d80fc1e8d3f94deaa35643c381fa8d98f362", size = 6303425, upload-time = "2025-07-24T18:53:50.847Z" }, + { url = "https://files.pythonhosted.org/packages/a6/0e/bac93147b9a164f759497bc6913e74af1cb632c733c7af62c0336782bd38/grpcio-1.74.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:0f87bddd6e27fc776aacf7ebfec367b6d49cad0455123951e4488ea99d9b9b8f", size = 6996533, upload-time = "2025-07-24T18:53:52.747Z" }, + { url = "https://files.pythonhosted.org/packages/84/35/9f6b2503c1fd86d068b46818bbd7329db26a87cdd8c01e0d1a9abea1104c/grpcio-1.74.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3b03d8f2a07f0fea8c8f74deb59f8352b770e3900d143b3d1475effcb08eec20", size = 6491489, upload-time = "2025-07-24T18:53:55.06Z" }, + { url = "https://files.pythonhosted.org/packages/75/33/a04e99be2a82c4cbc4039eb3a76f6c3632932b9d5d295221389d10ac9ca7/grpcio-1.74.0-cp313-cp313-win32.whl", hash = "sha256:b6a73b2ba83e663b2480a90b82fdae6a7aa6427f62bf43b29912c0cfd1aa2bfa", size = 3805811, upload-time = "2025-07-24T18:53:56.798Z" }, + { url = "https://files.pythonhosted.org/packages/34/80/de3eb55eb581815342d097214bed4c59e806b05f1b3110df03b2280d6dfd/grpcio-1.74.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd3c71aeee838299c5887230b8a1822795325ddfea635edd82954c1eaa831e24", size = 4489214, upload-time = "2025-07-24T18:53:59.771Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -1268,9 +1412,12 @@ name = "ipython" version = "9.5.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.11' and sys_platform == 'darwin'", - "python_full_version >= '3.11' and sys_platform == 'linux'", - "python_full_version >= '3.11' and sys_platform == 'win32'", + "python_full_version >= '3.13' and sys_platform == 'darwin'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and sys_platform == 'linux'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'linux'", + "python_full_version >= '3.13' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'win32'", ] dependencies = [ { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, @@ -1664,6 +1811,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583, upload-time = "2025-03-14T23:51:03.016Z" }, ] +[[package]] +name = "msrest" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "certifi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "isodate", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "requests-oauthlib", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/77/8397c8fb8fc257d8ea0fa66f8068e073278c65f05acb17dcb22a02bfdc42/msrest-0.7.1.zip", hash = "sha256:6e7661f46f3afd88b75667b7187a92829924446c7ea1d169be8c4bb7eeb788b9", size = 175332, upload-time = "2022-06-13T22:41:25.111Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/cf/f2966a2638144491f8696c27320d5219f48a072715075d168b31d3237720/msrest-0.7.1-py3-none-any.whl", hash = "sha256:21120a810e1233e5e6cc7fe40b474eeb4ec6f757a15d7cf86702c369f9567c32", size = 85384, upload-time = "2022-06-13T22:41:22.42Z" }, +] + [[package]] name = "multidict" version = "6.6.4" @@ -1909,9 +2072,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, ] +[[package]] +name = "oauthlib" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" }, +] + [[package]] name = "openai" -version = "1.103.0" +version = "1.106.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -1923,9 +2095,9 @@ dependencies = [ { name = "tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cb/0b/4cacc14f976601edf35b74f7c5c2d6305f7402257cb13b9956b4eaabf94d/openai-1.103.0.tar.gz", hash = "sha256:f84f8741536f01adfdae1acfe31ec1874fc0985d33f53344f9edca773f150a36", size = 556049, upload-time = "2025-09-02T14:03:11.533Z" } +sdist = { url = "https://files.pythonhosted.org/packages/79/b6/1aff7d6b8e9f0c3ac26bfbb57b9861a6711d5d60bd7dd5f7eebbf80509b7/openai-1.106.1.tar.gz", hash = "sha256:5f575967e3a05555825c43829cdcd50be6e49ab6a3e5262f0937a3f791f917f1", size = 561095, upload-time = "2025-09-04T18:17:15.303Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/c0/f5d440153d96c6be42e5d05c39adf33e0c324c9f035daf0e537a71fe2d11/openai-1.103.0-py3-none-any.whl", hash = "sha256:60a69224f0d210a720e7364947d3b712fe0036373f25dc1cb801fc25abb3f864", size = 926169, upload-time = "2025-09-02T14:03:09.666Z" }, + { url = "https://files.pythonhosted.org/packages/00/e1/47887212baa7bc0532880d33d5eafbdb46fcc4b53789b903282a74a85b5b/openai-1.106.1-py3-none-any.whl", hash = "sha256:bfdef37c949f80396c59f2c17e0eda35414979bc07ef3379596a93c9ed044f3a", size = 930768, upload-time = "2025-09-04T18:17:13.349Z" }, ] [[package]] @@ -1941,6 +2113,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bb/ee/6b08dde0a022c463b88f55ae81149584b125a42183407dc1045c486cc870/opentelemetry_api-1.36.0-py3-none-any.whl", hash = "sha256:02f20bcacf666e1333b6b1f04e647dc1d5111f86b8e510238fcc56d7762cda8c", size = 65564, upload-time = "2025-07-29T15:11:47.998Z" }, ] +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.36.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/34/da/7747e57eb341c59886052d733072bc878424bf20f1d8cf203d508bbece5b/opentelemetry_exporter_otlp_proto_common-1.36.0.tar.gz", hash = "sha256:6c496ccbcbe26b04653cecadd92f73659b814c6e3579af157d8716e5f9f25cbf", size = 20302, upload-time = "2025-07-29T15:12:07.71Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/ed/22290dca7db78eb32e0101738366b5bbda00d0407f00feffb9bf8c3fdf87/opentelemetry_exporter_otlp_proto_common-1.36.0-py3-none-any.whl", hash = "sha256:0fc002a6ed63eac235ada9aa7056e5492e9a71728214a61745f6ad04b923f840", size = 18349, upload-time = "2025-07-29T15:11:51.327Z" }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.36.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "grpcio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-exporter-otlp-proto-common", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-proto", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/6f/6c1b0bdd0446e5532294d1d41bf11fbaea39c8a2423a4cdfe4fe6b708127/opentelemetry_exporter_otlp_proto_grpc-1.36.0.tar.gz", hash = "sha256:b281afbf7036b325b3588b5b6c8bb175069e3978d1bd24071f4a59d04c1e5bbf", size = 23822, upload-time = "2025-07-29T15:12:08.292Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/67/5f6bd188d66d0fd8e81e681bbf5822e53eb150034e2611dd2b935d3ab61a/opentelemetry_exporter_otlp_proto_grpc-1.36.0-py3-none-any.whl", hash = "sha256:734e841fc6a5d6f30e7be4d8053adb703c70ca80c562ae24e8083a28fadef211", size = 18828, upload-time = "2025-07-29T15:11:52.235Z" }, +] + [[package]] name = "opentelemetry-instrumentation" version = "0.57b0" @@ -1956,6 +2158,86 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/6f/f20cd1542959f43fb26a5bf9bb18cd81a1ea0700e8870c8f369bd07f5c65/opentelemetry_instrumentation-0.57b0-py3-none-any.whl", hash = "sha256:9109280f44882e07cec2850db28210b90600ae9110b42824d196de357cbddf7e", size = 32460, upload-time = "2025-07-29T15:41:40.883Z" }, ] +[[package]] +name = "opentelemetry-instrumentation-asgi" +version = "0.57b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asgiref", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/97/10/7ba59b586eb099fa0155521b387d857de476687c670096597f618d889323/opentelemetry_instrumentation_asgi-0.57b0.tar.gz", hash = "sha256:a6f880b5d1838f65688fc992c65fbb1d3571f319d370990c32e759d3160e510b", size = 24654, upload-time = "2025-07-29T15:42:48.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/07/ab97dd7e8bc680b479203f7d3b2771b7a097468135a669a38da3208f96cb/opentelemetry_instrumentation_asgi-0.57b0-py3-none-any.whl", hash = "sha256:47debbde6af066a7e8e911f7193730d5e40d62effc1ac2e1119908347790a3ea", size = 16599, upload-time = "2025-07-29T15:41:48.332Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-dbapi" +version = "0.57b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "wrapt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/15/dc/5a17b2fb593901ba5257278073b28d0ed31497e56985990c26046e4da2d9/opentelemetry_instrumentation_dbapi-0.57b0.tar.gz", hash = "sha256:7ad9e39c91f6212f118435fd6fab842a1f78b2cbad1167f228c025bba2a8fc2d", size = 14176, upload-time = "2025-07-29T15:42:56.249Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/71/21a7e862dead70267b7c7bd5aa4e0b61fbc9fa9b4be57f4e183766abbad9/opentelemetry_instrumentation_dbapi-0.57b0-py3-none-any.whl", hash = "sha256:c1b110a5e86ec9b52b970460917523f47afa0c73f131e7f03c6a7c1921822dc4", size = 12466, upload-time = "2025-07-29T15:41:59.775Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-django" +version = "0.57b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation-wsgi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/88/d88268c37aabbd2bcc54f4f868394316fa6fdfd3b91e011d229617d862d3/opentelemetry_instrumentation_django-0.57b0.tar.gz", hash = "sha256:df4116d2ea2c6bbbbf8853b843deb74d66bd0d573ddd372ec84fd60adaf977c6", size = 25005, upload-time = "2025-07-29T15:42:56.88Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/f0/1d5022f2fe16d50b79d9f1f5b70bd08d0e59819e0f6b237cff82c3dbda0f/opentelemetry_instrumentation_django-0.57b0-py3-none-any.whl", hash = "sha256:3d702d79a9ec0c836ccf733becf34630c6afb3c86c25c330c5b7601debe1e7c5", size = 19597, upload-time = "2025-07-29T15:42:00.657Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-fastapi" +version = "0.57b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation-asgi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/47/a8/7c22a33ff5986523a7f9afcb5f4d749533842c3cc77ef55b46727580edd0/opentelemetry_instrumentation_fastapi-0.57b0.tar.gz", hash = "sha256:73ac22f3c472a8f9cb21d1fbe5a4bf2797690c295fff4a1c040e9b1b1688a105", size = 20277, upload-time = "2025-07-29T15:42:58.68Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/df/f20fc21c88c7af5311bfefc15fc4e606bab5edb7c193aa8c73c354904c35/opentelemetry_instrumentation_fastapi-0.57b0-py3-none-any.whl", hash = "sha256:61e6402749ffe0bfec582e58155e0d81dd38723cd9bc4562bca1acca80334006", size = 12712, upload-time = "2025-07-29T15:42:03.332Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-flask" +version = "0.57b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation-wsgi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/98/8a8fa41f624069ac2912141b65bd528fd345d65e14a359c4d896fc3dc291/opentelemetry_instrumentation_flask-0.57b0.tar.gz", hash = "sha256:c5244a40b03664db966d844a32f43c900181431b77929be62a68d4907e86ed25", size = 19381, upload-time = "2025-07-29T15:42:59.38Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/3f/79b6c9a240221f5614a143eab6a0ecacdcb23b93cc35ff2b78234f68804f/opentelemetry_instrumentation_flask-0.57b0-py3-none-any.whl", hash = "sha256:5ecd614f194825725b61ee9ba8e37dcd4d3f9b5d40fef759df8650d6a91b1cb9", size = 14688, upload-time = "2025-07-29T15:42:04.162Z" }, +] + [[package]] name = "opentelemetry-instrumentation-openai" version = "0.46.2" @@ -1971,6 +2253,105 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/da/db/f6637a16f15763f12e727405a8ed0caaaca3f2d786b283fff0cd33d599d5/opentelemetry_instrumentation_openai-0.46.2-py3-none-any.whl", hash = "sha256:0880685a00752c31fdc4c6d9b959342156d62257515e9a8410431fcf7febe2a2", size = 35269, upload-time = "2025-08-29T18:07:30.132Z" }, ] +[[package]] +name = "opentelemetry-instrumentation-psycopg2" +version = "0.57b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation-dbapi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/66/f2004cde131663810e62b47bb48b684660632876f120c6b1d400a04ccb06/opentelemetry_instrumentation_psycopg2-0.57b0.tar.gz", hash = "sha256:4e9d05d661c50985f0a5d7f090a7f399d453b467c9912c7611fcef693d15b038", size = 10722, upload-time = "2025-07-29T15:43:05.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/40/00f9c1334fb0c9d74c99d37c4a730cbe6dc941eea5fae6f9bc36e5a53d19/opentelemetry_instrumentation_psycopg2-0.57b0-py3-none-any.whl", hash = "sha256:94fdde02b7451c8e85d43b4b9dd13a34fee96ffd43324d1b3567f47d2903b99f", size = 10721, upload-time = "2025-07-29T15:42:15.698Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-requests" +version = "0.57b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0d/e1/01f5c28a60ffbc4c04946ad35bc8bf16382d333e41afaa042b31c35364b9/opentelemetry_instrumentation_requests-0.57b0.tar.gz", hash = "sha256:193bd3fd1f14737721876fb1952dffc7d43795586118df633a91ecd9057446ff", size = 15182, upload-time = "2025-07-29T15:43:11.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/7d/40144701fa22521e3b3fce23e2f0a5684a9385c90b119b70e7598b3cb607/opentelemetry_instrumentation_requests-0.57b0-py3-none-any.whl", hash = "sha256:66a576ac8080724ddc8a14c39d16bb5f430991bd504fdbea844c7a063f555971", size = 12966, upload-time = "2025-07-29T15:42:24.608Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-urllib" +version = "0.57b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/86/a5/9d400dd978ac5e81356fe8435ca264e140a7d4cf77a88db43791d62311d5/opentelemetry_instrumentation_urllib-0.57b0.tar.gz", hash = "sha256:657225ceae8bb52b67bd5c26dcb8a33f0efb041f1baea4c59dbd1adbc63a4162", size = 13929, upload-time = "2025-07-29T15:43:16.498Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/47/3c9535a68b9dd125eb6a25c086984e5cee7285e4f36bfa37eeb40e95d2b5/opentelemetry_instrumentation_urllib-0.57b0-py3-none-any.whl", hash = "sha256:bb3a01172109a6f56bfcc38ea83b9d4a61c4c2cac6b9a190e757063daadf545c", size = 12671, upload-time = "2025-07-29T15:42:34.561Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-urllib3" +version = "0.57b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "wrapt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9a/2d/c241e9716c94704dbddf64e2c7367b57642425455befdbc622936bec78e9/opentelemetry_instrumentation_urllib3-0.57b0.tar.gz", hash = "sha256:f49d8c3d1d81ae56304a08b14a7f564d250733ed75cd2210ccef815b5af2eea1", size = 15790, upload-time = "2025-07-29T15:43:17.05Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/0e/a5467ab57d815caa58cbabb3a7f3906c3718c599221ac770482d13187306/opentelemetry_instrumentation_urllib3-0.57b0-py3-none-any.whl", hash = "sha256:337ecac6df3ff92026b51c64df7dd4a3fff52f2dc96036ea9371670243bf83c6", size = 13186, upload-time = "2025-07-29T15:42:35.775Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-wsgi" +version = "0.57b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/3f/d1ab49d68f2f6ebbe3c2fa5ff609ee5603a9cc68915203c454afb3a38d5b/opentelemetry_instrumentation_wsgi-0.57b0.tar.gz", hash = "sha256:d7e16b3b87930c30fc4c1bbc8b58c5dd6eefade493a3a5e7343bc24d572bc5b7", size = 18376, upload-time = "2025-07-29T15:43:17.683Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/0c/7760f9e14f4f8128e4880b4fd5f232ef4eb00cb29ee560c972dbf7801369/opentelemetry_instrumentation_wsgi-0.57b0-py3-none-any.whl", hash = "sha256:b9cf0c6e61489f7503fc17ef04d169bd214e7a825650ee492f5d2b4d73b17b54", size = 14450, upload-time = "2025-07-29T15:42:37.351Z" }, +] + +[[package]] +name = "opentelemetry-proto" +version = "1.36.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/02/f6556142301d136e3b7e95ab8ea6a5d9dc28d879a99f3dd673b5f97dca06/opentelemetry_proto-1.36.0.tar.gz", hash = "sha256:0f10b3c72f74c91e0764a5ec88fd8f1c368ea5d9c64639fb455e2854ef87dd2f", size = 46152, upload-time = "2025-07-29T15:12:15.717Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/57/3361e06136225be8180e879199caea520f38026f8071366241ac458beb8d/opentelemetry_proto-1.36.0-py3-none-any.whl", hash = "sha256:151b3bf73a09f94afc658497cf77d45a565606f62ce0c17acb08cd9937ca206e", size = 72537, upload-time = "2025-07-29T15:12:02.243Z" }, +] + +[[package]] +name = "opentelemetry-resource-detector-azure" +version = "0.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/e4/0d359d48d03d447225b30c3dd889d5d454e3b413763ff721f9b0e4ac2e59/opentelemetry_resource_detector_azure-0.1.5.tar.gz", hash = "sha256:e0ba658a87c69eebc806e75398cd0e9f68a8898ea62de99bc1b7083136403710", size = 11503, upload-time = "2024-05-16T21:54:58.994Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/ae/c26d8da88ba2e438e9653a408b0c2ad6f17267801250a8f3cc6405a93a72/opentelemetry_resource_detector_azure-0.1.5-py3-none-any.whl", hash = "sha256:4dcc5d54ab5c3b11226af39509bc98979a8b9e0f8a24c1b888783755d3bf00eb", size = 14252, upload-time = "2024-05-16T21:54:57.208Z" }, +] + [[package]] name = "opentelemetry-sdk" version = "1.36.0" @@ -2007,6 +2388,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/35/b5/cf25da2218910f0d6cdf7f876a06bed118c4969eacaf60a887cbaef44f44/opentelemetry_semantic_conventions_ai-0.4.13-py3-none-any.whl", hash = "sha256:883a30a6bb5deaec0d646912b5f9f6dcbb9f6f72557b73d0f2560bf25d13e2d5", size = 6080, upload-time = "2025-08-22T10:14:16.477Z" }, ] +[[package]] +name = "opentelemetry-util-http" +version = "0.57b0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/1b/6229c45445e08e798fa825f5376f6d6a4211d29052a4088eed6d577fa653/opentelemetry_util_http-0.57b0.tar.gz", hash = "sha256:f7417595ead0eb42ed1863ec9b2f839fc740368cd7bbbfc1d0a47bc1ab0aba11", size = 9405, upload-time = "2025-07-29T15:43:19.916Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/a6/b98d508d189b9c208f5978d0906141747d7e6df7c7cafec03657ed1ed559/opentelemetry_util_http-0.57b0-py3-none-any.whl", hash = "sha256:e54c0df5543951e471c3d694f85474977cd5765a3b7654398c83bab3d2ffb8e9", size = 7643, upload-time = "2025-07-29T15:42:41.744Z" }, +] + [[package]] name = "packaging" version = "25.0" @@ -2204,6 +2594,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663, upload-time = "2025-06-09T22:56:04.484Z" }, ] +[[package]] +name = "protobuf" +version = "6.32.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/df/fb4a8eeea482eca989b51cffd274aac2ee24e825f0bf3cbce5281fa1567b/protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2", size = 440614, upload-time = "2025-08-14T21:21:25.015Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/18/df8c87da2e47f4f1dcc5153a81cd6bca4e429803f4069a299e236e4dd510/protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741", size = 424409, upload-time = "2025-08-14T21:21:12.366Z" }, + { url = "https://files.pythonhosted.org/packages/e1/59/0a820b7310f8139bd8d5a9388e6a38e1786d179d6f33998448609296c229/protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e", size = 435735, upload-time = "2025-08-14T21:21:15.046Z" }, + { url = "https://files.pythonhosted.org/packages/cc/5b/0d421533c59c789e9c9894683efac582c06246bf24bb26b753b149bd88e4/protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0", size = 426449, upload-time = "2025-08-14T21:21:16.687Z" }, + { url = "https://files.pythonhosted.org/packages/ec/7b/607764ebe6c7a23dcee06e054fd1de3d5841b7648a90fd6def9a3bb58c5e/protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1", size = 322869, upload-time = "2025-08-14T21:21:18.282Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/2e730bd1c25392fc32e3268e02446f0d77cb51a2c3a8486b1798e34d5805/protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c", size = 322009, upload-time = "2025-08-14T21:21:19.893Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f2/80ffc4677aac1bc3519b26bc7f7f5de7fce0ee2f7e36e59e27d8beb32dd1/protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783", size = 169287, upload-time = "2025-08-14T21:21:23.515Z" }, +] + [[package]] name = "psutil" version = "7.0.0" @@ -2406,20 +2810,20 @@ crypto = [ [[package]] name = "pyright" -version = "1.1.404" +version = "1.1.405" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e2/6e/026be64c43af681d5632722acd100b06d3d39f383ec382ff50a71a6d5bce/pyright-1.1.404.tar.gz", hash = "sha256:455e881a558ca6be9ecca0b30ce08aa78343ecc031d37a198ffa9a7a1abeb63e", size = 4065679, upload-time = "2025-08-20T18:46:14.029Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/6c/ba4bbee22e76af700ea593a1d8701e3225080956753bee9750dcc25e2649/pyright-1.1.405.tar.gz", hash = "sha256:5c2a30e1037af27eb463a1cc0b9f6d65fec48478ccf092c1ac28385a15c55763", size = 4068319, upload-time = "2025-09-04T03:37:06.776Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/30/89aa7f7d7a875bbb9a577d4b1dc5a3e404e3d2ae2657354808e905e358e0/pyright-1.1.404-py3-none-any.whl", hash = "sha256:c7b7ff1fdb7219c643079e4c3e7d4125f0dafcc19d253b47e898d130ea426419", size = 5902951, upload-time = "2025-08-20T18:46:12.096Z" }, + { url = "https://files.pythonhosted.org/packages/d5/1a/524f832e1ff1962a22a1accc775ca7b143ba2e9f5924bb6749dce566784a/pyright-1.1.405-py3-none-any.whl", hash = "sha256:a2cb13700b5508ce8e5d4546034cb7ea4aedb60215c6c33f56cec7f53996035a", size = 5905038, upload-time = "2025-09-04T03:37:04.913Z" }, ] [[package]] name = "pytest" -version = "8.4.1" +version = "8.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -2430,9 +2834,9 @@ dependencies = [ { name = "pygments", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "tomli", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, ] [[package]] @@ -2701,6 +3105,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] +[[package]] +name = "requests-oauthlib" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "oauthlib", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, +] + [[package]] name = "rich" version = "14.1.0" @@ -2860,28 +3277,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.12.11" +version = "0.12.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/de/55/16ab6a7d88d93001e1ae4c34cbdcfb376652d761799459ff27c1dc20f6fa/ruff-0.12.11.tar.gz", hash = "sha256:c6b09ae8426a65bbee5425b9d0b82796dbb07cb1af045743c79bfb163001165d", size = 5347103, upload-time = "2025-08-28T13:59:08.87Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/f0/e0965dd709b8cabe6356811c0ee8c096806bb57d20b5019eb4e48a117410/ruff-0.12.12.tar.gz", hash = "sha256:b86cd3415dbe31b3b46a71c598f4c4b2f550346d1ccf6326b347cc0c8fd063d6", size = 5359915, upload-time = "2025-09-04T16:50:18.273Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/a2/3b3573e474de39a7a475f3fbaf36a25600bfeb238e1a90392799163b64a0/ruff-0.12.11-py3-none-linux_armv6l.whl", hash = "sha256:93fce71e1cac3a8bf9200e63a38ac5c078f3b6baebffb74ba5274fb2ab276065", size = 11979885, upload-time = "2025-08-28T13:58:26.654Z" }, - { url = "https://files.pythonhosted.org/packages/76/e4/235ad6d1785a2012d3ded2350fd9bc5c5af8c6f56820e696b0118dfe7d24/ruff-0.12.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8e33ac7b28c772440afa80cebb972ffd823621ded90404f29e5ab6d1e2d4b93", size = 12742364, upload-time = "2025-08-28T13:58:30.256Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0d/15b72c5fe6b1e402a543aa9d8960e0a7e19dfb079f5b0b424db48b7febab/ruff-0.12.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d69fb9d4937aa19adb2e9f058bc4fbfe986c2040acb1a4a9747734834eaa0bfd", size = 11920111, upload-time = "2025-08-28T13:58:33.677Z" }, - { url = "https://files.pythonhosted.org/packages/3e/c0/f66339d7893798ad3e17fa5a1e587d6fd9806f7c1c062b63f8b09dda6702/ruff-0.12.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:411954eca8464595077a93e580e2918d0a01a19317af0a72132283e28ae21bee", size = 12160060, upload-time = "2025-08-28T13:58:35.74Z" }, - { url = "https://files.pythonhosted.org/packages/03/69/9870368326db26f20c946205fb2d0008988aea552dbaec35fbacbb46efaa/ruff-0.12.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a2c0a2e1a450f387bf2c6237c727dd22191ae8c00e448e0672d624b2bbd7fb0", size = 11799848, upload-time = "2025-08-28T13:58:38.051Z" }, - { url = "https://files.pythonhosted.org/packages/25/8c/dd2c7f990e9b3a8a55eee09d4e675027d31727ce33cdb29eab32d025bdc9/ruff-0.12.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ca4c3a7f937725fd2413c0e884b5248a19369ab9bdd850b5781348ba283f644", size = 13536288, upload-time = "2025-08-28T13:58:40.046Z" }, - { url = "https://files.pythonhosted.org/packages/7a/30/d5496fa09aba59b5e01ea76775a4c8897b13055884f56f1c35a4194c2297/ruff-0.12.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4d1df0098124006f6a66ecf3581a7f7e754c4df7644b2e6704cd7ca80ff95211", size = 14490633, upload-time = "2025-08-28T13:58:42.285Z" }, - { url = "https://files.pythonhosted.org/packages/9b/2f/81f998180ad53445d403c386549d6946d0748e536d58fce5b5e173511183/ruff-0.12.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a8dd5f230efc99a24ace3b77e3555d3fbc0343aeed3fc84c8d89e75ab2ff793", size = 13888430, upload-time = "2025-08-28T13:58:44.641Z" }, - { url = "https://files.pythonhosted.org/packages/87/71/23a0d1d5892a377478c61dbbcffe82a3476b050f38b5162171942a029ef3/ruff-0.12.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dc75533039d0ed04cd33fb8ca9ac9620b99672fe7ff1533b6402206901c34ee", size = 12913133, upload-time = "2025-08-28T13:58:47.039Z" }, - { url = "https://files.pythonhosted.org/packages/80/22/3c6cef96627f89b344c933781ed38329bfb87737aa438f15da95907cbfd5/ruff-0.12.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fc58f9266d62c6eccc75261a665f26b4ef64840887fc6cbc552ce5b29f96cc8", size = 13169082, upload-time = "2025-08-28T13:58:49.157Z" }, - { url = "https://files.pythonhosted.org/packages/05/b5/68b3ff96160d8b49e8dd10785ff3186be18fd650d356036a3770386e6c7f/ruff-0.12.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5a0113bd6eafd545146440225fe60b4e9489f59eb5f5f107acd715ba5f0b3d2f", size = 13139490, upload-time = "2025-08-28T13:58:51.593Z" }, - { url = "https://files.pythonhosted.org/packages/59/b9/050a3278ecd558f74f7ee016fbdf10591d50119df8d5f5da45a22c6afafc/ruff-0.12.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0d737b4059d66295c3ea5720e6efc152623bb83fde5444209b69cd33a53e2000", size = 11958928, upload-time = "2025-08-28T13:58:53.943Z" }, - { url = "https://files.pythonhosted.org/packages/f9/bc/93be37347db854806904a43b0493af8d6873472dfb4b4b8cbb27786eb651/ruff-0.12.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:916fc5defee32dbc1fc1650b576a8fed68f5e8256e2180d4d9855aea43d6aab2", size = 11764513, upload-time = "2025-08-28T13:58:55.976Z" }, - { url = "https://files.pythonhosted.org/packages/7a/a1/1471751e2015a81fd8e166cd311456c11df74c7e8769d4aabfbc7584c7ac/ruff-0.12.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c984f07d7adb42d3ded5be894fb4007f30f82c87559438b4879fe7aa08c62b39", size = 12745154, upload-time = "2025-08-28T13:58:58.16Z" }, - { url = "https://files.pythonhosted.org/packages/68/ab/2542b14890d0f4872dd81b7b2a6aed3ac1786fae1ce9b17e11e6df9e31e3/ruff-0.12.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e07fbb89f2e9249f219d88331c833860489b49cdf4b032b8e4432e9b13e8a4b9", size = 13227653, upload-time = "2025-08-28T13:59:00.276Z" }, - { url = "https://files.pythonhosted.org/packages/22/16/2fbfc61047dbfd009c58a28369a693a1484ad15441723be1cd7fe69bb679/ruff-0.12.11-py3-none-win32.whl", hash = "sha256:c792e8f597c9c756e9bcd4d87cf407a00b60af77078c96f7b6366ea2ce9ba9d3", size = 11944270, upload-time = "2025-08-28T13:59:02.347Z" }, - { url = "https://files.pythonhosted.org/packages/08/a5/34276984705bfe069cd383101c45077ee029c3fe3b28225bf67aa35f0647/ruff-0.12.11-py3-none-win_amd64.whl", hash = "sha256:a3283325960307915b6deb3576b96919ee89432ebd9c48771ca12ee8afe4a0fd", size = 13046600, upload-time = "2025-08-28T13:59:04.751Z" }, - { url = "https://files.pythonhosted.org/packages/84/a8/001d4a7c2b37623a3fd7463208267fb906df40ff31db496157549cfd6e72/ruff-0.12.11-py3-none-win_arm64.whl", hash = "sha256:bae4d6e6a2676f8fb0f98b74594a048bae1b944aab17e9f5d504062303c6dbea", size = 12135290, upload-time = "2025-08-28T13:59:06.933Z" }, + { url = "https://files.pythonhosted.org/packages/09/79/8d3d687224d88367b51c7974cec1040c4b015772bfbeffac95face14c04a/ruff-0.12.12-py3-none-linux_armv6l.whl", hash = "sha256:de1c4b916d98ab289818e55ce481e2cacfaad7710b01d1f990c497edf217dafc", size = 12116602, upload-time = "2025-09-04T16:49:18.892Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c3/6e599657fe192462f94861a09aae935b869aea8a1da07f47d6eae471397c/ruff-0.12.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7acd6045e87fac75a0b0cdedacf9ab3e1ad9d929d149785903cff9bb69ad9727", size = 12868393, upload-time = "2025-09-04T16:49:23.043Z" }, + { url = "https://files.pythonhosted.org/packages/e8/d2/9e3e40d399abc95336b1843f52fc0daaceb672d0e3c9290a28ff1a96f79d/ruff-0.12.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:abf4073688d7d6da16611f2f126be86523a8ec4343d15d276c614bda8ec44edb", size = 12036967, upload-time = "2025-09-04T16:49:26.04Z" }, + { url = "https://files.pythonhosted.org/packages/e9/03/6816b2ed08836be272e87107d905f0908be5b4a40c14bfc91043e76631b8/ruff-0.12.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:968e77094b1d7a576992ac078557d1439df678a34c6fe02fd979f973af167577", size = 12276038, upload-time = "2025-09-04T16:49:29.056Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d5/707b92a61310edf358a389477eabd8af68f375c0ef858194be97ca5b6069/ruff-0.12.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42a67d16e5b1ffc6d21c5f67851e0e769517fb57a8ebad1d0781b30888aa704e", size = 11901110, upload-time = "2025-09-04T16:49:32.07Z" }, + { url = "https://files.pythonhosted.org/packages/9d/3d/f8b1038f4b9822e26ec3d5b49cf2bc313e3c1564cceb4c1a42820bf74853/ruff-0.12.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b216ec0a0674e4b1214dcc998a5088e54eaf39417327b19ffefba1c4a1e4971e", size = 13668352, upload-time = "2025-09-04T16:49:35.148Z" }, + { url = "https://files.pythonhosted.org/packages/98/0e/91421368ae6c4f3765dd41a150f760c5f725516028a6be30e58255e3c668/ruff-0.12.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:59f909c0fdd8f1dcdbfed0b9569b8bf428cf144bec87d9de298dcd4723f5bee8", size = 14638365, upload-time = "2025-09-04T16:49:38.892Z" }, + { url = "https://files.pythonhosted.org/packages/74/5d/88f3f06a142f58ecc8ecb0c2fe0b82343e2a2b04dcd098809f717cf74b6c/ruff-0.12.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ac93d87047e765336f0c18eacad51dad0c1c33c9df7484c40f98e1d773876f5", size = 14060812, upload-time = "2025-09-04T16:49:42.732Z" }, + { url = "https://files.pythonhosted.org/packages/13/fc/8962e7ddd2e81863d5c92400820f650b86f97ff919c59836fbc4c1a6d84c/ruff-0.12.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01543c137fd3650d322922e8b14cc133b8ea734617c4891c5a9fccf4bfc9aa92", size = 13050208, upload-time = "2025-09-04T16:49:46.434Z" }, + { url = "https://files.pythonhosted.org/packages/53/06/8deb52d48a9a624fd37390555d9589e719eac568c020b27e96eed671f25f/ruff-0.12.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afc2fa864197634e549d87fb1e7b6feb01df0a80fd510d6489e1ce8c0b1cc45", size = 13311444, upload-time = "2025-09-04T16:49:49.931Z" }, + { url = "https://files.pythonhosted.org/packages/2a/81/de5a29af7eb8f341f8140867ffb93f82e4fde7256dadee79016ac87c2716/ruff-0.12.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0c0945246f5ad776cb8925e36af2438e66188d2b57d9cf2eed2c382c58b371e5", size = 13279474, upload-time = "2025-09-04T16:49:53.465Z" }, + { url = "https://files.pythonhosted.org/packages/7f/14/d9577fdeaf791737ada1b4f5c6b59c21c3326f3f683229096cccd7674e0c/ruff-0.12.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a0fbafe8c58e37aae28b84a80ba1817f2ea552e9450156018a478bf1fa80f4e4", size = 12070204, upload-time = "2025-09-04T16:49:56.882Z" }, + { url = "https://files.pythonhosted.org/packages/77/04/a910078284b47fad54506dc0af13839c418ff704e341c176f64e1127e461/ruff-0.12.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b9c456fb2fc8e1282affa932c9e40f5ec31ec9cbb66751a316bd131273b57c23", size = 11880347, upload-time = "2025-09-04T16:49:59.729Z" }, + { url = "https://files.pythonhosted.org/packages/df/58/30185fcb0e89f05e7ea82e5817b47798f7fa7179863f9d9ba6fd4fe1b098/ruff-0.12.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5f12856123b0ad0147d90b3961f5c90e7427f9acd4b40050705499c98983f489", size = 12891844, upload-time = "2025-09-04T16:50:02.591Z" }, + { url = "https://files.pythonhosted.org/packages/21/9c/28a8dacce4855e6703dcb8cdf6c1705d0b23dd01d60150786cd55aa93b16/ruff-0.12.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:26a1b5a2bf7dd2c47e3b46d077cd9c0fc3b93e6c6cc9ed750bd312ae9dc302ee", size = 13360687, upload-time = "2025-09-04T16:50:05.8Z" }, + { url = "https://files.pythonhosted.org/packages/c8/fa/05b6428a008e60f79546c943e54068316f32ec8ab5c4f73e4563934fbdc7/ruff-0.12.12-py3-none-win32.whl", hash = "sha256:173be2bfc142af07a01e3a759aba6f7791aa47acf3604f610b1c36db888df7b1", size = 12052870, upload-time = "2025-09-04T16:50:09.121Z" }, + { url = "https://files.pythonhosted.org/packages/85/60/d1e335417804df452589271818749d061b22772b87efda88354cf35cdb7a/ruff-0.12.12-py3-none-win_amd64.whl", hash = "sha256:e99620bf01884e5f38611934c09dd194eb665b0109104acae3ba6102b600fd0d", size = 13178016, upload-time = "2025-09-04T16:50:12.559Z" }, + { url = "https://files.pythonhosted.org/packages/28/7e/61c42657f6e4614a4258f1c3b0c5b93adc4d1f8575f5229d1906b483099b/ruff-0.12.12-py3-none-win_arm64.whl", hash = "sha256:2a8199cab4ce4d72d158319b63370abf60991495fb733db96cd923a34c52d093", size = 12256762, upload-time = "2025-09-04T16:50:15.737Z" }, ] [[package]] @@ -2958,9 +3375,12 @@ name = "sphinx" version = "8.2.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.11' and sys_platform == 'darwin'", - "python_full_version >= '3.11' and sys_platform == 'linux'", - "python_full_version >= '3.11' and sys_platform == 'win32'", + "python_full_version >= '3.13' and sys_platform == 'darwin'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and sys_platform == 'linux'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'linux'", + "python_full_version >= '3.13' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'win32'", ] dependencies = [ { name = "alabaster", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, @@ -3013,9 +3433,12 @@ name = "sphinx-autobuild" version = "2025.8.25" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.11' and sys_platform == 'darwin'", - "python_full_version >= '3.11' and sys_platform == 'linux'", - "python_full_version >= '3.11' and sys_platform == 'win32'", + "python_full_version >= '3.13' and sys_platform == 'darwin'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and sys_platform == 'linux'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'linux'", + "python_full_version >= '3.13' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'win32'", ] dependencies = [ { name = "colorama", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, @@ -3345,28 +3768,28 @@ wheels = [ [[package]] name = "uv" -version = "0.8.14" +version = "0.8.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e2/b0/c3bc06ba5f6b72ba3ad278e854292d81b7aaaea2b6988e40fdb892f813f8/uv-0.8.14.tar.gz", hash = "sha256:7c68e0cde3d048500c073696881c07c2bd97503fc77d7091e1454d3fd58febb4", size = 3543853, upload-time = "2025-08-28T21:55:59.769Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/7c/ab905b0425f88842f3d8e5da50491524f45a231b7a3dc9c988608162adb2/uv-0.8.15.tar.gz", hash = "sha256:8ea57b78be9f0911a2a50b6814d15aec7d1f8aa6517059dc8250b1414156f93a", size = 3602914, upload-time = "2025-09-03T14:32:15.552Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/a3/bf0a80a7770f5c11a735345073fdf085a031ecd0525ae229ceb3ed7496f5/uv-0.8.14-py3-none-linux_armv6l.whl", hash = "sha256:bae6621a72e6643f140c4e62f10d3a52d210ccdec48bf4f733e6a25d5739e533", size = 18810682, upload-time = "2025-08-28T21:55:07.027Z" }, - { url = "https://files.pythonhosted.org/packages/61/de/e8d3c1669edb70ae165ad6c06598ff237ddbc1dc743cc590a2c30c245b93/uv-0.8.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2334945ef3dba395067164c7e25b0c1420d8fdab9637d33cb753b5dbe0499b2c", size = 18939300, upload-time = "2025-08-28T21:55:11.244Z" }, - { url = "https://files.pythonhosted.org/packages/dc/61/9e4c3382f79cef69229f4f301ce1b391121f5a9d1015dd82487e08f0d718/uv-0.8.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9a65096847d3341713be92e98cb35d5315d172690032405e8ae4e1b0c366a19a", size = 17555624, upload-time = "2025-08-28T21:55:14.107Z" }, - { url = "https://files.pythonhosted.org/packages/03/6d/5200cba528844e33586fadae78c06c054774e7702063356795f6cc124331/uv-0.8.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:f7a5d72e4fefae57f675cf0ac0adb9e68fb638f3f95be142b7f072fc6fddfe3e", size = 18151749, upload-time = "2025-08-28T21:55:16.904Z" }, - { url = "https://files.pythonhosted.org/packages/5a/b6/6f9407a792f0ca566b61276cadbffa032cff4039847ac77c47959151f753/uv-0.8.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:935b602d40f0c6a41337de81a02850d6892b0c8c6b5d98543fa229d5bb247364", size = 18472626, upload-time = "2025-08-28T21:55:19.994Z" }, - { url = "https://files.pythonhosted.org/packages/14/a2/2eadfccb1d6aa3672c947071b18c50cee41bdb9c9dba6d8af011a5c44e50/uv-0.8.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34286de8d1244f06124c5bd7b4bfb5ef5791c147e0aa4473c7856c02fedc58ff", size = 19292728, upload-time = "2025-08-28T21:55:22.441Z" }, - { url = "https://files.pythonhosted.org/packages/b6/db/96071cddd37e4bfc9bd10c4daab0942c3d610da92f32c74de07621990455/uv-0.8.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d26ea49a595992bc58d31bb6a10660a8015d902b6845c8ceed1e011866013593", size = 20577332, upload-time = "2025-08-28T21:55:25.774Z" }, - { url = "https://files.pythonhosted.org/packages/c9/4c/8e0da19b4bd5612bd782a82a1869c71e8ea059b59c547230146d36583a39/uv-0.8.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2aa721841812e9a74cad883dbd0f6cf908309cc40a86ab33d3576a8b369595a9", size = 20317704, upload-time = "2025-08-28T21:55:28.537Z" }, - { url = "https://files.pythonhosted.org/packages/1c/f2/4ad6abe850e31663d3971eb4af4a3b6ef216870f4f2115ae65e72917ea02/uv-0.8.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5088fa0ceff698a3fb2464f5cd7ebb4af59aa85db4ba83150d4c3af027251228", size = 19615504, upload-time = "2025-08-28T21:55:31.695Z" }, - { url = "https://files.pythonhosted.org/packages/ed/6c/b86f5f2f5aeebb0028034ea180399af23c8cbc42748bba0672c9cabdde38/uv-0.8.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3853202f4eb0bedbe31b0b62b1323521e97306f44f8f4b6ed4bb13b636797873", size = 19605107, upload-time = "2025-08-28T21:55:34.33Z" }, - { url = "https://files.pythonhosted.org/packages/b4/04/7b019c63d26d296bf6dfd8ad9b86e51f84b2ec7f37d68f8b93138a3fa404/uv-0.8.14-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:e45047a89592a5b38c88caa6da5d1b70a05c9762ff1c5100f9700f85f533dc99", size = 18412515, upload-time = "2025-08-28T21:55:37.185Z" }, - { url = "https://files.pythonhosted.org/packages/59/b8/c277b6ff1e4fc6d2c4f000ebccef9c2879603875ab092390f7073b911bdf/uv-0.8.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:72971573f21e617267b3737750cdb8a9ae99862b06d23df7fde60fc9f8ef78d6", size = 19290057, upload-time = "2025-08-28T21:55:39.769Z" }, - { url = "https://files.pythonhosted.org/packages/ed/09/59f84ea996bc3bf52c88bc7ba2d988bc5edfd7d0a9aee7cc0500f77d83ce/uv-0.8.14-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:ab22d9712f6b06b04359cfaf625722a81fcd0f2335868738dbee26a79a93bd99", size = 18433918, upload-time = "2025-08-28T21:55:42.262Z" }, - { url = "https://files.pythonhosted.org/packages/fa/2c/8a76455ea1f578fab8a88457c4d50c28928860335d3420956b75661f5e7b/uv-0.8.14-py3-none-musllinux_1_1_i686.whl", hash = "sha256:b5003c30c44065b70e03f083d73af45c094f1f96d9c394acafd8f547c2aee4d0", size = 18800856, upload-time = "2025-08-28T21:55:44.697Z" }, - { url = "https://files.pythonhosted.org/packages/f7/87/16699c592d816325554702d771024fbe5ec39127bfbc06d5cb54843673bb/uv-0.8.14-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:dacfad1193c7facd3a414cc2f3468b4a79a07c565c776a3136f97527a628b960", size = 19704752, upload-time = "2025-08-28T21:55:47.375Z" }, - { url = "https://files.pythonhosted.org/packages/ce/e9/0cdeed22e6c540db493ea364040b17af09fabaa7a56c8ff02b9152819442/uv-0.8.14-py3-none-win32.whl", hash = "sha256:0a4abb2a327e3709ef02765dc392ee10e204275bdb107b492977f88633a1e6b0", size = 18630132, upload-time = "2025-08-28T21:55:51.988Z" }, - { url = "https://files.pythonhosted.org/packages/45/5e/9bf7004bd53e9279265d73a131fe2a6c7d74c1125c53e805b5e9f4047f37/uv-0.8.14-py3-none-win_amd64.whl", hash = "sha256:5091d588753bbbd1f120f13311ede2ae113d7ec2760e149fc502a237f2516075", size = 20672637, upload-time = "2025-08-28T21:55:55.341Z" }, - { url = "https://files.pythonhosted.org/packages/d8/7f/41074c81faa36a34d44524997c345a857bd82d7f73ea60e24dca606306ec/uv-0.8.14-py3-none-win_arm64.whl", hash = "sha256:7c424fd4561f4528d8b52fc8c16991d0ad0000d3ad12c82e01e722f314b2669d", size = 19171656, upload-time = "2025-08-28T21:55:57.799Z" }, + { url = "https://files.pythonhosted.org/packages/67/1d/794352a01b40f2b0a0abe02f4f020219b3f59ee6ed900561be3b2b47a82b/uv-0.8.15-py3-none-linux_armv6l.whl", hash = "sha256:f02e6b8be08b840f86b8d5997b658b657acdda95bc216ecf62fce6c71414bdc7", size = 20136396, upload-time = "2025-09-03T14:31:30.404Z" }, + { url = "https://files.pythonhosted.org/packages/8f/89/528f01cff01eb8d10dd396f437656266443e399dda2fe4787b2cf6983698/uv-0.8.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b0461bb1ad616c8bcb59c9b39ae9363245ca33815ebb1d11130385236eca21b9", size = 19297422, upload-time = "2025-09-03T14:31:34.412Z" }, + { url = "https://files.pythonhosted.org/packages/94/03/532af32a64d162894a1daebb7bc5028ba00225ea720cf0f287e934dc2bd5/uv-0.8.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:069eed78b79d1e88bced23e3d4303348edb0a0209e7cae0f20024c42430bf50f", size = 17882409, upload-time = "2025-09-03T14:31:36.993Z" }, + { url = "https://files.pythonhosted.org/packages/25/21/57df6d53fbadfa947d9d65a0926e5d8540199f49aa958d23be2707262a80/uv-0.8.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:333a93bb6af64f3b95ee99e82b4ea227e2af6362c45f91c89a24e2bfefb628f9", size = 19557216, upload-time = "2025-09-03T14:31:39.245Z" }, + { url = "https://files.pythonhosted.org/packages/68/22/c3784749e1c78119e5375ec34c6ea29e944192a601f17c746339611db237/uv-0.8.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7d5b19ac2bdda3d1456b5d6013af50b443ffb0e40c66d42874f71190a5364711", size = 19781097, upload-time = "2025-09-03T14:31:42.314Z" }, + { url = "https://files.pythonhosted.org/packages/00/28/0597599fb35408dd73e0a7d25108dca1fa6ce8f8d570c8f24151b0016eef/uv-0.8.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3330bb4f206a6180679a75a8b2e77ff0f933fcb06c028b6f4da877b10a5e4f95", size = 20741549, upload-time = "2025-09-03T14:31:44.574Z" }, + { url = "https://files.pythonhosted.org/packages/4f/61/98fa07981722660f5a3c28b987df99c2486f63d01b1256e6cca05a43bdce/uv-0.8.15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:de9896ad4fa724ab317a8048f4891b9b23df1403b3724e96606f3be2dbbbf009", size = 22193727, upload-time = "2025-09-03T14:31:46.915Z" }, + { url = "https://files.pythonhosted.org/packages/fa/65/523188e11a759144b00f0fe48943f6d00706fcd9b5f561a54a07b9fd4541/uv-0.8.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:226360003e71084e0a73cbec72170e88634b045e95529654d067ea3741bba242", size = 21817550, upload-time = "2025-09-03T14:31:49.548Z" }, + { url = "https://files.pythonhosted.org/packages/99/3c/7898acf3d9ed2d3a2986cccc8209c14d3e9ac72dfaa616e49d329423b1d3/uv-0.8.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9488260536b35b94a79962fea76837f279c0cd0ae5021c761e66b311f47ffa70", size = 21024011, upload-time = "2025-09-03T14:31:51.789Z" }, + { url = "https://files.pythonhosted.org/packages/13/fc/e0da45ee179367dcc1e1040ad00ed8a99b78355d43024b0b5fc2edf5c389/uv-0.8.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07765f99fd5fd3b257d7e210e8d0844c0a8fd111612e31fcca66a85656cc728e", size = 21009338, upload-time = "2025-09-03T14:31:54.104Z" }, + { url = "https://files.pythonhosted.org/packages/ce/5d/180904fa7ed49081b27f00e86f7220ca62cc098d7ef6459f0c69a8ae8f74/uv-0.8.15-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:c4868e6a4e1a8c777a5ba3cff452c405837318fb0b272ff203bfda0e1b8fc54d", size = 19799578, upload-time = "2025-09-03T14:31:56.47Z" }, + { url = "https://files.pythonhosted.org/packages/b6/09/fed823212e695b6765bdb8462850abffbe685cd965c4de905efed5e2e5c9/uv-0.8.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:3ec78a54a8eb0bbb9a9c653982390af84673657c8a48a0be6cdcb81d7d3e95c3", size = 20845428, upload-time = "2025-09-03T14:31:59.475Z" }, + { url = "https://files.pythonhosted.org/packages/b9/f3/9c4211897c00f79b7973a10800166e0580eaad20fe27f7c06adb7b248ac7/uv-0.8.15-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:a022a752d20da80d2a49fc0721522a81e3a32efe539152d756d84ebdba29dbc3", size = 19728113, upload-time = "2025-09-03T14:32:01.686Z" }, + { url = "https://files.pythonhosted.org/packages/3b/43/4ec6047150e2fba494d80d36b881a1a973835afa497ae9ccdf51828cae4f/uv-0.8.15-py3-none-musllinux_1_1_i686.whl", hash = "sha256:3780d2f3951d83e55812fdeb7eee233787b70c774497dbfc55b0fdf6063aa345", size = 20169115, upload-time = "2025-09-03T14:32:03.995Z" }, + { url = "https://files.pythonhosted.org/packages/ea/98/b4220bf462fb225c4a2d74ef4f105020238472b4b0da94ebc17a310d7b4e/uv-0.8.15-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:56f2451c9193ee1754ce1d8390ded68e9cb8dee0aaf7e2f38a9bd04d99be1be7", size = 21129804, upload-time = "2025-09-03T14:32:06.204Z" }, + { url = "https://files.pythonhosted.org/packages/5e/b8/40ce3d385254ac87a664a5d9a4664fac697e2734352f404382b81d03235b/uv-0.8.15-py3-none-win32.whl", hash = "sha256:89c7c10089e07d944c72d388fd88666c650dec2f8c79ca541e365f32843882c6", size = 19077103, upload-time = "2025-09-03T14:32:08.628Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9d/081a0af395c0e307c0c930e80161a2aa551c25064cfb636d060574566fa4/uv-0.8.15-py3-none-win_amd64.whl", hash = "sha256:6aa824ab933dfafe11efe32e6541c6bcd65ecaa927e8e834ea6b14d3821020f6", size = 21179816, upload-time = "2025-09-03T14:32:11.42Z" }, + { url = "https://files.pythonhosted.org/packages/30/47/d8f50264a8c8ebbb9a44a8fed08b6e873d943adf299d944fe3a776ff5fbf/uv-0.8.15-py3-none-win_arm64.whl", hash = "sha256:a395fa1fc8948eacdd18e4592ed489fad13558b13fea6b3544cb16e5006c5b02", size = 19448833, upload-time = "2025-09-03T14:32:13.639Z" }, ] [[package]] From adb901cf19b9e7df4fb00e9d6f69594b3e18f9da Mon Sep 17 00:00:00 2001 From: Chris <66376200+crickman@users.noreply.github.com> Date: Wed, 10 Sep 2025 10:45:11 -0700 Subject: [PATCH 25/32] .NET Workflows - Add declarative action events (#679) * Updated * Typos * Update sample * Typo * Add parent and prior action ids to event --- .../Workflows/Declarative/Program.cs | 12 +++- .../ConversationUpdateEvent.cs | 14 ---- .../DeclarativeWorkflowEvents.cs | 64 +++++++++++++++++++ .../Interpreter/DeclarativeActionExecutor.cs | 22 +++++-- .../DeclarativeWorkflowExecutor.cs | 11 ++-- .../Interpreter/DelegateActionExecutor.cs | 5 +- .../Interpreter/WorkflowActionVisitor.cs | 8 +-- .../ObjectModel/ConditionGroupExecutor.cs | 7 ++ .../ObjectModel/ForeachExecutor.cs | 15 ++++- .../ObjectModel/WorkflowActionExecutorTest.cs | 2 + 10 files changed, 124 insertions(+), 36 deletions(-) delete mode 100644 dotnet/src/Microsoft.Agents.Workflows.Declarative/ConversationUpdateEvent.cs create mode 100644 dotnet/src/Microsoft.Agents.Workflows.Declarative/DeclarativeWorkflowEvents.cs diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs b/dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs index d2301c0311..78e582dfc9 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs @@ -94,11 +94,19 @@ internal sealed class Program { if (evt is ExecutorInvokeEvent executorInvoked) { - Debug.WriteLine($"STEP ENTER #{executorInvoked.ExecutorId}"); + Debug.WriteLine($"EXECUTOR ENTER #{executorInvoked.ExecutorId}"); } else if (evt is ExecutorCompleteEvent executorComplete) { - Debug.WriteLine($"STEP EXIT #{executorComplete.ExecutorId}"); + Debug.WriteLine($"EXECUTOR EXIT #{executorComplete.ExecutorId}"); + } + if (evt is DeclarativeActionInvokeEvent actionInvoked) + { + Debug.WriteLine($"ACTION ENTER #{actionInvoked.ActionId} [{actionInvoked.ActionType}]"); + } + else if (evt is DeclarativeActionCompleteEvent actionComplete) + { + Debug.WriteLine($"ACTION EXIT #{actionComplete.ActionId} [{actionComplete.ActionType}]"); } else if (evt is ExecutorFailureEvent executorFailure) { diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/ConversationUpdateEvent.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/ConversationUpdateEvent.cs deleted file mode 100644 index 7984ffcd11..0000000000 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/ConversationUpdateEvent.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -namespace Microsoft.Agents.Workflows.Declarative; - -/// -/// Event that represents a message produced by a declarative workflow. -/// -public class ConversationUpdateEvent(string executorid, string conversationId) : ExecutorEvent(executorid, conversationId) -{ - /// - /// The conversation ID associated with the workflow. - /// - public string ConversationId { get; } = conversationId; -} diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/DeclarativeWorkflowEvents.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/DeclarativeWorkflowEvents.cs new file mode 100644 index 0000000000..74970f62f1 --- /dev/null +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/DeclarativeWorkflowEvents.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft. All rights reserved. + +using Microsoft.Agents.Workflows.Declarative.Extensions; +using Microsoft.Bot.ObjectModel; + +namespace Microsoft.Agents.Workflows.Declarative; + +/// +/// Event that broadcasts the conversation identifier. +/// +public class ConversationUpdateEvent(string executorid, string conversationId) : ExecutorEvent(executorid, conversationId) +{ + /// + /// The conversation ID associated with the workflow. + /// + public string ConversationId { get; } = conversationId; +} + +/// +/// Event that indicates a declarative action has been invoked. +/// +public class DeclarativeActionInvokeEvent(string actionId, DialogAction action, string? priorActionId) : WorkflowEvent(action) +{ + /// + /// The declarative action id. + /// + public string ActionId => actionId; + + /// + /// The declarative action type name. + /// + public string ActionType => action.GetType().Name; + + /// + /// Identifier of the parent action. + /// + public string? ParentActionId => action.GetParentId(); + + /// + /// Identifier of the previous action. + /// + public string? PriorActionId => priorActionId; +} + +/// +/// Event that indicates a declarative action has completed. +/// +public class DeclarativeActionCompleteEvent(string actionId, DialogAction action) : WorkflowEvent(action) +{ + /// + /// The declarative action identifier. + /// + public string ActionId => actionId; + + /// + /// The declarative action type name. + /// + public string ActionType => action.GetType().Name; + + /// + /// Identifier of the parent action. + /// + public string? ParentActionId => action.GetParentId(); +} diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DeclarativeActionExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DeclarativeActionExecutor.cs index 0d0138f261..2fa157bcca 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DeclarativeActionExecutor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DeclarativeActionExecutor.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.Agents.Workflows.Declarative.Extensions; -using Microsoft.Agents.Workflows.Reflection; using Microsoft.Bot.ObjectModel; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -25,9 +24,7 @@ internal abstract class DeclarativeActionExecutor(TAction model, Declar public new TAction Model => (TAction)base.Model; } -internal abstract class WorkflowActionExecutor : - ReflectingExecutor, - IMessageHandler +internal abstract class WorkflowActionExecutor : Executor { public const string RootActionId = "(root)"; @@ -60,8 +57,10 @@ internal abstract class WorkflowActionExecutor : protected DeclarativeWorkflowState State { get; } + protected virtual bool IsDiscreteAction => true; + /// - public async ValueTask HandleAsync(DeclarativeExecutorResult message, IWorkflowContext context) + public override async ValueTask HandleAsync(DeclarativeExecutorResult message, IWorkflowContext context) { if (this.Model.Disabled) { @@ -69,6 +68,8 @@ internal abstract class WorkflowActionExecutor : return; } + await this.RaiseInvocationEventAsync(context, message.ExecutorId).ConfigureAwait(false); + await this.State.RestoreAsync(context, default).ConfigureAwait(false); try @@ -87,6 +88,13 @@ internal abstract class WorkflowActionExecutor : Debug.WriteLine($"ERROR [{this.Id}] {exception.GetType().Name}\n{exception.Message}"); throw new DeclarativeActionException($"Unhandled workflow failure - #{this.Id} ({this.Model.GetType().Name})", exception); } + finally + { + if (this.IsDiscreteAction) + { + await this.RaiseCompletionEventAsync(context).ConfigureAwait(false); + } + } } protected abstract ValueTask ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken = default); @@ -117,4 +125,8 @@ internal abstract class WorkflowActionExecutor : string message = $"Unexpected workflow failure during {this.Model.GetType().Name} [{this.Id}]: {text}"; return exception is null ? new(message) : new(message, exception); } + + protected ValueTask RaiseInvocationEventAsync(IWorkflowContext context, string? priorEventId = null) => context.AddEventAsync(new DeclarativeActionInvokeEvent(this.Id, this.Model, priorEventId)); + + protected ValueTask RaiseCompletionEventAsync(IWorkflowContext context) => context.AddEventAsync(new DeclarativeActionCompleteEvent(this.Id, this.Model)); } diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DeclarativeWorkflowExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DeclarativeWorkflowExecutor.cs index 573e931f9c..f65bb7f5f6 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DeclarativeWorkflowExecutor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DeclarativeWorkflowExecutor.cs @@ -3,7 +3,6 @@ using System; using System.Threading.Tasks; using Microsoft.Agents.Workflows.Declarative.PowerFx; -using Microsoft.Agents.Workflows.Reflection; using Microsoft.Extensions.AI; namespace Microsoft.Agents.Workflows.Declarative.Interpreter; @@ -11,12 +10,14 @@ namespace Microsoft.Agents.Workflows.Declarative.Interpreter; /// /// The root executor for a declarative workflow. /// -internal sealed class DeclarativeWorkflowExecutor(string workflowId, DeclarativeWorkflowState state, Func inputTransform) : - ReflectingExecutor>(workflowId), - IMessageHandler +internal sealed class DeclarativeWorkflowExecutor( + string workflowId, + DeclarativeWorkflowState state, + Func inputTransform) : + Executor(workflowId) where TInput : notnull { - public async ValueTask HandleAsync(TInput message, IWorkflowContext context) + public override async ValueTask HandleAsync(TInput message, IWorkflowContext context) { ChatMessage input = inputTransform.Invoke(message); await state.SetLastMessageAsync(context, input).ConfigureAwait(false); diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DelegateActionExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DelegateActionExecutor.cs index 7bcf0b9868..59afb516ff 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DelegateActionExecutor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/Interpreter/DelegateActionExecutor.cs @@ -2,13 +2,12 @@ using System.Threading; using System.Threading.Tasks; -using Microsoft.Agents.Workflows.Reflection; namespace Microsoft.Agents.Workflows.Declarative.Interpreter; internal delegate ValueTask DelegateAction(IWorkflowContext context, CancellationToken cancellationToken); -internal sealed class DelegateActionExecutor : ReflectingExecutor, IMessageHandler +internal sealed class DelegateActionExecutor : Executor { private readonly DelegateAction? _action; @@ -18,7 +17,7 @@ internal sealed class DelegateActionExecutor : ReflectingExecutor this.ContinuationFor(parentId, parentId); + private string ContinuationFor(string parentId, DelegateAction? stepAction = null) => this.ContinuationFor(parentId, parentId, stepAction); - private string ContinuationFor(string actionId, string parentId) + private string ContinuationFor(string actionId, string parentId, DelegateAction? stepAction = null) { actionId = PostId(actionId); - this._workflowModel.AddNode(this.CreateStep(actionId), parentId); + this._workflowModel.AddNode(this.CreateStep(actionId, stepAction), parentId); return actionId; } diff --git a/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ConditionGroupExecutor.cs b/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ConditionGroupExecutor.cs index a3cbfb9ff4..b8d1662210 100644 --- a/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ConditionGroupExecutor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows.Declarative/ObjectModel/ConditionGroupExecutor.cs @@ -31,6 +31,8 @@ internal sealed class ConditionGroupExecutor : DeclarativeActionExecutor false; + public bool IsMatch(ConditionItem conditionItem, object? result) { if (result is not DeclarativeExecutorResult message) @@ -72,4 +74,9 @@ internal sealed class ConditionGroupExecutor : DeclarativeActionExecutor public bool HasValue { get; private set; } + protected override bool IsDiscreteAction => false; + protected override async ValueTask ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken) { this._index = 0; @@ -78,10 +80,17 @@ internal sealed class ForeachExecutor : DeclarativeActionExecutor public async ValueTask ResetAsync(IWorkflowContext context, CancellationToken cancellationToken) { - this.State.Reset(Throw.IfNull(this.Model.Value)); - if (this.Model.Index is not null) + try { - this.State.Reset(this.Model.Index); + this.State.Reset(Throw.IfNull(this.Model.Value)); + if (this.Model.Index is not null) + { + this.State.Reset(this.Model.Index); + } + } + finally + { + await this.RaiseCompletionEventAsync(context).ConfigureAwait(false); } } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/WorkflowActionExecutorTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/WorkflowActionExecutorTest.cs index 13ce04e1b9..8d7393864b 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/WorkflowActionExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/ObjectModel/WorkflowActionExecutorTest.cs @@ -33,6 +33,8 @@ public abstract class WorkflowActionExecutorTest(ITestOutputHelper output) : Wor workflowBuilder.AddEdge(workflowExecutor, executor); StreamingRun run = await InProcessExecution.StreamAsync(workflowBuilder.Build(), this.Scopes); WorkflowEvent[] events = await run.WatchStreamAsync().ToArrayAsync(); + Assert.Contains(events, e => e is DeclarativeActionInvokeEvent); + Assert.Contains(events, e => e is DeclarativeActionCompleteEvent); return events; } From 2393351a03cfbdcaca916106c679eddf25032344 Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Date: Wed, 10 Sep 2025 19:34:08 +0100 Subject: [PATCH 26/32] .NET: Adding Image Multi Modality Sample (#688) * Adding Multi Modal Sample * Address typos * Update sample to Azure OpenAI * Update readme * Update readme * Update readme * Use thread --- dotnet/agent-framework-dotnet.slnx | 1 + .../Agent_Step11_UsingImages.csproj | 22 ++++++++ .../Agent_Step11_UsingImages/Program.cs | 30 +++++++++++ .../Agents/Agent_Step11_UsingImages/README.md | 52 +++++++++++++++++++ .../samples/GettingStarted/Agents/README.md | 1 + .../SemanticKernelToAgentFrameworkReport.md | 0 6 files changed, 106 insertions(+) create mode 100644 dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Agent_Step11_UsingImages.csproj create mode 100644 dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Program.cs create mode 100644 dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/README.md create mode 100644 dotnet/samples/SemanticKernelMigration/NotMigratedUseCases/SemanticKernelBasic/.github/SemanticKernelToAgentFrameworkReport.md diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index cd28a9e723..c26e25487e 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -48,6 +48,7 @@ + diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Agent_Step11_UsingImages.csproj b/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Agent_Step11_UsingImages.csproj new file mode 100644 index 0000000000..a438862616 --- /dev/null +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Agent_Step11_UsingImages.csproj @@ -0,0 +1,22 @@ + + + + Exe + net9.0 + 12 + + enable + disable + + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Program.cs new file mode 100644 index 0000000000..318788f352 --- /dev/null +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/Program.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample shows how to use Image Multi-Modality with an AI agent. + +using System; +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Extensions.AI; +using OpenAI; + +var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); +var deploymentName = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o"; + +var agent = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()) + .GetChatClient(deploymentName) + .CreateAIAgent( + name: "VisionAgent", + instructions: "You are a helpful agent that can analyze images"); + +ChatMessage message = new(ChatRole.User, [ + new TextContent("What do you see in this image?"), + new UriContent("https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg", "image/jpeg") +]); + +var thread = agent.GetNewThread(); + +await foreach (var update in agent.RunStreamingAsync(message, thread)) +{ + Console.WriteLine(update); +} diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/README.md b/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/README.md new file mode 100644 index 0000000000..49d1bff4a2 --- /dev/null +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step11_UsingImages/README.md @@ -0,0 +1,52 @@ +# Using Images with AI Agents + +This sample demonstrates how to use image multi-modality with an AI agent. It shows how to create a vision-enabled agent that can analyze and describe images using Azure OpenAI. + +## What this sample demonstrates + +- Creating a persistent AI agent with vision capabilities +- Sending both text and image content to an agent in a single message +- Using `UriContent` to Uri referenced images +- Processing multimodal input (text + image) with an AI agent + +## Key features + +- **Vision Agent**: Creates an agent specifically instructed to analyze images +- **Multimodal Input**: Combines text questions with image uri in a single message +- **Azure OpenAI Integration**: Uses AzureOpenAI LLM agents + +## Prerequisites + +Before running this sample, ensure you have: + +1. An Azure OpenAI project set up +2. A compatible model deployment (e.g., gpt-4o) +3. Azure CLI installed and authenticated + +## Environment Variables + +Set the following environment variables: + +```powershell +$env:AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" # Replace with your Azure OpenAI endpoint +$env:AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o" # Replace with your model deployment name (optional, defaults to gpt-4o) +``` + +## Run the sample + +Navigate to the sample directory and run: + +```powershell +cd Agent_Step11_UsingImages +dotnet run +``` + +## Expected behavior + +The sample will: + +1. Create a vision-enabled agent named "VisionAgent" +2. Send a message containing both text ("What do you see in this image?") and a Uri image of a green walk +3. The agent will analyze the image and provide a description +4. Clean up resources by deleting the thread and agent + diff --git a/dotnet/samples/GettingStarted/Agents/README.md b/dotnet/samples/GettingStarted/Agents/README.md index 03e9edc247..c6959aee65 100644 --- a/dotnet/samples/GettingStarted/Agents/README.md +++ b/dotnet/samples/GettingStarted/Agents/README.md @@ -36,6 +36,7 @@ Before you begin, ensure you have the following prerequisites: |[Telemetry with a simple agent](./Agent_Step08_Telemetry/)|This sample demonstrates how to add telemetry to a simple agent| |[Dependency injection with a simple agent](./Agent_Step09_DependencyInjection/)|This sample demonstrates how to add and resolve an agent with a dependency injection container| |[Exposing a simple agent as MCP tool](./Agent_Step10_AsMcpTool/)|This sample demonstrates how to expose an agent as an MCP tool| +|[Using images with a simple agent](./Agent_Step11_UsingImages/)|This sample demonstrates how to use image multi-modality with an AI agent| ## Running the samples from the console diff --git a/dotnet/samples/SemanticKernelMigration/NotMigratedUseCases/SemanticKernelBasic/.github/SemanticKernelToAgentFrameworkReport.md b/dotnet/samples/SemanticKernelMigration/NotMigratedUseCases/SemanticKernelBasic/.github/SemanticKernelToAgentFrameworkReport.md new file mode 100644 index 0000000000..e69de29bb2 From 89c8418705e8f29b1c16cde99f3a0d78e6da7b30 Mon Sep 17 00:00:00 2001 From: Tao Chen Date: Wed, 10 Sep 2025 11:47:13 -0700 Subject: [PATCH 27/32] .NET: Workflow getting started samples in .Net (Second batch) (#650) * Add loop and agents in workflows samples * Add foundry agent and workflow as agent samples * Checkpoint sample WIP * Checkpoint sample 1 Done * Add HIL samples * Fix formatting * Force folder name change step 1 * Force folder name change step 2 * Fix formatting * Fix formatting * Add checkpoint and rehydrate sample * _Foundational * Fix formatting --- dotnet/agent-framework-dotnet.slnx | 26 +- .../CustomAgentExecutors.csproj} | 0 .../Agents/CustomAgentExecutors/Program.cs | 246 ++++++++++++++++++ .../Agents/FoundryAgent/FoundryAgent.csproj | 23 ++ .../Workflows/Agents/FoundryAgent/Program.cs | 82 ++++++ .../Agents/WorkflowAsAnAgent/Program.cs | 90 +++++++ .../WorkflowAsAnAgent.csproj | 24 ++ .../WorkflowAsAnAgent/WorkflowHelper.cs | 95 +++++++ .../CheckpointAndRehydrate.csproj} | 0 .../CheckpointAndRehydrate/Program.cs | 96 +++++++ .../CheckpointAndRehydrate/WorkflowHelper.cs | 165 ++++++++++++ .../CheckpointAndResume.csproj | 16 ++ .../Checkpoint/CheckpointAndResume/Program.cs | 91 +++++++ .../CheckpointAndResume/WorkflowHelper.cs | 165 ++++++++++++ .../CheckpointWithHumanInTheLoop.csproj} | 0 .../CheckpointWithHumanInTheLoop/Program.cs | 138 ++++++++++ .../WorkflowHelper.cs | 110 ++++++++ .../Workflows/Concurrent/Program.cs | 4 +- .../Workflows/Declarative/Program.cs | 6 +- .../HumanInTheLoopBasic.csproj | 16 ++ .../HumanInTheLoopBasic/Program.cs | 86 ++++++ .../HumanInTheLoopBasic/WorkflowHelper.cs | 75 ++++++ .../GettingStarted/Workflows/Loop/Loop.csproj | 16 ++ .../GettingStarted/Workflows/Loop/Program.cs | 139 ++++++++++ .../GettingStarted/Workflows/README.md | 38 ++- .../01_ExecutorsAndEdges.csproj | 16 ++ .../01_ExecutorsAndEdges/Program.cs | 2 +- .../02_Streaming/02_Streaming.csproj | 16 ++ .../02_Streaming/Program.cs | 4 +- .../03_AgentsInWorkflows.csproj | 24 ++ .../03_AgentsInWorkflows/Program.cs | 0 .../Microsoft.Agents.Workflows/Executor.cs | 4 +- ...leteEvent.cs => ExecutorCompletedEvent.cs} | 2 +- ...InvokeEvent.cs => ExecutorInvokedEvent.cs} | 2 +- .../DeclarativeWorkflowTest.cs | 14 +- .../Sample/01_Simple_Workflow_Sequential.cs | 4 +- .../Sample/01a_Simple_Workflow_Sequential.cs | 4 +- .../Sample/02_Simple_Workflow_Condition.cs | 4 +- .../Sample/03_Simple_Workflow_Loop.cs | 4 +- .../04_Simple_Workflow_ExternalRequest.cs | 4 +- .../05_Simple_Workflow_Checkpointing.cs | 2 +- .../Sample/06_GroupChat_Workflow.cs | 4 +- 42 files changed, 1815 insertions(+), 42 deletions(-) rename dotnet/samples/GettingStarted/Workflows/{Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj => Agents/CustomAgentExecutors/CustomAgentExecutors.csproj} (100%) create mode 100644 dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs create mode 100644 dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/FoundryAgent.csproj create mode 100644 dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/Program.cs create mode 100644 dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/Program.cs create mode 100644 dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowAsAnAgent.csproj create mode 100644 dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowHelper.cs rename dotnet/samples/GettingStarted/Workflows/{Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj => Checkpoint/CheckpointAndRehydrate/CheckpointAndRehydrate.csproj} (100%) create mode 100644 dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/Program.cs create mode 100644 dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/WorkflowHelper.cs create mode 100644 dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/CheckpointAndResume.csproj create mode 100644 dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/Program.cs create mode 100644 dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/WorkflowHelper.cs rename dotnet/samples/GettingStarted/Workflows/{Foundational/02_Streaming/02_Streaming.csproj => Checkpoint/CheckpointWithHumanInTheLoop/CheckpointWithHumanInTheLoop.csproj} (100%) create mode 100644 dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/Program.cs create mode 100644 dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/WorkflowHelper.cs create mode 100644 dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/HumanInTheLoopBasic.csproj create mode 100644 dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/Program.cs create mode 100644 dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/WorkflowHelper.cs create mode 100644 dotnet/samples/GettingStarted/Workflows/Loop/Loop.csproj create mode 100644 dotnet/samples/GettingStarted/Workflows/Loop/Program.cs create mode 100644 dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj rename dotnet/samples/GettingStarted/Workflows/{Foundational => _Foundational}/01_ExecutorsAndEdges/Program.cs (98%) create mode 100644 dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/02_Streaming.csproj rename dotnet/samples/GettingStarted/Workflows/{Foundational => _Foundational}/02_Streaming/Program.cs (95%) create mode 100644 dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj rename dotnet/samples/GettingStarted/Workflows/{Foundational => _Foundational}/03_AgentsInWorkflows/Program.cs (100%) rename dotnet/src/Microsoft.Agents.Workflows/{ExecutorCompleteEvent.cs => ExecutorCompletedEvent.cs} (76%) rename dotnet/src/Microsoft.Agents.Workflows/{ExecutorInvokeEvent.cs => ExecutorInvokedEvent.cs} (73%) diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index c26e25487e..f7a78e5535 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -83,14 +83,30 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/dotnet/samples/GettingStarted/Workflows/Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj b/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/CustomAgentExecutors.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj rename to dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/CustomAgentExecutors.csproj diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs b/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs new file mode 100644 index 0000000000..fe1b612603 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs @@ -0,0 +1,246 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Agents.Workflows; +using Microsoft.Agents.Workflows.Reflection; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.AI.Agents; + +namespace WorkflowCustomAgentExecutorsSample; + +/// +/// This sample demonstrates how to create custom executors for AI agents. +/// This is useful when you want more control over the agent's behaviors in a workflow. +/// +/// In this example, we create two custom executors: +/// 1. SloganWriterExecutor: An AI agent that generates slogans based on a given task. +/// 2. FeedbackExecutor: An AI agent that provides feedback on the generated slogans. +/// (These two executors manage the agent instances and their conversation threads.) +/// +/// The workflow alternates between these two executors until the slogan meets a certain +/// quality threshold or a maximum number of attempts is reached. +/// +/// +/// Pre-requisites: +/// - Foundational samples should be completed first. +/// - An Azure OpenAI chat completion deployment that supports structured outputs must be configured. +/// +public static class Program +{ + private static async Task Main() + { + // Set up the Azure OpenAI client + var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); + var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + var chatClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()).GetChatClient(deploymentName).AsIChatClient(); + + // Create the executors + var sloganWriter = new SloganWriterExecutor(chatClient); + var feedbackProvider = new FeedbackExecutor(chatClient); + + // Build the workflow by adding executors and connecting them + WorkflowBuilder builder = new(sloganWriter); + builder.AddEdge(sloganWriter, feedbackProvider); + builder.AddEdge(feedbackProvider, sloganWriter); + var workflow = builder.Build(); + + // Execute the workflow + var ask = "Create a slogan for a new electric SUV that is affordable and fun to drive."; + StreamingRun run = await InProcessExecution.StreamAsync(workflow, ask); + await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false)) + { + if (evt is SloganGeneratedEvent || evt is FeedbackEvent) + { + // Custom events to allow us to monitor the progress of the workflow. + Console.WriteLine($"{evt}"); + } + + if (evt is WorkflowCompletedEvent completedEvent) + { + Console.WriteLine($"{completedEvent}"); + } + } + } +} + +/// +/// A class representing the output of the slogan writer agent. +/// +public sealed class SloganResult +{ + [JsonPropertyName("task")] + public required string Task { get; set; } + + [JsonPropertyName("slogan")] + public required string Slogan { get; set; } +} + +/// +/// A class representing the output of the feedback agent. +/// +public sealed class FeedbackResult +{ + [JsonPropertyName("comments")] + public string Comments { get; set; } = string.Empty; + + [JsonPropertyName("rating")] + public int Rating { get; set; } + + [JsonPropertyName("actions")] + public string Actions { get; set; } = string.Empty; +} + +/// +/// A custom event to indicate that a slogan has been generated. +/// +internal sealed class SloganGeneratedEvent(SloganResult sloganResult) : WorkflowEvent(sloganResult) +{ + public override string ToString() => $"Slogan: {sloganResult.Slogan}"; +} + +/// +/// A custom executor that uses an AI agent to generate slogans based on a given task. +/// Note that this executor has two message handlers: +/// 1. HandleAsync(string message): Handles the initial task to create a slogan. +/// 2. HandleAsync(Feedback message): Handles feedback to improve the slogan. +/// +internal sealed class SloganWriterExecutor + : ReflectingExecutor, + IMessageHandler, + IMessageHandler +{ + private const string Instruction = """ + You are a professional slogan writer. You will be given a task to create a slogan. + """; + + private readonly AIAgent _agent; + private readonly AgentThread _thread; + + /// + /// Initializes a new instance of the class. + /// + /// The chat client to use for the AI agent. + public SloganWriterExecutor(IChatClient chatClient) + { + var agentOptions = new ChatClientAgentOptions(instructions: Instruction) + { + ChatOptions = new() + { + ResponseFormat = ChatResponseFormatJson.ForJsonSchema( + schema: AIJsonUtilities.CreateJsonSchema(typeof(SloganResult)) + ) + } + }; + + this._agent = new ChatClientAgent(chatClient, agentOptions); + this._thread = this._agent.GetNewThread(); + } + + public async ValueTask HandleAsync(string message, IWorkflowContext context) + { + var result = await this._agent.RunAsync(message, this._thread); + + var sloganResult = JsonSerializer.Deserialize(result.Text) ?? throw new InvalidOperationException("Failed to deserialize slogan result."); + + await context.AddEventAsync(new SloganGeneratedEvent(sloganResult)); + return sloganResult; + } + + public async ValueTask HandleAsync(FeedbackResult message, IWorkflowContext context) + { + var feedbackMessage = $""" + Here is the feedback on your previous slogan: + Comments: {message.Comments} + Rating: {message.Rating} + Suggested Actions: {message.Actions} + + Please use this feedback to improve your slogan. + """; + + var result = await this._agent.RunAsync(feedbackMessage, this._thread); + var sloganResult = JsonSerializer.Deserialize(result.Text) ?? throw new InvalidOperationException("Failed to deserialize slogan result."); + + await context.AddEventAsync(new SloganGeneratedEvent(sloganResult)); + return sloganResult; + } +} + +/// +/// A custom event to indicate that feedback has been provided. +/// +internal sealed class FeedbackEvent(FeedbackResult feedbackResult) : WorkflowEvent(feedbackResult) +{ + private readonly JsonSerializerOptions _options = new() { WriteIndented = true }; + public override string ToString() => $"Feedback:\n{JsonSerializer.Serialize(feedbackResult, this._options)}"; +} + +/// +/// A custom executor that uses an AI agent to provide feedback on a slogan. +/// +internal sealed class FeedbackExecutor : ReflectingExecutor, IMessageHandler +{ + private const string Instruction = """ + You are a professional editor. You will be given a slogan and the task it is meant to accomplish. + """; + + private readonly AIAgent _agent; + private readonly AgentThread _thread; + + public int MinimumRating { get; init; } = 8; + + public int MaxAttempts { get; init; } = 3; + + private int _attempts = 0; + + /// + /// Initializes a new instance of the class. + /// + /// The chat client to use for the AI agent. + public FeedbackExecutor(IChatClient chatClient) + { + var agentOptions = new ChatClientAgentOptions(instructions: Instruction) + { + ChatOptions = new() + { + ResponseFormat = ChatResponseFormatJson.ForJsonSchema( + schema: AIJsonUtilities.CreateJsonSchema(typeof(FeedbackResult)) + ) + } + }; + + this._agent = new ChatClientAgent(chatClient, agentOptions); + this._thread = this._agent.GetNewThread(); + } + + public async ValueTask HandleAsync(SloganResult message, IWorkflowContext context) + { + var sloganMessage = $""" + Here is a slogan for the task '{message.Task}': + Slogan: {message.Slogan} + Please provide feedback on this slogan, including comments, a rating from 1 to 10, and suggested actions for improvement. + """; + + var response = await this._agent.RunAsync(sloganMessage, this._thread); + var feedback = JsonSerializer.Deserialize(response.Text) ?? throw new InvalidOperationException("Failed to deserialize feedback."); + + await context.AddEventAsync(new FeedbackEvent(feedback)); + if (feedback.Rating >= this.MinimumRating) + { + await context.AddEventAsync(new WorkflowCompletedEvent($"The following slogan was accepted:\n\n{message.Slogan}")); + return; + } + if (this._attempts >= this.MaxAttempts) + { + await context.AddEventAsync(new WorkflowCompletedEvent($"The slogan was rejected after {this.MaxAttempts} attempts. Final slogan:\n\n{message.Slogan}")); + return; + } + + await context.SendMessageAsync(feedback); + this._attempts++; + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/FoundryAgent.csproj b/dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/FoundryAgent.csproj new file mode 100644 index 0000000000..564e214dac --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/FoundryAgent.csproj @@ -0,0 +1,23 @@ + + + + Exe + net9.0 + 12 + + enable + disable + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/Program.cs b/dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/Program.cs new file mode 100644 index 0000000000..586780f1f3 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Agents/FoundryAgent/Program.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Threading.Tasks; +using Azure.AI.Agents.Persistent; +using Azure.Identity; +using Microsoft.Agents.Workflows; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.AI.Agents; + +namespace WorkflowFoundryAgentSample; + +/// +/// This sample shows how to use Azure Foundry Agents within a workflow. +/// +/// +/// Pre-requisites: +/// - Foundational samples should be completed first. +/// - An Azure Foundry project endpoint and model id. +/// +public static class Program +{ + private static async Task Main() + { + // Set up the Azure OpenAI client + var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") + ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set."); + var model = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_MODEL_ID") ?? "gpt-4o-mini"; + var persistentAgentsClient = new PersistentAgentsClient(endpoint, new AzureCliCredential()); + + // Create agents + AIAgent frenchAgent = await GetTranslationAgentAsync("French", persistentAgentsClient, model); + AIAgent spanishAgent = await GetTranslationAgentAsync("Spanish", persistentAgentsClient, model); + AIAgent englishAgent = await GetTranslationAgentAsync("English", persistentAgentsClient, model); + + // Build the workflow by adding executors and connecting them + WorkflowBuilder builder = new(frenchAgent); + builder.AddEdge(frenchAgent, spanishAgent); + builder.AddEdge(spanishAgent, englishAgent); + var workflow = builder.Build(); + + // Execute the workflow + StreamingRun run = await InProcessExecution.StreamAsync(workflow, new ChatMessage(ChatRole.User, "Hello World!")); + // Must send the turn token to trigger the agents. + // The agents are wrapped as executors. When they receive messages, + // they will cache the messages and only start processing when they receive a TurnToken. + await run.TrySendMessageAsync(new TurnToken(emitEvents: true)); + await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false)) + { + if (evt is AgentRunUpdateEvent executorComplete) + { + Console.WriteLine($"{executorComplete.ExecutorId}: {executorComplete.Data}"); + } + } + + // Cleanup the agents created for the sample. + await persistentAgentsClient.Administration.DeleteAgentAsync(frenchAgent.Id); + await persistentAgentsClient.Administration.DeleteAgentAsync(spanishAgent.Id); + await persistentAgentsClient.Administration.DeleteAgentAsync(englishAgent.Id); + } + + /// + /// Creates a translation agent for the specified target language. + /// + /// The target language for translation + /// The PersistentAgentsClient to create the agent + /// The model to use for the agent + /// A ChatClientAgent configured for the specified language + private static async Task GetTranslationAgentAsync( + string targetLanguage, + PersistentAgentsClient persistentAgentsClient, + string model) + { + string instructions = $"You are a translation assistant that translates the provided text to {targetLanguage}."; + var agentMetadata = await persistentAgentsClient.Administration.CreateAgentAsync( + model: model, + name: $"{targetLanguage} Translator", + instructions: instructions); + + return await persistentAgentsClient.GetAIAgentAsync(agentMetadata.Value.Id); + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/Program.cs b/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/Program.cs new file mode 100644 index 0000000000..7611b14984 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/Program.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Azure.AI.OpenAI; +using Azure.Identity; +using Microsoft.Agents.Workflows; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.AI.Agents; + +namespace WorkflowAsAnAgentsSample; + +/// +/// This sample introduces the concepts workflows as agents, where a workflow can be +/// treated as an . This allows you to interact with a workflow +/// as if it were a single agent. +/// +/// In this example, we create a workflow that uses two language agents to process +/// input concurrently, one that responds in French and another that responds in English. +/// +/// You will interact with the workflow in an interactive loop, sending messages and receiving +/// streaming responses from the workflow as if it were an agent who responds in both languages. +/// +/// +/// Pre-requisites: +/// - Foundational samples should be completed first. +/// - This sample uses concurrent processing. +/// - An Azure OpenAI endpoint and deployment name. +/// +public static class Program +{ + private static async Task Main() + { + // Set up the Azure OpenAI client + var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); + var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + var chatClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()).GetChatClient(deploymentName).AsIChatClient(); + + // Create the workflow and turn it into an agent + var workflow = WorkflowHelper.GetWorkflow(chatClient); + var agent = workflow.AsAgent("workflow-agent", "Workflow Agent"); + var thread = agent.GetNewThread(); + + // Start an interactive loop to interact with the workflow as if it were an agent + while (true) + { + Console.WriteLine(); + Console.Write("User (or 'exit' to quit): "); + string? input = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(input) || input.Equals("exit", StringComparison.OrdinalIgnoreCase)) + { + break; + } + + await ProcessInputAsync(agent, thread, input); + } + + // Helper method to process user input and display streaming responses. To display + // multiple interleaved responses correctly, we buffer updates by message ID and + // re-render all messages on each update. + static async Task ProcessInputAsync(AIAgent agent, AgentThread thread, string input) + { + Dictionary> buffer = []; + await foreach (AgentRunResponseUpdate update in agent.RunStreamingAsync(input, thread).ConfigureAwait(false)) + { + if (update.MessageId == null) + { + // skip updates that don't have a message ID + continue; + } + Console.Clear(); + + if (!buffer.TryGetValue(update.MessageId, out List? value)) + { + value = []; + buffer[update.MessageId] = value; + } + value.Add(update); + + foreach (var (messageId, segments) in buffer) + { + string combinedText = string.Concat(segments); + Console.WriteLine($"{segments[0].AuthorName}: {combinedText}"); + Console.WriteLine(); + } + } + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowAsAnAgent.csproj b/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowAsAnAgent.csproj new file mode 100644 index 0000000000..b1f90ee600 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowAsAnAgent.csproj @@ -0,0 +1,24 @@ + + + + Exe + net9.0 + 12 + + enable + disable + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowHelper.cs b/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowHelper.cs new file mode 100644 index 0000000000..9470e22c10 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Agents/WorkflowAsAnAgent/WorkflowHelper.cs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Agents.Workflows; +using Microsoft.Agents.Workflows.Reflection; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.AI.Agents; + +namespace WorkflowAsAnAgentsSample; + +internal static class WorkflowHelper +{ + /// + /// Creates a workflow that uses two language agents to process input concurrently. + /// + /// The chat client to use for the agents + /// A workflow that processes input using two language agents + internal static Workflow> GetWorkflow(IChatClient chatClient) + { + // Create executors + var startExecutor = new ConcurrentStartExecutor(); + var aggregationExecutor = new ConcurrentAggregationExecutor(); + AIAgent frenchAgent = GetLanguageAgent("French", chatClient); + AIAgent englishAgent = GetLanguageAgent("English", chatClient); + + // Build the workflow by adding executors and connecting them + WorkflowBuilder builder = new(startExecutor); + builder.AddFanOutEdge(startExecutor, targets: [frenchAgent, englishAgent]); + builder.AddFanInEdge(aggregationExecutor, sources: [frenchAgent, englishAgent]); + + return builder.Build>(); + } + + /// + /// Creates a language agent for the specified target language. + /// + /// The target language for translation + /// The chat client to use for the agent + /// A ChatClientAgent configured for the specified language + private static ChatClientAgent GetLanguageAgent(string targetLanguage, IChatClient chatClient) + { + string instructions = $"You're a helpful assistant who always responds in {targetLanguage}."; + return new ChatClientAgent(chatClient, instructions, name: $"{targetLanguage}Agent"); + } + + /// + /// Executor that starts the concurrent processing by sending messages to the agents. + /// + private sealed class ConcurrentStartExecutor() : + ReflectingExecutor("ConcurrentStartExecutor"), + IMessageHandler> + { + /// + /// Starts the concurrent processing by sending messages to the agents. + /// + /// The user message to process + /// Workflow context for accessing workflow services and adding events + public async ValueTask HandleAsync(List message, IWorkflowContext context) + { + // Broadcast the message to all connected agents. Receiving agents will queue + // the message but will not start processing until they receive a turn token. + await context.SendMessageAsync(message); + // Broadcast the turn token to kick off the agents. + await context.SendMessageAsync(new TurnToken(emitEvents: true)); + } + } + + /// + /// Executor that aggregates the results from the concurrent agents. + /// + private sealed class ConcurrentAggregationExecutor() : + ReflectingExecutor("ConcurrentAggregationExecutor"), + IMessageHandler + { + private readonly List _messages = []; + + /// + /// Handles incoming messages from the agents and aggregates their responses. + /// + /// The message from the agent + /// Workflow context for accessing workflow services and adding events + public async ValueTask HandleAsync(ChatMessage message, IWorkflowContext context) + { + this._messages.Add(message); + + if (this._messages.Count == 2) + { + var formattedMessages = string.Join(Environment.NewLine, this._messages.Select(m => $"{m.Text}")); + await context.AddEventAsync(new WorkflowCompletedEvent(formattedMessages)); + } + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/CheckpointAndRehydrate.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj rename to dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/CheckpointAndRehydrate.csproj diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/Program.cs b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/Program.cs new file mode 100644 index 0000000000..d039b889b8 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/Program.cs @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Agents.Workflows; + +namespace WorkflowCheckpointAndRehydrateSample; + +/// +/// This sample introduces the concepts of check points and shows how to save and restore +/// the state of a workflow using checkpoints. +/// This sample demonstrates checkpoints, which allow you to save and restore a workflow's state. +/// Key concepts: +/// - Super Steps: A workflow executes in stages called "super steps". Each super step runs +/// one or more executors and completes when all those executors finish their work. +/// - Checkpoints: The system automatically saves the workflow's state at the end of each +/// super step. You can use these checkpoints to resume the workflow from any saved point. +/// - Rehydration: You can rehydrate a new workflow instance from a saved checkpoint, allowing +/// you to continue execution from that point. +/// +/// +/// Pre-requisites: +/// - Foundational samples should be completed first. +/// +public static class Program +{ + private static async Task Main() + { + // Create the workflow + var workflow = WorkflowHelper.GetWorkflow(); + + // Create checkpoint manager + var checkpointManager = new CheckpointManager(); + var checkpoints = new List(); + + // Execute the workflow and save checkpoints + Checkpointed checkpointedRun = await InProcessExecution + .StreamAsync(workflow, NumberSignal.Init, checkpointManager) + .ConfigureAwait(false); + await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false)) + { + if (evt is ExecutorCompletedEvent executorCompletedEvt) + { + Console.WriteLine($"* Executor {executorCompletedEvt.ExecutorId} completed."); + } + + if (evt is SuperStepCompletedEvent superStepCompletedEvt) + { + // Checkpoints are automatically created at the end of each super step when a + // checkpoint manager is provided. You can store the checkpoint info for later use. + CheckpointInfo? checkpoint = superStepCompletedEvt.CompletionInfo!.Checkpoint; + if (checkpoint != null) + { + checkpoints.Add(checkpoint); + Console.WriteLine($"** Checkpoint created at step {checkpoints.Count}."); + } + } + + if (evt is WorkflowCompletedEvent workflowCompletedEvt) + { + Console.WriteLine($"Workflow completed with result: {workflowCompletedEvt.Data}"); + } + } + + if (checkpoints.Count == 0) + { + throw new InvalidOperationException("No checkpoints were created during the workflow execution."); + } + Console.WriteLine($"Number of checkpoints created: {checkpoints.Count}"); + + // Rehydrate a new workflow instance from a saved checkpoint and continue execution + var newWorkflow = WorkflowHelper.GetWorkflow(); + var checkpointIndex = 5; + Console.WriteLine($"\n\nHydrating a new workflow instance from the {checkpointIndex + 1}th checkpoint."); + CheckpointInfo savedCheckpoint = checkpoints[checkpointIndex]; + + Checkpointed newCheckpointedRun = await InProcessExecution + .StreamAsync(newWorkflow, NumberSignal.Init, checkpointManager) + .ConfigureAwait(false); + await newCheckpointedRun.RestoreCheckpointAsync(savedCheckpoint, CancellationToken.None).ConfigureAwait(false); + await foreach (WorkflowEvent evt in newCheckpointedRun.Run.WatchStreamAsync().ConfigureAwait(false)) + { + if (evt is ExecutorCompletedEvent executorCompletedEvt) + { + Console.WriteLine($"* Executor {executorCompletedEvt.ExecutorId} completed."); + } + + if (evt is WorkflowCompletedEvent workflowCompletedEvt) + { + Console.WriteLine($"Workflow completed with result: {workflowCompletedEvt.Data}"); + } + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/WorkflowHelper.cs b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/WorkflowHelper.cs new file mode 100644 index 0000000000..09ffc8db74 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndRehydrate/WorkflowHelper.cs @@ -0,0 +1,165 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Agents.Workflows; +using Microsoft.Agents.Workflows.Reflection; + +namespace WorkflowCheckpointAndRehydrateSample; + +internal static class WorkflowHelper +{ + /// + /// Get a workflow that plays a number guessing game with checkpointing support. + /// The workflow consists of two executors that are connected in a feedback loop: + /// 1. GuessNumberExecutor: Makes a guess based on the current known bounds. + /// 2. JudgeExecutor: Evaluates the guess and provides feedback. + /// The workflow continues until the correct number is guessed. + /// + internal static Workflow GetWorkflow() + { + // Create the executors + GuessNumberExecutor guessNumberExecutor = new(1, 100); + JudgeExecutor judgeExecutor = new(42); + + // Build the workflow by connecting executors in a loop + var workflow = new WorkflowBuilder(guessNumberExecutor) + .AddEdge(guessNumberExecutor, judgeExecutor) + .AddEdge(judgeExecutor, guessNumberExecutor) + .Build(); + + return workflow; + } +} + +/// +/// Signals used for communication between GuessNumberExecutor and JudgeExecutor. +/// +internal enum NumberSignal +{ + Init, + Above, + Below, +} + +/// +/// Executor that makes a guess based on the current bounds. +/// +internal sealed class GuessNumberExecutor() : ReflectingExecutor("Guess"), IMessageHandler +{ + /// + /// The lower bound of the guessing range. + /// + public int LowerBound { get; private set; } + + /// + /// The upper bound of the guessing range. + /// + public int UpperBound { get; private set; } + + private const string StateKey = "GuessNumberExecutorState"; + + /// + /// Initializes a new instance of the class. + /// + /// The initial lower bound of the guessing range. + /// The initial upper bound of the guessing range. + public GuessNumberExecutor(int lowerBound, int upperBound) : this() + { + this.LowerBound = lowerBound; + this.UpperBound = upperBound; + } + + private int NextGuess => (this.LowerBound + this.UpperBound) / 2; + + public async ValueTask HandleAsync(NumberSignal message, IWorkflowContext context) + { + switch (message) + { + case NumberSignal.Init: + await context.SendMessageAsync(this.NextGuess).ConfigureAwait(false); + break; + case NumberSignal.Above: + this.UpperBound = this.NextGuess - 1; + await context.SendMessageAsync(this.NextGuess).ConfigureAwait(false); + break; + case NumberSignal.Below: + this.LowerBound = this.NextGuess + 1; + await context.SendMessageAsync(this.NextGuess).ConfigureAwait(false); + break; + } + } + + /// + /// Checkpoint the current state of the executor. + /// This must be overridden to save any state that is needed to resume the executor. + /// + protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) + { + return context.QueueStateUpdateAsync(StateKey, (this.LowerBound, this.UpperBound)); + } + + /// + /// Restore the state of the executor from a checkpoint. + /// This must be overridden to restore any state that was saved during checkpointing. + /// + protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) + { + (this.LowerBound, this.UpperBound) = await context.ReadStateAsync<(int, int)>(StateKey).ConfigureAwait(false); + } +} + +/// +/// Executor that judges the guess and provides feedback. +/// +internal sealed class JudgeExecutor() : ReflectingExecutor("Judge"), IMessageHandler +{ + private readonly int _targetNumber; + private int _tries = 0; + private const string StateKey = "JudgeExecutorState"; + + /// + /// Initializes a new instance of the class. + /// + /// The number to be guessed. + public JudgeExecutor(int targetNumber) : this() + { + this._targetNumber = targetNumber; + } + + public async ValueTask HandleAsync(int message, IWorkflowContext context) + { + this._tries++; + if (message == this._targetNumber) + { + await context.AddEventAsync(new WorkflowCompletedEvent($"{this._targetNumber} found in {this._tries} tries!")) + .ConfigureAwait(false); + } + else if (message < this._targetNumber) + { + await context.SendMessageAsync(NumberSignal.Below).ConfigureAwait(false); + } + else + { + await context.SendMessageAsync(NumberSignal.Above).ConfigureAwait(false); + } + } + + /// + /// Checkpoint the current state of the executor. + /// This must be overridden to save any state that is needed to resume the executor. + /// + protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) + { + return context.QueueStateUpdateAsync(StateKey, this._tries); + } + + /// + /// Restore the state of the executor from a checkpoint. + /// This must be overridden to restore any state that was saved during checkpointing. + /// + protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) + { + this._tries = await context.ReadStateAsync(StateKey).ConfigureAwait(false); + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/CheckpointAndResume.csproj b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/CheckpointAndResume.csproj new file mode 100644 index 0000000000..1025cb45c9 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/CheckpointAndResume.csproj @@ -0,0 +1,16 @@ + + + + Exe + net9.0 + 12 + + enable + disable + + + + + + + \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/Program.cs b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/Program.cs new file mode 100644 index 0000000000..ebabba2141 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/Program.cs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Agents.Workflows; + +namespace WorkflowCheckpointAndResumeSample; + +/// +/// This sample introduces the concepts of check points and shows how to save and restore +/// the state of a workflow using checkpoints. +/// This sample demonstrates checkpoints, which allow you to save and restore a workflow's state. +/// Key concepts: +/// - Super Steps: A workflow executes in stages called "super steps". Each super step runs +/// one or more executors and completes when all those executors finish their work. +/// - Checkpoints: The system automatically saves the workflow's state at the end of each +/// super step. You can use these checkpoints to resume the workflow from any saved point. +/// - Resume: If needed, you can restore a checkpoint and continue execution from that state. +/// +/// +/// Pre-requisites: +/// - Foundational samples should be completed first. +/// +public static class Program +{ + private static async Task Main() + { + // Create the workflow + var workflow = WorkflowHelper.GetWorkflow(); + + // Create checkpoint manager + var checkpointManager = new CheckpointManager(); + var checkpoints = new List(); + + // Execute the workflow and save checkpoints + Checkpointed checkpointedRun = await InProcessExecution + .StreamAsync(workflow, NumberSignal.Init, checkpointManager) + .ConfigureAwait(false); + await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false)) + { + if (evt is ExecutorCompletedEvent executorCompletedEvt) + { + Console.WriteLine($"* Executor {executorCompletedEvt.ExecutorId} completed."); + } + + if (evt is SuperStepCompletedEvent superStepCompletedEvt) + { + // Checkpoints are automatically created at the end of each super step when a + // checkpoint manager is provided. You can store the checkpoint info for later use. + CheckpointInfo? checkpoint = superStepCompletedEvt.CompletionInfo!.Checkpoint; + if (checkpoint != null) + { + checkpoints.Add(checkpoint); + Console.WriteLine($"** Checkpoint created at step {checkpoints.Count}."); + } + } + + if (evt is WorkflowCompletedEvent workflowCompletedEvt) + { + Console.WriteLine($"Workflow completed with result: {workflowCompletedEvt.Data}"); + } + } + + if (checkpoints.Count == 0) + { + throw new InvalidOperationException("No checkpoints were created during the workflow execution."); + } + Console.WriteLine($"Number of checkpoints created: {checkpoints.Count}"); + + // Restoring from a checkpoint and resuming execution + var checkpointIndex = 5; + Console.WriteLine($"\n\nRestoring from the {checkpointIndex + 1}th checkpoint."); + CheckpointInfo savedCheckpoint = checkpoints[checkpointIndex]; + // Note that we are restoring the state directly to the same run instance. + await checkpointedRun.RestoreCheckpointAsync(savedCheckpoint, CancellationToken.None).ConfigureAwait(false); + await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false)) + { + if (evt is ExecutorCompletedEvent executorCompletedEvt) + { + Console.WriteLine($"* Executor {executorCompletedEvt.ExecutorId} completed."); + } + + if (evt is WorkflowCompletedEvent workflowCompletedEvt) + { + Console.WriteLine($"Workflow completed with result: {workflowCompletedEvt.Data}"); + } + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/WorkflowHelper.cs b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/WorkflowHelper.cs new file mode 100644 index 0000000000..aa4f5506ac --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointAndResume/WorkflowHelper.cs @@ -0,0 +1,165 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Agents.Workflows; +using Microsoft.Agents.Workflows.Reflection; + +namespace WorkflowCheckpointAndResumeSample; + +internal static class WorkflowHelper +{ + /// + /// Get a workflow that plays a number guessing game with checkpointing support. + /// The workflow consists of two executors that are connected in a feedback loop: + /// 1. GuessNumberExecutor: Makes a guess based on the current known bounds. + /// 2. JudgeExecutor: Evaluates the guess and provides feedback. + /// The workflow continues until the correct number is guessed. + /// + internal static Workflow GetWorkflow() + { + // Create the executors + GuessNumberExecutor guessNumberExecutor = new(1, 100); + JudgeExecutor judgeExecutor = new(42); + + // Build the workflow by connecting executors in a loop + var workflow = new WorkflowBuilder(guessNumberExecutor) + .AddEdge(guessNumberExecutor, judgeExecutor) + .AddEdge(judgeExecutor, guessNumberExecutor) + .Build(); + + return workflow; + } +} + +/// +/// Signals used for communication between GuessNumberExecutor and JudgeExecutor. +/// +internal enum NumberSignal +{ + Init, + Above, + Below, +} + +/// +/// Executor that makes a guess based on the current bounds. +/// +internal sealed class GuessNumberExecutor() : ReflectingExecutor("Guess"), IMessageHandler +{ + /// + /// The lower bound of the guessing range. + /// + public int LowerBound { get; private set; } + + /// + /// The upper bound of the guessing range. + /// + public int UpperBound { get; private set; } + + private const string StateKey = "GuessNumberExecutorState"; + + /// + /// Initializes a new instance of the class. + /// + /// The initial lower bound of the guessing range. + /// The initial upper bound of the guessing range. + public GuessNumberExecutor(int lowerBound, int upperBound) : this() + { + this.LowerBound = lowerBound; + this.UpperBound = upperBound; + } + + private int NextGuess => (this.LowerBound + this.UpperBound) / 2; + + public async ValueTask HandleAsync(NumberSignal message, IWorkflowContext context) + { + switch (message) + { + case NumberSignal.Init: + await context.SendMessageAsync(this.NextGuess).ConfigureAwait(false); + break; + case NumberSignal.Above: + this.UpperBound = this.NextGuess - 1; + await context.SendMessageAsync(this.NextGuess).ConfigureAwait(false); + break; + case NumberSignal.Below: + this.LowerBound = this.NextGuess + 1; + await context.SendMessageAsync(this.NextGuess).ConfigureAwait(false); + break; + } + } + + /// + /// Checkpoint the current state of the executor. + /// This must be overridden to save any state that is needed to resume the executor. + /// + protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) + { + return context.QueueStateUpdateAsync(StateKey, (this.LowerBound, this.UpperBound)); + } + + /// + /// Restore the state of the executor from a checkpoint. + /// This must be overridden to restore any state that was saved during checkpointing. + /// + protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) + { + (this.LowerBound, this.UpperBound) = await context.ReadStateAsync<(int, int)>(StateKey).ConfigureAwait(false); + } +} + +/// +/// Executor that judges the guess and provides feedback. +/// +internal sealed class JudgeExecutor() : ReflectingExecutor("Judge"), IMessageHandler +{ + private readonly int _targetNumber; + private int _tries = 0; + private const string StateKey = "JudgeExecutorState"; + + /// + /// Initializes a new instance of the class. + /// + /// The number to be guessed. + public JudgeExecutor(int targetNumber) : this() + { + this._targetNumber = targetNumber; + } + + public async ValueTask HandleAsync(int message, IWorkflowContext context) + { + this._tries++; + if (message == this._targetNumber) + { + await context.AddEventAsync(new WorkflowCompletedEvent($"{this._targetNumber} found in {this._tries} tries!")) + .ConfigureAwait(false); + } + else if (message < this._targetNumber) + { + await context.SendMessageAsync(NumberSignal.Below).ConfigureAwait(false); + } + else + { + await context.SendMessageAsync(NumberSignal.Above).ConfigureAwait(false); + } + } + + /// + /// Checkpoint the current state of the executor. + /// This must be overridden to save any state that is needed to resume the executor. + /// + protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) + { + return context.QueueStateUpdateAsync(StateKey, this._tries); + } + + /// + /// Restore the state of the executor from a checkpoint. + /// This must be overridden to restore any state that was saved during checkpointing. + /// + protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) + { + this._tries = await context.ReadStateAsync(StateKey).ConfigureAwait(false); + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Foundational/02_Streaming/02_Streaming.csproj b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/CheckpointWithHumanInTheLoop.csproj similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Foundational/02_Streaming/02_Streaming.csproj rename to dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/CheckpointWithHumanInTheLoop.csproj diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/Program.cs b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/Program.cs new file mode 100644 index 0000000000..167cc760d3 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/Program.cs @@ -0,0 +1,138 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Agents.Workflows; + +namespace WorkflowCheckpointWithHumanInTheLoopSample; + +/// +/// This sample demonstrates how to create a workflow with human-in-the-loop interaction and +/// checkpointing support. The workflow plays a number guessing game where the user provides +/// guesses based on feedback from the workflow. The workflow state is checkpointed at the end +/// of each super step, allowing it to be restored and resumed later. +/// Each InputPort request and response cycle takes two super steps: +/// 1. The InputPort sends a RequestInfoEvent to request input from the external world. +/// 2. The external world sends a response back to the InputPort. +/// Thus, two checkpoints are created for each human-in-the-loop interaction. +/// +/// +/// Pre-requisites: +/// - Foundational samples should be completed first. +/// - This sample builds upon the HumanInTheLoopBasic sample. It's recommended to go through that +/// sample first to understand the basics of human-in-the-loop workflows. +/// - This sample also builds upon the CheckpointAndResume sample. It's recommended to +/// go through that sample first to understand the basics of checkpointing and resuming workflows. +/// +public static class Program +{ + private static async Task Main() + { + // Create the workflow + var workflow = WorkflowHelper.GetWorkflow(); + + // Create checkpoint manager + var checkpointManager = new CheckpointManager(); + var checkpoints = new List(); + + // Execute the workflow and save checkpoints + Checkpointed checkpointedRun = await InProcessExecution + .StreamAsync(workflow, new SignalWithNumber(NumberSignal.Init), checkpointManager) + .ConfigureAwait(false); + await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false)) + { + switch (evt) + { + case RequestInfoEvent requestInputEvt: + // Handle `RequestInfoEvent` from the workflow + ExternalResponse response = HandleExternalRequest(requestInputEvt.Request); + await checkpointedRun.Run.SendResponseAsync(response).ConfigureAwait(false); + break; + case ExecutorCompletedEvent executorCompletedEvt: + Console.WriteLine($"* Executor {executorCompletedEvt.ExecutorId} completed."); + break; + case SuperStepCompletedEvent superStepCompletedEvt: + // Checkpoints are automatically created at the end of each super step when a + // checkpoint manager is provided. You can store the checkpoint info for later use. + CheckpointInfo? checkpoint = superStepCompletedEvt.CompletionInfo!.Checkpoint; + if (checkpoint != null) + { + checkpoints.Add(checkpoint); + Console.WriteLine($"** Checkpoint created at step {checkpoints.Count}."); + } + break; + case WorkflowCompletedEvent workflowCompletedEvt: + Console.WriteLine($"Workflow completed with result: {workflowCompletedEvt.Data}"); + break; + } + } + + if (checkpoints.Count == 0) + { + throw new InvalidOperationException("No checkpoints were created during the workflow execution."); + } + Console.WriteLine($"Number of checkpoints created: {checkpoints.Count}"); + + // Restoring from a checkpoint and resuming execution + var checkpointIndex = 1; + Console.WriteLine($"\n\nRestoring from the {checkpointIndex + 1}th checkpoint."); + CheckpointInfo savedCheckpoint = checkpoints[checkpointIndex]; + // Note that we are restoring the state directly to the same run instance. + await checkpointedRun.RestoreCheckpointAsync(savedCheckpoint, CancellationToken.None).ConfigureAwait(false); + await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false)) + { + switch (evt) + { + case RequestInfoEvent requestInputEvt: + // Handle `RequestInfoEvent` from the workflow + ExternalResponse response = HandleExternalRequest(requestInputEvt.Request); + await checkpointedRun.Run.SendResponseAsync(response).ConfigureAwait(false); + break; + case ExecutorCompletedEvent executorCompletedEvt: + Console.WriteLine($"* Executor {executorCompletedEvt.ExecutorId} completed."); + break; + case WorkflowCompletedEvent workflowCompletedEvt: + Console.WriteLine($"Workflow completed with result: {workflowCompletedEvt.Data}"); + break; + } + } + } + + private static ExternalResponse HandleExternalRequest(ExternalRequest request) + { + if (request.Port.Request == typeof(SignalWithNumber)) + { + var signal = (SignalWithNumber)request.Data; + switch (signal.Signal) + { + case NumberSignal.Init: + int initialGuess = ReadIntegerFromConsole("Please provide your initial guess: "); + return request.CreateResponse(initialGuess); + case NumberSignal.Above: + int lowerGuess = ReadIntegerFromConsole($"You previously guessed {signal.Number} too large. Please provide a new guess: "); + return request.CreateResponse(lowerGuess); + case NumberSignal.Below: + int higherGuess = ReadIntegerFromConsole($"You previously guessed {signal.Number} too small. Please provide a new guess: "); + return request.CreateResponse(higherGuess); + } + } + + throw new NotSupportedException($"Request {request.Port.Request} is not supported"); + } + + private static int ReadIntegerFromConsole(string prompt) + { + while (true) + { + Console.Write(prompt); + string? input = Console.ReadLine(); + if (int.TryParse(input, out int value)) + { + return value; + } + Console.WriteLine("Invalid input. Please enter a valid integer."); + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/WorkflowHelper.cs b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/WorkflowHelper.cs new file mode 100644 index 0000000000..a3e81914b7 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Checkpoint/CheckpointWithHumanInTheLoop/WorkflowHelper.cs @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Agents.Workflows; +using Microsoft.Agents.Workflows.Reflection; + +namespace WorkflowCheckpointWithHumanInTheLoopSample; + +internal static class WorkflowHelper +{ + /// + /// Get a workflow that plays a number guessing game with human-in-the-loop interaction. + /// An input port allows the external world to provide inputs to the workflow upon requests. + /// + internal static Workflow GetWorkflow() + { + // Create the executors + InputPort numberInputPort = InputPort.Create("GuessNumber"); + JudgeExecutor judgeExecutor = new(42); + + // Build the workflow by connecting executors in a loop + var workflow = new WorkflowBuilder(numberInputPort) + .AddEdge(numberInputPort, judgeExecutor) + .AddEdge(judgeExecutor, numberInputPort) + .Build(); + + return workflow; + } +} + +/// +/// Signals indicating if the guess was too high, too low, or an initial guess. +/// +internal enum NumberSignal +{ + Init, + Above, + Below, +} + +/// +/// Signals used for communication between guesses and the JudgeExecutor. +/// +internal sealed class SignalWithNumber +{ + public NumberSignal Signal { get; } + public int? Number { get; } + + public SignalWithNumber(NumberSignal signal, int? number = null) + { + this.Signal = signal; + this.Number = number; + } +} + +/// +/// Executor that judges the guess and provides feedback. +/// +internal sealed class JudgeExecutor() : ReflectingExecutor("Judge"), IMessageHandler +{ + private readonly int _targetNumber; + private int _tries = 0; + private const string StateKey = "JudgeExecutorState"; + + /// + /// Initializes a new instance of the class. + /// + /// The number to be guessed. + public JudgeExecutor(int targetNumber) : this() + { + this._targetNumber = targetNumber; + } + + public async ValueTask HandleAsync(int message, IWorkflowContext context) + { + this._tries++; + if (message == this._targetNumber) + { + await context.AddEventAsync(new WorkflowCompletedEvent($"{this._targetNumber} found in {this._tries} tries!")) + .ConfigureAwait(false); + } + else if (message < this._targetNumber) + { + await context.SendMessageAsync(new SignalWithNumber(NumberSignal.Below, message)).ConfigureAwait(false); + } + else + { + await context.SendMessageAsync(new SignalWithNumber(NumberSignal.Above, message)).ConfigureAwait(false); + } + } + + /// + /// Checkpoint the current state of the executor. + /// This must be overridden to save any state that is needed to resume the executor. + /// + protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken cancellation = default) + { + return context.QueueStateUpdateAsync(StateKey, this._tries); + } + + /// + /// Restore the state of the executor from a checkpoint. + /// This must be overridden to restore any state that was saved during checkpointing. + /// + protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellation = default) + { + this._tries = await context.ReadStateAsync(StateKey).ConfigureAwait(false); + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Concurrent/Program.cs b/dotnet/samples/GettingStarted/Workflows/Concurrent/Program.cs index 2c3e1594c6..b6e6cfcc00 100644 --- a/dotnet/samples/GettingStarted/Workflows/Concurrent/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Concurrent/Program.cs @@ -85,7 +85,7 @@ internal sealed class ConcurrentStartExecutor() : /// /// The user message to process /// Workflow context for accessing workflow services and adding events - /// + /// A task representing the asynchronous operation public async ValueTask HandleAsync(string message, IWorkflowContext context) { // Broadcast the message to all connected agents. Receiving agents will queue @@ -110,7 +110,7 @@ internal sealed class ConcurrentAggregationExecutor() : /// /// The message from the agent /// Workflow context for accessing workflow services and adding events - /// + /// A task representing the asynchronous operation public async ValueTask HandleAsync(ChatMessage message, IWorkflowContext context) { this._messages.Add(message); diff --git a/dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs b/dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs index 78e582dfc9..ffacf3123a 100644 --- a/dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Declarative/Program.cs @@ -92,13 +92,13 @@ internal sealed class Program await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false)) { - if (evt is ExecutorInvokeEvent executorInvoked) + if (evt is ExecutorInvokedEvent executorInvoked) { Debug.WriteLine($"EXECUTOR ENTER #{executorInvoked.ExecutorId}"); } - else if (evt is ExecutorCompleteEvent executorComplete) + else if (evt is ExecutorCompletedEvent executorCompleted) { - Debug.WriteLine($"EXECUTOR EXIT #{executorComplete.ExecutorId}"); + Debug.WriteLine($"EXECUTOR EXIT #{executorCompleted.ExecutorId}"); } if (evt is DeclarativeActionInvokeEvent actionInvoked) { diff --git a/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/HumanInTheLoopBasic.csproj b/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/HumanInTheLoopBasic.csproj new file mode 100644 index 0000000000..bbee070910 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/HumanInTheLoopBasic.csproj @@ -0,0 +1,16 @@ + + + + Exe + net9.0 + 12 + + enable + disable + + + + + + + \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/Program.cs b/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/Program.cs new file mode 100644 index 0000000000..07ffe8c06d --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/Program.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Threading.Tasks; +using Microsoft.Agents.Workflows; + +namespace WorkflowHumanInTheLoopBasicSample; + +/// +/// This sample introduces the concept of InputPort and ExternalRequest to enable +/// human-in-the-loop interaction scenarios. +/// An input port can be used as if it were an executor in the workflow graph. Upon receiving +/// a message, the input port generates an RequestInfoEvent that gets emitted to the external world. +/// The external world can then respond to the request by sending an ExternalResponse back to +/// the workflow. +/// The sample implements a simple number guessing game where the external user tries to guess +/// a pre-defined target number. The workflow consists of a single JudgeExecutor that judges +/// the user's guesses and provides feedback. +/// +/// +/// Pre-requisites: +/// - Foundational samples should be completed first. +/// +public static class Program +{ + private static async Task Main() + { + // Create the workflow + var workflow = WorkflowHelper.GetWorkflow(); + + // Execute the workflow + StreamingRun handle = await InProcessExecution.StreamAsync(workflow, NumberSignal.Init).ConfigureAwait(false); + await foreach (WorkflowEvent evt in handle.WatchStreamAsync().ConfigureAwait(false)) + { + switch (evt) + { + case RequestInfoEvent requestInputEvt: + // Handle `RequestInfoEvent` from the workflow + ExternalResponse response = HandleExternalRequest(requestInputEvt.Request); + await handle.SendResponseAsync(response).ConfigureAwait(false); + break; + + case WorkflowCompletedEvent workflowCompleteEvt: + // The workflow has completed successfully + Console.WriteLine($"Workflow completed with result: {workflowCompleteEvt.Data}"); + return; + } + } + } + + private static ExternalResponse HandleExternalRequest(ExternalRequest request) + { + if (request.Port.Request == typeof(NumberSignal)) + { + var signal = (NumberSignal)request.Data; + switch (signal) + { + case NumberSignal.Init: + int initialGuess = ReadIntegerFromConsole("Please provide your initial guess: "); + return request.CreateResponse(initialGuess); + case NumberSignal.Above: + int lowerGuess = ReadIntegerFromConsole("You previously guessed too large. Please provide a new guess: "); + return request.CreateResponse(lowerGuess); + case NumberSignal.Below: + int higherGuess = ReadIntegerFromConsole("You previously guessed too small. Please provide a new guess: "); + return request.CreateResponse(higherGuess); + } + } + + throw new NotSupportedException($"Request {request.Port.Request} is not supported"); + } + + private static int ReadIntegerFromConsole(string prompt) + { + while (true) + { + Console.Write(prompt); + string? input = Console.ReadLine(); + if (int.TryParse(input, out int value)) + { + return value; + } + Console.WriteLine("Invalid input. Please enter a valid integer."); + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/WorkflowHelper.cs b/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/WorkflowHelper.cs new file mode 100644 index 0000000000..7e11a4b39d --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/HumanInTheLoop/HumanInTheLoopBasic/WorkflowHelper.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Threading.Tasks; +using Microsoft.Agents.Workflows; +using Microsoft.Agents.Workflows.Reflection; + +namespace WorkflowHumanInTheLoopBasicSample; + +internal static class WorkflowHelper +{ + /// + /// Get a workflow that plays a number guessing game with human-in-the-loop interaction. + /// An input port allows the external world to provide inputs to the workflow upon requests. + /// + internal static Workflow GetWorkflow() + { + // Create the executors + InputPort numberInputPort = InputPort.Create("GuessNumber"); + JudgeExecutor judgeExecutor = new(42); + + // Build the workflow by connecting executors in a loop + var workflow = new WorkflowBuilder(numberInputPort) + .AddEdge(numberInputPort, judgeExecutor) + .AddEdge(judgeExecutor, numberInputPort) + .Build(); + + return workflow; + } +} + +/// +/// Signals used for communication between guesses and the JudgeExecutor. +/// +internal enum NumberSignal +{ + Init, + Above, + Below, +} + +/// +/// Executor that judges the guess and provides feedback. +/// +internal sealed class JudgeExecutor() : ReflectingExecutor("Judge"), IMessageHandler +{ + private readonly int _targetNumber; + private int _tries = 0; + + /// + /// Initializes a new instance of the class. + /// + /// The number to be guessed. + public JudgeExecutor(int targetNumber) : this() + { + this._targetNumber = targetNumber; + } + + public async ValueTask HandleAsync(int message, IWorkflowContext context) + { + this._tries++; + if (message == this._targetNumber) + { + await context.AddEventAsync(new WorkflowCompletedEvent($"{this._targetNumber} found in {this._tries} tries!")) + .ConfigureAwait(false); + } + else if (message < this._targetNumber) + { + await context.SendMessageAsync(NumberSignal.Below).ConfigureAwait(false); + } + else + { + await context.SendMessageAsync(NumberSignal.Above).ConfigureAwait(false); + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/Loop/Loop.csproj b/dotnet/samples/GettingStarted/Workflows/Loop/Loop.csproj new file mode 100644 index 0000000000..75b48d7b65 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Loop/Loop.csproj @@ -0,0 +1,16 @@ + + + + Exe + net9.0 + 12 + + enable + disable + + + + + + + \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/Workflows/Loop/Program.cs b/dotnet/samples/GettingStarted/Workflows/Loop/Program.cs new file mode 100644 index 0000000000..97a21b8268 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/Loop/Program.cs @@ -0,0 +1,139 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Threading.Tasks; +using Microsoft.Agents.Workflows; +using Microsoft.Agents.Workflows.Reflection; + +namespace WorkflowLoopSample; + +/// +/// This sample demonstrates a simple number guessing game using a workflow with looping behavior. +/// +/// The workflow consists of two executors that are connected in a feedback loop: +/// 1. GuessNumberExecutor: Makes a guess based on the current known bounds. +/// 2. JudgeExecutor: Evaluates the guess and provides feedback. +/// The workflow continues until the correct number is guessed. +/// +/// +/// Pre-requisites: +/// - Foundational samples should be completed first. +/// +public static class Program +{ + private static async Task Main() + { + // Create the executors + GuessNumberExecutor guessNumberExecutor = new(1, 100); + JudgeExecutor judgeExecutor = new(42); + + // Build the workflow by connecting executors in a loop + var workflow = new WorkflowBuilder(guessNumberExecutor) + .AddEdge(guessNumberExecutor, judgeExecutor) + .AddEdge(judgeExecutor, guessNumberExecutor) + .Build(); + + // Execute the workflow + StreamingRun run = await InProcessExecution.StreamAsync(workflow, NumberSignal.Init).ConfigureAwait(false); + await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false)) + { + if (evt is WorkflowCompletedEvent workflowCompleteEvt) + { + Console.WriteLine($"Result: {workflowCompleteEvt}"); + } + } + } +} + +/// +/// Signals used for communication between GuessNumberExecutor and JudgeExecutor. +/// +internal enum NumberSignal +{ + Init, + Above, + Below, +} + +/// +/// Executor that makes a guess based on the current bounds. +/// +internal sealed class GuessNumberExecutor : ReflectingExecutor, IMessageHandler +{ + /// + /// The lower bound of the guessing range. + /// + public int LowerBound { get; private set; } + + /// + /// The upper bound of the guessing range. + /// + public int UpperBound { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The initial lower bound of the guessing range. + /// The initial upper bound of the guessing range. + public GuessNumberExecutor(int lowerBound, int upperBound) + { + this.LowerBound = lowerBound; + this.UpperBound = upperBound; + } + + private int NextGuess => (this.LowerBound + this.UpperBound) / 2; + + public async ValueTask HandleAsync(NumberSignal message, IWorkflowContext context) + { + switch (message) + { + case NumberSignal.Init: + await context.SendMessageAsync(this.NextGuess).ConfigureAwait(false); + break; + case NumberSignal.Above: + this.UpperBound = this.NextGuess - 1; + await context.SendMessageAsync(this.NextGuess).ConfigureAwait(false); + break; + case NumberSignal.Below: + this.LowerBound = this.NextGuess + 1; + await context.SendMessageAsync(this.NextGuess).ConfigureAwait(false); + break; + } + } +} + +/// +/// Executor that judges the guess and provides feedback. +/// +internal sealed class JudgeExecutor : ReflectingExecutor, IMessageHandler +{ + private readonly int _targetNumber; + private int _tries = 0; + + /// + /// Initializes a new instance of the class. + /// + /// The number to be guessed. + public JudgeExecutor(int targetNumber) + { + this._targetNumber = targetNumber; + } + + public async ValueTask HandleAsync(int message, IWorkflowContext context) + { + this._tries++; + if (message == this._targetNumber) + { + await context.AddEventAsync(new WorkflowCompletedEvent($"{this._targetNumber} found in {this._tries} tries!")) + .ConfigureAwait(false); + } + else if (message < this._targetNumber) + { + await context.SendMessageAsync(NumberSignal.Below).ConfigureAwait(false); + } + else + { + await context.SendMessageAsync(NumberSignal.Above).ConfigureAwait(false); + } + } +} diff --git a/dotnet/samples/GettingStarted/Workflows/README.md b/dotnet/samples/GettingStarted/Workflows/README.md index 13dda87e01..55ffa1a2df 100644 --- a/dotnet/samples/GettingStarted/Workflows/README.md +++ b/dotnet/samples/GettingStarted/Workflows/README.md @@ -6,24 +6,40 @@ The getting started with workflow samples demonstrate the fundamental concepts a ### Foundational Concepts - Start Here -Please begin with the [Foundational](./Foundational) samples in order. These three samples introduce the core concepts of executors, edges, agents in workflows, streaming, and workflow construction. +Please begin with the [Foundational](./_Foundational) samples in order. These three samples introduce the core concepts of executors, edges, agents in workflows, streaming, and workflow construction. + +> The folder name starts with an underscore (`_Foundational`) to ensure it appears first in the explorer view. | Sample | Concepts | |--------|----------| -| [Executors and Edges](./Foundational/01_ExecutorsAndEdges) | Minimal workflow with basic executors and edges | -| [Streaming](./Foundational/02_Streaming) | Extends workflows with event streaming | -| [Agents](./Foundational/03_AgentsInWorkflows) | Use agents in workflows | +| [Executors and Edges](./_Foundational/01_ExecutorsAndEdges) | Minimal workflow with basic executors and edges | +| [Streaming](./_Foundational/02_Streaming) | Extends workflows with event streaming | +| [Agents](./_Foundational/03_AgentsInWorkflows) | Use agents in workflows | Once completed, please proceed to other samples listed below. > Note that you don't need to follow a strict order after the foundational samples. However, some samples build upon concepts from previous ones, so it's beneficial to be aware of the dependencies. +### Agents + +| Sample | Concepts | +|--------|----------| +| [Foundry Agents in Workflows](./Agents/FoundryAgent) | Demonstrates using Azure Foundry Agents within a workflow | +| [Custom Agent Executors](./Agents/CustomAgentExecutors) | Shows how to create a custom agent executor for more complex scenarios | +| [Workflow as an Agent](./Agents/WorkflowAsAgent) | Illustrates how to encapsulate a workflow as an agent | + ### Concurrent Execution | Sample | Concepts | |--------|----------| | [Fan-Out and Fan-In](./Concurrent) | Introduces parallel processing with fan-out and fan-in patterns | +### Loop + +| Sample | Concepts | +|--------|----------| +| [Looping](./Loop) | Shows how to create a loop within a workflow | + ### Workflow Shared States | Sample | Concepts | @@ -40,10 +56,22 @@ Once completed, please proceed to other samples listed below. > These 3 samples build upon each other. It's recommended to explore them in sequence to fully grasp the concepts. - ### Declarative Workflows | Sample | Concepts | |--------|----------| | [DeclarativeWorkflow](./DeclarativeWorkflow) | Demonstrates execution of declartive workflows. | +### Checkpointing + +| Sample | Concepts | +|--------|----------| +| [Checkpoint and Resume](./Checkpoint/CheckpointAndResume) | Introduces checkpoints for saving and restoring workflow state for time travel purposes | +| [Checkpoint and Rehydrate](./Checkpoint/CheckpointAndRehydrate) | Demonstrates hydrating a new workflow instance from a saved checkpoint | +| [Checkpoint with Human-in-the-Loop](./Checkpoint/CheckpointWithHumanInTheLoop) | Combines checkpointing with human-in-the-loop interactions | + +### Human-in-the-Loop + +| Sample | Concepts | +|--------|----------| +| [Basic Human-in-the-Loop](./HumanInTheLoop/HumanIntheLoopBasic) | Introduces human-in-the-loop interaction using input ports and external requests | diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj b/dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj new file mode 100644 index 0000000000..bbee070910 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/01_ExecutorsAndEdges.csproj @@ -0,0 +1,16 @@ + + + + Exe + net9.0 + 12 + + enable + disable + + + + + + + \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/Workflows/Foundational/01_ExecutorsAndEdges/Program.cs b/dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/Program.cs similarity index 98% rename from dotnet/samples/GettingStarted/Workflows/Foundational/01_ExecutorsAndEdges/Program.cs rename to dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/Program.cs index 00682a60ef..85fd569605 100644 --- a/dotnet/samples/GettingStarted/Workflows/Foundational/01_ExecutorsAndEdges/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/Program.cs @@ -35,7 +35,7 @@ public static class Program Run run = await InProcessExecution.RunAsync(workflow, "Hello, World!"); foreach (WorkflowEvent evt in run.NewEvents) { - if (evt is ExecutorCompleteEvent executorComplete) + if (evt is ExecutorCompletedEvent executorComplete) { Console.WriteLine($"{executorComplete.ExecutorId}: {executorComplete.Data}"); } diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/02_Streaming.csproj b/dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/02_Streaming.csproj new file mode 100644 index 0000000000..bbee070910 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/02_Streaming.csproj @@ -0,0 +1,16 @@ + + + + Exe + net9.0 + 12 + + enable + disable + + + + + + + \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/Workflows/Foundational/02_Streaming/Program.cs b/dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/Program.cs similarity index 95% rename from dotnet/samples/GettingStarted/Workflows/Foundational/02_Streaming/Program.cs rename to dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/Program.cs index 01bd694a5d..ce0db4a7fd 100644 --- a/dotnet/samples/GettingStarted/Workflows/Foundational/02_Streaming/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/Program.cs @@ -34,9 +34,9 @@ public static class Program StreamingRun run = await InProcessExecution.StreamAsync(workflow, "Hello, World!"); await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false)) { - if (evt is ExecutorCompleteEvent executorComplete) + if (evt is ExecutorCompletedEvent executorCompleted) { - Console.WriteLine($"{executorComplete.ExecutorId}: {executorComplete.Data}"); + Console.WriteLine($"{executorCompleted.ExecutorId}: {executorCompleted.Data}"); } } } diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj b/dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj new file mode 100644 index 0000000000..b1f90ee600 --- /dev/null +++ b/dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/03_AgentsInWorkflows.csproj @@ -0,0 +1,24 @@ + + + + Exe + net9.0 + 12 + + enable + disable + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/samples/GettingStarted/Workflows/Foundational/03_AgentsInWorkflows/Program.cs b/dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/Program.cs similarity index 100% rename from dotnet/samples/GettingStarted/Workflows/Foundational/03_AgentsInWorkflows/Program.cs rename to dotnet/samples/GettingStarted/Workflows/_Foundational/03_AgentsInWorkflows/Program.cs diff --git a/dotnet/src/Microsoft.Agents.Workflows/Executor.cs b/dotnet/src/Microsoft.Agents.Workflows/Executor.cs index c3e704ed69..8b813c08c6 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/Executor.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/Executor.cs @@ -67,7 +67,7 @@ public abstract class Executor : IIdentified /// An exception is generated while handling the message. public async ValueTask ExecuteAsync(object message, Type messageType, IWorkflowContext context) { - await context.AddEventAsync(new ExecutorInvokeEvent(this.Id, message)).ConfigureAwait(false); + await context.AddEventAsync(new ExecutorInvokedEvent(this.Id, message)).ConfigureAwait(false); CallResult? result = await this.Router.RouteMessageAsync(message, context, requireRoute: true) .ConfigureAwait(false); @@ -75,7 +75,7 @@ public abstract class Executor : IIdentified ExecutorEvent executionResult; if (result == null || result.IsSuccess) { - executionResult = new ExecutorCompleteEvent(this.Id, result?.Result); + executionResult = new ExecutorCompletedEvent(this.Id, result?.Result); } else { diff --git a/dotnet/src/Microsoft.Agents.Workflows/ExecutorCompleteEvent.cs b/dotnet/src/Microsoft.Agents.Workflows/ExecutorCompletedEvent.cs similarity index 76% rename from dotnet/src/Microsoft.Agents.Workflows/ExecutorCompleteEvent.cs rename to dotnet/src/Microsoft.Agents.Workflows/ExecutorCompletedEvent.cs index 01a4de058e..43472af7e0 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/ExecutorCompleteEvent.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/ExecutorCompletedEvent.cs @@ -7,4 +7,4 @@ namespace Microsoft.Agents.Workflows; /// /// The unique identifier of the executor that has completed. /// The result produced by the executor upon completion, or null if no result is available. -public sealed class ExecutorCompleteEvent(string executorId, object? result) : ExecutorEvent(executorId, data: result); +public sealed class ExecutorCompletedEvent(string executorId, object? result) : ExecutorEvent(executorId, data: result); diff --git a/dotnet/src/Microsoft.Agents.Workflows/ExecutorInvokeEvent.cs b/dotnet/src/Microsoft.Agents.Workflows/ExecutorInvokedEvent.cs similarity index 73% rename from dotnet/src/Microsoft.Agents.Workflows/ExecutorInvokeEvent.cs rename to dotnet/src/Microsoft.Agents.Workflows/ExecutorInvokedEvent.cs index 022245924b..444da140e3 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/ExecutorInvokeEvent.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/ExecutorInvokedEvent.cs @@ -7,4 +7,4 @@ namespace Microsoft.Agents.Workflows; /// /// The unique identifier of the executor being invoked. /// The invocation message. -public sealed class ExecutorInvokeEvent(string executorId, object message) : ExecutorEvent(executorId, data: message); +public sealed class ExecutorInvokedEvent(string executorId, object message) : ExecutorEvent(executorId, data: message); diff --git a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowTest.cs b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowTest.cs index 701bdf04d1..fc7ee92cbf 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowTest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.Declarative.UnitTests/DeclarativeWorkflowTest.cs @@ -208,20 +208,20 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow private void AssertExecutionCount(int expectedCount) { - Assert.Equal(expectedCount + 2, this.WorkflowEventCounts[typeof(ExecutorInvokeEvent)]); - Assert.Equal(expectedCount + 2, this.WorkflowEventCounts[typeof(ExecutorCompleteEvent)]); + Assert.Equal(expectedCount + 2, this.WorkflowEventCounts[typeof(ExecutorInvokedEvent)]); + Assert.Equal(expectedCount + 2, this.WorkflowEventCounts[typeof(ExecutorCompletedEvent)]); } private void AssertNotExecuted(string executorId) { - Assert.DoesNotContain(this.WorkflowEvents.OfType(), e => e.ExecutorId == executorId); - Assert.DoesNotContain(this.WorkflowEvents.OfType(), e => e.ExecutorId == executorId); + Assert.DoesNotContain(this.WorkflowEvents.OfType(), e => e.ExecutorId == executorId); + Assert.DoesNotContain(this.WorkflowEvents.OfType(), e => e.ExecutorId == executorId); } private void AssertExecuted(string executorId) { - Assert.Contains(this.WorkflowEvents.OfType(), e => e.ExecutorId == executorId); - Assert.Contains(this.WorkflowEvents.OfType(), e => e.ExecutorId == executorId); + Assert.Contains(this.WorkflowEvents.OfType(), e => e.ExecutorId == executorId); + Assert.Contains(this.WorkflowEvents.OfType(), e => e.ExecutorId == executorId); } private void AssertMessage(string message) @@ -244,7 +244,7 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow this.WorkflowEvents = run.WatchStreamAsync().ToEnumerable().ToImmutableList(); foreach (WorkflowEvent workflowEvent in this.WorkflowEvents) { - if (workflowEvent is ExecutorInvokeEvent invokeEvent) + if (workflowEvent is ExecutorInvokedEvent invokeEvent) { DeclarativeExecutorResult? message = invokeEvent.Data as DeclarativeExecutorResult; this.Output.WriteLine($"EXEC: {invokeEvent.ExecutorId} << {message?.ExecutorId ?? "?"} [{message?.Result ?? "-"}]"); diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/01_Simple_Workflow_Sequential.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/01_Simple_Workflow_Sequential.cs index 229e6c3b39..169c805348 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/01_Simple_Workflow_Sequential.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/01_Simple_Workflow_Sequential.cs @@ -28,9 +28,9 @@ internal static class Step1EntryPoint await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false)) { - if (evt is ExecutorCompleteEvent executorComplete) + if (evt is ExecutorCompletedEvent executorCompleted) { - writer.WriteLine($"{executorComplete.ExecutorId}: {executorComplete.Data}"); + writer.WriteLine($"{executorCompleted.ExecutorId}: {executorCompleted.Data}"); } } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/01a_Simple_Workflow_Sequential.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/01a_Simple_Workflow_Sequential.cs index cf78998bf5..62b7ead009 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/01a_Simple_Workflow_Sequential.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/01a_Simple_Workflow_Sequential.cs @@ -17,9 +17,9 @@ internal static class Step1aEntryPoint foreach (WorkflowEvent evt in run.NewEvents) { - if (evt is ExecutorCompleteEvent executorComplete) + if (evt is ExecutorCompletedEvent executorCompleted) { - writer.WriteLine($"{executorComplete.ExecutorId}: {executorComplete.Data}"); + writer.WriteLine($"{executorCompleted.ExecutorId}: {executorCompleted.Data}"); } } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/02_Simple_Workflow_Condition.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/02_Simple_Workflow_Condition.cs index 25a726c893..9a5cbf64c0 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/02_Simple_Workflow_Condition.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/02_Simple_Workflow_Condition.cs @@ -39,8 +39,8 @@ internal static class Step2EntryPoint string workflowResult = workflowCompleteEvt.Data!.ToString()!; writer.WriteLine($"Result: {workflowResult}"); return workflowResult; - case ExecutorCompleteEvent executorCompleteEvt: - writer.WriteLine($"'{executorCompleteEvt.ExecutorId}: {executorCompleteEvt.Data}"); + case ExecutorCompletedEvent executorCompletedEvt: + writer.WriteLine($"'{executorCompletedEvt.ExecutorId}: {executorCompletedEvt.Data}"); break; } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/03_Simple_Workflow_Loop.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/03_Simple_Workflow_Loop.cs index d886f2a6c7..b56efaff50 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/03_Simple_Workflow_Loop.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/03_Simple_Workflow_Loop.cs @@ -37,8 +37,8 @@ internal static class Step3EntryPoint string workflowResult = workflowCompleteEvt.Data!.ToString()!; writer.WriteLine($"Result: {workflowResult}"); return workflowResult; - case ExecutorCompleteEvent executorCompleteEvt: - writer.WriteLine($"'{executorCompleteEvt.ExecutorId}: {executorCompleteEvt.Data}"); + case ExecutorCompletedEvent executorCompletedEvt: + writer.WriteLine($"'{executorCompletedEvt.ExecutorId}: {executorCompletedEvt.Data}"); break; } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/04_Simple_Workflow_ExternalRequest.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/04_Simple_Workflow_ExternalRequest.cs index 1f078ecd29..5f94475cb3 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/04_Simple_Workflow_ExternalRequest.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/04_Simple_Workflow_ExternalRequest.cs @@ -46,8 +46,8 @@ internal static class Step4EntryPoint string workflowResult = workflowCompleteEvt.Data!.ToString()!; writer.WriteLine($"Result: {workflowResult}"); return workflowResult; - case ExecutorCompleteEvent executorCompleteEvt: - writer.WriteLine($"'{executorCompleteEvt.ExecutorId}: {executorCompleteEvt.Data}"); + case ExecutorCompletedEvent executorCompletedEvt: + writer.WriteLine($"'{executorCompletedEvt.ExecutorId}: {executorCompletedEvt.Data}"); break; } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/05_Simple_Workflow_Checkpointing.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/05_Simple_Workflow_Checkpointing.cs index 7658871f79..9093645810 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/05_Simple_Workflow_Checkpointing.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/05_Simple_Workflow_Checkpointing.cs @@ -85,7 +85,7 @@ internal static class Step5EntryPoint string workflowResult = workflowCompleteEvt.Data!.ToString()!; writer.WriteLine($"Result: {workflowResult}"); return workflowResult; - case ExecutorCompleteEvent executorCompleteEvt: + case ExecutorCompletedEvent executorCompleteEvt: writer.WriteLine($"'{executorCompleteEvt.ExecutorId}: {executorCompleteEvt.Data}"); break; } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs index df818a932c..47e5128cd9 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs @@ -37,9 +37,9 @@ internal static class Step6EntryPoint await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false)) { - if (evt is ExecutorCompleteEvent executorComplete) + if (evt is ExecutorCompletedEvent executorCompleted) { - Debug.WriteLine($"{executorComplete.ExecutorId}: {executorComplete.Data}"); + Debug.WriteLine($"{executorCompleted.ExecutorId}: {executorCompleted.Data}"); } else if (evt is AgentRunUpdateEvent update) { From 57d09afe044cfaddb2465910aba5c1b68546645f Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Wed, 10 Sep 2025 14:11:42 -0700 Subject: [PATCH 28/32] Python: Context providers abstraction and Mem0 implementation (#631) * Added context provider abstractions * Added mem0 implementation * Example and small fixes * Added unit tests for agent * Added unit tests for mem0 provider * Updated README * Small doc updates * Update python/packages/mem0/agent_framework_mem0/_provider.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Small fixes in tests * Renaming based on PR feedback * Small fixes * Added tests for AggregateContextProvider * Small improvements * More improvements based on PR feedback * Small constant update * Added more examples * Added README for Mem0 examples * Small updates to API * Updated initialization logic * Updates for context manager * Updated Context class * Dependency update * Revert changes * Fixed tests --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Chris <66376200+crickman@users.noreply.github.com> --- python/.env.example | 2 + .../packages/main/agent_framework/__init__.py | 1 + .../packages/main/agent_framework/_agents.py | 55 +- .../packages/main/agent_framework/_clients.py | 4 + .../packages/main/agent_framework/_memory.py | 188 + .../main/agent_framework/mem0/__init__.py | 24 + .../main/agent_framework/mem0/__init__.pyi | 5 + .../openai/_responses_client.py | 2 +- python/packages/main/pyproject.toml | 2 +- python/packages/main/tests/main/conftest.py | 12 +- .../packages/main/tests/main/test_agents.py | 212 +- .../packages/main/tests/main/test_memory.py | 302 ++ python/packages/mem0/LICENSE | 21 + python/packages/mem0/README.md | 21 + .../mem0/agent_framework_mem0/__init__.py | 15 + .../mem0/agent_framework_mem0/_provider.py | 180 + python/packages/mem0/pyproject.toml | 94 + python/packages/mem0/tests/__init__.py | 0 .../packages/mem0/tests/test_cross_package.py | 28 + python/packages/mem0/tests/test_provider.py | 481 ++ python/pyproject.toml | 6 +- .../context_providers/mem0/README.md | 51 + .../context_providers/mem0/mem0_basic.py | 72 + .../context_providers/mem0/mem0_threads.py | 164 + python/uv.lock | 4139 +++++++++-------- 25 files changed, 4166 insertions(+), 1915 deletions(-) create mode 100644 python/packages/main/agent_framework/_memory.py create mode 100644 python/packages/main/agent_framework/mem0/__init__.py create mode 100644 python/packages/main/agent_framework/mem0/__init__.pyi create mode 100644 python/packages/main/tests/main/test_memory.py create mode 100644 python/packages/mem0/LICENSE create mode 100644 python/packages/mem0/README.md create mode 100644 python/packages/mem0/agent_framework_mem0/__init__.py create mode 100644 python/packages/mem0/agent_framework_mem0/_provider.py create mode 100644 python/packages/mem0/pyproject.toml create mode 100644 python/packages/mem0/tests/__init__.py create mode 100644 python/packages/mem0/tests/test_cross_package.py create mode 100644 python/packages/mem0/tests/test_provider.py create mode 100644 python/samples/getting_started/context_providers/mem0/README.md create mode 100644 python/samples/getting_started/context_providers/mem0/mem0_basic.py create mode 100644 python/samples/getting_started/context_providers/mem0/mem0_threads.py diff --git a/python/.env.example b/python/.env.example index ecbdef60a8..54cb688999 100644 --- a/python/.env.example +++ b/python/.env.example @@ -15,3 +15,5 @@ AGENT_FRAMEWORK_OTLP_ENDPOINT="http://localhost:4317/" AGENT_FRAMEWORK_ENABLE_OTEL=true AGENT_FRAMEWORK_ENABLE_SENSITIVE_DATA=true AGENT_FRAMEWORK_WORKFLOW_ENABLE_OTEL=true +# Mem0 +MEM0_API_KEY="" diff --git a/python/packages/main/agent_framework/__init__.py b/python/packages/main/agent_framework/__init__.py index da8d8ee482..ca82fa412d 100644 --- a/python/packages/main/agent_framework/__init__.py +++ b/python/packages/main/agent_framework/__init__.py @@ -12,6 +12,7 @@ from ._agents import * # noqa: F403 from ._clients import * # noqa: F403 from ._logging import * # noqa: F403 from ._mcp import * # noqa: F403 +from ._memory import * # noqa: F403 from ._threads import * # noqa: F403 from ._tools import * # noqa: F403 from ._types import * # noqa: F403 diff --git a/python/packages/main/agent_framework/_agents.py b/python/packages/main/agent_framework/_agents.py index 2b63ca4ef0..0981112521 100644 --- a/python/packages/main/agent_framework/_agents.py +++ b/python/packages/main/agent_framework/_agents.py @@ -3,7 +3,6 @@ import sys from collections.abc import AsyncIterable, Callable, MutableMapping, Sequence from contextlib import AbstractAsyncContextManager, AsyncExitStack -from itertools import chain from typing import Any, ClassVar, Literal, Protocol, TypeVar, runtime_checkable from uuid import uuid4 @@ -12,6 +11,7 @@ from pydantic import BaseModel, Field, PrivateAttr from ._clients import BaseChatClient, ChatClientProtocol from ._logging import get_logger from ._mcp import MCPTool +from ._memory import AggregateContextProvider, Context, ContextProvider from ._pydantic import AFBaseModel from ._threads import AgentThread, ChatMessageStore, deserialize_thread_state, thread_on_new_messages from ._tools import FUNCTION_INVOKING_CHAT_CLIENT_MARKER, ToolProtocol @@ -137,12 +137,13 @@ class BaseAgent(AFBaseModel): name: The name of the agent, can be None. description: The description of the agent. display_name: The display name of the agent, which is either the name or id. - + context_providers: The collection of multiple context providers to include during agent invocation. """ id: str = Field(default_factory=lambda: str(uuid4())) name: str | None = None description: str | None = None + context_providers: AggregateContextProvider | None = None async def _notify_thread_of_new_messages( self, thread: AgentThread, new_messages: ChatMessage | Sequence[ChatMessage] @@ -214,6 +215,7 @@ class ChatAgent(BaseAgent): user: str | None = None, additional_properties: dict[str, Any] | None = None, chat_message_store_factory: Callable[[], ChatMessageStore] | None = None, + context_providers: ContextProvider | list[ContextProvider] | AggregateContextProvider | None = None, **kwargs: Any, ) -> None: """Create a ChatAgent. @@ -248,6 +250,7 @@ class ChatAgent(BaseAgent): additional_properties: additional properties to include in the request. chat_message_store_factory: factory function to create an instance of ChatMessageStore. If not provided, the default in-memory store will be used. + context_providers: The collection of multiple context providers to include during agent invocation. kwargs: any additional keyword arguments. Unused, can be used by subclasses of this Agent. """ @@ -258,6 +261,8 @@ class ChatAgent(BaseAgent): kwargs.update(additional_properties or {}) + aggregate_context_providers = self._prepare_context_providers(context_providers) + # We ignore the MCP Servers here and store them separately, # we add their functions to the tools list at runtime normalized_tools = [] if tools is None else tools if isinstance(tools, list) else [tools] @@ -266,6 +271,7 @@ class ChatAgent(BaseAgent): args: dict[str, Any] = { "chat_client": chat_client, "chat_message_store_factory": chat_message_store_factory, + "context_providers": aggregate_context_providers, "chat_options": ChatOptions( ai_model_id=model, frequency_penalty=frequency_penalty, @@ -301,12 +307,16 @@ class ChatAgent(BaseAgent): async def __aenter__(self) -> "Self": """Async context manager entry. - If either the chat_client or the local_mcp_tools are context managers, + If any of the chat_client, local_mcp_tools, or context_providers are context managers, they will be entered into the async exit stack to ensure proper cleanup. This list might be extended in the future. """ - for context_manager in chain([self.chat_client], self._local_mcp_tools): + context_managers = [self.chat_client, *self._local_mcp_tools] + if self.context_providers: + context_managers.append(self.context_providers) + + for context_manager in context_managers: if isinstance(context_manager, AbstractAsyncContextManager): await self._async_exit_stack.enter_async_context(context_manager) return self @@ -388,7 +398,10 @@ class ChatAgent(BaseAgent): will only be passed to functions that are called. """ input_messages = self._normalize_messages(messages) - thread, thread_messages = await self._prepare_thread_and_messages(thread=thread, input_messages=input_messages) + context = await self.context_providers.model_invoking(input_messages) if self.context_providers else None + thread, thread_messages = await self._prepare_thread_and_messages( + thread=thread, context=context, input_messages=input_messages + ) agent_name = self._get_agent_name() # Resolve final tool list (runtime provided tools + local MCP server tools) @@ -441,6 +454,10 @@ class ChatAgent(BaseAgent): await self._notify_thread_of_new_messages(thread, input_messages) await self._notify_thread_of_new_messages(thread, response.messages) + if self.context_providers: + await self.context_providers.thread_created(response.conversation_id) + await self.context_providers.messages_adding(thread.service_thread_id, input_messages + response.messages) + return AgentRunResponse( messages=response.messages, response_id=response.response_id, @@ -509,7 +526,10 @@ class ChatAgent(BaseAgent): """ input_messages = self._normalize_messages(messages) - thread, thread_messages = await self._prepare_thread_and_messages(thread=thread, input_messages=input_messages) + context = await self.context_providers.model_invoking(input_messages) if self.context_providers else None + thread, thread_messages = await self._prepare_thread_and_messages( + thread=thread, context=context, input_messages=input_messages + ) agent_name = self._get_agent_name() response_updates: list[ChatResponseUpdate] = [] @@ -575,6 +595,10 @@ class ChatAgent(BaseAgent): await self._notify_thread_of_new_messages(thread, input_messages) await self._notify_thread_of_new_messages(thread, response.messages) + if self.context_providers: + await self.context_providers.thread_created(response.conversation_id) + await self.context_providers.messages_adding(thread.service_thread_id, input_messages + response.messages) + def get_new_thread(self) -> AgentThread: message_store: ChatMessageStore | None = None @@ -617,12 +641,14 @@ class ChatAgent(BaseAgent): self, *, thread: AgentThread | None, + context: Context | None, input_messages: list[ChatMessage] | None = None, ) -> tuple[AgentThread, list[ChatMessage]]: """Prepare the messages for agent execution. Args: thread: The conversation thread. + context: Context to include in messages. input_messages: Messages to process. Returns: @@ -636,6 +662,8 @@ class ChatAgent(BaseAgent): messages: list[ChatMessage] = [] if self.instructions: messages.append(ChatMessage(role=Role.SYSTEM, text=self.instructions)) + if context and context.contents: + messages.append(ChatMessage(role=Role.SYSTEM, contents=context.contents)) if thread.message_store: messages.extend(await thread.message_store.list_messages() or []) messages.extend(input_messages or []) @@ -658,3 +686,18 @@ class ChatAgent(BaseAgent): def _get_agent_name(self) -> str: return self.name or "UnnamedAgent" + + def _prepare_context_providers( + self, + context_providers: ContextProvider | list[ContextProvider] | AggregateContextProvider | None = None, + ) -> AggregateContextProvider | None: + if not context_providers: + return None + + if isinstance(context_providers, AggregateContextProvider): + return context_providers + + if isinstance(context_providers, ContextProvider): + return AggregateContextProvider([context_providers]) + + return AggregateContextProvider(context_providers) diff --git a/python/packages/main/agent_framework/_clients.py b/python/packages/main/agent_framework/_clients.py index 2a6735fe0b..4d5775c96b 100644 --- a/python/packages/main/agent_framework/_clients.py +++ b/python/packages/main/agent_framework/_clients.py @@ -9,6 +9,7 @@ from pydantic import BaseModel, Field from ._logging import get_logger from ._mcp import MCPTool +from ._memory import AggregateContextProvider, ContextProvider from ._pydantic import AFBaseModel from ._threads import ChatMessageStore from ._tools import ToolProtocol @@ -463,6 +464,7 @@ class BaseChatClient(AFBaseModel, ABC): | list[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]] | None = None, chat_message_store_factory: Callable[[], ChatMessageStore] | None = None, + context_providers: ContextProvider | list[ContextProvider] | AggregateContextProvider | None = None, **kwargs: Any, ) -> "ChatAgent": """Create an agent with the given name and instructions. @@ -473,6 +475,7 @@ class BaseChatClient(AFBaseModel, ABC): tools: Optional list of tools to associate with the agent. chat_message_store_factory: Factory function to create an instance of ChatMessageStore. If not provided, the default in-memory store will be used. + context_providers: Context providers to include during agent invocation. **kwargs: Additional keyword arguments to pass to the agent. See ChatAgent for all the available options. @@ -487,6 +490,7 @@ class BaseChatClient(AFBaseModel, ABC): instructions=instructions, tools=tools, chat_message_store_factory=chat_message_store_factory, + context_providers=context_providers, **kwargs, ) diff --git a/python/packages/main/agent_framework/_memory.py b/python/packages/main/agent_framework/_memory.py new file mode 100644 index 0000000000..cdb3f2ff61 --- /dev/null +++ b/python/packages/main/agent_framework/_memory.py @@ -0,0 +1,188 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +import sys +from abc import ABC, abstractmethod +from collections.abc import MutableSequence, Sequence +from contextlib import AsyncExitStack +from types import TracebackType + +from ._pydantic import AFBaseModel +from ._types import ChatMessage, Contents + +if sys.version_info >= (3, 11): + from typing import Self # pragma: no cover +else: + from typing_extensions import Self # pragma: no cover + +# region Context + + +class Context(AFBaseModel): + """A class containing any context that should be provided to the AI model as supplied by an ContextProvider. + + Each ContextProvider has the ability to provide its own context for each invocation. + The Context class contains the additional context supplied by the ContextProvider. + This context will be combined with context supplied by other providers before being passed to the AI model. + This context is per invocation, and will not be stored as part of the chat history. + """ + + contents: list[Contents] | None = None + """ + Any content to pass to the AI model in addition to any other prompts + that it may already have (in the case of an agent), or chat history that may already exist. + """ + + +# region ContextProvider + + +class ContextProvider(AFBaseModel, ABC): + """Base class for all context providers. + + A context provider is a component that can be used to enhance the AI's context management. + It can listen to changes in the conversation and provide additional context to the AI model + just before invocation. + """ + + async def thread_created(self, thread_id: str | None) -> None: + """Called just after a new thread is created. + + Implementers can use this method to do any operations required at the creation of a new thread. + For example, checking long term storage for any data that is relevant + to the current session based on the input text. + + Args: + thread_id: The ID of the new thread. + """ + pass + + async def messages_adding(self, thread_id: str | None, new_messages: ChatMessage | Sequence[ChatMessage]) -> None: + """Called just before messages are added to the chat by any participant. + + Inheritors can use this method to update their context based on new messages. + + Args: + thread_id: The ID of the thread for the new message. + new_messages: New messages to add. + """ + pass + + @abstractmethod + async def model_invoking(self, messages: ChatMessage | MutableSequence[ChatMessage]) -> Context: + """Called just before the Model/Agent/etc. is invoked. + + Implementers can load any additional context required at this time, + and they should return any context that should be passed to the agent. + + Args: + messages: The most recent messages that the agent is being invoked with. + """ + pass + + async def __aenter__(self) -> "Self": + """Async context manager entry. + + Override this method to perform any setup operations when the context provider is entered. + + Returns: + Self for chaining. + """ + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + """Async context manager exit. + + Override this method to perform any cleanup operations when the context provider is exited. + + Args: + exc_type: Exception type if an exception occurred, None otherwise. + exc_val: Exception value if an exception occurred, None otherwise. + exc_tb: Exception traceback if an exception occurred, None otherwise. + """ + pass + + +# region AggregateContextProvider + + +class AggregateContextProvider(ContextProvider): + """A ContextProvider that contains multiple context providers. + + It delegates events to multiple context providers and aggregates responses from those events before returning. + """ + + providers: list[ContextProvider] + """List of registered context providers.""" + + def __init__(self, context_providers: Sequence[ContextProvider] | None = None) -> None: + """Initialize AggregateContextProvider with context providers. + + Args: + context_providers: Context providers to add. + """ + super().__init__(providers=list(context_providers or [])) # type: ignore + self._exit_stack: AsyncExitStack | None = None + + def add(self, context_provider: ContextProvider) -> None: + """Adds new context provider. + + Args: + context_provider: Context provider to add. + """ + self.providers.append(context_provider) + + async def thread_created(self, thread_id: str | None = None) -> None: + await asyncio.gather(*[x.thread_created(thread_id) for x in self.providers]) + + async def messages_adding(self, thread_id: str | None, new_messages: ChatMessage | Sequence[ChatMessage]) -> None: + await asyncio.gather(*[x.messages_adding(thread_id, new_messages) for x in self.providers]) + + async def model_invoking(self, messages: ChatMessage | MutableSequence[ChatMessage]) -> Context: + sub_contexts = await asyncio.gather(*[x.model_invoking(messages) for x in self.providers]) + combined_context = Context() + # Flatten the list of lists and filter out None values + all_contents = [] + for ctx in sub_contexts: + if ctx.contents: + all_contents.extend(ctx.contents) + + combined_context.contents = all_contents if all_contents else None + return combined_context + + async def __aenter__(self) -> "Self": + """Enter async context manager and set up all providers. + + Returns: + Self for chaining. + """ + self._exit_stack = AsyncExitStack() + await self._exit_stack.__aenter__() + + # Enter all context providers + for provider in self.providers: + await self._exit_stack.enter_async_context(provider) + + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + """Exit async context manager and clean up all providers. + + Args: + exc_type: Exception type if an exception occurred, None otherwise. + exc_val: Exception value if an exception occurred, None otherwise. + exc_tb: Exception traceback if an exception occurred, None otherwise. + """ + if self._exit_stack is not None: + await self._exit_stack.__aexit__(exc_type, exc_val, exc_tb) + self._exit_stack = None diff --git a/python/packages/main/agent_framework/mem0/__init__.py b/python/packages/main/agent_framework/mem0/__init__.py new file mode 100644 index 0000000000..e2028a27e5 --- /dev/null +++ b/python/packages/main/agent_framework/mem0/__init__.py @@ -0,0 +1,24 @@ +# Copyright (c) Microsoft. All rights reserved. + +import importlib +from typing import Any + +PACKAGE_NAME = "agent_framework_mem0" +PACKAGE_EXTRA = "mem0" +_IMPORTS = ["__version__", "Mem0Provider"] + + +def __getattr__(name: str) -> Any: + if name in _IMPORTS: + try: + return getattr(importlib.import_module(PACKAGE_NAME), name) + except ModuleNotFoundError as exc: + raise ModuleNotFoundError( + f"The '{PACKAGE_EXTRA}' extra is not installed, " + f"please do `pip install agent-framework[{PACKAGE_EXTRA}]`" + ) from exc + raise AttributeError(f"Module {PACKAGE_NAME} has no attribute {name}.") + + +def __dir__() -> list[str]: + return _IMPORTS diff --git a/python/packages/main/agent_framework/mem0/__init__.pyi b/python/packages/main/agent_framework/mem0/__init__.pyi new file mode 100644 index 0000000000..53fb1720f2 --- /dev/null +++ b/python/packages/main/agent_framework/mem0/__init__.pyi @@ -0,0 +1,5 @@ +# Copyright (c) Microsoft. All rights reserved. + +from agent_framework_mem0 import Mem0Provider, __version__ + +__all__ = ["Mem0Provider", "__version__"] diff --git a/python/packages/main/agent_framework/openai/_responses_client.py b/python/packages/main/agent_framework/openai/_responses_client.py index 61c40eaf79..9bc646c5f5 100644 --- a/python/packages/main/agent_framework/openai/_responses_client.py +++ b/python/packages/main/agent_framework/openai/_responses_client.py @@ -455,7 +455,7 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient): ) response_tools.append( WebSearchToolParam( - type="web_search", + type="web_search_preview", user_location=WebSearchUserLocation( type="approximate", city=location.get("city", None), diff --git a/python/packages/main/pyproject.toml b/python/packages/main/pyproject.toml index 28746c286a..2d06652356 100644 --- a/python/packages/main/pyproject.toml +++ b/python/packages/main/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "openai>=1.103.0", + "openai>=1.99.0", "pydantic>=2.11.7", "pydantic-settings>=2.10.1", "typing-extensions>=4.14.0", diff --git a/python/packages/main/tests/main/conftest.py b/python/packages/main/tests/main/conftest.py index 3391a3266e..001510f877 100644 --- a/python/packages/main/tests/main/conftest.py +++ b/python/packages/main/tests/main/conftest.py @@ -151,11 +151,19 @@ class MockBaseChatClient(BaseChatClient): logger.debug(f"Running base chat client inner, with: {messages=}, {chat_options=}, {kwargs=}") if not self.run_responses: return ChatResponse(messages=ChatMessage(role="assistant", text=f"test response - {messages[0].text}")) + + response = self.run_responses.pop(0) + if chat_options.tool_choice == "none": return ChatResponse( - messages=ChatMessage(role="assistant", text="I broke out of the function invocation loop...") + messages=ChatMessage( + role="assistant", + text="I broke out of the function invocation loop...", + ), + conversation_id=response.conversation_id, ) - return self.run_responses.pop(0) + + return response @override async def _inner_get_streaming_response( diff --git a/python/packages/main/tests/main/test_agents.py b/python/packages/main/tests/main/test_agents.py index 7af3252708..9737810b60 100644 --- a/python/packages/main/tests/main/test_agents.py +++ b/python/packages/main/tests/main/test_agents.py @@ -1,6 +1,6 @@ # Copyright (c) Microsoft. All rights reserved. -from collections.abc import AsyncIterable +from collections.abc import AsyncIterable, MutableSequence, Sequence from uuid import uuid4 from pytest import raises @@ -15,10 +15,12 @@ from agent_framework import ( ChatMessage, ChatMessageList, ChatResponse, + Contents, HostedCodeInterpreterTool, Role, TextContent, ) +from agent_framework._memory import AggregateContextProvider, Context, ContextProvider from agent_framework.exceptions import AgentExecutionException @@ -100,6 +102,7 @@ async def test_chat_client_agent_prepare_thread_and_messages(chat_client: ChatCl _, result_messages = await agent._prepare_thread_and_messages( # type: ignore[reportPrivateUsage] thread=thread, + context=Context(), input_messages=[ChatMessage(role=Role.USER, text="Test")], ) @@ -184,3 +187,210 @@ async def test_chat_client_agent_author_name_is_used_from_response(chat_client_b result = await agent.run("Hello") assert result.text == "test response" assert result.messages[0].author_name == "TestAuthor" + + +# Mock context provider for testing +class MockContextProvider(ContextProvider): + context_contents: list[Contents] | None = None + thread_created_called: bool = False + messages_adding_called: bool = False + model_invoking_called: bool = False + thread_created_thread_id: str | None = None + messages_adding_thread_id: str | None = None + new_messages: list[ChatMessage] = [] + + def __init__(self, contents: list[Contents] | None = None) -> None: + super().__init__() + self.context_contents = contents + self.thread_created_called = False + self.messages_adding_called = False + self.model_invoking_called = False + self.thread_created_thread_id = None + self.messages_adding_thread_id = None + self.new_messages = [] + + async def thread_created(self, thread_id: str | None) -> None: + self.thread_created_called = True + self.thread_created_thread_id = thread_id + + async def messages_adding(self, thread_id: str | None, new_messages: ChatMessage | Sequence[ChatMessage]) -> None: + self.messages_adding_called = True + self.messages_adding_thread_id = thread_id + if isinstance(new_messages, ChatMessage): + self.new_messages.append(new_messages) + else: + self.new_messages.extend(new_messages) + + async def model_invoking(self, messages: ChatMessage | MutableSequence[ChatMessage]) -> Context: + self.model_invoking_called = True + return Context(contents=self.context_contents) + + +async def test_chat_agent_context_providers_model_invoking(chat_client: ChatClientProtocol) -> None: + """Test that context providers' model_invoking is called during agent run.""" + mock_provider = MockContextProvider(contents=[TextContent("Test context instructions")]) + agent = ChatAgent(chat_client=chat_client, context_providers=mock_provider) + + await agent.run("Hello") + + assert mock_provider.model_invoking_called + + +async def test_chat_agent_context_providers_thread_created(chat_client_base: ChatClientProtocol) -> None: + """Test that context providers' thread_created is called during agent run.""" + mock_provider = MockContextProvider() + chat_client_base.run_responses = [ + ChatResponse( + messages=[ChatMessage(role=Role.ASSISTANT, contents=[TextContent("test response")])], + conversation_id="test-thread-id", + ) + ] + + agent = ChatAgent(chat_client=chat_client_base, context_providers=mock_provider) + + await agent.run("Hello") + + assert mock_provider.thread_created_called + assert mock_provider.thread_created_thread_id == "test-thread-id" + + +async def test_chat_agent_context_providers_messages_adding(chat_client: ChatClientProtocol) -> None: + """Test that context providers' messages_adding is called during agent run.""" + mock_provider = MockContextProvider() + agent = ChatAgent(chat_client=chat_client, context_providers=mock_provider) + + await agent.run("Hello") + + assert mock_provider.messages_adding_called + # Should be called with both input and response messages + assert len(mock_provider.new_messages) >= 2 + + +async def test_chat_agent_context_instructions_in_messages(chat_client: ChatClientProtocol) -> None: + """Test that AI context instructions are included in messages.""" + mock_provider = MockContextProvider(contents=[TextContent("Context-specific instructions")]) + agent = ChatAgent(chat_client=chat_client, instructions="Agent instructions", context_providers=mock_provider) + + # We need to test the _prepare_thread_and_messages method directly + context = Context(contents=[TextContent("Context-specific instructions")]) + _, messages = await agent._prepare_thread_and_messages( # type: ignore[reportPrivateUsage] + thread=None, context=context, input_messages=[ChatMessage(role=Role.USER, text="Hello")] + ) + + # Should have agent instructions, context instructions, and user message + assert len(messages) == 3 + assert messages[0].role == Role.SYSTEM + assert messages[0].text == "Agent instructions" + assert messages[1].role == Role.SYSTEM + assert messages[1].text == "Context-specific instructions" + assert messages[2].role == Role.USER + assert messages[2].text == "Hello" + + +async def test_chat_agent_context_instructions_without_agent_instructions(chat_client: ChatClientProtocol) -> None: + """Test that AI context instructions work when agent has no instructions.""" + agent = ChatAgent(chat_client=chat_client) # No instructions + context = Context(contents=[TextContent("Context-only instructions")]) + + _, messages = await agent._prepare_thread_and_messages( # type: ignore[reportPrivateUsage] + thread=None, context=context, input_messages=[ChatMessage(role=Role.USER, text="Hello")] + ) + + # Should have context instructions and user message only + assert len(messages) == 2 + assert messages[0].role == Role.SYSTEM + assert messages[0].text == "Context-only instructions" + assert messages[1].role == Role.USER + assert messages[1].text == "Hello" + + +async def test_chat_agent_no_context_instructions(chat_client: ChatClientProtocol) -> None: + """Test behavior when AI context has no instructions.""" + agent = ChatAgent(chat_client=chat_client, instructions="Agent instructions") + context = Context() # No instructions + + _, messages = await agent._prepare_thread_and_messages( # type: ignore[reportPrivateUsage] + thread=None, context=context, input_messages=[ChatMessage(role=Role.USER, text="Hello")] + ) + + # Should have agent instructions and user message only + assert len(messages) == 2 + assert messages[0].role == Role.SYSTEM + assert messages[0].text == "Agent instructions" + assert messages[1].role == Role.USER + assert messages[1].text == "Hello" + + +async def test_chat_agent_run_stream_context_providers(chat_client: ChatClientProtocol) -> None: + """Test that context providers work with run_stream method.""" + mock_provider = MockContextProvider(contents=[TextContent("Stream context instructions")]) + agent = ChatAgent(chat_client=chat_client, context_providers=mock_provider) + + # Collect all stream updates + updates: list[AgentRunResponseUpdate] = [] + async for update in agent.run_stream("Hello"): + updates.append(update) + + # Verify context provider was called + assert mock_provider.model_invoking_called + assert mock_provider.thread_created_called + assert mock_provider.messages_adding_called + + +async def test_chat_agent_multiple_context_providers(chat_client: ChatClientProtocol) -> None: + """Test that multiple context providers work together.""" + provider1 = MockContextProvider(contents=[TextContent("First provider instructions")]) + provider2 = MockContextProvider(contents=[TextContent("Second provider instructions")]) + + agent = ChatAgent(chat_client=chat_client, context_providers=[provider1, provider2]) + + await agent.run("Hello") + + # Both providers should be called + assert provider1.model_invoking_called + assert provider1.thread_created_called + assert provider1.messages_adding_called + + assert provider2.model_invoking_called + assert provider2.thread_created_called + assert provider2.messages_adding_called + + +async def test_chat_agent_aggregate_context_provider_combines_instructions() -> None: + """Test that AggregateContextProvider combines instructions from multiple providers.""" + provider1 = MockContextProvider(contents=[TextContent("First instruction")]) + provider2 = MockContextProvider(contents=[TextContent("Second instruction")]) + + aggregate = AggregateContextProvider() + aggregate.providers.append(provider1) + aggregate.providers.append(provider2) + + # Test model_invoking combines instructions + result = await aggregate.model_invoking([ChatMessage(role=Role.USER, text="Test")]) + + assert result.contents + assert isinstance(result.contents[0], TextContent) + assert isinstance(result.contents[1], TextContent) + assert result.contents[0].text == "First instruction" + assert result.contents[1].text == "Second instruction" + + +async def test_chat_agent_context_providers_with_thread_service_id(chat_client_base: ChatClientProtocol) -> None: + """Test context providers with service-managed thread.""" + mock_provider = MockContextProvider() + chat_client_base.run_responses = [ + ChatResponse( + messages=[ChatMessage(role=Role.ASSISTANT, contents=[TextContent("test response")])], + conversation_id="service-thread-123", + ) + ] + + agent = ChatAgent(chat_client=chat_client_base, context_providers=mock_provider) + + # Use existing service-managed thread + thread = AgentThread(service_thread_id="existing-thread-id") + await agent.run("Hello", thread=thread) + + # messages_adding should be called with the service thread ID from response + assert mock_provider.messages_adding_called + assert mock_provider.messages_adding_thread_id == "service-thread-123" # Updated thread ID from response diff --git a/python/packages/main/tests/main/test_memory.py b/python/packages/main/tests/main/test_memory.py new file mode 100644 index 0000000000..5c823066a2 --- /dev/null +++ b/python/packages/main/tests/main/test_memory.py @@ -0,0 +1,302 @@ +# Copyright (c) Microsoft. All rights reserved. + +from collections.abc import MutableSequence, Sequence +from unittest.mock import AsyncMock, Mock + +from agent_framework import ChatMessage, Contents, Role, TextContent +from agent_framework._memory import AggregateContextProvider, Context, ContextProvider + + +class MockContextProvider(ContextProvider): + """Mock ContextProvider for testing.""" + + context_contents: list[Contents] | None = None + thread_created_called: bool = False + messages_adding_called: bool = False + model_invoking_called: bool = False + thread_created_thread_id: str | None = None + messages_adding_thread_id: str | None = None + messages_adding_new_messages: ChatMessage | Sequence[ChatMessage] | None = None + model_invoking_messages: ChatMessage | MutableSequence[ChatMessage] | None = None + + def __init__(self, context_contents: list[Contents] | None = None) -> None: + super().__init__() + self.context_contents = context_contents + self.thread_created_called = False + self.messages_adding_called = False + self.model_invoking_called = False + self.thread_created_thread_id = None + self.messages_adding_thread_id = None + self.messages_adding_new_messages = None + self.model_invoking_messages = None + + async def thread_created(self, thread_id: str | None) -> None: + """Track thread_created calls.""" + self.thread_created_called = True + self.thread_created_thread_id = thread_id + + async def messages_adding(self, thread_id: str | None, new_messages: ChatMessage | Sequence[ChatMessage]) -> None: + """Track messages_adding calls.""" + self.messages_adding_called = True + self.messages_adding_thread_id = thread_id + self.messages_adding_new_messages = new_messages + + async def model_invoking(self, messages: ChatMessage | MutableSequence[ChatMessage]) -> Context: + """Track model_invoking calls and return context.""" + self.model_invoking_called = True + self.model_invoking_messages = messages + context = Context() + context.contents = self.context_contents + return context + + +class TestAggregateContextProvider: + """Tests for AggregateContextProvider class.""" + + def test_init_with_no_providers(self) -> None: + """Test initialization with no providers.""" + aggregate = AggregateContextProvider() + assert aggregate.providers == [] + + def test_init_with_none_providers(self) -> None: + """Test initialization with None providers.""" + aggregate = AggregateContextProvider(None) + assert aggregate.providers == [] + + def test_init_with_providers(self) -> None: + """Test initialization with providers.""" + provider1 = MockContextProvider([TextContent("instructions1")]) + provider2 = MockContextProvider([TextContent("instructions1")]) + providers = [provider1, provider2] + + aggregate = AggregateContextProvider(providers) + assert len(aggregate.providers) == 2 + assert aggregate.providers[0] is provider1 + assert aggregate.providers[1] is provider2 + + def test_add_provider(self) -> None: + """Test adding a provider.""" + aggregate = AggregateContextProvider() + provider = MockContextProvider([TextContent("instructions")]) + + aggregate.add(provider) + assert len(aggregate.providers) == 1 + assert aggregate.providers[0] is provider + + def test_add_multiple_providers(self) -> None: + """Test adding multiple providers.""" + aggregate = AggregateContextProvider() + provider1 = MockContextProvider([TextContent("instructions1")]) + provider2 = MockContextProvider([TextContent("instructions2")]) + + aggregate.add(provider1) + aggregate.add(provider2) + + assert len(aggregate.providers) == 2 + assert aggregate.providers[0] is provider1 + assert aggregate.providers[1] is provider2 + + async def test_thread_created_with_no_providers(self) -> None: + """Test thread_created with no providers.""" + aggregate = AggregateContextProvider() + + # Should not raise an exception + await aggregate.thread_created("thread-123") + + async def test_thread_created_with_providers(self) -> None: + """Test thread_created calls all providers.""" + provider1 = MockContextProvider([TextContent("instructions1")]) + provider2 = MockContextProvider([TextContent("instructions2")]) + aggregate = AggregateContextProvider([provider1, provider2]) + + thread_id = "thread-123" + await aggregate.thread_created(thread_id) + + assert provider1.thread_created_called + assert provider1.thread_created_thread_id == thread_id + assert provider2.thread_created_called + assert provider2.thread_created_thread_id == thread_id + + async def test_thread_created_with_none_thread_id(self) -> None: + """Test thread_created with None thread_id.""" + provider = MockContextProvider([TextContent("instructions")]) + aggregate = AggregateContextProvider([provider]) + + await aggregate.thread_created(None) + + assert provider.thread_created_called + assert provider.thread_created_thread_id is None + + async def test_messages_adding_with_no_providers(self) -> None: + """Test messages_adding with no providers.""" + aggregate = AggregateContextProvider() + message = ChatMessage(text="Hello", role=Role.USER) + + # Should not raise an exception + await aggregate.messages_adding("thread-123", message) + + async def test_messages_adding_with_single_message(self) -> None: + """Test messages_adding with a single message.""" + provider1 = MockContextProvider([TextContent("instructions1")]) + provider2 = MockContextProvider([TextContent("instructions2")]) + aggregate = AggregateContextProvider([provider1, provider2]) + + thread_id = "thread-123" + message = ChatMessage(text="Hello", role=Role.USER) + await aggregate.messages_adding(thread_id, message) + + assert provider1.messages_adding_called + assert provider1.messages_adding_thread_id == thread_id + assert provider1.messages_adding_new_messages == message + assert provider2.messages_adding_called + assert provider2.messages_adding_thread_id == thread_id + assert provider2.messages_adding_new_messages == message + + async def test_messages_adding_with_message_sequence(self) -> None: + """Test messages_adding with a sequence of messages.""" + provider = MockContextProvider([TextContent("instructions")]) + aggregate = AggregateContextProvider([provider]) + + thread_id = "thread-123" + messages = [ + ChatMessage(text="Hello", role=Role.USER), + ChatMessage(text="Hi there", role=Role.ASSISTANT), + ] + await aggregate.messages_adding(thread_id, messages) + + assert provider.messages_adding_called + assert provider.messages_adding_thread_id == thread_id + assert provider.messages_adding_new_messages == messages + + async def test_model_invoking_with_no_providers(self) -> None: + """Test model_invoking with no providers.""" + aggregate = AggregateContextProvider() + message = ChatMessage(text="Hello", role=Role.USER) + + context = await aggregate.model_invoking(message) + + assert isinstance(context, Context) + assert not context.contents + + async def test_model_invoking_with_single_provider(self) -> None: + """Test model_invoking with a single provider.""" + provider = MockContextProvider([TextContent("Test instructions")]) + aggregate = AggregateContextProvider([provider]) + + message = ChatMessage(text="Hello", role=Role.USER) + context = await aggregate.model_invoking(message) + + assert provider.model_invoking_called + assert provider.model_invoking_messages == message + assert isinstance(context, Context) + + assert context.contents + assert isinstance(context.contents[0], TextContent) + assert context.contents[0].text == "Test instructions" + + async def test_model_invoking_with_multiple_providers(self) -> None: + """Test model_invoking combines contexts from multiple providers.""" + provider1 = MockContextProvider([TextContent("Instructions 1")]) + provider2 = MockContextProvider([TextContent("Instructions 2")]) + provider3 = MockContextProvider([TextContent("Instructions 3")]) + aggregate = AggregateContextProvider([provider1, provider2, provider3]) + + messages = [ChatMessage(text="Hello", role=Role.USER)] + context = await aggregate.model_invoking(messages) + + assert provider1.model_invoking_called + assert provider1.model_invoking_messages == messages + assert provider2.model_invoking_called + assert provider2.model_invoking_messages == messages + assert provider3.model_invoking_called + assert provider3.model_invoking_messages == messages + + assert isinstance(context, Context) + + assert context.contents + assert isinstance(context.contents[0], TextContent) + assert isinstance(context.contents[1], TextContent) + assert isinstance(context.contents[2], TextContent) + assert context.contents[0].text == "Instructions 1" + assert context.contents[1].text == "Instructions 2" + assert context.contents[2].text == "Instructions 3" + + async def test_model_invoking_with_none_instructions(self) -> None: + """Test model_invoking filters out None instructions.""" + provider1 = MockContextProvider([TextContent("Instructions 1")]) + provider2 = MockContextProvider(None) # None instructions + provider3 = MockContextProvider([TextContent("Instructions 3")]) + aggregate = AggregateContextProvider([provider1, provider2, provider3]) + + message = ChatMessage(text="Hello", role=Role.USER) + context = await aggregate.model_invoking(message) + + assert isinstance(context, Context) + assert context.contents + assert isinstance(context.contents[0], TextContent) + assert isinstance(context.contents[1], TextContent) + assert context.contents[0].text == "Instructions 1" + assert context.contents[1].text == "Instructions 3" + + async def test_model_invoking_with_all_none_instructions(self) -> None: + """Test model_invoking when all providers return None instructions.""" + provider1 = MockContextProvider(None) + provider2 = MockContextProvider(None) + aggregate = AggregateContextProvider([provider1, provider2]) + + message = ChatMessage(text="Hello", role=Role.USER) + context = await aggregate.model_invoking(message) + + assert isinstance(context, Context) + assert not context.contents + + async def test_model_invoking_with_mutable_sequence(self) -> None: + """Test model_invoking with MutableSequence of messages.""" + provider = MockContextProvider([TextContent("Test instructions")]) + aggregate = AggregateContextProvider([provider]) + + messages = [ChatMessage(text="Hello", role=Role.USER)] + context = await aggregate.model_invoking(messages) + + assert provider.model_invoking_called + assert provider.model_invoking_messages == messages + assert isinstance(context, Context) + assert context.contents + assert isinstance(context.contents[0], TextContent) + assert context.contents[0].text == "Test instructions" + + async def test_async_methods_concurrent_execution(self) -> None: + """Test that async methods execute providers concurrently.""" + # Use AsyncMock to verify concurrent execution + provider1 = Mock(spec=ContextProvider) + provider1.thread_created = AsyncMock() + provider1.messages_adding = AsyncMock() + provider1.model_invoking = AsyncMock(return_value=Context(contents=[TextContent("Test 1")])) + + provider2 = Mock(spec=ContextProvider) + provider2.thread_created = AsyncMock() + provider2.messages_adding = AsyncMock() + provider2.model_invoking = AsyncMock(return_value=Context(contents=[TextContent("Test 2")])) + + aggregate = AggregateContextProvider([provider1, provider2]) + + # Test thread_created + await aggregate.thread_created("thread-123") + provider1.thread_created.assert_called_once_with("thread-123") + provider2.thread_created.assert_called_once_with("thread-123") + + # Test messages_adding + message = ChatMessage(text="Hello", role=Role.USER) + await aggregate.messages_adding("thread-123", message) + provider1.messages_adding.assert_called_once_with("thread-123", message) + provider2.messages_adding.assert_called_once_with("thread-123", message) + + # Test model_invoking + context = await aggregate.model_invoking(message) + provider1.model_invoking.assert_called_once_with(message) + provider2.model_invoking.assert_called_once_with(message) + assert context.contents + assert isinstance(context.contents[0], TextContent) + assert isinstance(context.contents[1], TextContent) + assert context.contents[0].text == "Test 1" + assert context.contents[1].text == "Test 2" diff --git a/python/packages/mem0/LICENSE b/python/packages/mem0/LICENSE new file mode 100644 index 0000000000..9e841e7a26 --- /dev/null +++ b/python/packages/mem0/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/python/packages/mem0/README.md b/python/packages/mem0/README.md new file mode 100644 index 0000000000..10784d337f --- /dev/null +++ b/python/packages/mem0/README.md @@ -0,0 +1,21 @@ +# Get Started with Microsoft Agent Framework Mem0 + +Please install this package as the extra for `agent-framework`: + +```bash +pip install agent-framework[mem0] +``` + +## Memory Context Provider + +The Mem0 context provider enables persistent memory capabilities for your agents, allowing them to remember user preferences and conversation context across different sessions and threads. + +### Basic Usage Example + +See the [Mem0 basic example](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/context_providers/mem0/mem0_basic.py) which demonstrates: + +- Setting up an agent with Mem0 context provider +- Teaching the agent user preferences +- Retrieving information using remembered context across new threads +- Persistent memory + diff --git a/python/packages/mem0/agent_framework_mem0/__init__.py b/python/packages/mem0/agent_framework_mem0/__init__.py new file mode 100644 index 0000000000..256e15356c --- /dev/null +++ b/python/packages/mem0/agent_framework_mem0/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) Microsoft. All rights reserved. + +import importlib.metadata + +from ._provider import Mem0Provider + +try: + __version__ = importlib.metadata.version(__name__) +except importlib.metadata.PackageNotFoundError: + __version__ = "0.0.0" # Fallback for development mode + +__all__ = [ + "Mem0Provider", + "__version__", +] diff --git a/python/packages/mem0/agent_framework_mem0/_provider.py b/python/packages/mem0/agent_framework_mem0/_provider.py new file mode 100644 index 0000000000..7fb2bbbf65 --- /dev/null +++ b/python/packages/mem0/agent_framework_mem0/_provider.py @@ -0,0 +1,180 @@ +# Copyright (c) Microsoft. All rights reserved. + +import sys +from collections.abc import MutableSequence, Sequence +from typing import Any, Final + +from agent_framework import ChatMessage, Context, ContextProvider, TextContent +from agent_framework.exceptions import ServiceInitializationError +from pydantic import PrivateAttr + +if sys.version_info >= (3, 11): + from typing import Self # pragma: no cover +else: + from typing_extensions import Self # pragma: no cover + + +DEFAULT_CONTEXT_PROMPT: Final[str] = "## Memories\nConsider the following memories when answering user questions:" + + +class Mem0Provider(ContextProvider): + api_key: str | None = None + application_id: str | None = None + agent_id: str | None = None + thread_id: str | None = None + user_id: str | None = None + scope_to_per_operation_thread_id: bool = False + context_prompt: str = DEFAULT_CONTEXT_PROMPT + # Use Any to avoid forward reference issues with AsyncMemoryClient + mem0_client: Any = None + + _should_close_client: bool = PrivateAttr(default=False) # Track whether we should close client connection + + def __init__( + self, + api_key: str | None = None, + application_id: str | None = None, + agent_id: str | None = None, + thread_id: str | None = None, + user_id: str | None = None, + scope_to_per_operation_thread_id: bool = False, + context_prompt: str = DEFAULT_CONTEXT_PROMPT, + mem0_client: Any = None, + ) -> None: + """Initializes a new instance of the Mem0Provider class. + + Args: + api_key: The API key for authenticating with the Mem0 API. If not + provided, it will attempt to use the MEM0_API_KEY environment variable. + application_id: The application ID for scoping memories or None. + agent_id: The agent ID for scoping memories or None. + thread_id: The thread ID for scoping memories or None. + user_id: The user ID for scoping memories or None. + scope_to_per_operation_thread_id: Whether to scope memories to per-operation thread ID. + context_prompt: The prompt to prepend to retrieved memories. + mem0_client: A pre-created Mem0 MemoryClient or None to create a default client. + """ + should_close_client = False + if mem0_client is None: + from mem0 import AsyncMemoryClient + + mem0_client = AsyncMemoryClient(api_key=api_key) + should_close_client = True + + super().__init__( + api_key=api_key, # type: ignore[reportCallIssue] + application_id=application_id, # type: ignore[reportCallIssue] + agent_id=agent_id, # type: ignore[reportCallIssue] + thread_id=thread_id, # type: ignore[reportCallIssue] + user_id=user_id, # type: ignore[reportCallIssue] + scope_to_per_operation_thread_id=scope_to_per_operation_thread_id, # type: ignore[reportCallIssue] + context_prompt=context_prompt, # type: ignore[reportCallIssue] + mem0_client=mem0_client, # type: ignore[reportCallIssue] + ) + + self._per_operation_thread_id: str | None = None + self._should_close_client = should_close_client + + async def __aenter__(self) -> "Self": + """Async context manager entry.""" + if self.mem0_client: + await self.mem0_client.__aenter__() + return self + + async def __aexit__(self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: Any) -> None: + """Async context manager exit.""" + if self._should_close_client and self.mem0_client: + await self.mem0_client.__aexit__(exc_type, exc_val, exc_tb) + + async def thread_created(self, thread_id: str | None = None) -> None: + """Called when a new thread is created. + + Args: + thread_id: The ID of the thread or None. + """ + self._validate_per_operation_thread_id(thread_id) + self._per_operation_thread_id = self._per_operation_thread_id or thread_id + + async def messages_adding(self, thread_id: str | None, new_messages: ChatMessage | Sequence[ChatMessage]) -> None: + """Called when a new message is being added to the thread. + + Args: + thread_id: The ID of the thread or None. + new_messages: New messages to add. + """ + self._validate_filters() + self._validate_per_operation_thread_id(thread_id) + self._per_operation_thread_id = self._per_operation_thread_id or thread_id + + messages_list = [new_messages] if isinstance(new_messages, ChatMessage) else list(new_messages) + + messages: list[dict[str, str]] = [ + {"role": message.role.value, "content": message.text} + for message in messages_list + if message.role.value in {"user", "assistant", "system"} and message.text and message.text.strip() + ] + + if messages: + await self.mem0_client.add( # type: ignore[misc] + messages=messages, + user_id=self.user_id, + agent_id=self.agent_id, + run_id=self._per_operation_thread_id if self.scope_to_per_operation_thread_id else self.thread_id, + metadata={"application_id": self.application_id}, + ) + + async def model_invoking(self, messages: ChatMessage | MutableSequence[ChatMessage]) -> Context: + """Called before invoking the AI model to provide context. + + Args: + messages: List of new messages in the thread. + + Returns: + Context: Context object containing instructions with memories. + """ + self._validate_filters() + messages_list = [messages] if isinstance(messages, ChatMessage) else list(messages) + input_text = "\n".join(msg.text for msg in messages_list if msg and msg.text and msg.text.strip()) + + memories = await self.mem0_client.search( # type: ignore[misc] + query=input_text, + user_id=self.user_id, + agent_id=self.agent_id, + run_id=self._per_operation_thread_id if self.scope_to_per_operation_thread_id else self.thread_id, + ) + + line_separated_memories = "\n".join(memory.get("memory", "") for memory in memories) + + content = TextContent(f"{self.context_prompt}\n{line_separated_memories}") if line_separated_memories else None + + return Context(contents=[content] if content else None) + + def _validate_filters(self) -> None: + """Validates that at least one filter is provided. + + Raises: + ServiceInitializationError: If no filters are provided. + """ + if not self.agent_id and not self.user_id and not self.application_id and not self.thread_id: + raise ServiceInitializationError( + "At least one of the filters: agent_id, user_id, application_id, or thread_id is required." + ) + + def _validate_per_operation_thread_id(self, thread_id: str | None) -> None: + """Validates that a new thread ID doesn't conflict with an existing one when scoped. + + Args: + thread_id: The new thread ID or None. + + Raises: + ValueError: If a new thread ID is provided when one already exists. + """ + if ( + self.scope_to_per_operation_thread_id + and thread_id + and self._per_operation_thread_id + and thread_id != self._per_operation_thread_id + ): + raise ValueError( + "Mem0Provider can only be used with one thread at a time when scope_to_per_operation_thread_id is True." + ) diff --git a/python/packages/mem0/pyproject.toml b/python/packages/mem0/pyproject.toml new file mode 100644 index 0000000000..64619181b5 --- /dev/null +++ b/python/packages/mem0/pyproject.toml @@ -0,0 +1,94 @@ +[project] +name = "agent-framework-mem0" +description = "Mem0 integration for Microsoft Agent Framework." +authors = [{ name = "Microsoft", email = "SK-Support@microsoft.com"}] +readme = "README.md" +requires-python = ">=3.10" +version = "0.1.0b1" +license-files = ["LICENSE"] +urls.homepage = "https://learn.microsoft.com/en-us/semantic-kernel/overview/" +urls.source = "https://github.com/microsoft/agent-framework/tree/main/python" +urls.release_notes = "https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true" +urls.issues = "https://github.com/microsoft/agent-framework/issues" +classifiers = [ + "License :: OSI Approved :: MIT License", + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Framework :: Pydantic :: 2", + "Typing :: Typed", +] +dependencies = [ + "agent-framework", + "mem0ai>=0.1.117", +] + +[tool.uv] +prerelease = "if-necessary-or-explicit" +environments = [ + "sys_platform == 'darwin'", + "sys_platform == 'linux'", + "sys_platform == 'win32'" +] + +[tool.uv-dynamic-versioning] +fallback-version = "0.0.0" +[tool.pytest.ini_options] +testpaths = 'tests' +addopts = "-ra -q -r fEX" +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "function" +filterwarnings = [ + "ignore:Support for class-based `config` is deprecated:DeprecationWarning:pydantic.*" +] +timeout = 120 + +[tool.ruff] +extend = "../../pyproject.toml" + +[tool.coverage.run] +omit = [ + "**/__init__.py" +] + +[tool.pyright] +extend = "../../pyproject.toml" +exclude = ['tests'] + +[tool.mypy] +plugins = ['pydantic.mypy'] +strict = true +python_version = "3.10" +ignore_missing_imports = true +disallow_untyped_defs = true +no_implicit_optional = true +check_untyped_defs = true +warn_return_any = true +show_error_codes = true +warn_unused_ignores = false +disallow_incomplete_defs = true +disallow_untyped_decorators = true +disallow_any_unimported = true + +[tool.bandit] +targets = ["agent_framework_mem0"] +exclude_dirs = ["tests"] + +[tool.poe] +executor.type = "uv" +include = "../../shared_tasks.toml" +[tool.poe.tasks] +mypy = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_mem0" +test = "pytest --cov=agent_framework_mem0 --cov-report=term-missing:skip-covered tests" + +[tool.uv.build-backend] +module-name = "agent_framework_mem0" +module-root = "" + +[build-system] +requires = ["uv_build>=0.8.2,<0.9.0"] +build-backend = "uv_build" diff --git a/python/packages/mem0/tests/__init__.py b/python/packages/mem0/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/packages/mem0/tests/test_cross_package.py b/python/packages/mem0/tests/test_cross_package.py new file mode 100644 index 0000000000..7d6e156702 --- /dev/null +++ b/python/packages/mem0/tests/test_cross_package.py @@ -0,0 +1,28 @@ +# Copyright (c) Microsoft. All rights reserved. + + +def test_self_through_main() -> None: + try: + from agent_framework.mem0 import __version__ + except ImportError: + __version__ = None + + assert __version__ is not None + + +def test_self() -> None: + try: + from agent_framework_mem0 import __version__ + except ImportError: + __version__ = None + + assert __version__ is not None + + +def test_agent_framework() -> None: + try: + from agent_framework import __version__ + except ImportError: + __version__ = None + + assert __version__ is not None diff --git a/python/packages/mem0/tests/test_provider.py b/python/packages/mem0/tests/test_provider.py new file mode 100644 index 0000000000..812eb8ac2a --- /dev/null +++ b/python/packages/mem0/tests/test_provider.py @@ -0,0 +1,481 @@ +# Copyright (c) Microsoft. All rights reserved. +# pyright: reportPrivateUsage=false + +from unittest.mock import AsyncMock, patch + +import pytest +from agent_framework import ChatMessage, Context, Role, TextContent +from agent_framework.exceptions import ServiceInitializationError +from agent_framework.mem0 import Mem0Provider + + +def test_mem0_provider_import(): + """Test that Mem0Provider can be imported.""" + assert Mem0Provider is not None + + +@pytest.fixture +def mock_mem0_client() -> AsyncMock: + """Create a mock Mem0 AsyncMemoryClient.""" + mock_client = AsyncMock() + mock_client.add = AsyncMock() + mock_client.search = AsyncMock() + mock_client.__aenter__ = AsyncMock(return_value=mock_client) + mock_client.__aexit__ = AsyncMock() + mock_client.async_client = AsyncMock() + mock_client.async_client.aclose = AsyncMock() + return mock_client + + +@pytest.fixture +def sample_messages() -> list[ChatMessage]: + """Create sample chat messages for testing.""" + return [ + ChatMessage(role=Role.USER, text="Hello, how are you?"), + ChatMessage(role=Role.ASSISTANT, text="I'm doing well, thank you!"), + ChatMessage(role=Role.SYSTEM, text="You are a helpful assistant"), + ] + + +class TestMem0ProviderInitialization: + """Test initialization and configuration of Mem0Provider.""" + + def test_init_with_all_ids(self, mock_mem0_client: AsyncMock): + """Test initialization with all IDs provided.""" + provider = Mem0Provider( + user_id="user123", + agent_id="agent123", + application_id="app123", + thread_id="thread123", + mem0_client=mock_mem0_client, + ) + assert provider.user_id == "user123" + assert provider.agent_id == "agent123" + assert provider.application_id == "app123" + assert provider.thread_id == "thread123" + + def test_init_without_filters_succeeds(self, mock_mem0_client: AsyncMock): + """Test that initialization succeeds even without filters (validation happens during invocation).""" + provider = Mem0Provider(mem0_client=mock_mem0_client) + assert provider.user_id is None + assert provider.agent_id is None + assert provider.application_id is None + assert provider.thread_id is None + + def test_init_with_custom_context_prompt(self, mock_mem0_client: AsyncMock): + """Test initialization with custom context prompt.""" + custom_prompt = "## Custom Memories\nConsider these memories:" + provider = Mem0Provider(user_id="user123", context_prompt=custom_prompt, mem0_client=mock_mem0_client) + assert provider.context_prompt == custom_prompt + + def test_init_with_scope_to_per_operation_thread_id(self, mock_mem0_client: AsyncMock): + """Test initialization with scope_to_per_operation_thread_id enabled.""" + provider = Mem0Provider( + user_id="user123", + scope_to_per_operation_thread_id=True, + mem0_client=mock_mem0_client, + ) + assert provider.scope_to_per_operation_thread_id is True + + @patch("mem0.AsyncMemoryClient") + def test_init_creates_default_client_when_none_provided(self, mock_memory_client_class: AsyncMock): + """Test that a default client is created when none is provided.""" + mock_client = AsyncMock() + mock_memory_client_class.return_value = mock_client + + provider = Mem0Provider(user_id="user123", api_key="test_api_key") + + mock_memory_client_class.assert_called_once_with(api_key="test_api_key") + assert provider.mem0_client == mock_client + assert provider._should_close_client is True + + def test_init_with_provided_client_should_not_close(self, mock_mem0_client: AsyncMock): + """Test that provided client should not be closed by provider.""" + provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) + assert provider._should_close_client is False + + +class TestMem0ProviderAsyncContextManager: + """Test async context manager behavior.""" + + async def test_async_context_manager_entry(self, mock_mem0_client: AsyncMock): + """Test async context manager entry returns self.""" + provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) + async with provider as ctx: + assert ctx is provider + + async def test_async_context_manager_exit_closes_client_when_should_close(self): + """Test that async context manager closes client when it should.""" + mock_client = AsyncMock() + mock_client.__aenter__ = AsyncMock(return_value=mock_client) + mock_client.__aexit__ = AsyncMock() + mock_client.async_client = AsyncMock() + mock_client.async_client.aclose = AsyncMock() + + with patch("mem0.AsyncMemoryClient", return_value=mock_client): + provider = Mem0Provider(user_id="user123", api_key="test_key") + assert provider._should_close_client is True + + async with provider: + pass + + mock_client.__aexit__.assert_called_once() + + async def test_async_context_manager_exit_does_not_close_provided_client(self, mock_mem0_client: AsyncMock): + """Test that async context manager does not close provided client.""" + provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) + assert provider._should_close_client is False + + async with provider: + pass + + mock_mem0_client.__aexit__.assert_not_called() + + +class TestMem0ProviderThreadMethods: + """Test thread lifecycle methods.""" + + async def test_thread_created_sets_per_operation_thread_id(self, mock_mem0_client: AsyncMock): + """Test that thread_created sets per-operation thread ID.""" + provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) + + await provider.thread_created("thread123") + + assert provider._per_operation_thread_id == "thread123" + + async def test_thread_created_with_existing_thread_id(self, mock_mem0_client: AsyncMock): + """Test thread_created when thread ID already exists.""" + provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) + provider._per_operation_thread_id = "existing_thread" + + await provider.thread_created("thread123") + + # Should not overwrite existing thread ID + assert provider._per_operation_thread_id == "existing_thread" + + async def test_thread_created_validation_with_scope_enabled(self, mock_mem0_client: AsyncMock): + """Test thread_created validation when scope_to_per_operation_thread_id is enabled.""" + provider = Mem0Provider( + user_id="user123", + scope_to_per_operation_thread_id=True, + mem0_client=mock_mem0_client, + ) + provider._per_operation_thread_id = "existing_thread" + + with pytest.raises(ValueError) as exc_info: + await provider.thread_created("different_thread") + + assert "can only be used with one thread at a time" in str(exc_info.value) + + async def test_messages_adding_sets_per_operation_thread_id( + self, mock_mem0_client: AsyncMock, sample_messages: list[ChatMessage] + ): + """Test that messages_adding sets per-operation thread ID.""" + provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) + + await provider.messages_adding("thread123", sample_messages) + + assert provider._per_operation_thread_id == "thread123" + + +class TestMem0ProviderMessagesAdding: + """Test messages_adding method.""" + + async def test_messages_adding_fails_without_filters(self, mock_mem0_client: AsyncMock): + """Test that messages_adding fails when no filters are provided.""" + provider = Mem0Provider(mem0_client=mock_mem0_client) + message = ChatMessage(role=Role.USER, text="Hello!") + + with pytest.raises(ServiceInitializationError) as exc_info: + await provider.messages_adding("thread123", message) + + assert "At least one of the filters" in str(exc_info.value) + + async def test_messages_adding_single_message(self, mock_mem0_client: AsyncMock): + """Test adding a single message.""" + provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) + message = ChatMessage(role=Role.USER, text="Hello!") + + await provider.messages_adding("thread123", message) + + mock_mem0_client.add.assert_called_once() + call_args = mock_mem0_client.add.call_args + assert call_args.kwargs["messages"] == [{"role": "user", "content": "Hello!"}] + assert call_args.kwargs["user_id"] == "user123" + + async def test_messages_adding_multiple_messages( + self, mock_mem0_client: AsyncMock, sample_messages: list[ChatMessage] + ): + """Test adding multiple messages.""" + provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) + + await provider.messages_adding("thread123", sample_messages) + + mock_mem0_client.add.assert_called_once() + call_args = mock_mem0_client.add.call_args + expected_messages = [ + {"role": "user", "content": "Hello, how are you?"}, + {"role": "assistant", "content": "I'm doing well, thank you!"}, + {"role": "system", "content": "You are a helpful assistant"}, + ] + assert call_args.kwargs["messages"] == expected_messages + + async def test_messages_adding_with_agent_id(self, mock_mem0_client: AsyncMock, sample_messages: list[ChatMessage]): + """Test adding messages with agent_id.""" + provider = Mem0Provider(agent_id="agent123", mem0_client=mock_mem0_client) + + await provider.messages_adding("thread123", sample_messages) + + call_args = mock_mem0_client.add.call_args + assert call_args.kwargs["agent_id"] == "agent123" + assert call_args.kwargs["user_id"] is None + + async def test_messages_adding_with_application_id( + self, mock_mem0_client: AsyncMock, sample_messages: list[ChatMessage] + ): + """Test adding messages with application_id in metadata.""" + provider = Mem0Provider(user_id="user123", application_id="app123", mem0_client=mock_mem0_client) + + await provider.messages_adding("thread123", sample_messages) + + call_args = mock_mem0_client.add.call_args + assert call_args.kwargs["metadata"] == {"application_id": "app123"} + + async def test_messages_adding_with_scope_to_per_operation_thread_id( + self, mock_mem0_client: AsyncMock, sample_messages: list[ChatMessage] + ): + """Test adding messages with scope_to_per_operation_thread_id enabled.""" + provider = Mem0Provider( + user_id="user123", + thread_id="base_thread", + scope_to_per_operation_thread_id=True, + mem0_client=mock_mem0_client, + ) + provider._per_operation_thread_id = "operation_thread" + + await provider.messages_adding("operation_thread", sample_messages) + + call_args = mock_mem0_client.add.call_args + assert call_args.kwargs["run_id"] == "operation_thread" + + async def test_messages_adding_without_scope_uses_base_thread_id( + self, mock_mem0_client: AsyncMock, sample_messages: list[ChatMessage] + ): + """Test adding messages without scope uses base thread_id.""" + provider = Mem0Provider( + user_id="user123", + thread_id="base_thread", + scope_to_per_operation_thread_id=False, + mem0_client=mock_mem0_client, + ) + + await provider.messages_adding("operation_thread", sample_messages) + + call_args = mock_mem0_client.add.call_args + assert call_args.kwargs["run_id"] == "base_thread" + + async def test_messages_adding_filters_empty_messages(self, mock_mem0_client: AsyncMock): + """Test that empty or invalid messages are filtered out.""" + provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) + messages = [ + ChatMessage(role=Role.USER, text=""), # Empty text + ChatMessage(role=Role.USER, text=" "), # Whitespace only + ChatMessage(role=Role.USER, text="Valid message"), + ] + + await provider.messages_adding("thread123", messages) + + call_args = mock_mem0_client.add.call_args + # Should only include the valid message + assert call_args.kwargs["messages"] == [{"role": "user", "content": "Valid message"}] + + async def test_messages_adding_skips_when_no_valid_messages(self, mock_mem0_client: AsyncMock): + """Test that mem0 client is not called when no valid messages exist.""" + provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) + messages = [ + ChatMessage(role=Role.USER, text=""), + ChatMessage(role=Role.USER, text=" "), + ] + + await provider.messages_adding("thread123", messages) + + mock_mem0_client.add.assert_not_called() + + +class TestMem0ProviderModelInvoking: + """Test model_invoking method.""" + + async def test_model_invoking_fails_without_filters(self, mock_mem0_client: AsyncMock): + """Test that model_invoking fails when no filters are provided.""" + provider = Mem0Provider(mem0_client=mock_mem0_client) + message = ChatMessage(role=Role.USER, text="What's the weather?") + + with pytest.raises(ServiceInitializationError) as exc_info: + await provider.model_invoking(message) + + assert "At least one of the filters" in str(exc_info.value) + + async def test_model_invoking_single_message(self, mock_mem0_client: AsyncMock): + """Test model_invoking with a single message.""" + provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) + message = ChatMessage(role=Role.USER, text="What's the weather?") + + # Mock search results + mock_mem0_client.search.return_value = [ + {"memory": "User likes outdoor activities"}, + {"memory": "User lives in Seattle"}, + ] + + context = await provider.model_invoking(message) + + mock_mem0_client.search.assert_called_once() + call_args = mock_mem0_client.search.call_args + assert call_args.kwargs["query"] == "What's the weather?" + assert call_args.kwargs["user_id"] == "user123" + + assert isinstance(context, Context) + expected_instructions = ( + "## Memories\nConsider the following memories when answering user questions:\n" + "User likes outdoor activities\nUser lives in Seattle" + ) + + assert context.contents + assert isinstance(context.contents[0], TextContent) + assert context.contents[0].text == expected_instructions + + async def test_model_invoking_multiple_messages( + self, mock_mem0_client: AsyncMock, sample_messages: list[ChatMessage] + ): + """Test model_invoking with multiple messages.""" + provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) + + mock_mem0_client.search.return_value = [{"memory": "Previous conversation context"}] + + await provider.model_invoking(sample_messages) + + call_args = mock_mem0_client.search.call_args + expected_query = "Hello, how are you?\nI'm doing well, thank you!\nYou are a helpful assistant" + assert call_args.kwargs["query"] == expected_query + + async def test_model_invoking_with_agent_id(self, mock_mem0_client: AsyncMock): + """Test model_invoking with agent_id.""" + provider = Mem0Provider(agent_id="agent123", mem0_client=mock_mem0_client) + message = ChatMessage(role=Role.USER, text="Hello") + + mock_mem0_client.search.return_value = [] + + await provider.model_invoking(message) + + call_args = mock_mem0_client.search.call_args + assert call_args.kwargs["agent_id"] == "agent123" + assert call_args.kwargs["user_id"] is None + + async def test_model_invoking_with_scope_to_per_operation_thread_id(self, mock_mem0_client: AsyncMock): + """Test model_invoking with scope_to_per_operation_thread_id enabled.""" + provider = Mem0Provider( + user_id="user123", + thread_id="base_thread", + scope_to_per_operation_thread_id=True, + mem0_client=mock_mem0_client, + ) + provider._per_operation_thread_id = "operation_thread" + message = ChatMessage(role=Role.USER, text="Hello") + + mock_mem0_client.search.return_value = [] + + await provider.model_invoking(message) + + call_args = mock_mem0_client.search.call_args + assert call_args.kwargs["run_id"] == "operation_thread" + + async def test_model_invoking_no_memories_returns_none_instructions(self, mock_mem0_client: AsyncMock): + """Test that no memories returns context with None instructions.""" + provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) + message = ChatMessage(role=Role.USER, text="Hello") + + mock_mem0_client.search.return_value = [] + + context = await provider.model_invoking(message) + + assert isinstance(context, Context) + assert not context.contents + + async def test_model_invoking_filters_empty_message_text(self, mock_mem0_client: AsyncMock): + """Test that empty message text is filtered out from query.""" + provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) + messages = [ + ChatMessage(role=Role.USER, text=""), + ChatMessage(role=Role.USER, text="Valid message"), + ChatMessage(role=Role.USER, text=" "), + ] + + mock_mem0_client.search.return_value = [] + + await provider.model_invoking(messages) + + call_args = mock_mem0_client.search.call_args + assert call_args.kwargs["query"] == "Valid message" + + async def test_model_invoking_custom_context_prompt(self, mock_mem0_client: AsyncMock): + """Test model_invoking with custom context prompt.""" + custom_prompt = "## Custom Context\nRemember these details:" + provider = Mem0Provider( + user_id="user123", + context_prompt=custom_prompt, + mem0_client=mock_mem0_client, + ) + message = ChatMessage(role=Role.USER, text="Hello") + + mock_mem0_client.search.return_value = [{"memory": "Test memory"}] + + context = await provider.model_invoking(message) + + expected_instructions = "## Custom Context\nRemember these details:\nTest memory" + assert context.contents + assert isinstance(context.contents[0], TextContent) + assert context.contents[0].text == expected_instructions + + +class TestMem0ProviderValidation: + """Test validation methods.""" + + def test_validate_per_operation_thread_id_success(self, mock_mem0_client: AsyncMock): + """Test successful validation of per-operation thread ID.""" + provider = Mem0Provider( + user_id="user123", + scope_to_per_operation_thread_id=True, + mem0_client=mock_mem0_client, + ) + provider._per_operation_thread_id = "thread123" + + # Should not raise exception for same thread ID + provider._validate_per_operation_thread_id("thread123") + + # Should not raise exception for None + provider._validate_per_operation_thread_id(None) + + def test_validate_per_operation_thread_id_failure(self, mock_mem0_client: AsyncMock): + """Test validation failure for conflicting thread IDs.""" + provider = Mem0Provider( + user_id="user123", + scope_to_per_operation_thread_id=True, + mem0_client=mock_mem0_client, + ) + provider._per_operation_thread_id = "thread123" + + with pytest.raises(ValueError) as exc_info: + provider._validate_per_operation_thread_id("different_thread") + + assert "can only be used with one thread at a time" in str(exc_info.value) + + def test_validate_per_operation_thread_id_disabled_scope(self, mock_mem0_client: AsyncMock): + """Test that validation is skipped when scope is disabled.""" + provider = Mem0Provider( + user_id="user123", + scope_to_per_operation_thread_id=False, + mem0_client=mock_mem0_client, + ) + provider._per_operation_thread_id = "thread123" + + # Should not raise exception even with different thread ID + provider._validate_per_operation_thread_id("different_thread") diff --git a/python/pyproject.toml b/python/pyproject.toml index 55f7c6fdb5..a8b1d7f979 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -7,6 +7,7 @@ dependencies = [ "agent-framework", "agent-framework-azure", "agent-framework-foundry", + "agent-framework-mem0", "agent-framework-workflow", ] @@ -63,8 +64,9 @@ exclude = [ "packages/agent_framework_project.egg-info" ] agent-framework = { workspace = true } agent-framework-azure = { workspace = true } agent-framework-foundry = { workspace = true } -agent-framework-workflow = { workspace = true } +agent-framework-mem0 = { workspace = true } agent-framework-runtime = { workspace = true } +agent-framework-workflow = { workspace = true } [tool.ruff] line-length = 120 @@ -191,7 +193,7 @@ build = "python run_tasks_in_packages_if_exists.py build" # combined checks check = ["fmt", "lint", "pyright", "mypy", "test", "markdown-code-lint", "samples-code-check"] pre-commit-check = ["fmt", "lint", "pyright", "markdown-code-lint", "samples-code-check"] -all-tests = "pytest --cov=agent_framework --cov=agent_framework_azure --cov=agent_framework_foundry --cov=agent_framework_workflow --cov-report=term-missing:skip-covered packages/**/tests" +all-tests = "pytest --import-mode=importlib --cov=agent_framework --cov=agent_framework_azure --cov=agent_framework_foundry --cov=agent_framework_mem0 --cov=agent_framework_workflow --cov-report=term-missing:skip-covered packages/azure/tests packages/foundry/tests packages/main/tests packages/mem0/tests packages/workflow/tests" [tool.poe.tasks.venv] cmd = "uv venv --clear --python $python" diff --git a/python/samples/getting_started/context_providers/mem0/README.md b/python/samples/getting_started/context_providers/mem0/README.md new file mode 100644 index 0000000000..dcf68e1527 --- /dev/null +++ b/python/samples/getting_started/context_providers/mem0/README.md @@ -0,0 +1,51 @@ +# Mem0 Context Provider Examples + +[Mem0](https://mem0.ai/) is a self-improving memory layer for Large Language Models that enables applications to have long-term memory capabilities. The Agent Framework's Mem0 context provider integrates with Mem0's API to provide persistent memory across conversation sessions. + +This folder contains examples demonstrating how to use the Mem0 context provider with the Agent Framework for persistent memory and context management across conversations. + +## Examples + +| File | Description | +|------|-------------| +| [`mem0_basic.py`](mem0_basic.py) | Basic example of using Mem0 context provider to store and retrieve user preferences across different conversation threads. | +| [`mem0_threads.py`](mem0_threads.py) | Advanced example demonstrating different thread scoping strategies with Mem0. Covers global thread scope (memories shared across all operations), per-operation thread scope (memories isolated per thread), and multiple agents with different memory configurations for personal vs. work contexts. | + +## Prerequisites + +### Required Resources + +1. [Mem0 API Key](https://app.mem0.ai/) - Sign up for a Mem0 account and get your API key +2. Azure AI Foundry project endpoint (used in these examples) +3. Azure CLI authentication (run `az login`) + +## Configuration + +### Environment Variables + +Set the following environment variables: + +**For Mem0:** +- `MEM0_API_KEY`: Your Mem0 API key (alternatively, pass it as `api_key` parameter to `Mem0Provider`) + +**For Azure AI Foundry:** +- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint +- `FOUNDRY_MODEL_DEPLOYMENT_NAME`: The name of your model deployment + +## Key Concepts + +### Memory Scoping + +The Mem0 context provider supports different scoping strategies: + +- **Global Scope** (`scope_to_per_operation_thread_id=False`): Memories are shared across all conversation threads +- **Thread Scope** (`scope_to_per_operation_thread_id=True`): Memories are isolated per conversation thread + +### Memory Association + +Mem0 records can be associated with different identifiers: + +- `user_id`: Associate memories with a specific user +- `agent_id`: Associate memories with a specific agent +- `thread_id`: Associate memories with a specific conversation thread +- `application_id`: Associate memories with an application context diff --git a/python/samples/getting_started/context_providers/mem0/mem0_basic.py b/python/samples/getting_started/context_providers/mem0/mem0_basic.py new file mode 100644 index 0000000000..008542c7b4 --- /dev/null +++ b/python/samples/getting_started/context_providers/mem0/mem0_basic.py @@ -0,0 +1,72 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +import uuid + +from agent_framework.foundry import FoundryChatClient +from agent_framework.mem0 import Mem0Provider +from azure.identity.aio import AzureCliCredential + + +def retrieve_company_report(company_code: str, detailed: bool) -> str: + if company_code != "CNTS": + raise ValueError("Company code not found") + if not detailed: + return "CNTS is a company that specializes in technology." + return ( + "CNTS is a company that specializes in technology. " + "It had a revenue of $10 million in 2022. It has 100 employees." + ) + + +async def main() -> None: + """Example of memory usage with Mem0 context provider.""" + print("=== Mem0 Context Provider Example ===") + + # Each record in Mem0 should be associated with agent_id or user_id or application_id or thread_id. + # In this example, we associate Mem0 records with user_id. + user_id = str(uuid.uuid4()) + + # For Azure authentication, run `az login` command in terminal or replace AzureCliCredential with preferred + # authentication option. + # For Mem0 authentication, set Mem0 API key via "api_key" parameter or MEM0_API_KEY environment variable. + async with ( + AzureCliCredential() as credential, + FoundryChatClient(async_credential=credential).create_agent( + name="FriendlyAssistant", + instructions="You are a friendly assistant.", + tools=retrieve_company_report, + context_providers=Mem0Provider(user_id=user_id), + ) as agent, + ): + # First ask the agent to retrieve a company report with no previous context. + # The agent will not be able to invoke the tool, since it doesn't know + # the company code or the report format, so it should ask for clarification. + query = "Please retrieve my company report" + print(f"User: {query}") + result = await agent.run(query) + print(f"Agent: {result}\n") + + # Now tell the agent the company code and the report format that you want to use + # and it should be able to invoke the tool and return the report. + query = "I always work with CNTS and I always want a detailed report format. Please remember and retrieve it." + print(f"User: {query}") + result = await agent.run(query) + print(f"Agent: {result}\n") + + print("\nRequest within a new thread:") + # Create a new thread for the agent. + # The new thread has no context of the previous conversation. + thread = agent.get_new_thread() + + # Since we have the mem0 component in the thread, the agent should be able to + # retrieve the company report without asking for clarification, as it will + # be able to remember the user preferences from Mem0 component. + query = "Please retrieve my company report" + print(f"User: {query}") + result = await agent.run(query, thread=thread) + print(f"Agent: {result}\n") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/getting_started/context_providers/mem0/mem0_threads.py b/python/samples/getting_started/context_providers/mem0/mem0_threads.py new file mode 100644 index 0000000000..7c7c87c1a8 --- /dev/null +++ b/python/samples/getting_started/context_providers/mem0/mem0_threads.py @@ -0,0 +1,164 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +import uuid + +from agent_framework.foundry import FoundryChatClient +from agent_framework.mem0 import Mem0Provider +from azure.identity.aio import AzureCliCredential + + +def get_user_preferences(user_id: str) -> str: + """Mock function to get user preferences.""" + preferences = { + "user123": "Prefers concise responses and technical details", + "user456": "Likes detailed explanations with examples", + } + return preferences.get(user_id, "No specific preferences found") + + +async def example_global_thread_scope() -> None: + """Example 1: Global thread_id scope (memories shared across all operations).""" + print("1. Global Thread Scope Example:") + print("-" * 40) + + global_thread_id = str(uuid.uuid4()) + user_id = "user123" + + async with ( + AzureCliCredential() as credential, + FoundryChatClient(async_credential=credential).create_agent( + name="GlobalMemoryAssistant", + instructions="You are an assistant that remembers user preferences across conversations.", + tools=get_user_preferences, + context_providers=Mem0Provider( + user_id=user_id, + thread_id=global_thread_id, + scope_to_per_operation_thread_id=False, # Share memories across all threads + ), + ) as global_agent, + ): + # Store some preferences in the global scope + query = "Remember that I prefer technical responses with code examples when discussing programming." + print(f"User: {query}") + result = await global_agent.run(query) + print(f"Agent: {result}\n") + + # Create a new thread - but memories should still be accessible due to global scope + new_thread = global_agent.get_new_thread() + query = "What do you know about my preferences?" + print(f"User (new thread): {query}") + result = await global_agent.run(query, thread=new_thread) + print(f"Agent: {result}\n") + + +async def example_per_operation_thread_scope() -> None: + """Example 2: Per-operation thread scope (memories isolated per thread). + + Note: When scope_to_per_operation_thread_id=True, the provider is bound to a single thread + throughout its lifetime. Use the same thread object for all operations with that provider. + """ + print("2. Per-Operation Thread Scope Example:") + print("-" * 40) + + user_id = "user123" + + async with ( + AzureCliCredential() as credential, + FoundryChatClient(async_credential=credential).create_agent( + name="ScopedMemoryAssistant", + instructions="You are an assistant with thread-scoped memory.", + tools=get_user_preferences, + context_providers=Mem0Provider( + user_id=user_id, + scope_to_per_operation_thread_id=True, # Isolate memories per thread + ), + ) as scoped_agent, + ): + # Create a specific thread for this scoped provider + dedicated_thread = scoped_agent.get_new_thread() + + # Store some information in the dedicated thread + query = "Remember that for this conversation, I'm working on a Python project about data analysis." + print(f"User (dedicated thread): {query}") + result = await scoped_agent.run(query, thread=dedicated_thread) + print(f"Agent: {result}\n") + + # Test memory retrieval in the same dedicated thread + query = "What project am I working on?" + print(f"User (same dedicated thread): {query}") + result = await scoped_agent.run(query, thread=dedicated_thread) + print(f"Agent: {result}\n") + + # Store more information in the same thread + query = "Also remember that I prefer using pandas and matplotlib for this project." + print(f"User (same dedicated thread): {query}") + result = await scoped_agent.run(query, thread=dedicated_thread) + print(f"Agent: {result}\n") + + # Test comprehensive memory retrieval + query = "What do you know about my current project and preferences?" + print(f"User (same dedicated thread): {query}") + result = await scoped_agent.run(query, thread=dedicated_thread) + print(f"Agent: {result}\n") + + +async def example_multiple_agents() -> None: + """Example 3: Multiple agents with different thread configurations.""" + print("3. Multiple Agents with Different Thread Configurations:") + print("-" * 40) + + agent_id_1 = "agent_personal" + agent_id_2 = "agent_work" + + async with ( + AzureCliCredential() as credential, + FoundryChatClient(async_credential=credential).create_agent( + name="PersonalAssistant", + instructions="You are a personal assistant that helps with personal tasks.", + context_providers=Mem0Provider( + agent_id=agent_id_1, + ), + ) as personal_agent, + FoundryChatClient(async_credential=credential).create_agent( + name="WorkAssistant", + instructions="You are a work assistant that helps with professional tasks.", + context_providers=Mem0Provider( + agent_id=agent_id_2, + ), + ) as work_agent, + ): + # Store personal information + query = "Remember that I like to exercise at 6 AM and prefer outdoor activities." + print(f"User to Personal Agent: {query}") + result = await personal_agent.run(query) + print(f"Personal Agent: {result}\n") + + # Store work information + query = "Remember that I have team meetings every Tuesday at 2 PM." + print(f"User to Work Agent: {query}") + result = await work_agent.run(query) + print(f"Work Agent: {result}\n") + + # Test memory isolation + query = "What do you know about my schedule?" + print(f"User to Personal Agent: {query}") + result = await personal_agent.run(query) + print(f"Personal Agent: {result}\n") + + print(f"User to Work Agent: {query}") + result = await work_agent.run(query) + print(f"Work Agent: {result}\n") + + +async def main() -> None: + """Run all Mem0 thread management examples.""" + print("=== Mem0 Thread Management Example ===\n") + + await example_global_thread_scope() + await example_per_operation_thread_scope() + await example_multiple_agents() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/uv.lock b/python/uv.lock index a987125e3b..463f9b0f1c 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -1,15 +1,18 @@ version = 1 -revision = 3 +revision = 1 requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.13' and sys_platform == 'darwin'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", "python_full_version < '3.11' and sys_platform == 'darwin'", "python_full_version >= '3.13' and sys_platform == 'linux'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and sys_platform == 'linux'", "python_full_version < '3.11' and sys_platform == 'linux'", "python_full_version >= '3.13' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", "python_full_version < '3.11' and sys_platform == 'win32'", ] supported-markers = [ @@ -23,6 +26,7 @@ members = [ "agent-framework", "agent-framework-azure", "agent-framework-foundry", + "agent-framework-mem0", "agent-framework-project", "agent-framework-runtime", "agent-framework-workflow", @@ -35,9 +39,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pygments", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899, upload-time = "2024-05-10T11:23:10.216Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903, upload-time = "2024-05-10T11:23:08.421Z" }, + { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903 }, ] [[package]] @@ -80,7 +84,7 @@ requires-dist = [ { name = "azure-monitor-opentelemetry", specifier = ">=1.7.0" }, { name = "azure-monitor-opentelemetry-exporter", specifier = ">=1.0.0b41" }, { name = "mcp", specifier = ">=1.12" }, - { name = "openai", specifier = ">=1.103.0" }, + { name = "openai", specifier = ">=1.99.0" }, { name = "opentelemetry-api", specifier = "~=1.24" }, { name = "opentelemetry-exporter-otlp-proto-grpc", specifier = ">=1.36.0" }, { name = "opentelemetry-sdk", specifier = "~=1.24" }, @@ -124,6 +128,21 @@ requires-dist = [ { name = "azure-ai-projects", specifier = ">=1.0.0b11" }, ] +[[package]] +name = "agent-framework-mem0" +version = "0.1.0b1" +source = { editable = "packages/mem0" } +dependencies = [ + { name = "agent-framework", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "mem0ai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] + +[package.metadata] +requires-dist = [ + { name = "agent-framework", editable = "packages/main" }, + { name = "mem0ai", specifier = ">=0.1.117" }, +] + [[package]] name = "agent-framework-project" version = "0.0.0" @@ -132,6 +151,7 @@ dependencies = [ { name = "agent-framework", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-azure", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-foundry", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "agent-framework-mem0", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "agent-framework-workflow", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] @@ -175,6 +195,7 @@ requires-dist = [ { name = "agent-framework", editable = "packages/main" }, { name = "agent-framework-azure", editable = "packages/azure" }, { name = "agent-framework-foundry", editable = "packages/foundry" }, + { name = "agent-framework-mem0", editable = "packages/mem0" }, { name = "agent-framework-workflow", editable = "packages/workflow" }, ] @@ -246,18 +267,18 @@ provides-extras = ["viz"] name = "aiofiles" version = "24.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c", size = 30247, upload-time = "2024-06-24T11:02:03.584Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c", size = 30247 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896, upload-time = "2024-06-24T11:02:01.529Z" }, + { url = "https://files.pythonhosted.org/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896 }, ] [[package]] name = "aiohappyeyeballs" version = "2.6.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, ] [[package]] @@ -274,76 +295,76 @@ dependencies = [ { name = "propcache", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "yarl", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2", size = 7823716, upload-time = "2025-07-29T05:52:32.215Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2", size = 7823716 } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/dc/ef9394bde9080128ad401ac7ede185267ed637df03b51f05d14d1c99ad67/aiohttp-3.12.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b6fc902bff74d9b1879ad55f5404153e2b33a82e72a95c89cec5eb6cc9e92fbc", size = 703921, upload-time = "2025-07-29T05:49:43.584Z" }, - { url = "https://files.pythonhosted.org/packages/8f/42/63fccfc3a7ed97eb6e1a71722396f409c46b60a0552d8a56d7aad74e0df5/aiohttp-3.12.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:098e92835b8119b54c693f2f88a1dec690e20798ca5f5fe5f0520245253ee0af", size = 480288, upload-time = "2025-07-29T05:49:47.851Z" }, - { url = "https://files.pythonhosted.org/packages/9c/a2/7b8a020549f66ea2a68129db6960a762d2393248f1994499f8ba9728bbed/aiohttp-3.12.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:40b3fee496a47c3b4a39a731954c06f0bd9bd3e8258c059a4beb76ac23f8e421", size = 468063, upload-time = "2025-07-29T05:49:49.789Z" }, - { url = "https://files.pythonhosted.org/packages/8f/f5/d11e088da9176e2ad8220338ae0000ed5429a15f3c9dfd983f39105399cd/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ce13fcfb0bb2f259fb42106cdc63fa5515fb85b7e87177267d89a771a660b79", size = 1650122, upload-time = "2025-07-29T05:49:51.874Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6b/b60ce2757e2faed3d70ed45dafee48cee7bfb878785a9423f7e883f0639c/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3beb14f053222b391bf9cf92ae82e0171067cc9c8f52453a0f1ec7c37df12a77", size = 1624176, upload-time = "2025-07-29T05:49:53.805Z" }, - { url = "https://files.pythonhosted.org/packages/dd/de/8c9fde2072a1b72c4fadecf4f7d4be7a85b1d9a4ab333d8245694057b4c6/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c39e87afe48aa3e814cac5f535bc6199180a53e38d3f51c5e2530f5aa4ec58c", size = 1696583, upload-time = "2025-07-29T05:49:55.338Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ad/07f863ca3d895a1ad958a54006c6dafb4f9310f8c2fdb5f961b8529029d3/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f1b4ce5bc528a6ee38dbf5f39bbf11dd127048726323b72b8e85769319ffc4", size = 1738896, upload-time = "2025-07-29T05:49:57.045Z" }, - { url = "https://files.pythonhosted.org/packages/20/43/2bd482ebe2b126533e8755a49b128ec4e58f1a3af56879a3abdb7b42c54f/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1004e67962efabbaf3f03b11b4c43b834081c9e3f9b32b16a7d97d4708a9abe6", size = 1643561, upload-time = "2025-07-29T05:49:58.762Z" }, - { url = "https://files.pythonhosted.org/packages/23/40/2fa9f514c4cf4cbae8d7911927f81a1901838baf5e09a8b2c299de1acfe5/aiohttp-3.12.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8faa08fcc2e411f7ab91d1541d9d597d3a90e9004180edb2072238c085eac8c2", size = 1583685, upload-time = "2025-07-29T05:50:00.375Z" }, - { url = "https://files.pythonhosted.org/packages/b8/c3/94dc7357bc421f4fb978ca72a201a6c604ee90148f1181790c129396ceeb/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fe086edf38b2222328cdf89af0dde2439ee173b8ad7cb659b4e4c6f385b2be3d", size = 1627533, upload-time = "2025-07-29T05:50:02.306Z" }, - { url = "https://files.pythonhosted.org/packages/bf/3f/1f8911fe1844a07001e26593b5c255a685318943864b27b4e0267e840f95/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:79b26fe467219add81d5e47b4a4ba0f2394e8b7c7c3198ed36609f9ba161aecb", size = 1638319, upload-time = "2025-07-29T05:50:04.282Z" }, - { url = "https://files.pythonhosted.org/packages/4e/46/27bf57a99168c4e145ffee6b63d0458b9c66e58bb70687c23ad3d2f0bd17/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b761bac1192ef24e16706d761aefcb581438b34b13a2f069a6d343ec8fb693a5", size = 1613776, upload-time = "2025-07-29T05:50:05.863Z" }, - { url = "https://files.pythonhosted.org/packages/0f/7e/1d2d9061a574584bb4ad3dbdba0da90a27fdc795bc227def3a46186a8bc1/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e153e8adacfe2af562861b72f8bc47f8a5c08e010ac94eebbe33dc21d677cd5b", size = 1693359, upload-time = "2025-07-29T05:50:07.563Z" }, - { url = "https://files.pythonhosted.org/packages/08/98/bee429b52233c4a391980a5b3b196b060872a13eadd41c3a34be9b1469ed/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fc49c4de44977aa8601a00edbf157e9a421f227aa7eb477d9e3df48343311065", size = 1716598, upload-time = "2025-07-29T05:50:09.33Z" }, - { url = "https://files.pythonhosted.org/packages/57/39/b0314c1ea774df3392751b686104a3938c63ece2b7ce0ba1ed7c0b4a934f/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2776c7ec89c54a47029940177e75c8c07c29c66f73464784971d6a81904ce9d1", size = 1644940, upload-time = "2025-07-29T05:50:11.334Z" }, - { url = "https://files.pythonhosted.org/packages/1b/83/3dacb8d3f8f512c8ca43e3fa8a68b20583bd25636ffa4e56ee841ffd79ae/aiohttp-3.12.15-cp310-cp310-win32.whl", hash = "sha256:2c7d81a277fa78b2203ab626ced1487420e8c11a8e373707ab72d189fcdad20a", size = 429239, upload-time = "2025-07-29T05:50:12.803Z" }, - { url = "https://files.pythonhosted.org/packages/eb/f9/470b5daba04d558c9673ca2034f28d067f3202a40e17804425f0c331c89f/aiohttp-3.12.15-cp310-cp310-win_amd64.whl", hash = "sha256:83603f881e11f0f710f8e2327817c82e79431ec976448839f3cd05d7afe8f830", size = 452297, upload-time = "2025-07-29T05:50:14.266Z" }, - { url = "https://files.pythonhosted.org/packages/20/19/9e86722ec8e835959bd97ce8c1efa78cf361fa4531fca372551abcc9cdd6/aiohttp-3.12.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d3ce17ce0220383a0f9ea07175eeaa6aa13ae5a41f30bc61d84df17f0e9b1117", size = 711246, upload-time = "2025-07-29T05:50:15.937Z" }, - { url = "https://files.pythonhosted.org/packages/71/f9/0a31fcb1a7d4629ac9d8f01f1cb9242e2f9943f47f5d03215af91c3c1a26/aiohttp-3.12.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:010cc9bbd06db80fe234d9003f67e97a10fe003bfbedb40da7d71c1008eda0fe", size = 483515, upload-time = "2025-07-29T05:50:17.442Z" }, - { url = "https://files.pythonhosted.org/packages/62/6c/94846f576f1d11df0c2e41d3001000527c0fdf63fce7e69b3927a731325d/aiohttp-3.12.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f9d7c55b41ed687b9d7165b17672340187f87a773c98236c987f08c858145a9", size = 471776, upload-time = "2025-07-29T05:50:19.568Z" }, - { url = "https://files.pythonhosted.org/packages/f8/6c/f766d0aaafcee0447fad0328da780d344489c042e25cd58fde566bf40aed/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc4fbc61bb3548d3b482f9ac7ddd0f18c67e4225aaa4e8552b9f1ac7e6bda9e5", size = 1741977, upload-time = "2025-07-29T05:50:21.665Z" }, - { url = "https://files.pythonhosted.org/packages/17/e5/fb779a05ba6ff44d7bc1e9d24c644e876bfff5abe5454f7b854cace1b9cc/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7fbc8a7c410bb3ad5d595bb7118147dfbb6449d862cc1125cf8867cb337e8728", size = 1690645, upload-time = "2025-07-29T05:50:23.333Z" }, - { url = "https://files.pythonhosted.org/packages/37/4e/a22e799c2035f5d6a4ad2cf8e7c1d1bd0923192871dd6e367dafb158b14c/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74dad41b3458dbb0511e760fb355bb0b6689e0630de8a22b1b62a98777136e16", size = 1789437, upload-time = "2025-07-29T05:50:25.007Z" }, - { url = "https://files.pythonhosted.org/packages/28/e5/55a33b991f6433569babb56018b2fb8fb9146424f8b3a0c8ecca80556762/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b6f0af863cf17e6222b1735a756d664159e58855da99cfe965134a3ff63b0b0", size = 1828482, upload-time = "2025-07-29T05:50:26.693Z" }, - { url = "https://files.pythonhosted.org/packages/c6/82/1ddf0ea4f2f3afe79dffed5e8a246737cff6cbe781887a6a170299e33204/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5b7fe4972d48a4da367043b8e023fb70a04d1490aa7d68800e465d1b97e493b", size = 1730944, upload-time = "2025-07-29T05:50:28.382Z" }, - { url = "https://files.pythonhosted.org/packages/1b/96/784c785674117b4cb3877522a177ba1b5e4db9ce0fd519430b5de76eec90/aiohttp-3.12.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6443cca89553b7a5485331bc9bedb2342b08d073fa10b8c7d1c60579c4a7b9bd", size = 1668020, upload-time = "2025-07-29T05:50:30.032Z" }, - { url = "https://files.pythonhosted.org/packages/12/8a/8b75f203ea7e5c21c0920d84dd24a5c0e971fe1e9b9ebbf29ae7e8e39790/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c5f40ec615e5264f44b4282ee27628cea221fcad52f27405b80abb346d9f3f8", size = 1716292, upload-time = "2025-07-29T05:50:31.983Z" }, - { url = "https://files.pythonhosted.org/packages/47/0b/a1451543475bb6b86a5cfc27861e52b14085ae232896a2654ff1231c0992/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2abbb216a1d3a2fe86dbd2edce20cdc5e9ad0be6378455b05ec7f77361b3ab50", size = 1711451, upload-time = "2025-07-29T05:50:33.989Z" }, - { url = "https://files.pythonhosted.org/packages/55/fd/793a23a197cc2f0d29188805cfc93aa613407f07e5f9da5cd1366afd9d7c/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:db71ce547012a5420a39c1b744d485cfb823564d01d5d20805977f5ea1345676", size = 1691634, upload-time = "2025-07-29T05:50:35.846Z" }, - { url = "https://files.pythonhosted.org/packages/ca/bf/23a335a6670b5f5dfc6d268328e55a22651b440fca341a64fccf1eada0c6/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ced339d7c9b5030abad5854aa5413a77565e5b6e6248ff927d3e174baf3badf7", size = 1785238, upload-time = "2025-07-29T05:50:37.597Z" }, - { url = "https://files.pythonhosted.org/packages/57/4f/ed60a591839a9d85d40694aba5cef86dde9ee51ce6cca0bb30d6eb1581e7/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7c7dd29c7b5bda137464dc9bfc738d7ceea46ff70309859ffde8c022e9b08ba7", size = 1805701, upload-time = "2025-07-29T05:50:39.591Z" }, - { url = "https://files.pythonhosted.org/packages/85/e0/444747a9455c5de188c0f4a0173ee701e2e325d4b2550e9af84abb20cdba/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:421da6fd326460517873274875c6c5a18ff225b40da2616083c5a34a7570b685", size = 1718758, upload-time = "2025-07-29T05:50:41.292Z" }, - { url = "https://files.pythonhosted.org/packages/36/ab/1006278d1ffd13a698e5dd4bfa01e5878f6bddefc296c8b62649753ff249/aiohttp-3.12.15-cp311-cp311-win32.whl", hash = "sha256:4420cf9d179ec8dfe4be10e7d0fe47d6d606485512ea2265b0d8c5113372771b", size = 428868, upload-time = "2025-07-29T05:50:43.063Z" }, - { url = "https://files.pythonhosted.org/packages/10/97/ad2b18700708452400278039272032170246a1bf8ec5d832772372c71f1a/aiohttp-3.12.15-cp311-cp311-win_amd64.whl", hash = "sha256:edd533a07da85baa4b423ee8839e3e91681c7bfa19b04260a469ee94b778bf6d", size = 453273, upload-time = "2025-07-29T05:50:44.613Z" }, - { url = "https://files.pythonhosted.org/packages/63/97/77cb2450d9b35f517d6cf506256bf4f5bda3f93a66b4ad64ba7fc917899c/aiohttp-3.12.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:802d3868f5776e28f7bf69d349c26fc0efadb81676d0afa88ed00d98a26340b7", size = 702333, upload-time = "2025-07-29T05:50:46.507Z" }, - { url = "https://files.pythonhosted.org/packages/83/6d/0544e6b08b748682c30b9f65640d006e51f90763b41d7c546693bc22900d/aiohttp-3.12.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2800614cd560287be05e33a679638e586a2d7401f4ddf99e304d98878c29444", size = 476948, upload-time = "2025-07-29T05:50:48.067Z" }, - { url = "https://files.pythonhosted.org/packages/3a/1d/c8c40e611e5094330284b1aea8a4b02ca0858f8458614fa35754cab42b9c/aiohttp-3.12.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8466151554b593909d30a0a125d638b4e5f3836e5aecde85b66b80ded1cb5b0d", size = 469787, upload-time = "2025-07-29T05:50:49.669Z" }, - { url = "https://files.pythonhosted.org/packages/38/7d/b76438e70319796bfff717f325d97ce2e9310f752a267bfdf5192ac6082b/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e5a495cb1be69dae4b08f35a6c4579c539e9b5706f606632102c0f855bcba7c", size = 1716590, upload-time = "2025-07-29T05:50:51.368Z" }, - { url = "https://files.pythonhosted.org/packages/79/b1/60370d70cdf8b269ee1444b390cbd72ce514f0d1cd1a715821c784d272c9/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6404dfc8cdde35c69aaa489bb3542fb86ef215fc70277c892be8af540e5e21c0", size = 1699241, upload-time = "2025-07-29T05:50:53.628Z" }, - { url = "https://files.pythonhosted.org/packages/a3/2b/4968a7b8792437ebc12186db31523f541943e99bda8f30335c482bea6879/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ead1c00f8521a5c9070fcb88f02967b1d8a0544e6d85c253f6968b785e1a2ab", size = 1754335, upload-time = "2025-07-29T05:50:55.394Z" }, - { url = "https://files.pythonhosted.org/packages/fb/c1/49524ed553f9a0bec1a11fac09e790f49ff669bcd14164f9fab608831c4d/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6990ef617f14450bc6b34941dba4f12d5613cbf4e33805932f853fbd1cf18bfb", size = 1800491, upload-time = "2025-07-29T05:50:57.202Z" }, - { url = "https://files.pythonhosted.org/packages/de/5e/3bf5acea47a96a28c121b167f5ef659cf71208b19e52a88cdfa5c37f1fcc/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd736ed420f4db2b8148b52b46b88ed038d0354255f9a73196b7bbce3ea97545", size = 1719929, upload-time = "2025-07-29T05:50:59.192Z" }, - { url = "https://files.pythonhosted.org/packages/39/94/8ae30b806835bcd1cba799ba35347dee6961a11bd507db634516210e91d8/aiohttp-3.12.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c5092ce14361a73086b90c6efb3948ffa5be2f5b6fbcf52e8d8c8b8848bb97c", size = 1635733, upload-time = "2025-07-29T05:51:01.394Z" }, - { url = "https://files.pythonhosted.org/packages/7a/46/06cdef71dd03acd9da7f51ab3a9107318aee12ad38d273f654e4f981583a/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aaa2234bb60c4dbf82893e934d8ee8dea30446f0647e024074237a56a08c01bd", size = 1696790, upload-time = "2025-07-29T05:51:03.657Z" }, - { url = "https://files.pythonhosted.org/packages/02/90/6b4cfaaf92ed98d0ec4d173e78b99b4b1a7551250be8937d9d67ecb356b4/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6d86a2fbdd14192e2f234a92d3b494dd4457e683ba07e5905a0b3ee25389ac9f", size = 1718245, upload-time = "2025-07-29T05:51:05.911Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e6/2593751670fa06f080a846f37f112cbe6f873ba510d070136a6ed46117c6/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a041e7e2612041a6ddf1c6a33b883be6a421247c7afd47e885969ee4cc58bd8d", size = 1658899, upload-time = "2025-07-29T05:51:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/8f/28/c15bacbdb8b8eb5bf39b10680d129ea7410b859e379b03190f02fa104ffd/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5015082477abeafad7203757ae44299a610e89ee82a1503e3d4184e6bafdd519", size = 1738459, upload-time = "2025-07-29T05:51:09.56Z" }, - { url = "https://files.pythonhosted.org/packages/00/de/c269cbc4faa01fb10f143b1670633a8ddd5b2e1ffd0548f7aa49cb5c70e2/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56822ff5ddfd1b745534e658faba944012346184fbfe732e0d6134b744516eea", size = 1766434, upload-time = "2025-07-29T05:51:11.423Z" }, - { url = "https://files.pythonhosted.org/packages/52/b0/4ff3abd81aa7d929b27d2e1403722a65fc87b763e3a97b3a2a494bfc63bc/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2acbbfff69019d9014508c4ba0401822e8bae5a5fdc3b6814285b71231b60f3", size = 1726045, upload-time = "2025-07-29T05:51:13.689Z" }, - { url = "https://files.pythonhosted.org/packages/71/16/949225a6a2dd6efcbd855fbd90cf476052e648fb011aa538e3b15b89a57a/aiohttp-3.12.15-cp312-cp312-win32.whl", hash = "sha256:d849b0901b50f2185874b9a232f38e26b9b3d4810095a7572eacea939132d4e1", size = 423591, upload-time = "2025-07-29T05:51:15.452Z" }, - { url = "https://files.pythonhosted.org/packages/2b/d8/fa65d2a349fe938b76d309db1a56a75c4fb8cc7b17a398b698488a939903/aiohttp-3.12.15-cp312-cp312-win_amd64.whl", hash = "sha256:b390ef5f62bb508a9d67cb3bba9b8356e23b3996da7062f1a57ce1a79d2b3d34", size = 450266, upload-time = "2025-07-29T05:51:17.239Z" }, - { url = "https://files.pythonhosted.org/packages/f2/33/918091abcf102e39d15aba2476ad9e7bd35ddb190dcdd43a854000d3da0d/aiohttp-3.12.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9f922ffd05034d439dde1c77a20461cf4a1b0831e6caa26151fe7aa8aaebc315", size = 696741, upload-time = "2025-07-29T05:51:19.021Z" }, - { url = "https://files.pythonhosted.org/packages/b5/2a/7495a81e39a998e400f3ecdd44a62107254803d1681d9189be5c2e4530cd/aiohttp-3.12.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ee8a8ac39ce45f3e55663891d4b1d15598c157b4d494a4613e704c8b43112cd", size = 474407, upload-time = "2025-07-29T05:51:21.165Z" }, - { url = "https://files.pythonhosted.org/packages/49/fc/a9576ab4be2dcbd0f73ee8675d16c707cfc12d5ee80ccf4015ba543480c9/aiohttp-3.12.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3eae49032c29d356b94eee45a3f39fdf4b0814b397638c2f718e96cfadf4c4e4", size = 466703, upload-time = "2025-07-29T05:51:22.948Z" }, - { url = "https://files.pythonhosted.org/packages/09/2f/d4bcc8448cf536b2b54eed48f19682031ad182faa3a3fee54ebe5b156387/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97752ff12cc12f46a9b20327104448042fce5c33a624f88c18f66f9368091c7", size = 1705532, upload-time = "2025-07-29T05:51:25.211Z" }, - { url = "https://files.pythonhosted.org/packages/f1/f3/59406396083f8b489261e3c011aa8aee9df360a96ac8fa5c2e7e1b8f0466/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:894261472691d6fe76ebb7fcf2e5870a2ac284c7406ddc95823c8598a1390f0d", size = 1686794, upload-time = "2025-07-29T05:51:27.145Z" }, - { url = "https://files.pythonhosted.org/packages/dc/71/164d194993a8d114ee5656c3b7ae9c12ceee7040d076bf7b32fb98a8c5c6/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5fa5d9eb82ce98959fc1031c28198b431b4d9396894f385cb63f1e2f3f20ca6b", size = 1738865, upload-time = "2025-07-29T05:51:29.366Z" }, - { url = "https://files.pythonhosted.org/packages/1c/00/d198461b699188a93ead39cb458554d9f0f69879b95078dce416d3209b54/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0fa751efb11a541f57db59c1dd821bec09031e01452b2b6217319b3a1f34f3d", size = 1788238, upload-time = "2025-07-29T05:51:31.285Z" }, - { url = "https://files.pythonhosted.org/packages/85/b8/9e7175e1fa0ac8e56baa83bf3c214823ce250d0028955dfb23f43d5e61fd/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5346b93e62ab51ee2a9d68e8f73c7cf96ffb73568a23e683f931e52450e4148d", size = 1710566, upload-time = "2025-07-29T05:51:33.219Z" }, - { url = "https://files.pythonhosted.org/packages/59/e4/16a8eac9df39b48ae102ec030fa9f726d3570732e46ba0c592aeeb507b93/aiohttp-3.12.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:049ec0360f939cd164ecbfd2873eaa432613d5e77d6b04535e3d1fbae5a9e645", size = 1624270, upload-time = "2025-07-29T05:51:35.195Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f8/cd84dee7b6ace0740908fd0af170f9fab50c2a41ccbc3806aabcb1050141/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b52dcf013b57464b6d1e51b627adfd69a8053e84b7103a7cd49c030f9ca44461", size = 1677294, upload-time = "2025-07-29T05:51:37.215Z" }, - { url = "https://files.pythonhosted.org/packages/ce/42/d0f1f85e50d401eccd12bf85c46ba84f947a84839c8a1c2c5f6e8ab1eb50/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9b2af240143dd2765e0fb661fd0361a1b469cab235039ea57663cda087250ea9", size = 1708958, upload-time = "2025-07-29T05:51:39.328Z" }, - { url = "https://files.pythonhosted.org/packages/d5/6b/f6fa6c5790fb602538483aa5a1b86fcbad66244997e5230d88f9412ef24c/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac77f709a2cde2cc71257ab2d8c74dd157c67a0558a0d2799d5d571b4c63d44d", size = 1651553, upload-time = "2025-07-29T05:51:41.356Z" }, - { url = "https://files.pythonhosted.org/packages/04/36/a6d36ad545fa12e61d11d1932eef273928b0495e6a576eb2af04297fdd3c/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:47f6b962246f0a774fbd3b6b7be25d59b06fdb2f164cf2513097998fc6a29693", size = 1727688, upload-time = "2025-07-29T05:51:43.452Z" }, - { url = "https://files.pythonhosted.org/packages/aa/c8/f195e5e06608a97a4e52c5d41c7927301bf757a8e8bb5bbf8cef6c314961/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:760fb7db442f284996e39cf9915a94492e1896baac44f06ae551974907922b64", size = 1761157, upload-time = "2025-07-29T05:51:45.643Z" }, - { url = "https://files.pythonhosted.org/packages/05/6a/ea199e61b67f25ba688d3ce93f63b49b0a4e3b3d380f03971b4646412fc6/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad702e57dc385cae679c39d318def49aef754455f237499d5b99bea4ef582e51", size = 1710050, upload-time = "2025-07-29T05:51:48.203Z" }, - { url = "https://files.pythonhosted.org/packages/b4/2e/ffeb7f6256b33635c29dbed29a22a723ff2dd7401fff42ea60cf2060abfb/aiohttp-3.12.15-cp313-cp313-win32.whl", hash = "sha256:f813c3e9032331024de2eb2e32a88d86afb69291fbc37a3a3ae81cc9917fb3d0", size = 422647, upload-time = "2025-07-29T05:51:50.718Z" }, - { url = "https://files.pythonhosted.org/packages/1b/8e/78ee35774201f38d5e1ba079c9958f7629b1fd079459aea9467441dbfbf5/aiohttp-3.12.15-cp313-cp313-win_amd64.whl", hash = "sha256:1a649001580bdb37c6fdb1bebbd7e3bc688e8ec2b5c6f52edbb664662b17dc84", size = 449067, upload-time = "2025-07-29T05:51:52.549Z" }, + { url = "https://files.pythonhosted.org/packages/47/dc/ef9394bde9080128ad401ac7ede185267ed637df03b51f05d14d1c99ad67/aiohttp-3.12.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b6fc902bff74d9b1879ad55f5404153e2b33a82e72a95c89cec5eb6cc9e92fbc", size = 703921 }, + { url = "https://files.pythonhosted.org/packages/8f/42/63fccfc3a7ed97eb6e1a71722396f409c46b60a0552d8a56d7aad74e0df5/aiohttp-3.12.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:098e92835b8119b54c693f2f88a1dec690e20798ca5f5fe5f0520245253ee0af", size = 480288 }, + { url = "https://files.pythonhosted.org/packages/9c/a2/7b8a020549f66ea2a68129db6960a762d2393248f1994499f8ba9728bbed/aiohttp-3.12.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:40b3fee496a47c3b4a39a731954c06f0bd9bd3e8258c059a4beb76ac23f8e421", size = 468063 }, + { url = "https://files.pythonhosted.org/packages/8f/f5/d11e088da9176e2ad8220338ae0000ed5429a15f3c9dfd983f39105399cd/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ce13fcfb0bb2f259fb42106cdc63fa5515fb85b7e87177267d89a771a660b79", size = 1650122 }, + { url = "https://files.pythonhosted.org/packages/b0/6b/b60ce2757e2faed3d70ed45dafee48cee7bfb878785a9423f7e883f0639c/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3beb14f053222b391bf9cf92ae82e0171067cc9c8f52453a0f1ec7c37df12a77", size = 1624176 }, + { url = "https://files.pythonhosted.org/packages/dd/de/8c9fde2072a1b72c4fadecf4f7d4be7a85b1d9a4ab333d8245694057b4c6/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c39e87afe48aa3e814cac5f535bc6199180a53e38d3f51c5e2530f5aa4ec58c", size = 1696583 }, + { url = "https://files.pythonhosted.org/packages/0c/ad/07f863ca3d895a1ad958a54006c6dafb4f9310f8c2fdb5f961b8529029d3/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f1b4ce5bc528a6ee38dbf5f39bbf11dd127048726323b72b8e85769319ffc4", size = 1738896 }, + { url = "https://files.pythonhosted.org/packages/20/43/2bd482ebe2b126533e8755a49b128ec4e58f1a3af56879a3abdb7b42c54f/aiohttp-3.12.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1004e67962efabbaf3f03b11b4c43b834081c9e3f9b32b16a7d97d4708a9abe6", size = 1643561 }, + { url = "https://files.pythonhosted.org/packages/23/40/2fa9f514c4cf4cbae8d7911927f81a1901838baf5e09a8b2c299de1acfe5/aiohttp-3.12.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8faa08fcc2e411f7ab91d1541d9d597d3a90e9004180edb2072238c085eac8c2", size = 1583685 }, + { url = "https://files.pythonhosted.org/packages/b8/c3/94dc7357bc421f4fb978ca72a201a6c604ee90148f1181790c129396ceeb/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fe086edf38b2222328cdf89af0dde2439ee173b8ad7cb659b4e4c6f385b2be3d", size = 1627533 }, + { url = "https://files.pythonhosted.org/packages/bf/3f/1f8911fe1844a07001e26593b5c255a685318943864b27b4e0267e840f95/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:79b26fe467219add81d5e47b4a4ba0f2394e8b7c7c3198ed36609f9ba161aecb", size = 1638319 }, + { url = "https://files.pythonhosted.org/packages/4e/46/27bf57a99168c4e145ffee6b63d0458b9c66e58bb70687c23ad3d2f0bd17/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b761bac1192ef24e16706d761aefcb581438b34b13a2f069a6d343ec8fb693a5", size = 1613776 }, + { url = "https://files.pythonhosted.org/packages/0f/7e/1d2d9061a574584bb4ad3dbdba0da90a27fdc795bc227def3a46186a8bc1/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e153e8adacfe2af562861b72f8bc47f8a5c08e010ac94eebbe33dc21d677cd5b", size = 1693359 }, + { url = "https://files.pythonhosted.org/packages/08/98/bee429b52233c4a391980a5b3b196b060872a13eadd41c3a34be9b1469ed/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fc49c4de44977aa8601a00edbf157e9a421f227aa7eb477d9e3df48343311065", size = 1716598 }, + { url = "https://files.pythonhosted.org/packages/57/39/b0314c1ea774df3392751b686104a3938c63ece2b7ce0ba1ed7c0b4a934f/aiohttp-3.12.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2776c7ec89c54a47029940177e75c8c07c29c66f73464784971d6a81904ce9d1", size = 1644940 }, + { url = "https://files.pythonhosted.org/packages/1b/83/3dacb8d3f8f512c8ca43e3fa8a68b20583bd25636ffa4e56ee841ffd79ae/aiohttp-3.12.15-cp310-cp310-win32.whl", hash = "sha256:2c7d81a277fa78b2203ab626ced1487420e8c11a8e373707ab72d189fcdad20a", size = 429239 }, + { url = "https://files.pythonhosted.org/packages/eb/f9/470b5daba04d558c9673ca2034f28d067f3202a40e17804425f0c331c89f/aiohttp-3.12.15-cp310-cp310-win_amd64.whl", hash = "sha256:83603f881e11f0f710f8e2327817c82e79431ec976448839f3cd05d7afe8f830", size = 452297 }, + { url = "https://files.pythonhosted.org/packages/20/19/9e86722ec8e835959bd97ce8c1efa78cf361fa4531fca372551abcc9cdd6/aiohttp-3.12.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d3ce17ce0220383a0f9ea07175eeaa6aa13ae5a41f30bc61d84df17f0e9b1117", size = 711246 }, + { url = "https://files.pythonhosted.org/packages/71/f9/0a31fcb1a7d4629ac9d8f01f1cb9242e2f9943f47f5d03215af91c3c1a26/aiohttp-3.12.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:010cc9bbd06db80fe234d9003f67e97a10fe003bfbedb40da7d71c1008eda0fe", size = 483515 }, + { url = "https://files.pythonhosted.org/packages/62/6c/94846f576f1d11df0c2e41d3001000527c0fdf63fce7e69b3927a731325d/aiohttp-3.12.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f9d7c55b41ed687b9d7165b17672340187f87a773c98236c987f08c858145a9", size = 471776 }, + { url = "https://files.pythonhosted.org/packages/f8/6c/f766d0aaafcee0447fad0328da780d344489c042e25cd58fde566bf40aed/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc4fbc61bb3548d3b482f9ac7ddd0f18c67e4225aaa4e8552b9f1ac7e6bda9e5", size = 1741977 }, + { url = "https://files.pythonhosted.org/packages/17/e5/fb779a05ba6ff44d7bc1e9d24c644e876bfff5abe5454f7b854cace1b9cc/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7fbc8a7c410bb3ad5d595bb7118147dfbb6449d862cc1125cf8867cb337e8728", size = 1690645 }, + { url = "https://files.pythonhosted.org/packages/37/4e/a22e799c2035f5d6a4ad2cf8e7c1d1bd0923192871dd6e367dafb158b14c/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74dad41b3458dbb0511e760fb355bb0b6689e0630de8a22b1b62a98777136e16", size = 1789437 }, + { url = "https://files.pythonhosted.org/packages/28/e5/55a33b991f6433569babb56018b2fb8fb9146424f8b3a0c8ecca80556762/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b6f0af863cf17e6222b1735a756d664159e58855da99cfe965134a3ff63b0b0", size = 1828482 }, + { url = "https://files.pythonhosted.org/packages/c6/82/1ddf0ea4f2f3afe79dffed5e8a246737cff6cbe781887a6a170299e33204/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5b7fe4972d48a4da367043b8e023fb70a04d1490aa7d68800e465d1b97e493b", size = 1730944 }, + { url = "https://files.pythonhosted.org/packages/1b/96/784c785674117b4cb3877522a177ba1b5e4db9ce0fd519430b5de76eec90/aiohttp-3.12.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6443cca89553b7a5485331bc9bedb2342b08d073fa10b8c7d1c60579c4a7b9bd", size = 1668020 }, + { url = "https://files.pythonhosted.org/packages/12/8a/8b75f203ea7e5c21c0920d84dd24a5c0e971fe1e9b9ebbf29ae7e8e39790/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c5f40ec615e5264f44b4282ee27628cea221fcad52f27405b80abb346d9f3f8", size = 1716292 }, + { url = "https://files.pythonhosted.org/packages/47/0b/a1451543475bb6b86a5cfc27861e52b14085ae232896a2654ff1231c0992/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2abbb216a1d3a2fe86dbd2edce20cdc5e9ad0be6378455b05ec7f77361b3ab50", size = 1711451 }, + { url = "https://files.pythonhosted.org/packages/55/fd/793a23a197cc2f0d29188805cfc93aa613407f07e5f9da5cd1366afd9d7c/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:db71ce547012a5420a39c1b744d485cfb823564d01d5d20805977f5ea1345676", size = 1691634 }, + { url = "https://files.pythonhosted.org/packages/ca/bf/23a335a6670b5f5dfc6d268328e55a22651b440fca341a64fccf1eada0c6/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ced339d7c9b5030abad5854aa5413a77565e5b6e6248ff927d3e174baf3badf7", size = 1785238 }, + { url = "https://files.pythonhosted.org/packages/57/4f/ed60a591839a9d85d40694aba5cef86dde9ee51ce6cca0bb30d6eb1581e7/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7c7dd29c7b5bda137464dc9bfc738d7ceea46ff70309859ffde8c022e9b08ba7", size = 1805701 }, + { url = "https://files.pythonhosted.org/packages/85/e0/444747a9455c5de188c0f4a0173ee701e2e325d4b2550e9af84abb20cdba/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:421da6fd326460517873274875c6c5a18ff225b40da2616083c5a34a7570b685", size = 1718758 }, + { url = "https://files.pythonhosted.org/packages/36/ab/1006278d1ffd13a698e5dd4bfa01e5878f6bddefc296c8b62649753ff249/aiohttp-3.12.15-cp311-cp311-win32.whl", hash = "sha256:4420cf9d179ec8dfe4be10e7d0fe47d6d606485512ea2265b0d8c5113372771b", size = 428868 }, + { url = "https://files.pythonhosted.org/packages/10/97/ad2b18700708452400278039272032170246a1bf8ec5d832772372c71f1a/aiohttp-3.12.15-cp311-cp311-win_amd64.whl", hash = "sha256:edd533a07da85baa4b423ee8839e3e91681c7bfa19b04260a469ee94b778bf6d", size = 453273 }, + { url = "https://files.pythonhosted.org/packages/63/97/77cb2450d9b35f517d6cf506256bf4f5bda3f93a66b4ad64ba7fc917899c/aiohttp-3.12.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:802d3868f5776e28f7bf69d349c26fc0efadb81676d0afa88ed00d98a26340b7", size = 702333 }, + { url = "https://files.pythonhosted.org/packages/83/6d/0544e6b08b748682c30b9f65640d006e51f90763b41d7c546693bc22900d/aiohttp-3.12.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2800614cd560287be05e33a679638e586a2d7401f4ddf99e304d98878c29444", size = 476948 }, + { url = "https://files.pythonhosted.org/packages/3a/1d/c8c40e611e5094330284b1aea8a4b02ca0858f8458614fa35754cab42b9c/aiohttp-3.12.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8466151554b593909d30a0a125d638b4e5f3836e5aecde85b66b80ded1cb5b0d", size = 469787 }, + { url = "https://files.pythonhosted.org/packages/38/7d/b76438e70319796bfff717f325d97ce2e9310f752a267bfdf5192ac6082b/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e5a495cb1be69dae4b08f35a6c4579c539e9b5706f606632102c0f855bcba7c", size = 1716590 }, + { url = "https://files.pythonhosted.org/packages/79/b1/60370d70cdf8b269ee1444b390cbd72ce514f0d1cd1a715821c784d272c9/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6404dfc8cdde35c69aaa489bb3542fb86ef215fc70277c892be8af540e5e21c0", size = 1699241 }, + { url = "https://files.pythonhosted.org/packages/a3/2b/4968a7b8792437ebc12186db31523f541943e99bda8f30335c482bea6879/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ead1c00f8521a5c9070fcb88f02967b1d8a0544e6d85c253f6968b785e1a2ab", size = 1754335 }, + { url = "https://files.pythonhosted.org/packages/fb/c1/49524ed553f9a0bec1a11fac09e790f49ff669bcd14164f9fab608831c4d/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6990ef617f14450bc6b34941dba4f12d5613cbf4e33805932f853fbd1cf18bfb", size = 1800491 }, + { url = "https://files.pythonhosted.org/packages/de/5e/3bf5acea47a96a28c121b167f5ef659cf71208b19e52a88cdfa5c37f1fcc/aiohttp-3.12.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd736ed420f4db2b8148b52b46b88ed038d0354255f9a73196b7bbce3ea97545", size = 1719929 }, + { url = "https://files.pythonhosted.org/packages/39/94/8ae30b806835bcd1cba799ba35347dee6961a11bd507db634516210e91d8/aiohttp-3.12.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c5092ce14361a73086b90c6efb3948ffa5be2f5b6fbcf52e8d8c8b8848bb97c", size = 1635733 }, + { url = "https://files.pythonhosted.org/packages/7a/46/06cdef71dd03acd9da7f51ab3a9107318aee12ad38d273f654e4f981583a/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aaa2234bb60c4dbf82893e934d8ee8dea30446f0647e024074237a56a08c01bd", size = 1696790 }, + { url = "https://files.pythonhosted.org/packages/02/90/6b4cfaaf92ed98d0ec4d173e78b99b4b1a7551250be8937d9d67ecb356b4/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6d86a2fbdd14192e2f234a92d3b494dd4457e683ba07e5905a0b3ee25389ac9f", size = 1718245 }, + { url = "https://files.pythonhosted.org/packages/2e/e6/2593751670fa06f080a846f37f112cbe6f873ba510d070136a6ed46117c6/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a041e7e2612041a6ddf1c6a33b883be6a421247c7afd47e885969ee4cc58bd8d", size = 1658899 }, + { url = "https://files.pythonhosted.org/packages/8f/28/c15bacbdb8b8eb5bf39b10680d129ea7410b859e379b03190f02fa104ffd/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5015082477abeafad7203757ae44299a610e89ee82a1503e3d4184e6bafdd519", size = 1738459 }, + { url = "https://files.pythonhosted.org/packages/00/de/c269cbc4faa01fb10f143b1670633a8ddd5b2e1ffd0548f7aa49cb5c70e2/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56822ff5ddfd1b745534e658faba944012346184fbfe732e0d6134b744516eea", size = 1766434 }, + { url = "https://files.pythonhosted.org/packages/52/b0/4ff3abd81aa7d929b27d2e1403722a65fc87b763e3a97b3a2a494bfc63bc/aiohttp-3.12.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2acbbfff69019d9014508c4ba0401822e8bae5a5fdc3b6814285b71231b60f3", size = 1726045 }, + { url = "https://files.pythonhosted.org/packages/71/16/949225a6a2dd6efcbd855fbd90cf476052e648fb011aa538e3b15b89a57a/aiohttp-3.12.15-cp312-cp312-win32.whl", hash = "sha256:d849b0901b50f2185874b9a232f38e26b9b3d4810095a7572eacea939132d4e1", size = 423591 }, + { url = "https://files.pythonhosted.org/packages/2b/d8/fa65d2a349fe938b76d309db1a56a75c4fb8cc7b17a398b698488a939903/aiohttp-3.12.15-cp312-cp312-win_amd64.whl", hash = "sha256:b390ef5f62bb508a9d67cb3bba9b8356e23b3996da7062f1a57ce1a79d2b3d34", size = 450266 }, + { url = "https://files.pythonhosted.org/packages/f2/33/918091abcf102e39d15aba2476ad9e7bd35ddb190dcdd43a854000d3da0d/aiohttp-3.12.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9f922ffd05034d439dde1c77a20461cf4a1b0831e6caa26151fe7aa8aaebc315", size = 696741 }, + { url = "https://files.pythonhosted.org/packages/b5/2a/7495a81e39a998e400f3ecdd44a62107254803d1681d9189be5c2e4530cd/aiohttp-3.12.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ee8a8ac39ce45f3e55663891d4b1d15598c157b4d494a4613e704c8b43112cd", size = 474407 }, + { url = "https://files.pythonhosted.org/packages/49/fc/a9576ab4be2dcbd0f73ee8675d16c707cfc12d5ee80ccf4015ba543480c9/aiohttp-3.12.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3eae49032c29d356b94eee45a3f39fdf4b0814b397638c2f718e96cfadf4c4e4", size = 466703 }, + { url = "https://files.pythonhosted.org/packages/09/2f/d4bcc8448cf536b2b54eed48f19682031ad182faa3a3fee54ebe5b156387/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97752ff12cc12f46a9b20327104448042fce5c33a624f88c18f66f9368091c7", size = 1705532 }, + { url = "https://files.pythonhosted.org/packages/f1/f3/59406396083f8b489261e3c011aa8aee9df360a96ac8fa5c2e7e1b8f0466/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:894261472691d6fe76ebb7fcf2e5870a2ac284c7406ddc95823c8598a1390f0d", size = 1686794 }, + { url = "https://files.pythonhosted.org/packages/dc/71/164d194993a8d114ee5656c3b7ae9c12ceee7040d076bf7b32fb98a8c5c6/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5fa5d9eb82ce98959fc1031c28198b431b4d9396894f385cb63f1e2f3f20ca6b", size = 1738865 }, + { url = "https://files.pythonhosted.org/packages/1c/00/d198461b699188a93ead39cb458554d9f0f69879b95078dce416d3209b54/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0fa751efb11a541f57db59c1dd821bec09031e01452b2b6217319b3a1f34f3d", size = 1788238 }, + { url = "https://files.pythonhosted.org/packages/85/b8/9e7175e1fa0ac8e56baa83bf3c214823ce250d0028955dfb23f43d5e61fd/aiohttp-3.12.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5346b93e62ab51ee2a9d68e8f73c7cf96ffb73568a23e683f931e52450e4148d", size = 1710566 }, + { url = "https://files.pythonhosted.org/packages/59/e4/16a8eac9df39b48ae102ec030fa9f726d3570732e46ba0c592aeeb507b93/aiohttp-3.12.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:049ec0360f939cd164ecbfd2873eaa432613d5e77d6b04535e3d1fbae5a9e645", size = 1624270 }, + { url = "https://files.pythonhosted.org/packages/1f/f8/cd84dee7b6ace0740908fd0af170f9fab50c2a41ccbc3806aabcb1050141/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b52dcf013b57464b6d1e51b627adfd69a8053e84b7103a7cd49c030f9ca44461", size = 1677294 }, + { url = "https://files.pythonhosted.org/packages/ce/42/d0f1f85e50d401eccd12bf85c46ba84f947a84839c8a1c2c5f6e8ab1eb50/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9b2af240143dd2765e0fb661fd0361a1b469cab235039ea57663cda087250ea9", size = 1708958 }, + { url = "https://files.pythonhosted.org/packages/d5/6b/f6fa6c5790fb602538483aa5a1b86fcbad66244997e5230d88f9412ef24c/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac77f709a2cde2cc71257ab2d8c74dd157c67a0558a0d2799d5d571b4c63d44d", size = 1651553 }, + { url = "https://files.pythonhosted.org/packages/04/36/a6d36ad545fa12e61d11d1932eef273928b0495e6a576eb2af04297fdd3c/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:47f6b962246f0a774fbd3b6b7be25d59b06fdb2f164cf2513097998fc6a29693", size = 1727688 }, + { url = "https://files.pythonhosted.org/packages/aa/c8/f195e5e06608a97a4e52c5d41c7927301bf757a8e8bb5bbf8cef6c314961/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:760fb7db442f284996e39cf9915a94492e1896baac44f06ae551974907922b64", size = 1761157 }, + { url = "https://files.pythonhosted.org/packages/05/6a/ea199e61b67f25ba688d3ce93f63b49b0a4e3b3d380f03971b4646412fc6/aiohttp-3.12.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad702e57dc385cae679c39d318def49aef754455f237499d5b99bea4ef582e51", size = 1710050 }, + { url = "https://files.pythonhosted.org/packages/b4/2e/ffeb7f6256b33635c29dbed29a22a723ff2dd7401fff42ea60cf2060abfb/aiohttp-3.12.15-cp313-cp313-win32.whl", hash = "sha256:f813c3e9032331024de2eb2e32a88d86afb69291fbc37a3a3ae81cc9917fb3d0", size = 422647 }, + { url = "https://files.pythonhosted.org/packages/1b/8e/78ee35774201f38d5e1ba079c9958f7629b1fd079459aea9467441dbfbf5/aiohttp-3.12.15-cp313-cp313-win_amd64.whl", hash = "sha256:1a649001580bdb37c6fdb1bebbd7e3bc688e8ec2b5c6f52edbb664662b17dc84", size = 449067 }, ] [[package]] @@ -354,27 +375,27 @@ dependencies = [ { name = "frozenlist", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 }, ] [[package]] name = "alabaster" version = "1.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, + { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 }, ] [[package]] name = "annotated-types" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, ] [[package]] @@ -387,18 +408,18 @@ dependencies = [ { name = "sniffio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213 }, ] [[package]] name = "appnope" version = "0.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" } +sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170 } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, + { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, ] [[package]] @@ -408,36 +429,36 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/61/0aa957eec22ff70b830b22ff91f825e70e1ef732c06666a805730f28b36b/asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142", size = 36870, upload-time = "2025-07-08T09:07:43.344Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/61/0aa957eec22ff70b830b22ff91f825e70e1ef732c06666a805730f28b36b/asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142", size = 36870 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/3c/0464dcada90d5da0e71018c04a140ad6349558afb30b3051b4264cc5b965/asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c", size = 23790, upload-time = "2025-07-08T09:07:41.548Z" }, + { url = "https://files.pythonhosted.org/packages/7c/3c/0464dcada90d5da0e71018c04a140ad6349558afb30b3051b4264cc5b965/asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c", size = 23790 }, ] [[package]] name = "asttokens" version = "3.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978, upload-time = "2024-11-30T04:30:14.439Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918, upload-time = "2024-11-30T04:30:10.946Z" }, + { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, ] [[package]] name = "async-timeout" version = "5.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233 }, ] [[package]] name = "attrs" version = "25.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032 } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 }, ] [[package]] @@ -451,7 +472,7 @@ dependencies = [ { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/df/87120e2195f08d760bc5cf8a31cfa2381a6887517aa89453b23f1ae3354f/autodoc_pydantic-2.2.0-py3-none-any.whl", hash = "sha256:8c6a36fbf6ed2700ea9c6d21ea76ad541b621fbdf16b5a80ee04673548af4d95", size = 34001, upload-time = "2024-04-27T10:57:00.542Z" }, + { url = "https://files.pythonhosted.org/packages/7b/df/87120e2195f08d760bc5cf8a31cfa2381a6887517aa89453b23f1ae3354f/autodoc_pydantic-2.2.0-py3-none-any.whl", hash = "sha256:8c6a36fbf6ed2700ea9c6d21ea76ad541b621fbdf16b5a80ee04673548af4d95", size = 34001 }, ] [[package]] @@ -463,9 +484,9 @@ dependencies = [ { name = "isodate", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/52/3c1af9ed86582f09343f135d527ca26f0bf9659c01ccbddb650bbb952963/azure_ai_agents-1.2.0b3.tar.gz", hash = "sha256:440d7fca98c0b13654a57dcd159cdf64d1024f9baacd1a4354ce91a290d3741e", size = 362563, upload-time = "2025-08-22T22:41:58.609Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/52/3c1af9ed86582f09343f135d527ca26f0bf9659c01ccbddb650bbb952963/azure_ai_agents-1.2.0b3.tar.gz", hash = "sha256:440d7fca98c0b13654a57dcd159cdf64d1024f9baacd1a4354ce91a290d3741e", size = 362563 } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/a4/c916745e150b5e157688da9a7965d62efb82ad940f2991260d1d2b79fcf1/azure_ai_agents-1.2.0b3-py3-none-any.whl", hash = "sha256:fec3e92fac5de2c18dee2d4def734825c2a4880bee39b3c237a7ad8079bfa8a7", size = 208129, upload-time = "2025-08-22T22:42:00.249Z" }, + { url = "https://files.pythonhosted.org/packages/99/a4/c916745e150b5e157688da9a7965d62efb82ad940f2991260d1d2b79fcf1/azure_ai_agents-1.2.0b3-py3-none-any.whl", hash = "sha256:fec3e92fac5de2c18dee2d4def734825c2a4880bee39b3c237a7ad8079bfa8a7", size = 208129 }, ] [[package]] @@ -479,9 +500,9 @@ dependencies = [ { name = "isodate", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a7/dd/1dc533648c8cafe5b5fdf93ca2f1b681d5d718ff30b1d1aa8b90e87cbf9b/azure_ai_projects-1.1.0b3.tar.gz", hash = "sha256:f18397de1456e315c592e708a0c4af00d6b52f646f2d199b2f99554e8236d495", size = 145568, upload-time = "2025-08-26T23:16:05.836Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/dd/1dc533648c8cafe5b5fdf93ca2f1b681d5d718ff30b1d1aa8b90e87cbf9b/azure_ai_projects-1.1.0b3.tar.gz", hash = "sha256:f18397de1456e315c592e708a0c4af00d6b52f646f2d199b2f99554e8236d495", size = 145568 } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/8a/92085213bdf90e6df93a4678b1ec84e2f9ef0857ab649c6034a574960ec7/azure_ai_projects-1.1.0b3-py3-none-any.whl", hash = "sha256:6149e5b17f4cbd467eec812ae9b335be48b7e856d886e49c1c0cd55385a5fa78", size = 125797, upload-time = "2025-08-26T23:16:07.866Z" }, + { url = "https://files.pythonhosted.org/packages/64/8a/92085213bdf90e6df93a4678b1ec84e2f9ef0857ab649c6034a574960ec7/azure_ai_projects-1.1.0b3-py3-none-any.whl", hash = "sha256:6149e5b17f4cbd467eec812ae9b335be48b7e856d886e49c1c0cd55385a5fa78", size = 125797 }, ] [[package]] @@ -493,9 +514,9 @@ dependencies = [ { name = "six", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ce/89/f53968635b1b2e53e4aad2dd641488929fef4ca9dfb0b97927fa7697ddf3/azure_core-1.35.0.tar.gz", hash = "sha256:c0be528489485e9ede59b6971eb63c1eaacf83ef53001bfe3904e475e972be5c", size = 339689, upload-time = "2025-07-03T00:55:23.496Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/89/f53968635b1b2e53e4aad2dd641488929fef4ca9dfb0b97927fa7697ddf3/azure_core-1.35.0.tar.gz", hash = "sha256:c0be528489485e9ede59b6971eb63c1eaacf83ef53001bfe3904e475e972be5c", size = 339689 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/78/bf94897361fdd650850f0f2e405b2293e2f12808239046232bdedf554301/azure_core-1.35.0-py3-none-any.whl", hash = "sha256:8db78c72868a58f3de8991eb4d22c4d368fae226dac1002998d6c50437e7dad1", size = 210708, upload-time = "2025-07-03T00:55:25.238Z" }, + { url = "https://files.pythonhosted.org/packages/d4/78/bf94897361fdd650850f0f2e405b2293e2f12808239046232bdedf554301/azure_core-1.35.0-py3-none-any.whl", hash = "sha256:8db78c72868a58f3de8991eb4d22c4d368fae226dac1002998d6c50437e7dad1", size = 210708 }, ] [[package]] @@ -506,9 +527,9 @@ dependencies = [ { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/7f/5de13a331a5f2919417819cc37dcf7c897018f02f83aa82b733e6629a6a6/azure_core_tracing_opentelemetry-1.0.0b12.tar.gz", hash = "sha256:bb454142440bae11fd9d68c7c1d67ae38a1756ce808c5e4d736730a7b4b04144", size = 26010, upload-time = "2025-03-21T00:18:37.346Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/7f/5de13a331a5f2919417819cc37dcf7c897018f02f83aa82b733e6629a6a6/azure_core_tracing_opentelemetry-1.0.0b12.tar.gz", hash = "sha256:bb454142440bae11fd9d68c7c1d67ae38a1756ce808c5e4d736730a7b4b04144", size = 26010 } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/5e/97a471f66935e7f89f521d0e11ae49c7f0871ca38f5c319dccae2155c8d8/azure_core_tracing_opentelemetry-1.0.0b12-py3-none-any.whl", hash = "sha256:38fd42709f1cc4bbc4f2797008b1c30a6a01617e49910c05daa3a0d0c65053ac", size = 11962, upload-time = "2025-03-21T00:18:38.581Z" }, + { url = "https://files.pythonhosted.org/packages/76/5e/97a471f66935e7f89f521d0e11ae49c7f0871ca38f5c319dccae2155c8d8/azure_core_tracing_opentelemetry-1.0.0b12-py3-none-any.whl", hash = "sha256:38fd42709f1cc4bbc4f2797008b1c30a6a01617e49910c05daa3a0d0c65053ac", size = 11962 }, ] [[package]] @@ -522,14 +543,14 @@ dependencies = [ { name = "msal-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b5/44/f3ee20bacb220b6b4a2b0a6cf7e742eecb383a5ccf604dd79ec27c286b7e/azure_identity-1.24.0.tar.gz", hash = "sha256:6c3a40b2a70af831e920b89e6421e8dcd4af78a0cb38b9642d86c67643d4930c", size = 271630, upload-time = "2025-08-07T22:27:36.258Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/44/f3ee20bacb220b6b4a2b0a6cf7e742eecb383a5ccf604dd79ec27c286b7e/azure_identity-1.24.0.tar.gz", hash = "sha256:6c3a40b2a70af831e920b89e6421e8dcd4af78a0cb38b9642d86c67643d4930c", size = 271630 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/74/17428cb429e8d52f6d0d69ed685f4760a545cb0156594963a9337b53b6c9/azure_identity-1.24.0-py3-none-any.whl", hash = "sha256:9e04997cde0ab02ed66422c74748548e620b7b29361c72ce622acab0267ff7c4", size = 187890, upload-time = "2025-08-07T22:27:38.033Z" }, + { url = "https://files.pythonhosted.org/packages/a9/74/17428cb429e8d52f6d0d69ed685f4760a545cb0156594963a9337b53b6c9/azure_identity-1.24.0-py3-none-any.whl", hash = "sha256:9e04997cde0ab02ed66422c74748548e620b7b29361c72ce622acab0267ff7c4", size = 187890 }, ] [[package]] name = "azure-monitor-opentelemetry" -version = "1.7.0" +version = "1.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "azure-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -545,9 +566,9 @@ dependencies = [ { name = "opentelemetry-resource-detector-azure", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4d/77/be4ae57398fe54fdd97af90df32173f68f37593dc56610c7b04c1643da96/azure_monitor_opentelemetry-1.7.0.tar.gz", hash = "sha256:eba75e793a95d50f6e5bc35dd2781744e2c1a5cc801b530b688f649423f2ee00", size = 51735, upload-time = "2025-08-21T15:52:58.563Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/52/e83417a7932b104dec0423697b1d9558fde5bdbbe5e8c24c61faee200841/azure_monitor_opentelemetry-1.8.0.tar.gz", hash = "sha256:25623a69e208e6fdf869c0fbfd98c4b2661a7eb412d098b632e7bbd047921fe1", size = 53323 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/bd/b898a883f379d2b4f9bcb9473d4daac24160854d947f17219a7b9211ab34/azure_monitor_opentelemetry-1.7.0-py3-none-any.whl", hash = "sha256:937c60e9706f75c77b221979a273a27e811cc6529d6887099f53916719c66dd3", size = 26316, upload-time = "2025-08-21T15:53:00.153Z" }, + { url = "https://files.pythonhosted.org/packages/4a/46/356ffdc784d174b632afa82f036369be95f66a6df9360a51537596acc81d/azure_monitor_opentelemetry-1.8.0-py3-none-any.whl", hash = "sha256:b57846a4a325f580d47f24b3f9e7b165b2d8d9524e53618ec941ec133e2d4b09", size = 27168 }, ] [[package]] @@ -563,9 +584,9 @@ dependencies = [ { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "psutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6c/c3/2f18eaed17a40982ad04953ad0fa5b1a2dbc5a5c98b6d3ef68c1d7d285ae/azure_monitor_opentelemetry_exporter-1.0.0b41.tar.gz", hash = "sha256:b363e6f89c0dee16d02782a310a60d626e4c081ef49d533ff5225a40cbab12cc", size = 206710, upload-time = "2025-07-31T22:37:28.378Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/c3/2f18eaed17a40982ad04953ad0fa5b1a2dbc5a5c98b6d3ef68c1d7d285ae/azure_monitor_opentelemetry_exporter-1.0.0b41.tar.gz", hash = "sha256:b363e6f89c0dee16d02782a310a60d626e4c081ef49d533ff5225a40cbab12cc", size = 206710 } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/d4/8cc989c4eedcefb74dccdd5af2faf51e0237163f538d2b258eef7e35f33d/azure_monitor_opentelemetry_exporter-1.0.0b41-py2.py3-none-any.whl", hash = "sha256:cbba629cca53e0e33416c61e08ebaabe833e740cfbfd7f2e9151821f92c66a51", size = 162631, upload-time = "2025-07-31T22:37:29.809Z" }, + { url = "https://files.pythonhosted.org/packages/50/d4/8cc989c4eedcefb74dccdd5af2faf51e0237163f538d2b258eef7e35f33d/azure_monitor_opentelemetry_exporter-1.0.0b41-py2.py3-none-any.whl", hash = "sha256:cbba629cca53e0e33416c61e08ebaabe833e740cfbfd7f2e9151821f92c66a51", size = 162631 }, ] [[package]] @@ -578,27 +599,36 @@ dependencies = [ { name = "isodate", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/95/3e3414491ce45025a1cde107b6ae72bf72049e6021597c201cd6a3029b9a/azure_storage_blob-12.26.0.tar.gz", hash = "sha256:5dd7d7824224f7de00bfeb032753601c982655173061e242f13be6e26d78d71f", size = 583332, upload-time = "2025-07-16T21:34:07.644Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/95/3e3414491ce45025a1cde107b6ae72bf72049e6021597c201cd6a3029b9a/azure_storage_blob-12.26.0.tar.gz", hash = "sha256:5dd7d7824224f7de00bfeb032753601c982655173061e242f13be6e26d78d71f", size = 583332 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/64/63dbfdd83b31200ac58820a7951ddfdeed1fbee9285b0f3eae12d1357155/azure_storage_blob-12.26.0-py3-none-any.whl", hash = "sha256:8c5631b8b22b4f53ec5fff2f3bededf34cfef111e2af613ad42c9e6de00a77fe", size = 412907, upload-time = "2025-07-16T21:34:09.367Z" }, + { url = "https://files.pythonhosted.org/packages/5b/64/63dbfdd83b31200ac58820a7951ddfdeed1fbee9285b0f3eae12d1357155/azure_storage_blob-12.26.0-py3-none-any.whl", hash = "sha256:8c5631b8b22b4f53ec5fff2f3bededf34cfef111e2af613ad42c9e6de00a77fe", size = 412907 }, ] [[package]] name = "babel" version = "2.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, +] + +[[package]] +name = "backoff" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 }, ] [[package]] name = "backports-asyncio-runner" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, + { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313 }, ] [[package]] @@ -609,148 +639,173 @@ dependencies = [ { name = "soupsieve", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/2e/3e5079847e653b1f6dc647aa24549d68c6addb4c595cc0d902d1b19308ad/beautifulsoup4-4.13.5.tar.gz", hash = "sha256:5e70131382930e7c3de33450a2f54a63d5e4b19386eab43a5b34d594268f3695", size = 622954, upload-time = "2025-08-24T14:06:13.168Z" } +sdist = { url = "https://files.pythonhosted.org/packages/85/2e/3e5079847e653b1f6dc647aa24549d68c6addb4c595cc0d902d1b19308ad/beautifulsoup4-4.13.5.tar.gz", hash = "sha256:5e70131382930e7c3de33450a2f54a63d5e4b19386eab43a5b34d594268f3695", size = 622954 } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/eb/f4151e0c7377a6e08a38108609ba5cede57986802757848688aeedd1b9e8/beautifulsoup4-4.13.5-py3-none-any.whl", hash = "sha256:642085eaa22233aceadff9c69651bc51e8bf3f874fb6d7104ece2beb24b47c4a", size = 105113, upload-time = "2025-08-24T14:06:14.884Z" }, + { url = "https://files.pythonhosted.org/packages/04/eb/f4151e0c7377a6e08a38108609ba5cede57986802757848688aeedd1b9e8/beautifulsoup4-4.13.5-py3-none-any.whl", hash = "sha256:642085eaa22233aceadff9c69651bc51e8bf3f874fb6d7104ece2beb24b47c4a", size = 105113 }, ] [[package]] name = "certifi" version = "2025.8.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" }, + { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216 }, ] [[package]] name = "cffi" -version = "1.17.1" +version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pycparser", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pycparser", marker = "(implementation_name != 'PyPy' and sys_platform == 'darwin') or (implementation_name != 'PyPy' and sys_platform == 'linux') or (implementation_name != 'PyPy' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588 } wheels = [ - { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191, upload-time = "2024-09-04T20:43:30.027Z" }, - { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592, upload-time = "2024-09-04T20:43:32.108Z" }, - { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024, upload-time = "2024-09-04T20:43:34.186Z" }, - { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188, upload-time = "2024-09-04T20:43:36.286Z" }, - { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571, upload-time = "2024-09-04T20:43:38.586Z" }, - { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687, upload-time = "2024-09-04T20:43:40.084Z" }, - { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211, upload-time = "2024-09-04T20:43:41.526Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325, upload-time = "2024-09-04T20:43:43.117Z" }, - { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784, upload-time = "2024-09-04T20:43:45.256Z" }, - { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564, upload-time = "2024-09-04T20:43:46.779Z" }, - { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804, upload-time = "2024-09-04T20:43:48.186Z" }, - { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299, upload-time = "2024-09-04T20:43:49.812Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264, upload-time = "2024-09-04T20:43:51.124Z" }, - { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651, upload-time = "2024-09-04T20:43:52.872Z" }, - { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload-time = "2024-09-04T20:43:56.123Z" }, - { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload-time = "2024-09-04T20:43:57.891Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload-time = "2024-09-04T20:44:00.18Z" }, - { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload-time = "2024-09-04T20:44:01.585Z" }, - { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload-time = "2024-09-04T20:44:03.467Z" }, - { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload-time = "2024-09-04T20:44:05.023Z" }, - { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload-time = "2024-09-04T20:44:06.444Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload-time = "2024-09-04T20:44:08.206Z" }, - { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload-time = "2024-09-04T20:44:09.481Z" }, - { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload-time = "2024-09-04T20:44:10.873Z" }, - { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload-time = "2024-09-04T20:44:12.232Z" }, - { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload-time = "2024-09-04T20:44:13.739Z" }, - { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, - { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, - { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, - { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, - { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, - { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, - { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" }, - { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989, upload-time = "2024-09-04T20:44:28.956Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802, upload-time = "2024-09-04T20:44:30.289Z" }, - { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload-time = "2024-09-04T20:44:32.01Z" }, - { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload-time = "2024-09-04T20:44:33.606Z" }, - { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload-time = "2024-09-04T20:44:35.191Z" }, - { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload-time = "2024-09-04T20:44:36.743Z" }, - { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload-time = "2024-09-04T20:44:38.492Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload-time = "2024-09-04T20:44:40.046Z" }, - { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload-time = "2024-09-04T20:44:41.616Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload-time = "2024-09-04T20:44:43.733Z" }, - { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" }, + { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283 }, + { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504 }, + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811 }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402 }, + { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217 }, + { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079 }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475 }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829 }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211 }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036 }, + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184 }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790 }, + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344 }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560 }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613 }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476 }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374 }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597 }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574 }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971 }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972 }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078 }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076 }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820 }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635 }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271 }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048 }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529 }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097 }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983 }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519 }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572 }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963 }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361 }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932 }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557 }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762 }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230 }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043 }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446 }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101 }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948 }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422 }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499 }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928 }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302 }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909 }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402 }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780 }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320 }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487 }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049 }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793 }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300 }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244 }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828 }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926 }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328 }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650 }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687 }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773 }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013 }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593 }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354 }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480 }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584 }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443 }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437 }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487 }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726 }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195 }, ] [[package]] name = "cfgv" version = "3.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, ] [[package]] name = "charset-normalizer" version = "3.4.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/98/f3b8013223728a99b908c9344da3aa04ee6e3fa235f19409033eda92fb78/charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72", size = 207695, upload-time = "2025-08-09T07:55:36.452Z" }, - { url = "https://files.pythonhosted.org/packages/21/40/5188be1e3118c82dcb7c2a5ba101b783822cfb413a0268ed3be0468532de/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe", size = 147153, upload-time = "2025-08-09T07:55:38.467Z" }, - { url = "https://files.pythonhosted.org/packages/37/60/5d0d74bc1e1380f0b72c327948d9c2aca14b46a9efd87604e724260f384c/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601", size = 160428, upload-time = "2025-08-09T07:55:40.072Z" }, - { url = "https://files.pythonhosted.org/packages/85/9a/d891f63722d9158688de58d050c59dc3da560ea7f04f4c53e769de5140f5/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c", size = 157627, upload-time = "2025-08-09T07:55:41.706Z" }, - { url = "https://files.pythonhosted.org/packages/65/1a/7425c952944a6521a9cfa7e675343f83fd82085b8af2b1373a2409c683dc/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2", size = 152388, upload-time = "2025-08-09T07:55:43.262Z" }, - { url = "https://files.pythonhosted.org/packages/f0/c9/a2c9c2a355a8594ce2446085e2ec97fd44d323c684ff32042e2a6b718e1d/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0", size = 150077, upload-time = "2025-08-09T07:55:44.903Z" }, - { url = "https://files.pythonhosted.org/packages/3b/38/20a1f44e4851aa1c9105d6e7110c9d020e093dfa5836d712a5f074a12bf7/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0", size = 161631, upload-time = "2025-08-09T07:55:46.346Z" }, - { url = "https://files.pythonhosted.org/packages/a4/fa/384d2c0f57edad03d7bec3ebefb462090d8905b4ff5a2d2525f3bb711fac/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0", size = 159210, upload-time = "2025-08-09T07:55:47.539Z" }, - { url = "https://files.pythonhosted.org/packages/33/9e/eca49d35867ca2db336b6ca27617deed4653b97ebf45dfc21311ce473c37/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a", size = 153739, upload-time = "2025-08-09T07:55:48.744Z" }, - { url = "https://files.pythonhosted.org/packages/2a/91/26c3036e62dfe8de8061182d33be5025e2424002125c9500faff74a6735e/charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f", size = 99825, upload-time = "2025-08-09T07:55:50.305Z" }, - { url = "https://files.pythonhosted.org/packages/e2/c6/f05db471f81af1fa01839d44ae2a8bfeec8d2a8b4590f16c4e7393afd323/charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669", size = 107452, upload-time = "2025-08-09T07:55:51.461Z" }, - { url = "https://files.pythonhosted.org/packages/7f/b5/991245018615474a60965a7c9cd2b4efbaabd16d582a5547c47ee1c7730b/charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b", size = 204483, upload-time = "2025-08-09T07:55:53.12Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2a/ae245c41c06299ec18262825c1569c5d3298fc920e4ddf56ab011b417efd/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64", size = 145520, upload-time = "2025-08-09T07:55:54.712Z" }, - { url = "https://files.pythonhosted.org/packages/3a/a4/b3b6c76e7a635748c4421d2b92c7b8f90a432f98bda5082049af37ffc8e3/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91", size = 158876, upload-time = "2025-08-09T07:55:56.024Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e6/63bb0e10f90a8243c5def74b5b105b3bbbfb3e7bb753915fe333fb0c11ea/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f", size = 156083, upload-time = "2025-08-09T07:55:57.582Z" }, - { url = "https://files.pythonhosted.org/packages/87/df/b7737ff046c974b183ea9aa111b74185ac8c3a326c6262d413bd5a1b8c69/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07", size = 150295, upload-time = "2025-08-09T07:55:59.147Z" }, - { url = "https://files.pythonhosted.org/packages/61/f1/190d9977e0084d3f1dc169acd060d479bbbc71b90bf3e7bf7b9927dec3eb/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30", size = 148379, upload-time = "2025-08-09T07:56:00.364Z" }, - { url = "https://files.pythonhosted.org/packages/4c/92/27dbe365d34c68cfe0ca76f1edd70e8705d82b378cb54ebbaeabc2e3029d/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14", size = 160018, upload-time = "2025-08-09T07:56:01.678Z" }, - { url = "https://files.pythonhosted.org/packages/99/04/baae2a1ea1893a01635d475b9261c889a18fd48393634b6270827869fa34/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c", size = 157430, upload-time = "2025-08-09T07:56:02.87Z" }, - { url = "https://files.pythonhosted.org/packages/2f/36/77da9c6a328c54d17b960c89eccacfab8271fdaaa228305330915b88afa9/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae", size = 151600, upload-time = "2025-08-09T07:56:04.089Z" }, - { url = "https://files.pythonhosted.org/packages/64/d4/9eb4ff2c167edbbf08cdd28e19078bf195762e9bd63371689cab5ecd3d0d/charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849", size = 99616, upload-time = "2025-08-09T07:56:05.658Z" }, - { url = "https://files.pythonhosted.org/packages/f4/9c/996a4a028222e7761a96634d1820de8a744ff4327a00ada9c8942033089b/charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c", size = 107108, upload-time = "2025-08-09T07:56:07.176Z" }, - { url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655, upload-time = "2025-08-09T07:56:08.475Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223, upload-time = "2025-08-09T07:56:09.708Z" }, - { url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366, upload-time = "2025-08-09T07:56:11.326Z" }, - { url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104, upload-time = "2025-08-09T07:56:13.014Z" }, - { url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830, upload-time = "2025-08-09T07:56:14.428Z" }, - { url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854, upload-time = "2025-08-09T07:56:16.051Z" }, - { url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670, upload-time = "2025-08-09T07:56:17.314Z" }, - { url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501, upload-time = "2025-08-09T07:56:18.641Z" }, - { url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173, upload-time = "2025-08-09T07:56:20.289Z" }, - { url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822, upload-time = "2025-08-09T07:56:21.551Z" }, - { url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543, upload-time = "2025-08-09T07:56:23.115Z" }, - { url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326, upload-time = "2025-08-09T07:56:24.721Z" }, - { url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008, upload-time = "2025-08-09T07:56:26.004Z" }, - { url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196, upload-time = "2025-08-09T07:56:27.25Z" }, - { url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819, upload-time = "2025-08-09T07:56:28.515Z" }, - { url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350, upload-time = "2025-08-09T07:56:29.716Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644, upload-time = "2025-08-09T07:56:30.984Z" }, - { url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468, upload-time = "2025-08-09T07:56:32.252Z" }, - { url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187, upload-time = "2025-08-09T07:56:33.481Z" }, - { url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699, upload-time = "2025-08-09T07:56:34.739Z" }, - { url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580, upload-time = "2025-08-09T07:56:35.981Z" }, - { url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366, upload-time = "2025-08-09T07:56:37.339Z" }, - { url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342, upload-time = "2025-08-09T07:56:38.687Z" }, - { url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995, upload-time = "2025-08-09T07:56:40.048Z" }, - { url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640, upload-time = "2025-08-09T07:56:41.311Z" }, - { url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636, upload-time = "2025-08-09T07:56:43.195Z" }, - { url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939, upload-time = "2025-08-09T07:56:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580, upload-time = "2025-08-09T07:56:46.684Z" }, - { url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870, upload-time = "2025-08-09T07:56:47.941Z" }, - { url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797, upload-time = "2025-08-09T07:56:49.756Z" }, - { url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224, upload-time = "2025-08-09T07:56:51.369Z" }, - { url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086, upload-time = "2025-08-09T07:56:52.722Z" }, - { url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400, upload-time = "2025-08-09T07:56:55.172Z" }, - { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" }, + { url = "https://files.pythonhosted.org/packages/d6/98/f3b8013223728a99b908c9344da3aa04ee6e3fa235f19409033eda92fb78/charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72", size = 207695 }, + { url = "https://files.pythonhosted.org/packages/21/40/5188be1e3118c82dcb7c2a5ba101b783822cfb413a0268ed3be0468532de/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe", size = 147153 }, + { url = "https://files.pythonhosted.org/packages/37/60/5d0d74bc1e1380f0b72c327948d9c2aca14b46a9efd87604e724260f384c/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601", size = 160428 }, + { url = "https://files.pythonhosted.org/packages/85/9a/d891f63722d9158688de58d050c59dc3da560ea7f04f4c53e769de5140f5/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c", size = 157627 }, + { url = "https://files.pythonhosted.org/packages/65/1a/7425c952944a6521a9cfa7e675343f83fd82085b8af2b1373a2409c683dc/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2", size = 152388 }, + { url = "https://files.pythonhosted.org/packages/f0/c9/a2c9c2a355a8594ce2446085e2ec97fd44d323c684ff32042e2a6b718e1d/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0", size = 150077 }, + { url = "https://files.pythonhosted.org/packages/3b/38/20a1f44e4851aa1c9105d6e7110c9d020e093dfa5836d712a5f074a12bf7/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0", size = 161631 }, + { url = "https://files.pythonhosted.org/packages/a4/fa/384d2c0f57edad03d7bec3ebefb462090d8905b4ff5a2d2525f3bb711fac/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0", size = 159210 }, + { url = "https://files.pythonhosted.org/packages/33/9e/eca49d35867ca2db336b6ca27617deed4653b97ebf45dfc21311ce473c37/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a", size = 153739 }, + { url = "https://files.pythonhosted.org/packages/2a/91/26c3036e62dfe8de8061182d33be5025e2424002125c9500faff74a6735e/charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f", size = 99825 }, + { url = "https://files.pythonhosted.org/packages/e2/c6/f05db471f81af1fa01839d44ae2a8bfeec8d2a8b4590f16c4e7393afd323/charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669", size = 107452 }, + { url = "https://files.pythonhosted.org/packages/7f/b5/991245018615474a60965a7c9cd2b4efbaabd16d582a5547c47ee1c7730b/charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b", size = 204483 }, + { url = "https://files.pythonhosted.org/packages/c7/2a/ae245c41c06299ec18262825c1569c5d3298fc920e4ddf56ab011b417efd/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64", size = 145520 }, + { url = "https://files.pythonhosted.org/packages/3a/a4/b3b6c76e7a635748c4421d2b92c7b8f90a432f98bda5082049af37ffc8e3/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91", size = 158876 }, + { url = "https://files.pythonhosted.org/packages/e2/e6/63bb0e10f90a8243c5def74b5b105b3bbbfb3e7bb753915fe333fb0c11ea/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f", size = 156083 }, + { url = "https://files.pythonhosted.org/packages/87/df/b7737ff046c974b183ea9aa111b74185ac8c3a326c6262d413bd5a1b8c69/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07", size = 150295 }, + { url = "https://files.pythonhosted.org/packages/61/f1/190d9977e0084d3f1dc169acd060d479bbbc71b90bf3e7bf7b9927dec3eb/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30", size = 148379 }, + { url = "https://files.pythonhosted.org/packages/4c/92/27dbe365d34c68cfe0ca76f1edd70e8705d82b378cb54ebbaeabc2e3029d/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14", size = 160018 }, + { url = "https://files.pythonhosted.org/packages/99/04/baae2a1ea1893a01635d475b9261c889a18fd48393634b6270827869fa34/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c", size = 157430 }, + { url = "https://files.pythonhosted.org/packages/2f/36/77da9c6a328c54d17b960c89eccacfab8271fdaaa228305330915b88afa9/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae", size = 151600 }, + { url = "https://files.pythonhosted.org/packages/64/d4/9eb4ff2c167edbbf08cdd28e19078bf195762e9bd63371689cab5ecd3d0d/charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849", size = 99616 }, + { url = "https://files.pythonhosted.org/packages/f4/9c/996a4a028222e7761a96634d1820de8a744ff4327a00ada9c8942033089b/charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c", size = 107108 }, + { url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655 }, + { url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223 }, + { url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366 }, + { url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104 }, + { url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830 }, + { url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854 }, + { url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670 }, + { url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501 }, + { url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173 }, + { url = "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", size = 99822 }, + { url = "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", size = 107543 }, + { url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326 }, + { url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008 }, + { url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196 }, + { url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819 }, + { url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350 }, + { url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644 }, + { url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468 }, + { url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187 }, + { url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699 }, + { url = "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", size = 99580 }, + { url = "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", size = 107366 }, + { url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342 }, + { url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995 }, + { url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640 }, + { url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636 }, + { url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939 }, + { url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580 }, + { url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870 }, + { url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797 }, + { url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224 }, + { url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086 }, + { url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400 }, + { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175 }, ] [[package]] @@ -760,112 +815,112 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342 } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215 }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] [[package]] name = "comm" version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319 } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, + { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294 }, ] [[package]] name = "coverage" version = "7.10.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/14/70/025b179c993f019105b79575ac6edb5e084fb0f0e63f15cdebef4e454fb5/coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90", size = 823736, upload-time = "2025-08-29T15:35:16.668Z" } +sdist = { url = "https://files.pythonhosted.org/packages/14/70/025b179c993f019105b79575ac6edb5e084fb0f0e63f15cdebef4e454fb5/coverage-7.10.6.tar.gz", hash = "sha256:f644a3ae5933a552a29dbb9aa2f90c677a875f80ebea028e5a52a4f429044b90", size = 823736 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/1d/2e64b43d978b5bd184e0756a41415597dfef30fcbd90b747474bd749d45f/coverage-7.10.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70e7bfbd57126b5554aa482691145f798d7df77489a177a6bef80de78860a356", size = 217025, upload-time = "2025-08-29T15:32:57.169Z" }, - { url = "https://files.pythonhosted.org/packages/23/62/b1e0f513417c02cc10ef735c3ee5186df55f190f70498b3702d516aad06f/coverage-7.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e41be6f0f19da64af13403e52f2dec38bbc2937af54df8ecef10850ff8d35301", size = 217419, upload-time = "2025-08-29T15:32:59.908Z" }, - { url = "https://files.pythonhosted.org/packages/e7/16/b800640b7a43e7c538429e4d7223e0a94fd72453a1a048f70bf766f12e96/coverage-7.10.6-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c61fc91ab80b23f5fddbee342d19662f3d3328173229caded831aa0bd7595460", size = 244180, upload-time = "2025-08-29T15:33:01.608Z" }, - { url = "https://files.pythonhosted.org/packages/fb/6f/5e03631c3305cad187eaf76af0b559fff88af9a0b0c180d006fb02413d7a/coverage-7.10.6-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10356fdd33a7cc06e8051413140bbdc6f972137508a3572e3f59f805cd2832fd", size = 245992, upload-time = "2025-08-29T15:33:03.239Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a1/f30ea0fb400b080730125b490771ec62b3375789f90af0bb68bfb8a921d7/coverage-7.10.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80b1695cf7c5ebe7b44bf2521221b9bb8cdf69b1f24231149a7e3eb1ae5fa2fb", size = 247851, upload-time = "2025-08-29T15:33:04.603Z" }, - { url = "https://files.pythonhosted.org/packages/02/8e/cfa8fee8e8ef9a6bb76c7bef039f3302f44e615d2194161a21d3d83ac2e9/coverage-7.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2e4c33e6378b9d52d3454bd08847a8651f4ed23ddbb4a0520227bd346382bbc6", size = 245891, upload-time = "2025-08-29T15:33:06.176Z" }, - { url = "https://files.pythonhosted.org/packages/93/a9/51be09b75c55c4f6c16d8d73a6a1d46ad764acca0eab48fa2ffaef5958fe/coverage-7.10.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c8a3ec16e34ef980a46f60dc6ad86ec60f763c3f2fa0db6d261e6e754f72e945", size = 243909, upload-time = "2025-08-29T15:33:07.74Z" }, - { url = "https://files.pythonhosted.org/packages/e9/a6/ba188b376529ce36483b2d585ca7bdac64aacbe5aa10da5978029a9c94db/coverage-7.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7d79dabc0a56f5af990cc6da9ad1e40766e82773c075f09cc571e2076fef882e", size = 244786, upload-time = "2025-08-29T15:33:08.965Z" }, - { url = "https://files.pythonhosted.org/packages/d0/4c/37ed872374a21813e0d3215256180c9a382c3f5ced6f2e5da0102fc2fd3e/coverage-7.10.6-cp310-cp310-win32.whl", hash = "sha256:86b9b59f2b16e981906e9d6383eb6446d5b46c278460ae2c36487667717eccf1", size = 219521, upload-time = "2025-08-29T15:33:10.599Z" }, - { url = "https://files.pythonhosted.org/packages/8e/36/9311352fdc551dec5b973b61f4e453227ce482985a9368305880af4f85dd/coverage-7.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:e132b9152749bd33534e5bd8565c7576f135f157b4029b975e15ee184325f528", size = 220417, upload-time = "2025-08-29T15:33:11.907Z" }, - { url = "https://files.pythonhosted.org/packages/d4/16/2bea27e212c4980753d6d563a0803c150edeaaddb0771a50d2afc410a261/coverage-7.10.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c706db3cabb7ceef779de68270150665e710b46d56372455cd741184f3868d8f", size = 217129, upload-time = "2025-08-29T15:33:13.575Z" }, - { url = "https://files.pythonhosted.org/packages/2a/51/e7159e068831ab37e31aac0969d47b8c5ee25b7d307b51e310ec34869315/coverage-7.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e0c38dc289e0508ef68ec95834cb5d2e96fdbe792eaccaa1bccac3966bbadcc", size = 217532, upload-time = "2025-08-29T15:33:14.872Z" }, - { url = "https://files.pythonhosted.org/packages/e7/c0/246ccbea53d6099325d25cd208df94ea435cd55f0db38099dd721efc7a1f/coverage-7.10.6-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:752a3005a1ded28f2f3a6e8787e24f28d6abe176ca64677bcd8d53d6fe2ec08a", size = 247931, upload-time = "2025-08-29T15:33:16.142Z" }, - { url = "https://files.pythonhosted.org/packages/7d/fb/7435ef8ab9b2594a6e3f58505cc30e98ae8b33265d844007737946c59389/coverage-7.10.6-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:689920ecfd60f992cafca4f5477d55720466ad2c7fa29bb56ac8d44a1ac2b47a", size = 249864, upload-time = "2025-08-29T15:33:17.434Z" }, - { url = "https://files.pythonhosted.org/packages/51/f8/d9d64e8da7bcddb094d511154824038833c81e3a039020a9d6539bf303e9/coverage-7.10.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec98435796d2624d6905820a42f82149ee9fc4f2d45c2c5bc5a44481cc50db62", size = 251969, upload-time = "2025-08-29T15:33:18.822Z" }, - { url = "https://files.pythonhosted.org/packages/43/28/c43ba0ef19f446d6463c751315140d8f2a521e04c3e79e5c5fe211bfa430/coverage-7.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b37201ce4a458c7a758ecc4efa92fa8ed783c66e0fa3c42ae19fc454a0792153", size = 249659, upload-time = "2025-08-29T15:33:20.407Z" }, - { url = "https://files.pythonhosted.org/packages/79/3e/53635bd0b72beaacf265784508a0b386defc9ab7fad99ff95f79ce9db555/coverage-7.10.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2904271c80898663c810a6b067920a61dd8d38341244a3605bd31ab55250dad5", size = 247714, upload-time = "2025-08-29T15:33:21.751Z" }, - { url = "https://files.pythonhosted.org/packages/4c/55/0964aa87126624e8c159e32b0bc4e84edef78c89a1a4b924d28dd8265625/coverage-7.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5aea98383463d6e1fa4e95416d8de66f2d0cb588774ee20ae1b28df826bcb619", size = 248351, upload-time = "2025-08-29T15:33:23.105Z" }, - { url = "https://files.pythonhosted.org/packages/eb/ab/6cfa9dc518c6c8e14a691c54e53a9433ba67336c760607e299bfcf520cb1/coverage-7.10.6-cp311-cp311-win32.whl", hash = "sha256:e3fb1fa01d3598002777dd259c0c2e6d9d5e10e7222976fc8e03992f972a2cba", size = 219562, upload-time = "2025-08-29T15:33:24.717Z" }, - { url = "https://files.pythonhosted.org/packages/5b/18/99b25346690cbc55922e7cfef06d755d4abee803ef335baff0014268eff4/coverage-7.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:f35ed9d945bece26553d5b4c8630453169672bea0050a564456eb88bdffd927e", size = 220453, upload-time = "2025-08-29T15:33:26.482Z" }, - { url = "https://files.pythonhosted.org/packages/d8/ed/81d86648a07ccb124a5cf1f1a7788712b8d7216b593562683cd5c9b0d2c1/coverage-7.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:99e1a305c7765631d74b98bf7dbf54eeea931f975e80f115437d23848ee8c27c", size = 219127, upload-time = "2025-08-29T15:33:27.777Z" }, - { url = "https://files.pythonhosted.org/packages/26/06/263f3305c97ad78aab066d116b52250dd316e74fcc20c197b61e07eb391a/coverage-7.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5b2dd6059938063a2c9fee1af729d4f2af28fd1a545e9b7652861f0d752ebcea", size = 217324, upload-time = "2025-08-29T15:33:29.06Z" }, - { url = "https://files.pythonhosted.org/packages/e9/60/1e1ded9a4fe80d843d7d53b3e395c1db3ff32d6c301e501f393b2e6c1c1f/coverage-7.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:388d80e56191bf846c485c14ae2bc8898aa3124d9d35903fef7d907780477634", size = 217560, upload-time = "2025-08-29T15:33:30.748Z" }, - { url = "https://files.pythonhosted.org/packages/b8/25/52136173c14e26dfed8b106ed725811bb53c30b896d04d28d74cb64318b3/coverage-7.10.6-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:90cb5b1a4670662719591aa92d0095bb41714970c0b065b02a2610172dbf0af6", size = 249053, upload-time = "2025-08-29T15:33:32.041Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1d/ae25a7dc58fcce8b172d42ffe5313fc267afe61c97fa872b80ee72d9515a/coverage-7.10.6-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:961834e2f2b863a0e14260a9a273aff07ff7818ab6e66d2addf5628590c628f9", size = 251802, upload-time = "2025-08-29T15:33:33.625Z" }, - { url = "https://files.pythonhosted.org/packages/f5/7a/1f561d47743710fe996957ed7c124b421320f150f1d38523d8d9102d3e2a/coverage-7.10.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf9a19f5012dab774628491659646335b1928cfc931bf8d97b0d5918dd58033c", size = 252935, upload-time = "2025-08-29T15:33:34.909Z" }, - { url = "https://files.pythonhosted.org/packages/6c/ad/8b97cd5d28aecdfde792dcbf646bac141167a5cacae2cd775998b45fabb5/coverage-7.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99c4283e2a0e147b9c9cc6bc9c96124de9419d6044837e9799763a0e29a7321a", size = 250855, upload-time = "2025-08-29T15:33:36.922Z" }, - { url = "https://files.pythonhosted.org/packages/33/6a/95c32b558d9a61858ff9d79580d3877df3eb5bc9eed0941b1f187c89e143/coverage-7.10.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:282b1b20f45df57cc508c1e033403f02283adfb67d4c9c35a90281d81e5c52c5", size = 248974, upload-time = "2025-08-29T15:33:38.175Z" }, - { url = "https://files.pythonhosted.org/packages/0d/9c/8ce95dee640a38e760d5b747c10913e7a06554704d60b41e73fdea6a1ffd/coverage-7.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cdbe264f11afd69841bd8c0d83ca10b5b32853263ee62e6ac6a0ab63895f972", size = 250409, upload-time = "2025-08-29T15:33:39.447Z" }, - { url = "https://files.pythonhosted.org/packages/04/12/7a55b0bdde78a98e2eb2356771fd2dcddb96579e8342bb52aa5bc52e96f0/coverage-7.10.6-cp312-cp312-win32.whl", hash = "sha256:a517feaf3a0a3eca1ee985d8373135cfdedfbba3882a5eab4362bda7c7cf518d", size = 219724, upload-time = "2025-08-29T15:33:41.172Z" }, - { url = "https://files.pythonhosted.org/packages/36/4a/32b185b8b8e327802c9efce3d3108d2fe2d9d31f153a0f7ecfd59c773705/coverage-7.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:856986eadf41f52b214176d894a7de05331117f6035a28ac0016c0f63d887629", size = 220536, upload-time = "2025-08-29T15:33:42.524Z" }, - { url = "https://files.pythonhosted.org/packages/08/3a/d5d8dc703e4998038c3099eaf77adddb00536a3cec08c8dcd556a36a3eb4/coverage-7.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:acf36b8268785aad739443fa2780c16260ee3fa09d12b3a70f772ef100939d80", size = 219171, upload-time = "2025-08-29T15:33:43.974Z" }, - { url = "https://files.pythonhosted.org/packages/bd/e7/917e5953ea29a28c1057729c1d5af9084ab6d9c66217523fd0e10f14d8f6/coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6", size = 217351, upload-time = "2025-08-29T15:33:45.438Z" }, - { url = "https://files.pythonhosted.org/packages/eb/86/2e161b93a4f11d0ea93f9bebb6a53f113d5d6e416d7561ca41bb0a29996b/coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80", size = 217600, upload-time = "2025-08-29T15:33:47.269Z" }, - { url = "https://files.pythonhosted.org/packages/0e/66/d03348fdd8df262b3a7fb4ee5727e6e4936e39e2f3a842e803196946f200/coverage-7.10.6-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003", size = 248600, upload-time = "2025-08-29T15:33:48.953Z" }, - { url = "https://files.pythonhosted.org/packages/73/dd/508420fb47d09d904d962f123221bc249f64b5e56aa93d5f5f7603be475f/coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27", size = 251206, upload-time = "2025-08-29T15:33:50.697Z" }, - { url = "https://files.pythonhosted.org/packages/e9/1f/9020135734184f439da85c70ea78194c2730e56c2d18aee6e8ff1719d50d/coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4", size = 252478, upload-time = "2025-08-29T15:33:52.303Z" }, - { url = "https://files.pythonhosted.org/packages/a4/a4/3d228f3942bb5a2051fde28c136eea23a761177dc4ff4ef54533164ce255/coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d", size = 250637, upload-time = "2025-08-29T15:33:53.67Z" }, - { url = "https://files.pythonhosted.org/packages/36/e3/293dce8cdb9a83de971637afc59b7190faad60603b40e32635cbd15fbf61/coverage-7.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc", size = 248529, upload-time = "2025-08-29T15:33:55.022Z" }, - { url = "https://files.pythonhosted.org/packages/90/26/64eecfa214e80dd1d101e420cab2901827de0e49631d666543d0e53cf597/coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc", size = 250143, upload-time = "2025-08-29T15:33:56.386Z" }, - { url = "https://files.pythonhosted.org/packages/3e/70/bd80588338f65ea5b0d97e424b820fb4068b9cfb9597fbd91963086e004b/coverage-7.10.6-cp313-cp313-win32.whl", hash = "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e", size = 219770, upload-time = "2025-08-29T15:33:58.063Z" }, - { url = "https://files.pythonhosted.org/packages/a7/14/0b831122305abcc1060c008f6c97bbdc0a913ab47d65070a01dc50293c2b/coverage-7.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32", size = 220566, upload-time = "2025-08-29T15:33:59.766Z" }, - { url = "https://files.pythonhosted.org/packages/83/c6/81a83778c1f83f1a4a168ed6673eeedc205afb562d8500175292ca64b94e/coverage-7.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2", size = 219195, upload-time = "2025-08-29T15:34:01.191Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1c/ccccf4bf116f9517275fa85047495515add43e41dfe8e0bef6e333c6b344/coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b", size = 218059, upload-time = "2025-08-29T15:34:02.91Z" }, - { url = "https://files.pythonhosted.org/packages/92/97/8a3ceff833d27c7492af4f39d5da6761e9ff624831db9e9f25b3886ddbca/coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393", size = 218287, upload-time = "2025-08-29T15:34:05.106Z" }, - { url = "https://files.pythonhosted.org/packages/92/d8/50b4a32580cf41ff0423777a2791aaf3269ab60c840b62009aec12d3970d/coverage-7.10.6-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27", size = 259625, upload-time = "2025-08-29T15:34:06.575Z" }, - { url = "https://files.pythonhosted.org/packages/7e/7e/6a7df5a6fb440a0179d94a348eb6616ed4745e7df26bf2a02bc4db72c421/coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df", size = 261801, upload-time = "2025-08-29T15:34:08.006Z" }, - { url = "https://files.pythonhosted.org/packages/3a/4c/a270a414f4ed5d196b9d3d67922968e768cd971d1b251e1b4f75e9362f75/coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb", size = 264027, upload-time = "2025-08-29T15:34:09.806Z" }, - { url = "https://files.pythonhosted.org/packages/9c/8b/3210d663d594926c12f373c5370bf1e7c5c3a427519a8afa65b561b9a55c/coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282", size = 261576, upload-time = "2025-08-29T15:34:11.585Z" }, - { url = "https://files.pythonhosted.org/packages/72/d0/e1961eff67e9e1dba3fc5eb7a4caf726b35a5b03776892da8d79ec895775/coverage-7.10.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4", size = 259341, upload-time = "2025-08-29T15:34:13.159Z" }, - { url = "https://files.pythonhosted.org/packages/3a/06/d6478d152cd189b33eac691cba27a40704990ba95de49771285f34a5861e/coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21", size = 260468, upload-time = "2025-08-29T15:34:14.571Z" }, - { url = "https://files.pythonhosted.org/packages/ed/73/737440247c914a332f0b47f7598535b29965bf305e19bbc22d4c39615d2b/coverage-7.10.6-cp313-cp313t-win32.whl", hash = "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0", size = 220429, upload-time = "2025-08-29T15:34:16.394Z" }, - { url = "https://files.pythonhosted.org/packages/bd/76/b92d3214740f2357ef4a27c75a526eb6c28f79c402e9f20a922c295c05e2/coverage-7.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5", size = 221493, upload-time = "2025-08-29T15:34:17.835Z" }, - { url = "https://files.pythonhosted.org/packages/fc/8e/6dcb29c599c8a1f654ec6cb68d76644fe635513af16e932d2d4ad1e5ac6e/coverage-7.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b", size = 219757, upload-time = "2025-08-29T15:34:19.248Z" }, - { url = "https://files.pythonhosted.org/packages/d3/aa/76cf0b5ec00619ef208da4689281d48b57f2c7fde883d14bf9441b74d59f/coverage-7.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e", size = 217331, upload-time = "2025-08-29T15:34:20.846Z" }, - { url = "https://files.pythonhosted.org/packages/65/91/8e41b8c7c505d398d7730206f3cbb4a875a35ca1041efc518051bfce0f6b/coverage-7.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb", size = 217607, upload-time = "2025-08-29T15:34:22.433Z" }, - { url = "https://files.pythonhosted.org/packages/87/7f/f718e732a423d442e6616580a951b8d1ec3575ea48bcd0e2228386805e79/coverage-7.10.6-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034", size = 248663, upload-time = "2025-08-29T15:34:24.425Z" }, - { url = "https://files.pythonhosted.org/packages/e6/52/c1106120e6d801ac03e12b5285e971e758e925b6f82ee9b86db3aa10045d/coverage-7.10.6-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1", size = 251197, upload-time = "2025-08-29T15:34:25.906Z" }, - { url = "https://files.pythonhosted.org/packages/3d/ec/3a8645b1bb40e36acde9c0609f08942852a4af91a937fe2c129a38f2d3f5/coverage-7.10.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a", size = 252551, upload-time = "2025-08-29T15:34:27.337Z" }, - { url = "https://files.pythonhosted.org/packages/a1/70/09ecb68eeb1155b28a1d16525fd3a9b65fbe75337311a99830df935d62b6/coverage-7.10.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb", size = 250553, upload-time = "2025-08-29T15:34:29.065Z" }, - { url = "https://files.pythonhosted.org/packages/c6/80/47df374b893fa812e953b5bc93dcb1427a7b3d7a1a7d2db33043d17f74b9/coverage-7.10.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d", size = 248486, upload-time = "2025-08-29T15:34:30.897Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/9f98640979ecee1b0d1a7164b589de720ddf8100d1747d9bbdb84be0c0fb/coverage-7.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747", size = 249981, upload-time = "2025-08-29T15:34:32.365Z" }, - { url = "https://files.pythonhosted.org/packages/1f/55/eeb6603371e6629037f47bd25bef300387257ed53a3c5fdb159b7ac8c651/coverage-7.10.6-cp314-cp314-win32.whl", hash = "sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5", size = 220054, upload-time = "2025-08-29T15:34:34.124Z" }, - { url = "https://files.pythonhosted.org/packages/15/d1/a0912b7611bc35412e919a2cd59ae98e7ea3b475e562668040a43fb27897/coverage-7.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713", size = 220851, upload-time = "2025-08-29T15:34:35.651Z" }, - { url = "https://files.pythonhosted.org/packages/ef/2d/11880bb8ef80a45338e0b3e0725e4c2d73ffbb4822c29d987078224fd6a5/coverage-7.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32", size = 219429, upload-time = "2025-08-29T15:34:37.16Z" }, - { url = "https://files.pythonhosted.org/packages/83/c0/1f00caad775c03a700146f55536ecd097a881ff08d310a58b353a1421be0/coverage-7.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65", size = 218080, upload-time = "2025-08-29T15:34:38.919Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c4/b1c5d2bd7cc412cbeb035e257fd06ed4e3e139ac871d16a07434e145d18d/coverage-7.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6", size = 218293, upload-time = "2025-08-29T15:34:40.425Z" }, - { url = "https://files.pythonhosted.org/packages/3f/07/4468d37c94724bf6ec354e4ec2f205fda194343e3e85fd2e59cec57e6a54/coverage-7.10.6-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0", size = 259800, upload-time = "2025-08-29T15:34:41.996Z" }, - { url = "https://files.pythonhosted.org/packages/82/d8/f8fb351be5fee31690cd8da768fd62f1cfab33c31d9f7baba6cd8960f6b8/coverage-7.10.6-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e", size = 261965, upload-time = "2025-08-29T15:34:43.61Z" }, - { url = "https://files.pythonhosted.org/packages/e8/70/65d4d7cfc75c5c6eb2fed3ee5cdf420fd8ae09c4808723a89a81d5b1b9c3/coverage-7.10.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5", size = 264220, upload-time = "2025-08-29T15:34:45.387Z" }, - { url = "https://files.pythonhosted.org/packages/98/3c/069df106d19024324cde10e4ec379fe2fb978017d25e97ebee23002fbadf/coverage-7.10.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7", size = 261660, upload-time = "2025-08-29T15:34:47.288Z" }, - { url = "https://files.pythonhosted.org/packages/fc/8a/2974d53904080c5dc91af798b3a54a4ccb99a45595cc0dcec6eb9616a57d/coverage-7.10.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5", size = 259417, upload-time = "2025-08-29T15:34:48.779Z" }, - { url = "https://files.pythonhosted.org/packages/30/38/9616a6b49c686394b318974d7f6e08f38b8af2270ce7488e879888d1e5db/coverage-7.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0", size = 260567, upload-time = "2025-08-29T15:34:50.718Z" }, - { url = "https://files.pythonhosted.org/packages/76/16/3ed2d6312b371a8cf804abf4e14895b70e4c3491c6e53536d63fd0958a8d/coverage-7.10.6-cp314-cp314t-win32.whl", hash = "sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7", size = 220831, upload-time = "2025-08-29T15:34:52.653Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e5/d38d0cb830abede2adb8b147770d2a3d0e7fecc7228245b9b1ae6c24930a/coverage-7.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930", size = 221950, upload-time = "2025-08-29T15:34:54.212Z" }, - { url = "https://files.pythonhosted.org/packages/f4/51/e48e550f6279349895b0ffcd6d2a690e3131ba3a7f4eafccc141966d4dea/coverage-7.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b", size = 219969, upload-time = "2025-08-29T15:34:55.83Z" }, - { url = "https://files.pythonhosted.org/packages/44/0c/50db5379b615854b5cf89146f8f5bd1d5a9693d7f3a987e269693521c404/coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3", size = 208986, upload-time = "2025-08-29T15:35:14.506Z" }, + { url = "https://files.pythonhosted.org/packages/a8/1d/2e64b43d978b5bd184e0756a41415597dfef30fcbd90b747474bd749d45f/coverage-7.10.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70e7bfbd57126b5554aa482691145f798d7df77489a177a6bef80de78860a356", size = 217025 }, + { url = "https://files.pythonhosted.org/packages/23/62/b1e0f513417c02cc10ef735c3ee5186df55f190f70498b3702d516aad06f/coverage-7.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e41be6f0f19da64af13403e52f2dec38bbc2937af54df8ecef10850ff8d35301", size = 217419 }, + { url = "https://files.pythonhosted.org/packages/e7/16/b800640b7a43e7c538429e4d7223e0a94fd72453a1a048f70bf766f12e96/coverage-7.10.6-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c61fc91ab80b23f5fddbee342d19662f3d3328173229caded831aa0bd7595460", size = 244180 }, + { url = "https://files.pythonhosted.org/packages/fb/6f/5e03631c3305cad187eaf76af0b559fff88af9a0b0c180d006fb02413d7a/coverage-7.10.6-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10356fdd33a7cc06e8051413140bbdc6f972137508a3572e3f59f805cd2832fd", size = 245992 }, + { url = "https://files.pythonhosted.org/packages/eb/a1/f30ea0fb400b080730125b490771ec62b3375789f90af0bb68bfb8a921d7/coverage-7.10.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80b1695cf7c5ebe7b44bf2521221b9bb8cdf69b1f24231149a7e3eb1ae5fa2fb", size = 247851 }, + { url = "https://files.pythonhosted.org/packages/02/8e/cfa8fee8e8ef9a6bb76c7bef039f3302f44e615d2194161a21d3d83ac2e9/coverage-7.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2e4c33e6378b9d52d3454bd08847a8651f4ed23ddbb4a0520227bd346382bbc6", size = 245891 }, + { url = "https://files.pythonhosted.org/packages/93/a9/51be09b75c55c4f6c16d8d73a6a1d46ad764acca0eab48fa2ffaef5958fe/coverage-7.10.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c8a3ec16e34ef980a46f60dc6ad86ec60f763c3f2fa0db6d261e6e754f72e945", size = 243909 }, + { url = "https://files.pythonhosted.org/packages/e9/a6/ba188b376529ce36483b2d585ca7bdac64aacbe5aa10da5978029a9c94db/coverage-7.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7d79dabc0a56f5af990cc6da9ad1e40766e82773c075f09cc571e2076fef882e", size = 244786 }, + { url = "https://files.pythonhosted.org/packages/d0/4c/37ed872374a21813e0d3215256180c9a382c3f5ced6f2e5da0102fc2fd3e/coverage-7.10.6-cp310-cp310-win32.whl", hash = "sha256:86b9b59f2b16e981906e9d6383eb6446d5b46c278460ae2c36487667717eccf1", size = 219521 }, + { url = "https://files.pythonhosted.org/packages/8e/36/9311352fdc551dec5b973b61f4e453227ce482985a9368305880af4f85dd/coverage-7.10.6-cp310-cp310-win_amd64.whl", hash = "sha256:e132b9152749bd33534e5bd8565c7576f135f157b4029b975e15ee184325f528", size = 220417 }, + { url = "https://files.pythonhosted.org/packages/d4/16/2bea27e212c4980753d6d563a0803c150edeaaddb0771a50d2afc410a261/coverage-7.10.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c706db3cabb7ceef779de68270150665e710b46d56372455cd741184f3868d8f", size = 217129 }, + { url = "https://files.pythonhosted.org/packages/2a/51/e7159e068831ab37e31aac0969d47b8c5ee25b7d307b51e310ec34869315/coverage-7.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e0c38dc289e0508ef68ec95834cb5d2e96fdbe792eaccaa1bccac3966bbadcc", size = 217532 }, + { url = "https://files.pythonhosted.org/packages/e7/c0/246ccbea53d6099325d25cd208df94ea435cd55f0db38099dd721efc7a1f/coverage-7.10.6-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:752a3005a1ded28f2f3a6e8787e24f28d6abe176ca64677bcd8d53d6fe2ec08a", size = 247931 }, + { url = "https://files.pythonhosted.org/packages/7d/fb/7435ef8ab9b2594a6e3f58505cc30e98ae8b33265d844007737946c59389/coverage-7.10.6-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:689920ecfd60f992cafca4f5477d55720466ad2c7fa29bb56ac8d44a1ac2b47a", size = 249864 }, + { url = "https://files.pythonhosted.org/packages/51/f8/d9d64e8da7bcddb094d511154824038833c81e3a039020a9d6539bf303e9/coverage-7.10.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec98435796d2624d6905820a42f82149ee9fc4f2d45c2c5bc5a44481cc50db62", size = 251969 }, + { url = "https://files.pythonhosted.org/packages/43/28/c43ba0ef19f446d6463c751315140d8f2a521e04c3e79e5c5fe211bfa430/coverage-7.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b37201ce4a458c7a758ecc4efa92fa8ed783c66e0fa3c42ae19fc454a0792153", size = 249659 }, + { url = "https://files.pythonhosted.org/packages/79/3e/53635bd0b72beaacf265784508a0b386defc9ab7fad99ff95f79ce9db555/coverage-7.10.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:2904271c80898663c810a6b067920a61dd8d38341244a3605bd31ab55250dad5", size = 247714 }, + { url = "https://files.pythonhosted.org/packages/4c/55/0964aa87126624e8c159e32b0bc4e84edef78c89a1a4b924d28dd8265625/coverage-7.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5aea98383463d6e1fa4e95416d8de66f2d0cb588774ee20ae1b28df826bcb619", size = 248351 }, + { url = "https://files.pythonhosted.org/packages/eb/ab/6cfa9dc518c6c8e14a691c54e53a9433ba67336c760607e299bfcf520cb1/coverage-7.10.6-cp311-cp311-win32.whl", hash = "sha256:e3fb1fa01d3598002777dd259c0c2e6d9d5e10e7222976fc8e03992f972a2cba", size = 219562 }, + { url = "https://files.pythonhosted.org/packages/5b/18/99b25346690cbc55922e7cfef06d755d4abee803ef335baff0014268eff4/coverage-7.10.6-cp311-cp311-win_amd64.whl", hash = "sha256:f35ed9d945bece26553d5b4c8630453169672bea0050a564456eb88bdffd927e", size = 220453 }, + { url = "https://files.pythonhosted.org/packages/d8/ed/81d86648a07ccb124a5cf1f1a7788712b8d7216b593562683cd5c9b0d2c1/coverage-7.10.6-cp311-cp311-win_arm64.whl", hash = "sha256:99e1a305c7765631d74b98bf7dbf54eeea931f975e80f115437d23848ee8c27c", size = 219127 }, + { url = "https://files.pythonhosted.org/packages/26/06/263f3305c97ad78aab066d116b52250dd316e74fcc20c197b61e07eb391a/coverage-7.10.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5b2dd6059938063a2c9fee1af729d4f2af28fd1a545e9b7652861f0d752ebcea", size = 217324 }, + { url = "https://files.pythonhosted.org/packages/e9/60/1e1ded9a4fe80d843d7d53b3e395c1db3ff32d6c301e501f393b2e6c1c1f/coverage-7.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:388d80e56191bf846c485c14ae2bc8898aa3124d9d35903fef7d907780477634", size = 217560 }, + { url = "https://files.pythonhosted.org/packages/b8/25/52136173c14e26dfed8b106ed725811bb53c30b896d04d28d74cb64318b3/coverage-7.10.6-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:90cb5b1a4670662719591aa92d0095bb41714970c0b065b02a2610172dbf0af6", size = 249053 }, + { url = "https://files.pythonhosted.org/packages/cb/1d/ae25a7dc58fcce8b172d42ffe5313fc267afe61c97fa872b80ee72d9515a/coverage-7.10.6-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:961834e2f2b863a0e14260a9a273aff07ff7818ab6e66d2addf5628590c628f9", size = 251802 }, + { url = "https://files.pythonhosted.org/packages/f5/7a/1f561d47743710fe996957ed7c124b421320f150f1d38523d8d9102d3e2a/coverage-7.10.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf9a19f5012dab774628491659646335b1928cfc931bf8d97b0d5918dd58033c", size = 252935 }, + { url = "https://files.pythonhosted.org/packages/6c/ad/8b97cd5d28aecdfde792dcbf646bac141167a5cacae2cd775998b45fabb5/coverage-7.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99c4283e2a0e147b9c9cc6bc9c96124de9419d6044837e9799763a0e29a7321a", size = 250855 }, + { url = "https://files.pythonhosted.org/packages/33/6a/95c32b558d9a61858ff9d79580d3877df3eb5bc9eed0941b1f187c89e143/coverage-7.10.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:282b1b20f45df57cc508c1e033403f02283adfb67d4c9c35a90281d81e5c52c5", size = 248974 }, + { url = "https://files.pythonhosted.org/packages/0d/9c/8ce95dee640a38e760d5b747c10913e7a06554704d60b41e73fdea6a1ffd/coverage-7.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cdbe264f11afd69841bd8c0d83ca10b5b32853263ee62e6ac6a0ab63895f972", size = 250409 }, + { url = "https://files.pythonhosted.org/packages/04/12/7a55b0bdde78a98e2eb2356771fd2dcddb96579e8342bb52aa5bc52e96f0/coverage-7.10.6-cp312-cp312-win32.whl", hash = "sha256:a517feaf3a0a3eca1ee985d8373135cfdedfbba3882a5eab4362bda7c7cf518d", size = 219724 }, + { url = "https://files.pythonhosted.org/packages/36/4a/32b185b8b8e327802c9efce3d3108d2fe2d9d31f153a0f7ecfd59c773705/coverage-7.10.6-cp312-cp312-win_amd64.whl", hash = "sha256:856986eadf41f52b214176d894a7de05331117f6035a28ac0016c0f63d887629", size = 220536 }, + { url = "https://files.pythonhosted.org/packages/08/3a/d5d8dc703e4998038c3099eaf77adddb00536a3cec08c8dcd556a36a3eb4/coverage-7.10.6-cp312-cp312-win_arm64.whl", hash = "sha256:acf36b8268785aad739443fa2780c16260ee3fa09d12b3a70f772ef100939d80", size = 219171 }, + { url = "https://files.pythonhosted.org/packages/bd/e7/917e5953ea29a28c1057729c1d5af9084ab6d9c66217523fd0e10f14d8f6/coverage-7.10.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ffea0575345e9ee0144dfe5701aa17f3ba546f8c3bb48db62ae101afb740e7d6", size = 217351 }, + { url = "https://files.pythonhosted.org/packages/eb/86/2e161b93a4f11d0ea93f9bebb6a53f113d5d6e416d7561ca41bb0a29996b/coverage-7.10.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d91d7317cde40a1c249d6b7382750b7e6d86fad9d8eaf4fa3f8f44cf171e80", size = 217600 }, + { url = "https://files.pythonhosted.org/packages/0e/66/d03348fdd8df262b3a7fb4ee5727e6e4936e39e2f3a842e803196946f200/coverage-7.10.6-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e23dd5408fe71a356b41baa82892772a4cefcf758f2ca3383d2aa39e1b7a003", size = 248600 }, + { url = "https://files.pythonhosted.org/packages/73/dd/508420fb47d09d904d962f123221bc249f64b5e56aa93d5f5f7603be475f/coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27", size = 251206 }, + { url = "https://files.pythonhosted.org/packages/e9/1f/9020135734184f439da85c70ea78194c2730e56c2d18aee6e8ff1719d50d/coverage-7.10.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db4a1d897bbbe7339946ffa2fe60c10cc81c43fab8b062d3fcb84188688174a4", size = 252478 }, + { url = "https://files.pythonhosted.org/packages/a4/a4/3d228f3942bb5a2051fde28c136eea23a761177dc4ff4ef54533164ce255/coverage-7.10.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fd7879082953c156d5b13c74aa6cca37f6a6f4747b39538504c3f9c63d043d", size = 250637 }, + { url = "https://files.pythonhosted.org/packages/36/e3/293dce8cdb9a83de971637afc59b7190faad60603b40e32635cbd15fbf61/coverage-7.10.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:28395ca3f71cd103b8c116333fa9db867f3a3e1ad6a084aa3725ae002b6583bc", size = 248529 }, + { url = "https://files.pythonhosted.org/packages/90/26/64eecfa214e80dd1d101e420cab2901827de0e49631d666543d0e53cf597/coverage-7.10.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:61c950fc33d29c91b9e18540e1aed7d9f6787cc870a3e4032493bbbe641d12fc", size = 250143 }, + { url = "https://files.pythonhosted.org/packages/3e/70/bd80588338f65ea5b0d97e424b820fb4068b9cfb9597fbd91963086e004b/coverage-7.10.6-cp313-cp313-win32.whl", hash = "sha256:160c00a5e6b6bdf4e5984b0ef21fc860bc94416c41b7df4d63f536d17c38902e", size = 219770 }, + { url = "https://files.pythonhosted.org/packages/a7/14/0b831122305abcc1060c008f6c97bbdc0a913ab47d65070a01dc50293c2b/coverage-7.10.6-cp313-cp313-win_amd64.whl", hash = "sha256:628055297f3e2aa181464c3808402887643405573eb3d9de060d81531fa79d32", size = 220566 }, + { url = "https://files.pythonhosted.org/packages/83/c6/81a83778c1f83f1a4a168ed6673eeedc205afb562d8500175292ca64b94e/coverage-7.10.6-cp313-cp313-win_arm64.whl", hash = "sha256:df4ec1f8540b0bcbe26ca7dd0f541847cc8a108b35596f9f91f59f0c060bfdd2", size = 219195 }, + { url = "https://files.pythonhosted.org/packages/d7/1c/ccccf4bf116f9517275fa85047495515add43e41dfe8e0bef6e333c6b344/coverage-7.10.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c9a8b7a34a4de3ed987f636f71881cd3b8339f61118b1aa311fbda12741bff0b", size = 218059 }, + { url = "https://files.pythonhosted.org/packages/92/97/8a3ceff833d27c7492af4f39d5da6761e9ff624831db9e9f25b3886ddbca/coverage-7.10.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dd5af36092430c2b075cee966719898f2ae87b636cefb85a653f1d0ba5d5393", size = 218287 }, + { url = "https://files.pythonhosted.org/packages/92/d8/50b4a32580cf41ff0423777a2791aaf3269ab60c840b62009aec12d3970d/coverage-7.10.6-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0353b0f0850d49ada66fdd7d0c7cdb0f86b900bb9e367024fd14a60cecc1e27", size = 259625 }, + { url = "https://files.pythonhosted.org/packages/7e/7e/6a7df5a6fb440a0179d94a348eb6616ed4745e7df26bf2a02bc4db72c421/coverage-7.10.6-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b9ae13d5d3e8aeca9ca94198aa7b3ebbc5acfada557d724f2a1f03d2c0b0df", size = 261801 }, + { url = "https://files.pythonhosted.org/packages/3a/4c/a270a414f4ed5d196b9d3d67922968e768cd971d1b251e1b4f75e9362f75/coverage-7.10.6-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:675824a363cc05781b1527b39dc2587b8984965834a748177ee3c37b64ffeafb", size = 264027 }, + { url = "https://files.pythonhosted.org/packages/9c/8b/3210d663d594926c12f373c5370bf1e7c5c3a427519a8afa65b561b9a55c/coverage-7.10.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:692d70ea725f471a547c305f0d0fc6a73480c62fb0da726370c088ab21aed282", size = 261576 }, + { url = "https://files.pythonhosted.org/packages/72/d0/e1961eff67e9e1dba3fc5eb7a4caf726b35a5b03776892da8d79ec895775/coverage-7.10.6-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:851430a9a361c7a8484a36126d1d0ff8d529d97385eacc8dfdc9bfc8c2d2cbe4", size = 259341 }, + { url = "https://files.pythonhosted.org/packages/3a/06/d6478d152cd189b33eac691cba27a40704990ba95de49771285f34a5861e/coverage-7.10.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d9369a23186d189b2fc95cc08b8160ba242057e887d766864f7adf3c46b2df21", size = 260468 }, + { url = "https://files.pythonhosted.org/packages/ed/73/737440247c914a332f0b47f7598535b29965bf305e19bbc22d4c39615d2b/coverage-7.10.6-cp313-cp313t-win32.whl", hash = "sha256:92be86fcb125e9bda0da7806afd29a3fd33fdf58fba5d60318399adf40bf37d0", size = 220429 }, + { url = "https://files.pythonhosted.org/packages/bd/76/b92d3214740f2357ef4a27c75a526eb6c28f79c402e9f20a922c295c05e2/coverage-7.10.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6b3039e2ca459a70c79523d39347d83b73f2f06af5624905eba7ec34d64d80b5", size = 221493 }, + { url = "https://files.pythonhosted.org/packages/fc/8e/6dcb29c599c8a1f654ec6cb68d76644fe635513af16e932d2d4ad1e5ac6e/coverage-7.10.6-cp313-cp313t-win_arm64.whl", hash = "sha256:3fb99d0786fe17b228eab663d16bee2288e8724d26a199c29325aac4b0319b9b", size = 219757 }, + { url = "https://files.pythonhosted.org/packages/d3/aa/76cf0b5ec00619ef208da4689281d48b57f2c7fde883d14bf9441b74d59f/coverage-7.10.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6008a021907be8c4c02f37cdc3ffb258493bdebfeaf9a839f9e71dfdc47b018e", size = 217331 }, + { url = "https://files.pythonhosted.org/packages/65/91/8e41b8c7c505d398d7730206f3cbb4a875a35ca1041efc518051bfce0f6b/coverage-7.10.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5e75e37f23eb144e78940b40395b42f2321951206a4f50e23cfd6e8a198d3ceb", size = 217607 }, + { url = "https://files.pythonhosted.org/packages/87/7f/f718e732a423d442e6616580a951b8d1ec3575ea48bcd0e2228386805e79/coverage-7.10.6-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0f7cb359a448e043c576f0da00aa8bfd796a01b06aa610ca453d4dde09cc1034", size = 248663 }, + { url = "https://files.pythonhosted.org/packages/e6/52/c1106120e6d801ac03e12b5285e971e758e925b6f82ee9b86db3aa10045d/coverage-7.10.6-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c68018e4fc4e14b5668f1353b41ccf4bc83ba355f0e1b3836861c6f042d89ac1", size = 251197 }, + { url = "https://files.pythonhosted.org/packages/3d/ec/3a8645b1bb40e36acde9c0609f08942852a4af91a937fe2c129a38f2d3f5/coverage-7.10.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd4b2b0707fc55afa160cd5fc33b27ccbf75ca11d81f4ec9863d5793fc6df56a", size = 252551 }, + { url = "https://files.pythonhosted.org/packages/a1/70/09ecb68eeb1155b28a1d16525fd3a9b65fbe75337311a99830df935d62b6/coverage-7.10.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cec13817a651f8804a86e4f79d815b3b28472c910e099e4d5a0e8a3b6a1d4cb", size = 250553 }, + { url = "https://files.pythonhosted.org/packages/c6/80/47df374b893fa812e953b5bc93dcb1427a7b3d7a1a7d2db33043d17f74b9/coverage-7.10.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f2a6a8e06bbda06f78739f40bfb56c45d14eb8249d0f0ea6d4b3d48e1f7c695d", size = 248486 }, + { url = "https://files.pythonhosted.org/packages/4a/65/9f98640979ecee1b0d1a7164b589de720ddf8100d1747d9bbdb84be0c0fb/coverage-7.10.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:081b98395ced0d9bcf60ada7661a0b75f36b78b9d7e39ea0790bb4ed8da14747", size = 249981 }, + { url = "https://files.pythonhosted.org/packages/1f/55/eeb6603371e6629037f47bd25bef300387257ed53a3c5fdb159b7ac8c651/coverage-7.10.6-cp314-cp314-win32.whl", hash = "sha256:6937347c5d7d069ee776b2bf4e1212f912a9f1f141a429c475e6089462fcecc5", size = 220054 }, + { url = "https://files.pythonhosted.org/packages/15/d1/a0912b7611bc35412e919a2cd59ae98e7ea3b475e562668040a43fb27897/coverage-7.10.6-cp314-cp314-win_amd64.whl", hash = "sha256:adec1d980fa07e60b6ef865f9e5410ba760e4e1d26f60f7e5772c73b9a5b0713", size = 220851 }, + { url = "https://files.pythonhosted.org/packages/ef/2d/11880bb8ef80a45338e0b3e0725e4c2d73ffbb4822c29d987078224fd6a5/coverage-7.10.6-cp314-cp314-win_arm64.whl", hash = "sha256:a80f7aef9535442bdcf562e5a0d5a5538ce8abe6bb209cfbf170c462ac2c2a32", size = 219429 }, + { url = "https://files.pythonhosted.org/packages/83/c0/1f00caad775c03a700146f55536ecd097a881ff08d310a58b353a1421be0/coverage-7.10.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:0de434f4fbbe5af4fa7989521c655c8c779afb61c53ab561b64dcee6149e4c65", size = 218080 }, + { url = "https://files.pythonhosted.org/packages/a9/c4/b1c5d2bd7cc412cbeb035e257fd06ed4e3e139ac871d16a07434e145d18d/coverage-7.10.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6e31b8155150c57e5ac43ccd289d079eb3f825187d7c66e755a055d2c85794c6", size = 218293 }, + { url = "https://files.pythonhosted.org/packages/3f/07/4468d37c94724bf6ec354e4ec2f205fda194343e3e85fd2e59cec57e6a54/coverage-7.10.6-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:98cede73eb83c31e2118ae8d379c12e3e42736903a8afcca92a7218e1f2903b0", size = 259800 }, + { url = "https://files.pythonhosted.org/packages/82/d8/f8fb351be5fee31690cd8da768fd62f1cfab33c31d9f7baba6cd8960f6b8/coverage-7.10.6-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f863c08f4ff6b64fa8045b1e3da480f5374779ef187f07b82e0538c68cb4ff8e", size = 261965 }, + { url = "https://files.pythonhosted.org/packages/e8/70/65d4d7cfc75c5c6eb2fed3ee5cdf420fd8ae09c4808723a89a81d5b1b9c3/coverage-7.10.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b38261034fda87be356f2c3f42221fdb4171c3ce7658066ae449241485390d5", size = 264220 }, + { url = "https://files.pythonhosted.org/packages/98/3c/069df106d19024324cde10e4ec379fe2fb978017d25e97ebee23002fbadf/coverage-7.10.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e93b1476b79eae849dc3872faeb0bf7948fd9ea34869590bc16a2a00b9c82a7", size = 261660 }, + { url = "https://files.pythonhosted.org/packages/fc/8a/2974d53904080c5dc91af798b3a54a4ccb99a45595cc0dcec6eb9616a57d/coverage-7.10.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ff8a991f70f4c0cf53088abf1e3886edcc87d53004c7bb94e78650b4d3dac3b5", size = 259417 }, + { url = "https://files.pythonhosted.org/packages/30/38/9616a6b49c686394b318974d7f6e08f38b8af2270ce7488e879888d1e5db/coverage-7.10.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ac765b026c9f33044419cbba1da913cfb82cca1b60598ac1c7a5ed6aac4621a0", size = 260567 }, + { url = "https://files.pythonhosted.org/packages/76/16/3ed2d6312b371a8cf804abf4e14895b70e4c3491c6e53536d63fd0958a8d/coverage-7.10.6-cp314-cp314t-win32.whl", hash = "sha256:441c357d55f4936875636ef2cfb3bee36e466dcf50df9afbd398ce79dba1ebb7", size = 220831 }, + { url = "https://files.pythonhosted.org/packages/d5/e5/d38d0cb830abede2adb8b147770d2a3d0e7fecc7228245b9b1ae6c24930a/coverage-7.10.6-cp314-cp314t-win_amd64.whl", hash = "sha256:073711de3181b2e204e4870ac83a7c4853115b42e9cd4d145f2231e12d670930", size = 221950 }, + { url = "https://files.pythonhosted.org/packages/f4/51/e48e550f6279349895b0ffcd6d2a690e3131ba3a7f4eafccc141966d4dea/coverage-7.10.6-cp314-cp314t-win_arm64.whl", hash = "sha256:137921f2bac5559334ba66122b753db6dc5d1cf01eb7b64eb412bb0d064ef35b", size = 219969 }, + { url = "https://files.pythonhosted.org/packages/44/0c/50db5379b615854b5cf89146f8f5bd1d5a9693d7f3a987e269693521c404/coverage-7.10.6-py3-none-any.whl", hash = "sha256:92c4ecf6bf11b2e85fd4d8204814dc26e6a19f0c9d938c207c5cb0eadfcabbe3", size = 208986 }, ] [package.optional-dependencies] @@ -880,114 +935,114 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "(platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_python_implementation != 'PyPy' and sys_platform == 'linux') or (platform_python_implementation != 'PyPy' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a7/35/c495bffc2056f2dadb32434f1feedd79abde2a7f8363e1974afa9c33c7e2/cryptography-45.0.7.tar.gz", hash = "sha256:4b1654dfc64ea479c242508eb8c724044f1e964a47d1d1cacc5132292d851971", size = 744980, upload-time = "2025-09-01T11:15:03.146Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/35/c495bffc2056f2dadb32434f1feedd79abde2a7f8363e1974afa9c33c7e2/cryptography-45.0.7.tar.gz", hash = "sha256:4b1654dfc64ea479c242508eb8c724044f1e964a47d1d1cacc5132292d851971", size = 744980 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/91/925c0ac74362172ae4516000fe877912e33b5983df735ff290c653de4913/cryptography-45.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3be4f21c6245930688bd9e162829480de027f8bf962ede33d4f8ba7d67a00cee", size = 7041105, upload-time = "2025-09-01T11:13:59.684Z" }, - { url = "https://files.pythonhosted.org/packages/fc/63/43641c5acce3a6105cf8bd5baeceeb1846bb63067d26dae3e5db59f1513a/cryptography-45.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:67285f8a611b0ebc0857ced2081e30302909f571a46bfa7a3cc0ad303fe015c6", size = 4205799, upload-time = "2025-09-01T11:14:02.517Z" }, - { url = "https://files.pythonhosted.org/packages/bc/29/c238dd9107f10bfde09a4d1c52fd38828b1aa353ced11f358b5dd2507d24/cryptography-45.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:577470e39e60a6cd7780793202e63536026d9b8641de011ed9d8174da9ca5339", size = 4430504, upload-time = "2025-09-01T11:14:04.522Z" }, - { url = "https://files.pythonhosted.org/packages/62/62/24203e7cbcc9bd7c94739428cd30680b18ae6b18377ae66075c8e4771b1b/cryptography-45.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4bd3e5c4b9682bc112d634f2c6ccc6736ed3635fc3319ac2bb11d768cc5a00d8", size = 4209542, upload-time = "2025-09-01T11:14:06.309Z" }, - { url = "https://files.pythonhosted.org/packages/cd/e3/e7de4771a08620eef2389b86cd87a2c50326827dea5528feb70595439ce4/cryptography-45.0.7-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:465ccac9d70115cd4de7186e60cfe989de73f7bb23e8a7aa45af18f7412e75bf", size = 3889244, upload-time = "2025-09-01T11:14:08.152Z" }, - { url = "https://files.pythonhosted.org/packages/96/b8/bca71059e79a0bb2f8e4ec61d9c205fbe97876318566cde3b5092529faa9/cryptography-45.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:16ede8a4f7929b4b7ff3642eba2bf79aa1d71f24ab6ee443935c0d269b6bc513", size = 4461975, upload-time = "2025-09-01T11:14:09.755Z" }, - { url = "https://files.pythonhosted.org/packages/58/67/3f5b26937fe1218c40e95ef4ff8d23c8dc05aa950d54200cc7ea5fb58d28/cryptography-45.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8978132287a9d3ad6b54fcd1e08548033cc09dc6aacacb6c004c73c3eb5d3ac3", size = 4209082, upload-time = "2025-09-01T11:14:11.229Z" }, - { url = "https://files.pythonhosted.org/packages/0e/e4/b3e68a4ac363406a56cf7b741eeb80d05284d8c60ee1a55cdc7587e2a553/cryptography-45.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b6a0e535baec27b528cb07a119f321ac024592388c5681a5ced167ae98e9fff3", size = 4460397, upload-time = "2025-09-01T11:14:12.924Z" }, - { url = "https://files.pythonhosted.org/packages/22/49/2c93f3cd4e3efc8cb22b02678c1fad691cff9dd71bb889e030d100acbfe0/cryptography-45.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a24ee598d10befaec178efdff6054bc4d7e883f615bfbcd08126a0f4931c83a6", size = 4337244, upload-time = "2025-09-01T11:14:14.431Z" }, - { url = "https://files.pythonhosted.org/packages/04/19/030f400de0bccccc09aa262706d90f2ec23d56bc4eb4f4e8268d0ddf3fb8/cryptography-45.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa26fa54c0a9384c27fcdc905a2fb7d60ac6e47d14bc2692145f2b3b1e2cfdbd", size = 4568862, upload-time = "2025-09-01T11:14:16.185Z" }, - { url = "https://files.pythonhosted.org/packages/29/56/3034a3a353efa65116fa20eb3c990a8c9f0d3db4085429040a7eef9ada5f/cryptography-45.0.7-cp311-abi3-win32.whl", hash = "sha256:bef32a5e327bd8e5af915d3416ffefdbe65ed975b646b3805be81b23580b57b8", size = 2936578, upload-time = "2025-09-01T11:14:17.638Z" }, - { url = "https://files.pythonhosted.org/packages/b3/61/0ab90f421c6194705a99d0fa9f6ee2045d916e4455fdbb095a9c2c9a520f/cryptography-45.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:3808e6b2e5f0b46d981c24d79648e5c25c35e59902ea4391a0dcb3e667bf7443", size = 3405400, upload-time = "2025-09-01T11:14:18.958Z" }, - { url = "https://files.pythonhosted.org/packages/63/e8/c436233ddf19c5f15b25ace33979a9dd2e7aa1a59209a0ee8554179f1cc0/cryptography-45.0.7-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bfb4c801f65dd61cedfc61a83732327fafbac55a47282e6f26f073ca7a41c3b2", size = 7021824, upload-time = "2025-09-01T11:14:20.954Z" }, - { url = "https://files.pythonhosted.org/packages/bc/4c/8f57f2500d0ccd2675c5d0cc462095adf3faa8c52294ba085c036befb901/cryptography-45.0.7-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:81823935e2f8d476707e85a78a405953a03ef7b7b4f55f93f7c2d9680e5e0691", size = 4202233, upload-time = "2025-09-01T11:14:22.454Z" }, - { url = "https://files.pythonhosted.org/packages/eb/ac/59b7790b4ccaed739fc44775ce4645c9b8ce54cbec53edf16c74fd80cb2b/cryptography-45.0.7-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3994c809c17fc570c2af12c9b840d7cea85a9fd3e5c0e0491f4fa3c029216d59", size = 4423075, upload-time = "2025-09-01T11:14:24.287Z" }, - { url = "https://files.pythonhosted.org/packages/b8/56/d4f07ea21434bf891faa088a6ac15d6d98093a66e75e30ad08e88aa2b9ba/cryptography-45.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dad43797959a74103cb59c5dac71409f9c27d34c8a05921341fb64ea8ccb1dd4", size = 4204517, upload-time = "2025-09-01T11:14:25.679Z" }, - { url = "https://files.pythonhosted.org/packages/e8/ac/924a723299848b4c741c1059752c7cfe09473b6fd77d2920398fc26bfb53/cryptography-45.0.7-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ce7a453385e4c4693985b4a4a3533e041558851eae061a58a5405363b098fcd3", size = 3882893, upload-time = "2025-09-01T11:14:27.1Z" }, - { url = "https://files.pythonhosted.org/packages/83/dc/4dab2ff0a871cc2d81d3ae6d780991c0192b259c35e4d83fe1de18b20c70/cryptography-45.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b04f85ac3a90c227b6e5890acb0edbaf3140938dbecf07bff618bf3638578cf1", size = 4450132, upload-time = "2025-09-01T11:14:28.58Z" }, - { url = "https://files.pythonhosted.org/packages/12/dd/b2882b65db8fc944585d7fb00d67cf84a9cef4e77d9ba8f69082e911d0de/cryptography-45.0.7-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:48c41a44ef8b8c2e80ca4527ee81daa4c527df3ecbc9423c41a420a9559d0e27", size = 4204086, upload-time = "2025-09-01T11:14:30.572Z" }, - { url = "https://files.pythonhosted.org/packages/5d/fa/1d5745d878048699b8eb87c984d4ccc5da4f5008dfd3ad7a94040caca23a/cryptography-45.0.7-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f3df7b3d0f91b88b2106031fd995802a2e9ae13e02c36c1fc075b43f420f3a17", size = 4449383, upload-time = "2025-09-01T11:14:32.046Z" }, - { url = "https://files.pythonhosted.org/packages/36/8b/fc61f87931bc030598e1876c45b936867bb72777eac693e905ab89832670/cryptography-45.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd342f085542f6eb894ca00ef70236ea46070c8a13824c6bde0dfdcd36065b9b", size = 4332186, upload-time = "2025-09-01T11:14:33.95Z" }, - { url = "https://files.pythonhosted.org/packages/0b/11/09700ddad7443ccb11d674efdbe9a832b4455dc1f16566d9bd3834922ce5/cryptography-45.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1993a1bb7e4eccfb922b6cd414f072e08ff5816702a0bdb8941c247a6b1b287c", size = 4561639, upload-time = "2025-09-01T11:14:35.343Z" }, - { url = "https://files.pythonhosted.org/packages/71/ed/8f4c1337e9d3b94d8e50ae0b08ad0304a5709d483bfcadfcc77a23dbcb52/cryptography-45.0.7-cp37-abi3-win32.whl", hash = "sha256:18fcf70f243fe07252dcb1b268a687f2358025ce32f9f88028ca5c364b123ef5", size = 2926552, upload-time = "2025-09-01T11:14:36.929Z" }, - { url = "https://files.pythonhosted.org/packages/bc/ff/026513ecad58dacd45d1d24ebe52b852165a26e287177de1d545325c0c25/cryptography-45.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:7285a89df4900ed3bfaad5679b1e668cb4b38a8de1ccbfc84b05f34512da0a90", size = 3392742, upload-time = "2025-09-01T11:14:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/13/3e/e42f1528ca1ea82256b835191eab1be014e0f9f934b60d98b0be8a38ed70/cryptography-45.0.7-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:de58755d723e86175756f463f2f0bddd45cc36fbd62601228a3f8761c9f58252", size = 3572442, upload-time = "2025-09-01T11:14:39.836Z" }, - { url = "https://files.pythonhosted.org/packages/59/aa/e947693ab08674a2663ed2534cd8d345cf17bf6a1facf99273e8ec8986dc/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a20e442e917889d1a6b3c570c9e3fa2fdc398c20868abcea268ea33c024c4083", size = 4142233, upload-time = "2025-09-01T11:14:41.305Z" }, - { url = "https://files.pythonhosted.org/packages/24/06/09b6f6a2fc43474a32b8fe259038eef1500ee3d3c141599b57ac6c57612c/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:258e0dff86d1d891169b5af222d362468a9570e2532923088658aa866eb11130", size = 4376202, upload-time = "2025-09-01T11:14:43.047Z" }, - { url = "https://files.pythonhosted.org/packages/00/f2/c166af87e95ce6ae6d38471a7e039d3a0549c2d55d74e059680162052824/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d97cf502abe2ab9eff8bd5e4aca274da8d06dd3ef08b759a8d6143f4ad65d4b4", size = 4141900, upload-time = "2025-09-01T11:14:45.089Z" }, - { url = "https://files.pythonhosted.org/packages/16/b9/e96e0b6cb86eae27ea51fa8a3151535a18e66fe7c451fa90f7f89c85f541/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:c987dad82e8c65ebc985f5dae5e74a3beda9d0a2a4daf8a1115f3772b59e5141", size = 4375562, upload-time = "2025-09-01T11:14:47.166Z" }, - { url = "https://files.pythonhosted.org/packages/36/d0/36e8ee39274e9d77baf7d0dafda680cba6e52f3936b846f0d56d64fec915/cryptography-45.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c13b1e3afd29a5b3b2656257f14669ca8fa8d7956d509926f0b130b600b50ab7", size = 3322781, upload-time = "2025-09-01T11:14:48.747Z" }, - { url = "https://files.pythonhosted.org/packages/99/4e/49199a4c82946938a3e05d2e8ad9482484ba48bbc1e809e3d506c686d051/cryptography-45.0.7-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a862753b36620af6fc54209264f92c716367f2f0ff4624952276a6bbd18cbde", size = 3584634, upload-time = "2025-09-01T11:14:50.593Z" }, - { url = "https://files.pythonhosted.org/packages/16/ce/5f6ff59ea9c7779dba51b84871c19962529bdcc12e1a6ea172664916c550/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:06ce84dc14df0bf6ea84666f958e6080cdb6fe1231be2a51f3fc1267d9f3fb34", size = 4149533, upload-time = "2025-09-01T11:14:52.091Z" }, - { url = "https://files.pythonhosted.org/packages/ce/13/b3cfbd257ac96da4b88b46372e662009b7a16833bfc5da33bb97dd5631ae/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d0c5c6bac22b177bf8da7435d9d27a6834ee130309749d162b26c3105c0795a9", size = 4385557, upload-time = "2025-09-01T11:14:53.551Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c5/8c59d6b7c7b439ba4fc8d0cab868027fd095f215031bc123c3a070962912/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:2f641b64acc00811da98df63df7d59fd4706c0df449da71cb7ac39a0732b40ae", size = 4149023, upload-time = "2025-09-01T11:14:55.022Z" }, - { url = "https://files.pythonhosted.org/packages/55/32/05385c86d6ca9ab0b4d5bb442d2e3d85e727939a11f3e163fc776ce5eb40/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:f5414a788ecc6ee6bc58560e85ca624258a55ca434884445440a810796ea0e0b", size = 4385722, upload-time = "2025-09-01T11:14:57.319Z" }, - { url = "https://files.pythonhosted.org/packages/23/87/7ce86f3fa14bc11a5a48c30d8103c26e09b6465f8d8e9d74cf7a0714f043/cryptography-45.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:1f3d56f73595376f4244646dd5c5870c14c196949807be39e79e7bd9bac3da63", size = 3332908, upload-time = "2025-09-01T11:14:58.78Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/925c0ac74362172ae4516000fe877912e33b5983df735ff290c653de4913/cryptography-45.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3be4f21c6245930688bd9e162829480de027f8bf962ede33d4f8ba7d67a00cee", size = 7041105 }, + { url = "https://files.pythonhosted.org/packages/fc/63/43641c5acce3a6105cf8bd5baeceeb1846bb63067d26dae3e5db59f1513a/cryptography-45.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:67285f8a611b0ebc0857ced2081e30302909f571a46bfa7a3cc0ad303fe015c6", size = 4205799 }, + { url = "https://files.pythonhosted.org/packages/bc/29/c238dd9107f10bfde09a4d1c52fd38828b1aa353ced11f358b5dd2507d24/cryptography-45.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:577470e39e60a6cd7780793202e63536026d9b8641de011ed9d8174da9ca5339", size = 4430504 }, + { url = "https://files.pythonhosted.org/packages/62/62/24203e7cbcc9bd7c94739428cd30680b18ae6b18377ae66075c8e4771b1b/cryptography-45.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4bd3e5c4b9682bc112d634f2c6ccc6736ed3635fc3319ac2bb11d768cc5a00d8", size = 4209542 }, + { url = "https://files.pythonhosted.org/packages/cd/e3/e7de4771a08620eef2389b86cd87a2c50326827dea5528feb70595439ce4/cryptography-45.0.7-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:465ccac9d70115cd4de7186e60cfe989de73f7bb23e8a7aa45af18f7412e75bf", size = 3889244 }, + { url = "https://files.pythonhosted.org/packages/96/b8/bca71059e79a0bb2f8e4ec61d9c205fbe97876318566cde3b5092529faa9/cryptography-45.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:16ede8a4f7929b4b7ff3642eba2bf79aa1d71f24ab6ee443935c0d269b6bc513", size = 4461975 }, + { url = "https://files.pythonhosted.org/packages/58/67/3f5b26937fe1218c40e95ef4ff8d23c8dc05aa950d54200cc7ea5fb58d28/cryptography-45.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8978132287a9d3ad6b54fcd1e08548033cc09dc6aacacb6c004c73c3eb5d3ac3", size = 4209082 }, + { url = "https://files.pythonhosted.org/packages/0e/e4/b3e68a4ac363406a56cf7b741eeb80d05284d8c60ee1a55cdc7587e2a553/cryptography-45.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b6a0e535baec27b528cb07a119f321ac024592388c5681a5ced167ae98e9fff3", size = 4460397 }, + { url = "https://files.pythonhosted.org/packages/22/49/2c93f3cd4e3efc8cb22b02678c1fad691cff9dd71bb889e030d100acbfe0/cryptography-45.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a24ee598d10befaec178efdff6054bc4d7e883f615bfbcd08126a0f4931c83a6", size = 4337244 }, + { url = "https://files.pythonhosted.org/packages/04/19/030f400de0bccccc09aa262706d90f2ec23d56bc4eb4f4e8268d0ddf3fb8/cryptography-45.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa26fa54c0a9384c27fcdc905a2fb7d60ac6e47d14bc2692145f2b3b1e2cfdbd", size = 4568862 }, + { url = "https://files.pythonhosted.org/packages/29/56/3034a3a353efa65116fa20eb3c990a8c9f0d3db4085429040a7eef9ada5f/cryptography-45.0.7-cp311-abi3-win32.whl", hash = "sha256:bef32a5e327bd8e5af915d3416ffefdbe65ed975b646b3805be81b23580b57b8", size = 2936578 }, + { url = "https://files.pythonhosted.org/packages/b3/61/0ab90f421c6194705a99d0fa9f6ee2045d916e4455fdbb095a9c2c9a520f/cryptography-45.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:3808e6b2e5f0b46d981c24d79648e5c25c35e59902ea4391a0dcb3e667bf7443", size = 3405400 }, + { url = "https://files.pythonhosted.org/packages/63/e8/c436233ddf19c5f15b25ace33979a9dd2e7aa1a59209a0ee8554179f1cc0/cryptography-45.0.7-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bfb4c801f65dd61cedfc61a83732327fafbac55a47282e6f26f073ca7a41c3b2", size = 7021824 }, + { url = "https://files.pythonhosted.org/packages/bc/4c/8f57f2500d0ccd2675c5d0cc462095adf3faa8c52294ba085c036befb901/cryptography-45.0.7-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:81823935e2f8d476707e85a78a405953a03ef7b7b4f55f93f7c2d9680e5e0691", size = 4202233 }, + { url = "https://files.pythonhosted.org/packages/eb/ac/59b7790b4ccaed739fc44775ce4645c9b8ce54cbec53edf16c74fd80cb2b/cryptography-45.0.7-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3994c809c17fc570c2af12c9b840d7cea85a9fd3e5c0e0491f4fa3c029216d59", size = 4423075 }, + { url = "https://files.pythonhosted.org/packages/b8/56/d4f07ea21434bf891faa088a6ac15d6d98093a66e75e30ad08e88aa2b9ba/cryptography-45.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dad43797959a74103cb59c5dac71409f9c27d34c8a05921341fb64ea8ccb1dd4", size = 4204517 }, + { url = "https://files.pythonhosted.org/packages/e8/ac/924a723299848b4c741c1059752c7cfe09473b6fd77d2920398fc26bfb53/cryptography-45.0.7-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ce7a453385e4c4693985b4a4a3533e041558851eae061a58a5405363b098fcd3", size = 3882893 }, + { url = "https://files.pythonhosted.org/packages/83/dc/4dab2ff0a871cc2d81d3ae6d780991c0192b259c35e4d83fe1de18b20c70/cryptography-45.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b04f85ac3a90c227b6e5890acb0edbaf3140938dbecf07bff618bf3638578cf1", size = 4450132 }, + { url = "https://files.pythonhosted.org/packages/12/dd/b2882b65db8fc944585d7fb00d67cf84a9cef4e77d9ba8f69082e911d0de/cryptography-45.0.7-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:48c41a44ef8b8c2e80ca4527ee81daa4c527df3ecbc9423c41a420a9559d0e27", size = 4204086 }, + { url = "https://files.pythonhosted.org/packages/5d/fa/1d5745d878048699b8eb87c984d4ccc5da4f5008dfd3ad7a94040caca23a/cryptography-45.0.7-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f3df7b3d0f91b88b2106031fd995802a2e9ae13e02c36c1fc075b43f420f3a17", size = 4449383 }, + { url = "https://files.pythonhosted.org/packages/36/8b/fc61f87931bc030598e1876c45b936867bb72777eac693e905ab89832670/cryptography-45.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd342f085542f6eb894ca00ef70236ea46070c8a13824c6bde0dfdcd36065b9b", size = 4332186 }, + { url = "https://files.pythonhosted.org/packages/0b/11/09700ddad7443ccb11d674efdbe9a832b4455dc1f16566d9bd3834922ce5/cryptography-45.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1993a1bb7e4eccfb922b6cd414f072e08ff5816702a0bdb8941c247a6b1b287c", size = 4561639 }, + { url = "https://files.pythonhosted.org/packages/71/ed/8f4c1337e9d3b94d8e50ae0b08ad0304a5709d483bfcadfcc77a23dbcb52/cryptography-45.0.7-cp37-abi3-win32.whl", hash = "sha256:18fcf70f243fe07252dcb1b268a687f2358025ce32f9f88028ca5c364b123ef5", size = 2926552 }, + { url = "https://files.pythonhosted.org/packages/bc/ff/026513ecad58dacd45d1d24ebe52b852165a26e287177de1d545325c0c25/cryptography-45.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:7285a89df4900ed3bfaad5679b1e668cb4b38a8de1ccbfc84b05f34512da0a90", size = 3392742 }, + { url = "https://files.pythonhosted.org/packages/13/3e/e42f1528ca1ea82256b835191eab1be014e0f9f934b60d98b0be8a38ed70/cryptography-45.0.7-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:de58755d723e86175756f463f2f0bddd45cc36fbd62601228a3f8761c9f58252", size = 3572442 }, + { url = "https://files.pythonhosted.org/packages/59/aa/e947693ab08674a2663ed2534cd8d345cf17bf6a1facf99273e8ec8986dc/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a20e442e917889d1a6b3c570c9e3fa2fdc398c20868abcea268ea33c024c4083", size = 4142233 }, + { url = "https://files.pythonhosted.org/packages/24/06/09b6f6a2fc43474a32b8fe259038eef1500ee3d3c141599b57ac6c57612c/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:258e0dff86d1d891169b5af222d362468a9570e2532923088658aa866eb11130", size = 4376202 }, + { url = "https://files.pythonhosted.org/packages/00/f2/c166af87e95ce6ae6d38471a7e039d3a0549c2d55d74e059680162052824/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d97cf502abe2ab9eff8bd5e4aca274da8d06dd3ef08b759a8d6143f4ad65d4b4", size = 4141900 }, + { url = "https://files.pythonhosted.org/packages/16/b9/e96e0b6cb86eae27ea51fa8a3151535a18e66fe7c451fa90f7f89c85f541/cryptography-45.0.7-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:c987dad82e8c65ebc985f5dae5e74a3beda9d0a2a4daf8a1115f3772b59e5141", size = 4375562 }, + { url = "https://files.pythonhosted.org/packages/36/d0/36e8ee39274e9d77baf7d0dafda680cba6e52f3936b846f0d56d64fec915/cryptography-45.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c13b1e3afd29a5b3b2656257f14669ca8fa8d7956d509926f0b130b600b50ab7", size = 3322781 }, + { url = "https://files.pythonhosted.org/packages/99/4e/49199a4c82946938a3e05d2e8ad9482484ba48bbc1e809e3d506c686d051/cryptography-45.0.7-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a862753b36620af6fc54209264f92c716367f2f0ff4624952276a6bbd18cbde", size = 3584634 }, + { url = "https://files.pythonhosted.org/packages/16/ce/5f6ff59ea9c7779dba51b84871c19962529bdcc12e1a6ea172664916c550/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:06ce84dc14df0bf6ea84666f958e6080cdb6fe1231be2a51f3fc1267d9f3fb34", size = 4149533 }, + { url = "https://files.pythonhosted.org/packages/ce/13/b3cfbd257ac96da4b88b46372e662009b7a16833bfc5da33bb97dd5631ae/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d0c5c6bac22b177bf8da7435d9d27a6834ee130309749d162b26c3105c0795a9", size = 4385557 }, + { url = "https://files.pythonhosted.org/packages/1c/c5/8c59d6b7c7b439ba4fc8d0cab868027fd095f215031bc123c3a070962912/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:2f641b64acc00811da98df63df7d59fd4706c0df449da71cb7ac39a0732b40ae", size = 4149023 }, + { url = "https://files.pythonhosted.org/packages/55/32/05385c86d6ca9ab0b4d5bb442d2e3d85e727939a11f3e163fc776ce5eb40/cryptography-45.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:f5414a788ecc6ee6bc58560e85ca624258a55ca434884445440a810796ea0e0b", size = 4385722 }, + { url = "https://files.pythonhosted.org/packages/23/87/7ce86f3fa14bc11a5a48c30d8103c26e09b6465f8d8e9d74cf7a0714f043/cryptography-45.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:1f3d56f73595376f4244646dd5c5870c14c196949807be39e79e7bd9bac3da63", size = 3332908 }, ] [[package]] name = "debugpy" version = "1.8.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/d4/722d0bcc7986172ac2ef3c979ad56a1030e3afd44ced136d45f8142b1f4a/debugpy-1.8.16.tar.gz", hash = "sha256:31e69a1feb1cf6b51efbed3f6c9b0ef03bc46ff050679c4be7ea6d2e23540870", size = 1643809, upload-time = "2025-08-06T18:00:02.647Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/d4/722d0bcc7986172ac2ef3c979ad56a1030e3afd44ced136d45f8142b1f4a/debugpy-1.8.16.tar.gz", hash = "sha256:31e69a1feb1cf6b51efbed3f6c9b0ef03bc46ff050679c4be7ea6d2e23540870", size = 1643809 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/fd/f1b75ebc61d90882595b81d808efd3573c082e1c3407850d9dccac4ae904/debugpy-1.8.16-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:2a3958fb9c2f40ed8ea48a0d34895b461de57a1f9862e7478716c35d76f56c65", size = 2085511, upload-time = "2025-08-06T18:00:05.067Z" }, - { url = "https://files.pythonhosted.org/packages/df/5e/c5c1934352871128b30a1a144a58b5baa546e1b57bd47dbed788bad4431c/debugpy-1.8.16-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5ca7314042e8a614cc2574cd71f6ccd7e13a9708ce3c6d8436959eae56f2378", size = 3562094, upload-time = "2025-08-06T18:00:06.66Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d5/2ebe42377e5a78dc786afc25e61ee83c5628d63f32dfa41092597d52fe83/debugpy-1.8.16-cp310-cp310-win32.whl", hash = "sha256:8624a6111dc312ed8c363347a0b59c5acc6210d897e41a7c069de3c53235c9a6", size = 5234277, upload-time = "2025-08-06T18:00:08.429Z" }, - { url = "https://files.pythonhosted.org/packages/54/f8/e774ad16a60b9913213dbabb7472074c5a7b0d84f07c1f383040a9690057/debugpy-1.8.16-cp310-cp310-win_amd64.whl", hash = "sha256:fee6db83ea5c978baf042440cfe29695e1a5d48a30147abf4c3be87513609817", size = 5266011, upload-time = "2025-08-06T18:00:10.162Z" }, - { url = "https://files.pythonhosted.org/packages/63/d6/ad70ba8b49b23fa286fb21081cf732232cc19374af362051da9c7537ae52/debugpy-1.8.16-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:67371b28b79a6a12bcc027d94a06158f2fde223e35b5c4e0783b6f9d3b39274a", size = 2184063, upload-time = "2025-08-06T18:00:11.885Z" }, - { url = "https://files.pythonhosted.org/packages/aa/49/7b03e88dea9759a4c7910143f87f92beb494daaae25560184ff4ae883f9e/debugpy-1.8.16-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2abae6dd02523bec2dee16bd6b0781cccb53fd4995e5c71cc659b5f45581898", size = 3134837, upload-time = "2025-08-06T18:00:13.782Z" }, - { url = "https://files.pythonhosted.org/packages/5d/52/b348930316921de7565fbe37a487d15409041713004f3d74d03eb077dbd4/debugpy-1.8.16-cp311-cp311-win32.whl", hash = "sha256:f8340a3ac2ed4f5da59e064aa92e39edd52729a88fbde7bbaa54e08249a04493", size = 5159142, upload-time = "2025-08-06T18:00:15.391Z" }, - { url = "https://files.pythonhosted.org/packages/d8/ef/9aa9549ce1e10cea696d980292e71672a91ee4a6a691ce5f8629e8f48c49/debugpy-1.8.16-cp311-cp311-win_amd64.whl", hash = "sha256:70f5fcd6d4d0c150a878d2aa37391c52de788c3dc680b97bdb5e529cb80df87a", size = 5183117, upload-time = "2025-08-06T18:00:17.251Z" }, - { url = "https://files.pythonhosted.org/packages/61/fb/0387c0e108d842c902801bc65ccc53e5b91d8c169702a9bbf4f7efcedf0c/debugpy-1.8.16-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:b202e2843e32e80b3b584bcebfe0e65e0392920dc70df11b2bfe1afcb7a085e4", size = 2511822, upload-time = "2025-08-06T18:00:18.526Z" }, - { url = "https://files.pythonhosted.org/packages/37/44/19e02745cae22bf96440141f94e15a69a1afaa3a64ddfc38004668fcdebf/debugpy-1.8.16-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64473c4a306ba11a99fe0bb14622ba4fbd943eb004847d9b69b107bde45aa9ea", size = 4230135, upload-time = "2025-08-06T18:00:19.997Z" }, - { url = "https://files.pythonhosted.org/packages/f3/0b/19b1ba5ee4412f303475a2c7ad5858efb99c90eae5ec627aa6275c439957/debugpy-1.8.16-cp312-cp312-win32.whl", hash = "sha256:833a61ed446426e38b0dd8be3e9d45ae285d424f5bf6cd5b2b559c8f12305508", size = 5281271, upload-time = "2025-08-06T18:00:21.281Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e0/bc62e2dc141de53bd03e2c7cb9d7011de2e65e8bdcdaa26703e4d28656ba/debugpy-1.8.16-cp312-cp312-win_amd64.whl", hash = "sha256:75f204684581e9ef3dc2f67687c3c8c183fde2d6675ab131d94084baf8084121", size = 5323149, upload-time = "2025-08-06T18:00:23.033Z" }, - { url = "https://files.pythonhosted.org/packages/62/66/607ab45cc79e60624df386e233ab64a6d8d39ea02e7f80e19c1d451345bb/debugpy-1.8.16-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:85df3adb1de5258dca910ae0bb185e48c98801ec15018a263a92bb06be1c8787", size = 2496157, upload-time = "2025-08-06T18:00:24.361Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a0/c95baae08a75bceabb79868d663a0736655e427ab9c81fb848da29edaeac/debugpy-1.8.16-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee89e948bc236a5c43c4214ac62d28b29388453f5fd328d739035e205365f0b", size = 4222491, upload-time = "2025-08-06T18:00:25.806Z" }, - { url = "https://files.pythonhosted.org/packages/5b/2f/1c8db6ddd8a257c3cd2c46413b267f1d5fa3df910401c899513ce30392d6/debugpy-1.8.16-cp313-cp313-win32.whl", hash = "sha256:cf358066650439847ec5ff3dae1da98b5461ea5da0173d93d5e10f477c94609a", size = 5281126, upload-time = "2025-08-06T18:00:27.207Z" }, - { url = "https://files.pythonhosted.org/packages/d3/ba/c3e154ab307366d6c5a9c1b68de04914e2ce7fa2f50d578311d8cc5074b2/debugpy-1.8.16-cp313-cp313-win_amd64.whl", hash = "sha256:b5aea1083f6f50023e8509399d7dc6535a351cc9f2e8827d1e093175e4d9fa4c", size = 5323094, upload-time = "2025-08-06T18:00:29.03Z" }, - { url = "https://files.pythonhosted.org/packages/52/57/ecc9ae29fa5b2d90107cd1d9bf8ed19aacb74b2264d986ae9d44fe9bdf87/debugpy-1.8.16-py2.py3-none-any.whl", hash = "sha256:19c9521962475b87da6f673514f7fd610328757ec993bf7ec0d8c96f9a325f9e", size = 5287700, upload-time = "2025-08-06T18:00:42.333Z" }, + { url = "https://files.pythonhosted.org/packages/ce/fd/f1b75ebc61d90882595b81d808efd3573c082e1c3407850d9dccac4ae904/debugpy-1.8.16-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:2a3958fb9c2f40ed8ea48a0d34895b461de57a1f9862e7478716c35d76f56c65", size = 2085511 }, + { url = "https://files.pythonhosted.org/packages/df/5e/c5c1934352871128b30a1a144a58b5baa546e1b57bd47dbed788bad4431c/debugpy-1.8.16-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5ca7314042e8a614cc2574cd71f6ccd7e13a9708ce3c6d8436959eae56f2378", size = 3562094 }, + { url = "https://files.pythonhosted.org/packages/c9/d5/2ebe42377e5a78dc786afc25e61ee83c5628d63f32dfa41092597d52fe83/debugpy-1.8.16-cp310-cp310-win32.whl", hash = "sha256:8624a6111dc312ed8c363347a0b59c5acc6210d897e41a7c069de3c53235c9a6", size = 5234277 }, + { url = "https://files.pythonhosted.org/packages/54/f8/e774ad16a60b9913213dbabb7472074c5a7b0d84f07c1f383040a9690057/debugpy-1.8.16-cp310-cp310-win_amd64.whl", hash = "sha256:fee6db83ea5c978baf042440cfe29695e1a5d48a30147abf4c3be87513609817", size = 5266011 }, + { url = "https://files.pythonhosted.org/packages/63/d6/ad70ba8b49b23fa286fb21081cf732232cc19374af362051da9c7537ae52/debugpy-1.8.16-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:67371b28b79a6a12bcc027d94a06158f2fde223e35b5c4e0783b6f9d3b39274a", size = 2184063 }, + { url = "https://files.pythonhosted.org/packages/aa/49/7b03e88dea9759a4c7910143f87f92beb494daaae25560184ff4ae883f9e/debugpy-1.8.16-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2abae6dd02523bec2dee16bd6b0781cccb53fd4995e5c71cc659b5f45581898", size = 3134837 }, + { url = "https://files.pythonhosted.org/packages/5d/52/b348930316921de7565fbe37a487d15409041713004f3d74d03eb077dbd4/debugpy-1.8.16-cp311-cp311-win32.whl", hash = "sha256:f8340a3ac2ed4f5da59e064aa92e39edd52729a88fbde7bbaa54e08249a04493", size = 5159142 }, + { url = "https://files.pythonhosted.org/packages/d8/ef/9aa9549ce1e10cea696d980292e71672a91ee4a6a691ce5f8629e8f48c49/debugpy-1.8.16-cp311-cp311-win_amd64.whl", hash = "sha256:70f5fcd6d4d0c150a878d2aa37391c52de788c3dc680b97bdb5e529cb80df87a", size = 5183117 }, + { url = "https://files.pythonhosted.org/packages/61/fb/0387c0e108d842c902801bc65ccc53e5b91d8c169702a9bbf4f7efcedf0c/debugpy-1.8.16-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:b202e2843e32e80b3b584bcebfe0e65e0392920dc70df11b2bfe1afcb7a085e4", size = 2511822 }, + { url = "https://files.pythonhosted.org/packages/37/44/19e02745cae22bf96440141f94e15a69a1afaa3a64ddfc38004668fcdebf/debugpy-1.8.16-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64473c4a306ba11a99fe0bb14622ba4fbd943eb004847d9b69b107bde45aa9ea", size = 4230135 }, + { url = "https://files.pythonhosted.org/packages/f3/0b/19b1ba5ee4412f303475a2c7ad5858efb99c90eae5ec627aa6275c439957/debugpy-1.8.16-cp312-cp312-win32.whl", hash = "sha256:833a61ed446426e38b0dd8be3e9d45ae285d424f5bf6cd5b2b559c8f12305508", size = 5281271 }, + { url = "https://files.pythonhosted.org/packages/b1/e0/bc62e2dc141de53bd03e2c7cb9d7011de2e65e8bdcdaa26703e4d28656ba/debugpy-1.8.16-cp312-cp312-win_amd64.whl", hash = "sha256:75f204684581e9ef3dc2f67687c3c8c183fde2d6675ab131d94084baf8084121", size = 5323149 }, + { url = "https://files.pythonhosted.org/packages/62/66/607ab45cc79e60624df386e233ab64a6d8d39ea02e7f80e19c1d451345bb/debugpy-1.8.16-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:85df3adb1de5258dca910ae0bb185e48c98801ec15018a263a92bb06be1c8787", size = 2496157 }, + { url = "https://files.pythonhosted.org/packages/4d/a0/c95baae08a75bceabb79868d663a0736655e427ab9c81fb848da29edaeac/debugpy-1.8.16-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee89e948bc236a5c43c4214ac62d28b29388453f5fd328d739035e205365f0b", size = 4222491 }, + { url = "https://files.pythonhosted.org/packages/5b/2f/1c8db6ddd8a257c3cd2c46413b267f1d5fa3df910401c899513ce30392d6/debugpy-1.8.16-cp313-cp313-win32.whl", hash = "sha256:cf358066650439847ec5ff3dae1da98b5461ea5da0173d93d5e10f477c94609a", size = 5281126 }, + { url = "https://files.pythonhosted.org/packages/d3/ba/c3e154ab307366d6c5a9c1b68de04914e2ce7fa2f50d578311d8cc5074b2/debugpy-1.8.16-cp313-cp313-win_amd64.whl", hash = "sha256:b5aea1083f6f50023e8509399d7dc6535a351cc9f2e8827d1e093175e4d9fa4c", size = 5323094 }, + { url = "https://files.pythonhosted.org/packages/52/57/ecc9ae29fa5b2d90107cd1d9bf8ed19aacb74b2264d986ae9d44fe9bdf87/debugpy-1.8.16-py2.py3-none-any.whl", hash = "sha256:19c9521962475b87da6f673514f7fd610328757ec993bf7ec0d8c96f9a325f9e", size = 5287700 }, ] [[package]] name = "decorator" version = "5.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190 }, ] [[package]] name = "diskcache" version = "5.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, + { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550 }, ] [[package]] name = "distlib" version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605 } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047 }, ] [[package]] name = "distro" version = "1.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, ] [[package]] name = "docutils" version = "0.21.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, ] [[package]] @@ -995,150 +1050,150 @@ name = "exceptiongroup" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749 } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674 }, ] [[package]] name = "execnet" version = "2.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bb/ff/b4c0dc78fbe20c3e59c0c7334de0c27eb4001a2b2017999af398bf730817/execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3", size = 166524, upload-time = "2024-04-08T09:04:19.245Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/ff/b4c0dc78fbe20c3e59c0c7334de0c27eb4001a2b2017999af398bf730817/execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3", size = 166524 } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/09/2aea36ff60d16dd8879bdb2f5b3ee0ba8d08cbbdcdfe870e695ce3784385/execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", size = 40612, upload-time = "2024-04-08T09:04:17.414Z" }, + { url = "https://files.pythonhosted.org/packages/43/09/2aea36ff60d16dd8879bdb2f5b3ee0ba8d08cbbdcdfe870e695ce3784385/execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc", size = 40612 }, ] [[package]] name = "executing" version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317 }, ] [[package]] name = "fastjsonschema" version = "2.21.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130, upload-time = "2025-08-14T18:49:36.666Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024, upload-time = "2025-08-14T18:49:34.776Z" }, + { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024 }, ] [[package]] name = "filelock" version = "3.19.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } +sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687 } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, + { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988 }, ] [[package]] name = "fixedint" version = "0.1.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/32/c6/b1b9b3f69915d51909ef6ebe6352e286ec3d6f2077278af83ec6e3cc569c/fixedint-0.1.6.tar.gz", hash = "sha256:703005d090499d41ce7ce2ee7eae8f7a5589a81acdc6b79f1728a56495f2c799", size = 12750, upload-time = "2020-06-20T22:14:16.544Z" } +sdist = { url = "https://files.pythonhosted.org/packages/32/c6/b1b9b3f69915d51909ef6ebe6352e286ec3d6f2077278af83ec6e3cc569c/fixedint-0.1.6.tar.gz", hash = "sha256:703005d090499d41ce7ce2ee7eae8f7a5589a81acdc6b79f1728a56495f2c799", size = 12750 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/6d/8f5307d26ce700a89e5a67d1e1ad15eff977211f9ed3ae90d7b0d67f4e66/fixedint-0.1.6-py3-none-any.whl", hash = "sha256:b8cf9f913735d2904deadda7a6daa9f57100599da1de57a7448ea1be75ae8c9c", size = 12702, upload-time = "2020-06-20T22:14:15.454Z" }, + { url = "https://files.pythonhosted.org/packages/c8/6d/8f5307d26ce700a89e5a67d1e1ad15eff977211f9ed3ae90d7b0d67f4e66/fixedint-0.1.6-py3-none-any.whl", hash = "sha256:b8cf9f913735d2904deadda7a6daa9f57100599da1de57a7448ea1be75ae8c9c", size = 12702 }, ] [[package]] name = "frozenlist" version = "1.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/b1/b64018016eeb087db503b038296fd782586432b9c077fc5c7839e9cb6ef6/frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f", size = 45078, upload-time = "2025-06-09T23:02:35.538Z" } +sdist = { url = "https://files.pythonhosted.org/packages/79/b1/b64018016eeb087db503b038296fd782586432b9c077fc5c7839e9cb6ef6/frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f", size = 45078 } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/36/0da0a49409f6b47cc2d060dc8c9040b897b5902a8a4e37d9bc1deb11f680/frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a", size = 81304, upload-time = "2025-06-09T22:59:46.226Z" }, - { url = "https://files.pythonhosted.org/packages/77/f0/77c11d13d39513b298e267b22eb6cb559c103d56f155aa9a49097221f0b6/frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61", size = 47735, upload-time = "2025-06-09T22:59:48.133Z" }, - { url = "https://files.pythonhosted.org/packages/37/12/9d07fa18971a44150593de56b2f2947c46604819976784bcf6ea0d5db43b/frozenlist-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0fd1bad056a3600047fb9462cff4c5322cebc59ebf5d0a3725e0ee78955001d", size = 46775, upload-time = "2025-06-09T22:59:49.564Z" }, - { url = "https://files.pythonhosted.org/packages/70/34/f73539227e06288fcd1f8a76853e755b2b48bca6747e99e283111c18bcd4/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3789ebc19cb811163e70fe2bd354cea097254ce6e707ae42e56f45e31e96cb8e", size = 224644, upload-time = "2025-06-09T22:59:51.35Z" }, - { url = "https://files.pythonhosted.org/packages/fb/68/c1d9c2f4a6e438e14613bad0f2973567586610cc22dcb1e1241da71de9d3/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af369aa35ee34f132fcfad5be45fbfcde0e3a5f6a1ec0712857f286b7d20cca9", size = 222125, upload-time = "2025-06-09T22:59:52.884Z" }, - { url = "https://files.pythonhosted.org/packages/b9/d0/98e8f9a515228d708344d7c6986752be3e3192d1795f748c24bcf154ad99/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac64b6478722eeb7a3313d494f8342ef3478dff539d17002f849101b212ef97c", size = 233455, upload-time = "2025-06-09T22:59:54.74Z" }, - { url = "https://files.pythonhosted.org/packages/79/df/8a11bcec5600557f40338407d3e5bea80376ed1c01a6c0910fcfdc4b8993/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f89f65d85774f1797239693cef07ad4c97fdd0639544bad9ac4b869782eb1981", size = 227339, upload-time = "2025-06-09T22:59:56.187Z" }, - { url = "https://files.pythonhosted.org/packages/50/82/41cb97d9c9a5ff94438c63cc343eb7980dac4187eb625a51bdfdb7707314/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1073557c941395fdfcfac13eb2456cb8aad89f9de27bae29fabca8e563b12615", size = 212969, upload-time = "2025-06-09T22:59:57.604Z" }, - { url = "https://files.pythonhosted.org/packages/13/47/f9179ee5ee4f55629e4f28c660b3fdf2775c8bfde8f9c53f2de2d93f52a9/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed8d2fa095aae4bdc7fdd80351009a48d286635edffee66bf865e37a9125c50", size = 222862, upload-time = "2025-06-09T22:59:59.498Z" }, - { url = "https://files.pythonhosted.org/packages/1a/52/df81e41ec6b953902c8b7e3a83bee48b195cb0e5ec2eabae5d8330c78038/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:24c34bea555fe42d9f928ba0a740c553088500377448febecaa82cc3e88aa1fa", size = 222492, upload-time = "2025-06-09T23:00:01.026Z" }, - { url = "https://files.pythonhosted.org/packages/84/17/30d6ea87fa95a9408245a948604b82c1a4b8b3e153cea596421a2aef2754/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:69cac419ac6a6baad202c85aaf467b65ac860ac2e7f2ac1686dc40dbb52f6577", size = 238250, upload-time = "2025-06-09T23:00:03.401Z" }, - { url = "https://files.pythonhosted.org/packages/8f/00/ecbeb51669e3c3df76cf2ddd66ae3e48345ec213a55e3887d216eb4fbab3/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:960d67d0611f4c87da7e2ae2eacf7ea81a5be967861e0c63cf205215afbfac59", size = 218720, upload-time = "2025-06-09T23:00:05.282Z" }, - { url = "https://files.pythonhosted.org/packages/1a/c0/c224ce0e0eb31cc57f67742071bb470ba8246623c1823a7530be0e76164c/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:41be2964bd4b15bf575e5daee5a5ce7ed3115320fb3c2b71fca05582ffa4dc9e", size = 232585, upload-time = "2025-06-09T23:00:07.962Z" }, - { url = "https://files.pythonhosted.org/packages/55/3c/34cb694abf532f31f365106deebdeac9e45c19304d83cf7d51ebbb4ca4d1/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46d84d49e00c9429238a7ce02dc0be8f6d7cd0cd405abd1bebdc991bf27c15bd", size = 234248, upload-time = "2025-06-09T23:00:09.428Z" }, - { url = "https://files.pythonhosted.org/packages/98/c0/2052d8b6cecda2e70bd81299e3512fa332abb6dcd2969b9c80dfcdddbf75/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15900082e886edb37480335d9d518cec978afc69ccbc30bd18610b7c1b22a718", size = 221621, upload-time = "2025-06-09T23:00:11.32Z" }, - { url = "https://files.pythonhosted.org/packages/c5/bf/7dcebae315436903b1d98ffb791a09d674c88480c158aa171958a3ac07f0/frozenlist-1.7.0-cp310-cp310-win32.whl", hash = "sha256:400ddd24ab4e55014bba442d917203c73b2846391dd42ca5e38ff52bb18c3c5e", size = 39578, upload-time = "2025-06-09T23:00:13.526Z" }, - { url = "https://files.pythonhosted.org/packages/8f/5f/f69818f017fa9a3d24d1ae39763e29b7f60a59e46d5f91b9c6b21622f4cd/frozenlist-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:6eb93efb8101ef39d32d50bce242c84bcbddb4f7e9febfa7b524532a239b4464", size = 43830, upload-time = "2025-06-09T23:00:14.98Z" }, - { url = "https://files.pythonhosted.org/packages/34/7e/803dde33760128acd393a27eb002f2020ddb8d99d30a44bfbaab31c5f08a/frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a", size = 82251, upload-time = "2025-06-09T23:00:16.279Z" }, - { url = "https://files.pythonhosted.org/packages/75/a9/9c2c5760b6ba45eae11334db454c189d43d34a4c0b489feb2175e5e64277/frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750", size = 48183, upload-time = "2025-06-09T23:00:17.698Z" }, - { url = "https://files.pythonhosted.org/packages/47/be/4038e2d869f8a2da165f35a6befb9158c259819be22eeaf9c9a8f6a87771/frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd", size = 47107, upload-time = "2025-06-09T23:00:18.952Z" }, - { url = "https://files.pythonhosted.org/packages/79/26/85314b8a83187c76a37183ceed886381a5f992975786f883472fcb6dc5f2/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2", size = 237333, upload-time = "2025-06-09T23:00:20.275Z" }, - { url = "https://files.pythonhosted.org/packages/1f/fd/e5b64f7d2c92a41639ffb2ad44a6a82f347787abc0c7df5f49057cf11770/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f", size = 231724, upload-time = "2025-06-09T23:00:21.705Z" }, - { url = "https://files.pythonhosted.org/packages/20/fb/03395c0a43a5976af4bf7534759d214405fbbb4c114683f434dfdd3128ef/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30", size = 245842, upload-time = "2025-06-09T23:00:23.148Z" }, - { url = "https://files.pythonhosted.org/packages/d0/15/c01c8e1dffdac5d9803507d824f27aed2ba76b6ed0026fab4d9866e82f1f/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98", size = 239767, upload-time = "2025-06-09T23:00:25.103Z" }, - { url = "https://files.pythonhosted.org/packages/14/99/3f4c6fe882c1f5514b6848aa0a69b20cb5e5d8e8f51a339d48c0e9305ed0/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86", size = 224130, upload-time = "2025-06-09T23:00:27.061Z" }, - { url = "https://files.pythonhosted.org/packages/4d/83/220a374bd7b2aeba9d0725130665afe11de347d95c3620b9b82cc2fcab97/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae", size = 235301, upload-time = "2025-06-09T23:00:29.02Z" }, - { url = "https://files.pythonhosted.org/packages/03/3c/3e3390d75334a063181625343e8daab61b77e1b8214802cc4e8a1bb678fc/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8", size = 234606, upload-time = "2025-06-09T23:00:30.514Z" }, - { url = "https://files.pythonhosted.org/packages/23/1e/58232c19608b7a549d72d9903005e2d82488f12554a32de2d5fb59b9b1ba/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31", size = 248372, upload-time = "2025-06-09T23:00:31.966Z" }, - { url = "https://files.pythonhosted.org/packages/c0/a4/e4a567e01702a88a74ce8a324691e62a629bf47d4f8607f24bf1c7216e7f/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7", size = 229860, upload-time = "2025-06-09T23:00:33.375Z" }, - { url = "https://files.pythonhosted.org/packages/73/a6/63b3374f7d22268b41a9db73d68a8233afa30ed164c46107b33c4d18ecdd/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5", size = 245893, upload-time = "2025-06-09T23:00:35.002Z" }, - { url = "https://files.pythonhosted.org/packages/6d/eb/d18b3f6e64799a79673c4ba0b45e4cfbe49c240edfd03a68be20002eaeaa/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898", size = 246323, upload-time = "2025-06-09T23:00:36.468Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f5/720f3812e3d06cd89a1d5db9ff6450088b8f5c449dae8ffb2971a44da506/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56", size = 233149, upload-time = "2025-06-09T23:00:37.963Z" }, - { url = "https://files.pythonhosted.org/packages/69/68/03efbf545e217d5db8446acfd4c447c15b7c8cf4dbd4a58403111df9322d/frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7", size = 39565, upload-time = "2025-06-09T23:00:39.753Z" }, - { url = "https://files.pythonhosted.org/packages/58/17/fe61124c5c333ae87f09bb67186d65038834a47d974fc10a5fadb4cc5ae1/frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d", size = 44019, upload-time = "2025-06-09T23:00:40.988Z" }, - { url = "https://files.pythonhosted.org/packages/ef/a2/c8131383f1e66adad5f6ecfcce383d584ca94055a34d683bbb24ac5f2f1c/frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2", size = 81424, upload-time = "2025-06-09T23:00:42.24Z" }, - { url = "https://files.pythonhosted.org/packages/4c/9d/02754159955088cb52567337d1113f945b9e444c4960771ea90eb73de8db/frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb", size = 47952, upload-time = "2025-06-09T23:00:43.481Z" }, - { url = "https://files.pythonhosted.org/packages/01/7a/0046ef1bd6699b40acd2067ed6d6670b4db2f425c56980fa21c982c2a9db/frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478", size = 46688, upload-time = "2025-06-09T23:00:44.793Z" }, - { url = "https://files.pythonhosted.org/packages/d6/a2/a910bafe29c86997363fb4c02069df4ff0b5bc39d33c5198b4e9dd42d8f8/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8", size = 243084, upload-time = "2025-06-09T23:00:46.125Z" }, - { url = "https://files.pythonhosted.org/packages/64/3e/5036af9d5031374c64c387469bfcc3af537fc0f5b1187d83a1cf6fab1639/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08", size = 233524, upload-time = "2025-06-09T23:00:47.73Z" }, - { url = "https://files.pythonhosted.org/packages/06/39/6a17b7c107a2887e781a48ecf20ad20f1c39d94b2a548c83615b5b879f28/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4", size = 248493, upload-time = "2025-06-09T23:00:49.742Z" }, - { url = "https://files.pythonhosted.org/packages/be/00/711d1337c7327d88c44d91dd0f556a1c47fb99afc060ae0ef66b4d24793d/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b", size = 244116, upload-time = "2025-06-09T23:00:51.352Z" }, - { url = "https://files.pythonhosted.org/packages/24/fe/74e6ec0639c115df13d5850e75722750adabdc7de24e37e05a40527ca539/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e", size = 224557, upload-time = "2025-06-09T23:00:52.855Z" }, - { url = "https://files.pythonhosted.org/packages/8d/db/48421f62a6f77c553575201e89048e97198046b793f4a089c79a6e3268bd/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca", size = 241820, upload-time = "2025-06-09T23:00:54.43Z" }, - { url = "https://files.pythonhosted.org/packages/1d/fa/cb4a76bea23047c8462976ea7b7a2bf53997a0ca171302deae9d6dd12096/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df", size = 236542, upload-time = "2025-06-09T23:00:56.409Z" }, - { url = "https://files.pythonhosted.org/packages/5d/32/476a4b5cfaa0ec94d3f808f193301debff2ea42288a099afe60757ef6282/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5", size = 249350, upload-time = "2025-06-09T23:00:58.468Z" }, - { url = "https://files.pythonhosted.org/packages/8d/ba/9a28042f84a6bf8ea5dbc81cfff8eaef18d78b2a1ad9d51c7bc5b029ad16/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025", size = 225093, upload-time = "2025-06-09T23:01:00.015Z" }, - { url = "https://files.pythonhosted.org/packages/bc/29/3a32959e68f9cf000b04e79ba574527c17e8842e38c91d68214a37455786/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01", size = 245482, upload-time = "2025-06-09T23:01:01.474Z" }, - { url = "https://files.pythonhosted.org/packages/80/e8/edf2f9e00da553f07f5fa165325cfc302dead715cab6ac8336a5f3d0adc2/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08", size = 249590, upload-time = "2025-06-09T23:01:02.961Z" }, - { url = "https://files.pythonhosted.org/packages/1c/80/9a0eb48b944050f94cc51ee1c413eb14a39543cc4f760ed12657a5a3c45a/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43", size = 237785, upload-time = "2025-06-09T23:01:05.095Z" }, - { url = "https://files.pythonhosted.org/packages/f3/74/87601e0fb0369b7a2baf404ea921769c53b7ae00dee7dcfe5162c8c6dbf0/frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3", size = 39487, upload-time = "2025-06-09T23:01:06.54Z" }, - { url = "https://files.pythonhosted.org/packages/0b/15/c026e9a9fc17585a9d461f65d8593d281fedf55fbf7eb53f16c6df2392f9/frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a", size = 43874, upload-time = "2025-06-09T23:01:07.752Z" }, - { url = "https://files.pythonhosted.org/packages/24/90/6b2cebdabdbd50367273c20ff6b57a3dfa89bd0762de02c3a1eb42cb6462/frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee", size = 79791, upload-time = "2025-06-09T23:01:09.368Z" }, - { url = "https://files.pythonhosted.org/packages/83/2e/5b70b6a3325363293fe5fc3ae74cdcbc3e996c2a11dde2fd9f1fb0776d19/frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d", size = 47165, upload-time = "2025-06-09T23:01:10.653Z" }, - { url = "https://files.pythonhosted.org/packages/f4/25/a0895c99270ca6966110f4ad98e87e5662eab416a17e7fd53c364bf8b954/frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43", size = 45881, upload-time = "2025-06-09T23:01:12.296Z" }, - { url = "https://files.pythonhosted.org/packages/19/7c/71bb0bbe0832793c601fff68cd0cf6143753d0c667f9aec93d3c323f4b55/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d", size = 232409, upload-time = "2025-06-09T23:01:13.641Z" }, - { url = "https://files.pythonhosted.org/packages/c0/45/ed2798718910fe6eb3ba574082aaceff4528e6323f9a8570be0f7028d8e9/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee", size = 225132, upload-time = "2025-06-09T23:01:15.264Z" }, - { url = "https://files.pythonhosted.org/packages/ba/e2/8417ae0f8eacb1d071d4950f32f229aa6bf68ab69aab797b72a07ea68d4f/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb", size = 237638, upload-time = "2025-06-09T23:01:16.752Z" }, - { url = "https://files.pythonhosted.org/packages/f8/b7/2ace5450ce85f2af05a871b8c8719b341294775a0a6c5585d5e6170f2ce7/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f", size = 233539, upload-time = "2025-06-09T23:01:18.202Z" }, - { url = "https://files.pythonhosted.org/packages/46/b9/6989292c5539553dba63f3c83dc4598186ab2888f67c0dc1d917e6887db6/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60", size = 215646, upload-time = "2025-06-09T23:01:19.649Z" }, - { url = "https://files.pythonhosted.org/packages/72/31/bc8c5c99c7818293458fe745dab4fd5730ff49697ccc82b554eb69f16a24/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00", size = 232233, upload-time = "2025-06-09T23:01:21.175Z" }, - { url = "https://files.pythonhosted.org/packages/59/52/460db4d7ba0811b9ccb85af996019f5d70831f2f5f255f7cc61f86199795/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b", size = 227996, upload-time = "2025-06-09T23:01:23.098Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c9/f4b39e904c03927b7ecf891804fd3b4df3db29b9e487c6418e37988d6e9d/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c", size = 242280, upload-time = "2025-06-09T23:01:24.808Z" }, - { url = "https://files.pythonhosted.org/packages/b8/33/3f8d6ced42f162d743e3517781566b8481322be321b486d9d262adf70bfb/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949", size = 217717, upload-time = "2025-06-09T23:01:26.28Z" }, - { url = "https://files.pythonhosted.org/packages/3e/e8/ad683e75da6ccef50d0ab0c2b2324b32f84fc88ceee778ed79b8e2d2fe2e/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca", size = 236644, upload-time = "2025-06-09T23:01:27.887Z" }, - { url = "https://files.pythonhosted.org/packages/b2/14/8d19ccdd3799310722195a72ac94ddc677541fb4bef4091d8e7775752360/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b", size = 238879, upload-time = "2025-06-09T23:01:29.524Z" }, - { url = "https://files.pythonhosted.org/packages/ce/13/c12bf657494c2fd1079a48b2db49fa4196325909249a52d8f09bc9123fd7/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e", size = 232502, upload-time = "2025-06-09T23:01:31.287Z" }, - { url = "https://files.pythonhosted.org/packages/d7/8b/e7f9dfde869825489382bc0d512c15e96d3964180c9499efcec72e85db7e/frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1", size = 39169, upload-time = "2025-06-09T23:01:35.503Z" }, - { url = "https://files.pythonhosted.org/packages/35/89/a487a98d94205d85745080a37860ff5744b9820a2c9acbcdd9440bfddf98/frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba", size = 43219, upload-time = "2025-06-09T23:01:36.784Z" }, - { url = "https://files.pythonhosted.org/packages/56/d5/5c4cf2319a49eddd9dd7145e66c4866bdc6f3dbc67ca3d59685149c11e0d/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d", size = 84345, upload-time = "2025-06-09T23:01:38.295Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7d/ec2c1e1dc16b85bc9d526009961953df9cec8481b6886debb36ec9107799/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d", size = 48880, upload-time = "2025-06-09T23:01:39.887Z" }, - { url = "https://files.pythonhosted.org/packages/69/86/f9596807b03de126e11e7d42ac91e3d0b19a6599c714a1989a4e85eeefc4/frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b", size = 48498, upload-time = "2025-06-09T23:01:41.318Z" }, - { url = "https://files.pythonhosted.org/packages/5e/cb/df6de220f5036001005f2d726b789b2c0b65f2363b104bbc16f5be8084f8/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146", size = 292296, upload-time = "2025-06-09T23:01:42.685Z" }, - { url = "https://files.pythonhosted.org/packages/83/1f/de84c642f17c8f851a2905cee2dae401e5e0daca9b5ef121e120e19aa825/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74", size = 273103, upload-time = "2025-06-09T23:01:44.166Z" }, - { url = "https://files.pythonhosted.org/packages/88/3c/c840bfa474ba3fa13c772b93070893c6e9d5c0350885760376cbe3b6c1b3/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1", size = 292869, upload-time = "2025-06-09T23:01:45.681Z" }, - { url = "https://files.pythonhosted.org/packages/a6/1c/3efa6e7d5a39a1d5ef0abeb51c48fb657765794a46cf124e5aca2c7a592c/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1", size = 291467, upload-time = "2025-06-09T23:01:47.234Z" }, - { url = "https://files.pythonhosted.org/packages/4f/00/d5c5e09d4922c395e2f2f6b79b9a20dab4b67daaf78ab92e7729341f61f6/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384", size = 266028, upload-time = "2025-06-09T23:01:48.819Z" }, - { url = "https://files.pythonhosted.org/packages/4e/27/72765be905619dfde25a7f33813ac0341eb6b076abede17a2e3fbfade0cb/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb", size = 284294, upload-time = "2025-06-09T23:01:50.394Z" }, - { url = "https://files.pythonhosted.org/packages/88/67/c94103a23001b17808eb7dd1200c156bb69fb68e63fcf0693dde4cd6228c/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c", size = 281898, upload-time = "2025-06-09T23:01:52.234Z" }, - { url = "https://files.pythonhosted.org/packages/42/34/a3e2c00c00f9e2a9db5653bca3fec306349e71aff14ae45ecc6d0951dd24/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65", size = 290465, upload-time = "2025-06-09T23:01:53.788Z" }, - { url = "https://files.pythonhosted.org/packages/bb/73/f89b7fbce8b0b0c095d82b008afd0590f71ccb3dee6eee41791cf8cd25fd/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3", size = 266385, upload-time = "2025-06-09T23:01:55.769Z" }, - { url = "https://files.pythonhosted.org/packages/cd/45/e365fdb554159462ca12df54bc59bfa7a9a273ecc21e99e72e597564d1ae/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657", size = 288771, upload-time = "2025-06-09T23:01:57.4Z" }, - { url = "https://files.pythonhosted.org/packages/00/11/47b6117002a0e904f004d70ec5194fe9144f117c33c851e3d51c765962d0/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104", size = 288206, upload-time = "2025-06-09T23:01:58.936Z" }, - { url = "https://files.pythonhosted.org/packages/40/37/5f9f3c3fd7f7746082ec67bcdc204db72dad081f4f83a503d33220a92973/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf", size = 282620, upload-time = "2025-06-09T23:02:00.493Z" }, - { url = "https://files.pythonhosted.org/packages/0b/31/8fbc5af2d183bff20f21aa743b4088eac4445d2bb1cdece449ae80e4e2d1/frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81", size = 43059, upload-time = "2025-06-09T23:02:02.072Z" }, - { url = "https://files.pythonhosted.org/packages/bb/ed/41956f52105b8dbc26e457c5705340c67c8cc2b79f394b79bffc09d0e938/frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e", size = 47516, upload-time = "2025-06-09T23:02:03.779Z" }, - { url = "https://files.pythonhosted.org/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e", size = 13106, upload-time = "2025-06-09T23:02:34.204Z" }, + { url = "https://files.pythonhosted.org/packages/af/36/0da0a49409f6b47cc2d060dc8c9040b897b5902a8a4e37d9bc1deb11f680/frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a", size = 81304 }, + { url = "https://files.pythonhosted.org/packages/77/f0/77c11d13d39513b298e267b22eb6cb559c103d56f155aa9a49097221f0b6/frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61", size = 47735 }, + { url = "https://files.pythonhosted.org/packages/37/12/9d07fa18971a44150593de56b2f2947c46604819976784bcf6ea0d5db43b/frozenlist-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0fd1bad056a3600047fb9462cff4c5322cebc59ebf5d0a3725e0ee78955001d", size = 46775 }, + { url = "https://files.pythonhosted.org/packages/70/34/f73539227e06288fcd1f8a76853e755b2b48bca6747e99e283111c18bcd4/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3789ebc19cb811163e70fe2bd354cea097254ce6e707ae42e56f45e31e96cb8e", size = 224644 }, + { url = "https://files.pythonhosted.org/packages/fb/68/c1d9c2f4a6e438e14613bad0f2973567586610cc22dcb1e1241da71de9d3/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af369aa35ee34f132fcfad5be45fbfcde0e3a5f6a1ec0712857f286b7d20cca9", size = 222125 }, + { url = "https://files.pythonhosted.org/packages/b9/d0/98e8f9a515228d708344d7c6986752be3e3192d1795f748c24bcf154ad99/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac64b6478722eeb7a3313d494f8342ef3478dff539d17002f849101b212ef97c", size = 233455 }, + { url = "https://files.pythonhosted.org/packages/79/df/8a11bcec5600557f40338407d3e5bea80376ed1c01a6c0910fcfdc4b8993/frozenlist-1.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f89f65d85774f1797239693cef07ad4c97fdd0639544bad9ac4b869782eb1981", size = 227339 }, + { url = "https://files.pythonhosted.org/packages/50/82/41cb97d9c9a5ff94438c63cc343eb7980dac4187eb625a51bdfdb7707314/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1073557c941395fdfcfac13eb2456cb8aad89f9de27bae29fabca8e563b12615", size = 212969 }, + { url = "https://files.pythonhosted.org/packages/13/47/f9179ee5ee4f55629e4f28c660b3fdf2775c8bfde8f9c53f2de2d93f52a9/frozenlist-1.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed8d2fa095aae4bdc7fdd80351009a48d286635edffee66bf865e37a9125c50", size = 222862 }, + { url = "https://files.pythonhosted.org/packages/1a/52/df81e41ec6b953902c8b7e3a83bee48b195cb0e5ec2eabae5d8330c78038/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:24c34bea555fe42d9f928ba0a740c553088500377448febecaa82cc3e88aa1fa", size = 222492 }, + { url = "https://files.pythonhosted.org/packages/84/17/30d6ea87fa95a9408245a948604b82c1a4b8b3e153cea596421a2aef2754/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:69cac419ac6a6baad202c85aaf467b65ac860ac2e7f2ac1686dc40dbb52f6577", size = 238250 }, + { url = "https://files.pythonhosted.org/packages/8f/00/ecbeb51669e3c3df76cf2ddd66ae3e48345ec213a55e3887d216eb4fbab3/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:960d67d0611f4c87da7e2ae2eacf7ea81a5be967861e0c63cf205215afbfac59", size = 218720 }, + { url = "https://files.pythonhosted.org/packages/1a/c0/c224ce0e0eb31cc57f67742071bb470ba8246623c1823a7530be0e76164c/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:41be2964bd4b15bf575e5daee5a5ce7ed3115320fb3c2b71fca05582ffa4dc9e", size = 232585 }, + { url = "https://files.pythonhosted.org/packages/55/3c/34cb694abf532f31f365106deebdeac9e45c19304d83cf7d51ebbb4ca4d1/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46d84d49e00c9429238a7ce02dc0be8f6d7cd0cd405abd1bebdc991bf27c15bd", size = 234248 }, + { url = "https://files.pythonhosted.org/packages/98/c0/2052d8b6cecda2e70bd81299e3512fa332abb6dcd2969b9c80dfcdddbf75/frozenlist-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15900082e886edb37480335d9d518cec978afc69ccbc30bd18610b7c1b22a718", size = 221621 }, + { url = "https://files.pythonhosted.org/packages/c5/bf/7dcebae315436903b1d98ffb791a09d674c88480c158aa171958a3ac07f0/frozenlist-1.7.0-cp310-cp310-win32.whl", hash = "sha256:400ddd24ab4e55014bba442d917203c73b2846391dd42ca5e38ff52bb18c3c5e", size = 39578 }, + { url = "https://files.pythonhosted.org/packages/8f/5f/f69818f017fa9a3d24d1ae39763e29b7f60a59e46d5f91b9c6b21622f4cd/frozenlist-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:6eb93efb8101ef39d32d50bce242c84bcbddb4f7e9febfa7b524532a239b4464", size = 43830 }, + { url = "https://files.pythonhosted.org/packages/34/7e/803dde33760128acd393a27eb002f2020ddb8d99d30a44bfbaab31c5f08a/frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a", size = 82251 }, + { url = "https://files.pythonhosted.org/packages/75/a9/9c2c5760b6ba45eae11334db454c189d43d34a4c0b489feb2175e5e64277/frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750", size = 48183 }, + { url = "https://files.pythonhosted.org/packages/47/be/4038e2d869f8a2da165f35a6befb9158c259819be22eeaf9c9a8f6a87771/frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd", size = 47107 }, + { url = "https://files.pythonhosted.org/packages/79/26/85314b8a83187c76a37183ceed886381a5f992975786f883472fcb6dc5f2/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2", size = 237333 }, + { url = "https://files.pythonhosted.org/packages/1f/fd/e5b64f7d2c92a41639ffb2ad44a6a82f347787abc0c7df5f49057cf11770/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f", size = 231724 }, + { url = "https://files.pythonhosted.org/packages/20/fb/03395c0a43a5976af4bf7534759d214405fbbb4c114683f434dfdd3128ef/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30", size = 245842 }, + { url = "https://files.pythonhosted.org/packages/d0/15/c01c8e1dffdac5d9803507d824f27aed2ba76b6ed0026fab4d9866e82f1f/frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98", size = 239767 }, + { url = "https://files.pythonhosted.org/packages/14/99/3f4c6fe882c1f5514b6848aa0a69b20cb5e5d8e8f51a339d48c0e9305ed0/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86", size = 224130 }, + { url = "https://files.pythonhosted.org/packages/4d/83/220a374bd7b2aeba9d0725130665afe11de347d95c3620b9b82cc2fcab97/frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae", size = 235301 }, + { url = "https://files.pythonhosted.org/packages/03/3c/3e3390d75334a063181625343e8daab61b77e1b8214802cc4e8a1bb678fc/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8", size = 234606 }, + { url = "https://files.pythonhosted.org/packages/23/1e/58232c19608b7a549d72d9903005e2d82488f12554a32de2d5fb59b9b1ba/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31", size = 248372 }, + { url = "https://files.pythonhosted.org/packages/c0/a4/e4a567e01702a88a74ce8a324691e62a629bf47d4f8607f24bf1c7216e7f/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7", size = 229860 }, + { url = "https://files.pythonhosted.org/packages/73/a6/63b3374f7d22268b41a9db73d68a8233afa30ed164c46107b33c4d18ecdd/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5", size = 245893 }, + { url = "https://files.pythonhosted.org/packages/6d/eb/d18b3f6e64799a79673c4ba0b45e4cfbe49c240edfd03a68be20002eaeaa/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898", size = 246323 }, + { url = "https://files.pythonhosted.org/packages/5a/f5/720f3812e3d06cd89a1d5db9ff6450088b8f5c449dae8ffb2971a44da506/frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56", size = 233149 }, + { url = "https://files.pythonhosted.org/packages/69/68/03efbf545e217d5db8446acfd4c447c15b7c8cf4dbd4a58403111df9322d/frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7", size = 39565 }, + { url = "https://files.pythonhosted.org/packages/58/17/fe61124c5c333ae87f09bb67186d65038834a47d974fc10a5fadb4cc5ae1/frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d", size = 44019 }, + { url = "https://files.pythonhosted.org/packages/ef/a2/c8131383f1e66adad5f6ecfcce383d584ca94055a34d683bbb24ac5f2f1c/frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2", size = 81424 }, + { url = "https://files.pythonhosted.org/packages/4c/9d/02754159955088cb52567337d1113f945b9e444c4960771ea90eb73de8db/frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb", size = 47952 }, + { url = "https://files.pythonhosted.org/packages/01/7a/0046ef1bd6699b40acd2067ed6d6670b4db2f425c56980fa21c982c2a9db/frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478", size = 46688 }, + { url = "https://files.pythonhosted.org/packages/d6/a2/a910bafe29c86997363fb4c02069df4ff0b5bc39d33c5198b4e9dd42d8f8/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8", size = 243084 }, + { url = "https://files.pythonhosted.org/packages/64/3e/5036af9d5031374c64c387469bfcc3af537fc0f5b1187d83a1cf6fab1639/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08", size = 233524 }, + { url = "https://files.pythonhosted.org/packages/06/39/6a17b7c107a2887e781a48ecf20ad20f1c39d94b2a548c83615b5b879f28/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4", size = 248493 }, + { url = "https://files.pythonhosted.org/packages/be/00/711d1337c7327d88c44d91dd0f556a1c47fb99afc060ae0ef66b4d24793d/frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b", size = 244116 }, + { url = "https://files.pythonhosted.org/packages/24/fe/74e6ec0639c115df13d5850e75722750adabdc7de24e37e05a40527ca539/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e", size = 224557 }, + { url = "https://files.pythonhosted.org/packages/8d/db/48421f62a6f77c553575201e89048e97198046b793f4a089c79a6e3268bd/frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca", size = 241820 }, + { url = "https://files.pythonhosted.org/packages/1d/fa/cb4a76bea23047c8462976ea7b7a2bf53997a0ca171302deae9d6dd12096/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df", size = 236542 }, + { url = "https://files.pythonhosted.org/packages/5d/32/476a4b5cfaa0ec94d3f808f193301debff2ea42288a099afe60757ef6282/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5", size = 249350 }, + { url = "https://files.pythonhosted.org/packages/8d/ba/9a28042f84a6bf8ea5dbc81cfff8eaef18d78b2a1ad9d51c7bc5b029ad16/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025", size = 225093 }, + { url = "https://files.pythonhosted.org/packages/bc/29/3a32959e68f9cf000b04e79ba574527c17e8842e38c91d68214a37455786/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01", size = 245482 }, + { url = "https://files.pythonhosted.org/packages/80/e8/edf2f9e00da553f07f5fa165325cfc302dead715cab6ac8336a5f3d0adc2/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08", size = 249590 }, + { url = "https://files.pythonhosted.org/packages/1c/80/9a0eb48b944050f94cc51ee1c413eb14a39543cc4f760ed12657a5a3c45a/frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43", size = 237785 }, + { url = "https://files.pythonhosted.org/packages/f3/74/87601e0fb0369b7a2baf404ea921769c53b7ae00dee7dcfe5162c8c6dbf0/frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3", size = 39487 }, + { url = "https://files.pythonhosted.org/packages/0b/15/c026e9a9fc17585a9d461f65d8593d281fedf55fbf7eb53f16c6df2392f9/frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a", size = 43874 }, + { url = "https://files.pythonhosted.org/packages/24/90/6b2cebdabdbd50367273c20ff6b57a3dfa89bd0762de02c3a1eb42cb6462/frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee", size = 79791 }, + { url = "https://files.pythonhosted.org/packages/83/2e/5b70b6a3325363293fe5fc3ae74cdcbc3e996c2a11dde2fd9f1fb0776d19/frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d", size = 47165 }, + { url = "https://files.pythonhosted.org/packages/f4/25/a0895c99270ca6966110f4ad98e87e5662eab416a17e7fd53c364bf8b954/frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43", size = 45881 }, + { url = "https://files.pythonhosted.org/packages/19/7c/71bb0bbe0832793c601fff68cd0cf6143753d0c667f9aec93d3c323f4b55/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d", size = 232409 }, + { url = "https://files.pythonhosted.org/packages/c0/45/ed2798718910fe6eb3ba574082aaceff4528e6323f9a8570be0f7028d8e9/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee", size = 225132 }, + { url = "https://files.pythonhosted.org/packages/ba/e2/8417ae0f8eacb1d071d4950f32f229aa6bf68ab69aab797b72a07ea68d4f/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb", size = 237638 }, + { url = "https://files.pythonhosted.org/packages/f8/b7/2ace5450ce85f2af05a871b8c8719b341294775a0a6c5585d5e6170f2ce7/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f", size = 233539 }, + { url = "https://files.pythonhosted.org/packages/46/b9/6989292c5539553dba63f3c83dc4598186ab2888f67c0dc1d917e6887db6/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60", size = 215646 }, + { url = "https://files.pythonhosted.org/packages/72/31/bc8c5c99c7818293458fe745dab4fd5730ff49697ccc82b554eb69f16a24/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00", size = 232233 }, + { url = "https://files.pythonhosted.org/packages/59/52/460db4d7ba0811b9ccb85af996019f5d70831f2f5f255f7cc61f86199795/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b", size = 227996 }, + { url = "https://files.pythonhosted.org/packages/ba/c9/f4b39e904c03927b7ecf891804fd3b4df3db29b9e487c6418e37988d6e9d/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c", size = 242280 }, + { url = "https://files.pythonhosted.org/packages/b8/33/3f8d6ced42f162d743e3517781566b8481322be321b486d9d262adf70bfb/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949", size = 217717 }, + { url = "https://files.pythonhosted.org/packages/3e/e8/ad683e75da6ccef50d0ab0c2b2324b32f84fc88ceee778ed79b8e2d2fe2e/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca", size = 236644 }, + { url = "https://files.pythonhosted.org/packages/b2/14/8d19ccdd3799310722195a72ac94ddc677541fb4bef4091d8e7775752360/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b", size = 238879 }, + { url = "https://files.pythonhosted.org/packages/ce/13/c12bf657494c2fd1079a48b2db49fa4196325909249a52d8f09bc9123fd7/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e", size = 232502 }, + { url = "https://files.pythonhosted.org/packages/d7/8b/e7f9dfde869825489382bc0d512c15e96d3964180c9499efcec72e85db7e/frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1", size = 39169 }, + { url = "https://files.pythonhosted.org/packages/35/89/a487a98d94205d85745080a37860ff5744b9820a2c9acbcdd9440bfddf98/frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba", size = 43219 }, + { url = "https://files.pythonhosted.org/packages/56/d5/5c4cf2319a49eddd9dd7145e66c4866bdc6f3dbc67ca3d59685149c11e0d/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d", size = 84345 }, + { url = "https://files.pythonhosted.org/packages/a4/7d/ec2c1e1dc16b85bc9d526009961953df9cec8481b6886debb36ec9107799/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d", size = 48880 }, + { url = "https://files.pythonhosted.org/packages/69/86/f9596807b03de126e11e7d42ac91e3d0b19a6599c714a1989a4e85eeefc4/frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b", size = 48498 }, + { url = "https://files.pythonhosted.org/packages/5e/cb/df6de220f5036001005f2d726b789b2c0b65f2363b104bbc16f5be8084f8/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146", size = 292296 }, + { url = "https://files.pythonhosted.org/packages/83/1f/de84c642f17c8f851a2905cee2dae401e5e0daca9b5ef121e120e19aa825/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74", size = 273103 }, + { url = "https://files.pythonhosted.org/packages/88/3c/c840bfa474ba3fa13c772b93070893c6e9d5c0350885760376cbe3b6c1b3/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1", size = 292869 }, + { url = "https://files.pythonhosted.org/packages/a6/1c/3efa6e7d5a39a1d5ef0abeb51c48fb657765794a46cf124e5aca2c7a592c/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1", size = 291467 }, + { url = "https://files.pythonhosted.org/packages/4f/00/d5c5e09d4922c395e2f2f6b79b9a20dab4b67daaf78ab92e7729341f61f6/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384", size = 266028 }, + { url = "https://files.pythonhosted.org/packages/4e/27/72765be905619dfde25a7f33813ac0341eb6b076abede17a2e3fbfade0cb/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb", size = 284294 }, + { url = "https://files.pythonhosted.org/packages/88/67/c94103a23001b17808eb7dd1200c156bb69fb68e63fcf0693dde4cd6228c/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c", size = 281898 }, + { url = "https://files.pythonhosted.org/packages/42/34/a3e2c00c00f9e2a9db5653bca3fec306349e71aff14ae45ecc6d0951dd24/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65", size = 290465 }, + { url = "https://files.pythonhosted.org/packages/bb/73/f89b7fbce8b0b0c095d82b008afd0590f71ccb3dee6eee41791cf8cd25fd/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3", size = 266385 }, + { url = "https://files.pythonhosted.org/packages/cd/45/e365fdb554159462ca12df54bc59bfa7a9a273ecc21e99e72e597564d1ae/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657", size = 288771 }, + { url = "https://files.pythonhosted.org/packages/00/11/47b6117002a0e904f004d70ec5194fe9144f117c33c851e3d51c765962d0/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104", size = 288206 }, + { url = "https://files.pythonhosted.org/packages/40/37/5f9f3c3fd7f7746082ec67bcdc204db72dad081f4f83a503d33220a92973/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf", size = 282620 }, + { url = "https://files.pythonhosted.org/packages/0b/31/8fbc5af2d183bff20f21aa743b4088eac4445d2bb1cdece449ae80e4e2d1/frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81", size = 43059 }, + { url = "https://files.pythonhosted.org/packages/bb/ed/41956f52105b8dbc26e457c5705340c67c8cc2b79f394b79bffc09d0e938/frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e", size = 47516 }, + { url = "https://files.pythonhosted.org/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e", size = 13106 }, ] [[package]] @@ -1148,126 +1203,148 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/39/24/33db22342cf4a2ea27c9955e6713140fedd51e8b141b5ce5260897020f1a/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257", size = 145903, upload-time = "2025-04-14T10:17:02.924Z" } +sdist = { url = "https://files.pythonhosted.org/packages/39/24/33db22342cf4a2ea27c9955e6713140fedd51e8b141b5ce5260897020f1a/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257", size = 145903 } wheels = [ - { url = "https://files.pythonhosted.org/packages/86/f1/62a193f0227cf15a920390abe675f386dec35f7ae3ffe6da582d3ade42c7/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8", size = 294530, upload-time = "2025-04-14T10:17:01.271Z" }, + { url = "https://files.pythonhosted.org/packages/86/f1/62a193f0227cf15a920390abe675f386dec35f7ae3ffe6da582d3ade42c7/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8", size = 294530 }, ] [[package]] name = "graphviz" version = "0.21" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/b3/3ac91e9be6b761a4b30d66ff165e54439dcd48b83f4e20d644867215f6ca/graphviz-0.21.tar.gz", hash = "sha256:20743e7183be82aaaa8ad6c93f8893c923bd6658a04c32ee115edb3c8a835f78", size = 200434, upload-time = "2025-06-15T09:35:05.824Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/b3/3ac91e9be6b761a4b30d66ff165e54439dcd48b83f4e20d644867215f6ca/graphviz-0.21.tar.gz", hash = "sha256:20743e7183be82aaaa8ad6c93f8893c923bd6658a04c32ee115edb3c8a835f78", size = 200434 } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl", hash = "sha256:54f33de9f4f911d7e84e4191749cac8cc5653f815b06738c54db9a15ab8b1e42", size = 47300, upload-time = "2025-06-15T09:35:04.433Z" }, + { url = "https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl", hash = "sha256:54f33de9f4f911d7e84e4191749cac8cc5653f815b06738c54db9a15ab8b1e42", size = 47300 }, ] [[package]] name = "greenlet" version = "3.2.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260, upload-time = "2025-08-07T13:24:33.51Z" } +sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/ed/6bfa4109fcb23a58819600392564fea69cdc6551ffd5e69ccf1d52a40cbc/greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c", size = 271061, upload-time = "2025-08-07T13:17:15.373Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fc/102ec1a2fc015b3a7652abab7acf3541d58c04d3d17a8d3d6a44adae1eb1/greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590", size = 629475, upload-time = "2025-08-07T13:42:54.009Z" }, - { url = "https://files.pythonhosted.org/packages/c5/26/80383131d55a4ac0fb08d71660fd77e7660b9db6bdb4e8884f46d9f2cc04/greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c", size = 640802, upload-time = "2025-08-07T13:45:25.52Z" }, - { url = "https://files.pythonhosted.org/packages/9f/7c/e7833dbcd8f376f3326bd728c845d31dcde4c84268d3921afcae77d90d08/greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b", size = 636703, upload-time = "2025-08-07T13:53:12.622Z" }, - { url = "https://files.pythonhosted.org/packages/e9/49/547b93b7c0428ede7b3f309bc965986874759f7d89e4e04aeddbc9699acb/greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31", size = 635417, upload-time = "2025-08-07T13:18:25.189Z" }, - { url = "https://files.pythonhosted.org/packages/7f/91/ae2eb6b7979e2f9b035a9f612cf70f1bf54aad4e1d125129bef1eae96f19/greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d", size = 584358, upload-time = "2025-08-07T13:18:23.708Z" }, - { url = "https://files.pythonhosted.org/packages/f7/85/433de0c9c0252b22b16d413c9407e6cb3b41df7389afc366ca204dbc1393/greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5", size = 1113550, upload-time = "2025-08-07T13:42:37.467Z" }, - { url = "https://files.pythonhosted.org/packages/a1/8d/88f3ebd2bc96bf7747093696f4335a0a8a4c5acfcf1b757717c0d2474ba3/greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f", size = 1137126, upload-time = "2025-08-07T13:18:20.239Z" }, - { url = "https://files.pythonhosted.org/packages/d6/6f/b60b0291d9623c496638c582297ead61f43c4b72eef5e9c926ef4565ec13/greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c", size = 298654, upload-time = "2025-08-07T13:50:00.469Z" }, - { url = "https://files.pythonhosted.org/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2", size = 272305, upload-time = "2025-08-07T13:15:41.288Z" }, - { url = "https://files.pythonhosted.org/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246", size = 632472, upload-time = "2025-08-07T13:42:55.044Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8f/95d48d7e3d433e6dae5b1682e4292242a53f22df82e6d3dda81b1701a960/greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3", size = 644646, upload-time = "2025-08-07T13:45:26.523Z" }, - { url = "https://files.pythonhosted.org/packages/d5/5e/405965351aef8c76b8ef7ad370e5da58d57ef6068df197548b015464001a/greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633", size = 640519, upload-time = "2025-08-07T13:53:13.928Z" }, - { url = "https://files.pythonhosted.org/packages/25/5d/382753b52006ce0218297ec1b628e048c4e64b155379331f25a7316eb749/greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079", size = 639707, upload-time = "2025-08-07T13:18:27.146Z" }, - { url = "https://files.pythonhosted.org/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8", size = 587684, upload-time = "2025-08-07T13:18:25.164Z" }, - { url = "https://files.pythonhosted.org/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52", size = 1116647, upload-time = "2025-08-07T13:42:38.655Z" }, - { url = "https://files.pythonhosted.org/packages/3f/cc/b07000438a29ac5cfb2194bfc128151d52f333cee74dd7dfe3fb733fc16c/greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa", size = 1142073, upload-time = "2025-08-07T13:18:21.737Z" }, - { url = "https://files.pythonhosted.org/packages/d8/0f/30aef242fcab550b0b3520b8e3561156857c94288f0332a79928c31a52cf/greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9", size = 299100, upload-time = "2025-08-07T13:44:12.287Z" }, - { url = "https://files.pythonhosted.org/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079, upload-time = "2025-08-07T13:15:45.033Z" }, - { url = "https://files.pythonhosted.org/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997, upload-time = "2025-08-07T13:42:56.234Z" }, - { url = "https://files.pythonhosted.org/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968", size = 655185, upload-time = "2025-08-07T13:45:27.624Z" }, - { url = "https://files.pythonhosted.org/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9", size = 649926, upload-time = "2025-08-07T13:53:15.251Z" }, - { url = "https://files.pythonhosted.org/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6", size = 651839, upload-time = "2025-08-07T13:18:30.281Z" }, - { url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" }, - { url = "https://files.pythonhosted.org/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f", size = 1151142, upload-time = "2025-08-07T13:18:22.981Z" }, - { url = "https://files.pythonhosted.org/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02", size = 299899, upload-time = "2025-08-07T13:38:53.448Z" }, - { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814, upload-time = "2025-08-07T13:15:50.011Z" }, - { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073, upload-time = "2025-08-07T13:42:57.23Z" }, - { url = "https://files.pythonhosted.org/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191, upload-time = "2025-08-07T13:45:29.752Z" }, - { url = "https://files.pythonhosted.org/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516, upload-time = "2025-08-07T13:53:16.314Z" }, - { url = "https://files.pythonhosted.org/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169, upload-time = "2025-08-07T13:18:32.861Z" }, - { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497, upload-time = "2025-08-07T13:18:31.636Z" }, - { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662, upload-time = "2025-08-07T13:42:41.117Z" }, - { url = "https://files.pythonhosted.org/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210, upload-time = "2025-08-07T13:18:24.072Z" }, - { url = "https://files.pythonhosted.org/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685, upload-time = "2025-08-07T13:24:38.824Z" }, - { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586, upload-time = "2025-08-07T13:16:08.004Z" }, - { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346, upload-time = "2025-08-07T13:42:59.944Z" }, - { url = "https://files.pythonhosted.org/packages/c0/aa/687d6b12ffb505a4447567d1f3abea23bd20e73a5bed63871178e0831b7a/greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5", size = 699218, upload-time = "2025-08-07T13:45:30.969Z" }, - { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659, upload-time = "2025-08-07T13:53:17.759Z" }, - { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355, upload-time = "2025-08-07T13:18:34.517Z" }, - { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512, upload-time = "2025-08-07T13:18:33.969Z" }, - { url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425, upload-time = "2025-08-07T13:32:27.59Z" }, + { url = "https://files.pythonhosted.org/packages/7d/ed/6bfa4109fcb23a58819600392564fea69cdc6551ffd5e69ccf1d52a40cbc/greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c", size = 271061 }, + { url = "https://files.pythonhosted.org/packages/2a/fc/102ec1a2fc015b3a7652abab7acf3541d58c04d3d17a8d3d6a44adae1eb1/greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590", size = 629475 }, + { url = "https://files.pythonhosted.org/packages/c5/26/80383131d55a4ac0fb08d71660fd77e7660b9db6bdb4e8884f46d9f2cc04/greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c", size = 640802 }, + { url = "https://files.pythonhosted.org/packages/9f/7c/e7833dbcd8f376f3326bd728c845d31dcde4c84268d3921afcae77d90d08/greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b", size = 636703 }, + { url = "https://files.pythonhosted.org/packages/e9/49/547b93b7c0428ede7b3f309bc965986874759f7d89e4e04aeddbc9699acb/greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31", size = 635417 }, + { url = "https://files.pythonhosted.org/packages/7f/91/ae2eb6b7979e2f9b035a9f612cf70f1bf54aad4e1d125129bef1eae96f19/greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d", size = 584358 }, + { url = "https://files.pythonhosted.org/packages/f7/85/433de0c9c0252b22b16d413c9407e6cb3b41df7389afc366ca204dbc1393/greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5", size = 1113550 }, + { url = "https://files.pythonhosted.org/packages/a1/8d/88f3ebd2bc96bf7747093696f4335a0a8a4c5acfcf1b757717c0d2474ba3/greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f", size = 1137126 }, + { url = "https://files.pythonhosted.org/packages/d6/6f/b60b0291d9623c496638c582297ead61f43c4b72eef5e9c926ef4565ec13/greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c", size = 298654 }, + { url = "https://files.pythonhosted.org/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2", size = 272305 }, + { url = "https://files.pythonhosted.org/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246", size = 632472 }, + { url = "https://files.pythonhosted.org/packages/ae/8f/95d48d7e3d433e6dae5b1682e4292242a53f22df82e6d3dda81b1701a960/greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3", size = 644646 }, + { url = "https://files.pythonhosted.org/packages/d5/5e/405965351aef8c76b8ef7ad370e5da58d57ef6068df197548b015464001a/greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633", size = 640519 }, + { url = "https://files.pythonhosted.org/packages/25/5d/382753b52006ce0218297ec1b628e048c4e64b155379331f25a7316eb749/greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079", size = 639707 }, + { url = "https://files.pythonhosted.org/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8", size = 587684 }, + { url = "https://files.pythonhosted.org/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52", size = 1116647 }, + { url = "https://files.pythonhosted.org/packages/3f/cc/b07000438a29ac5cfb2194bfc128151d52f333cee74dd7dfe3fb733fc16c/greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa", size = 1142073 }, + { url = "https://files.pythonhosted.org/packages/d8/0f/30aef242fcab550b0b3520b8e3561156857c94288f0332a79928c31a52cf/greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9", size = 299100 }, + { url = "https://files.pythonhosted.org/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079 }, + { url = "https://files.pythonhosted.org/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997 }, + { url = "https://files.pythonhosted.org/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968", size = 655185 }, + { url = "https://files.pythonhosted.org/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9", size = 649926 }, + { url = "https://files.pythonhosted.org/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6", size = 651839 }, + { url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586 }, + { url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281 }, + { url = "https://files.pythonhosted.org/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f", size = 1151142 }, + { url = "https://files.pythonhosted.org/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02", size = 299899 }, + { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814 }, + { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073 }, + { url = "https://files.pythonhosted.org/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191 }, + { url = "https://files.pythonhosted.org/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516 }, + { url = "https://files.pythonhosted.org/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169 }, + { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497 }, + { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662 }, + { url = "https://files.pythonhosted.org/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210 }, + { url = "https://files.pythonhosted.org/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685 }, + { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586 }, + { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346 }, + { url = "https://files.pythonhosted.org/packages/c0/aa/687d6b12ffb505a4447567d1f3abea23bd20e73a5bed63871178e0831b7a/greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5", size = 699218 }, + { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659 }, + { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355 }, + { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512 }, + { url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425 }, ] [[package]] name = "grpcio" version = "1.74.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/38/b4/35feb8f7cab7239c5b94bd2db71abb3d6adb5f335ad8f131abb6060840b6/grpcio-1.74.0.tar.gz", hash = "sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1", size = 12756048, upload-time = "2025-07-24T18:54:23.039Z" } +sdist = { url = "https://files.pythonhosted.org/packages/38/b4/35feb8f7cab7239c5b94bd2db71abb3d6adb5f335ad8f131abb6060840b6/grpcio-1.74.0.tar.gz", hash = "sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1", size = 12756048 } wheels = [ - { url = "https://files.pythonhosted.org/packages/66/54/68e51a90797ad7afc5b0a7881426c337f6a9168ebab73c3210b76aa7c90d/grpcio-1.74.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:85bd5cdf4ed7b2d6438871adf6afff9af7096486fcf51818a81b77ef4dd30907", size = 5481935, upload-time = "2025-07-24T18:52:43.756Z" }, - { url = "https://files.pythonhosted.org/packages/32/2a/af817c7e9843929e93e54d09c9aee2555c2e8d81b93102a9426b36e91833/grpcio-1.74.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:68c8ebcca945efff9d86d8d6d7bfb0841cf0071024417e2d7f45c5e46b5b08eb", size = 10986796, upload-time = "2025-07-24T18:52:47.219Z" }, - { url = "https://files.pythonhosted.org/packages/d5/94/d67756638d7bb07750b07d0826c68e414124574b53840ba1ff777abcd388/grpcio-1.74.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:e154d230dc1bbbd78ad2fdc3039fa50ad7ffcf438e4eb2fa30bce223a70c7486", size = 5983663, upload-time = "2025-07-24T18:52:49.463Z" }, - { url = "https://files.pythonhosted.org/packages/35/f5/c5e4853bf42148fea8532d49e919426585b73eafcf379a712934652a8de9/grpcio-1.74.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8978003816c7b9eabe217f88c78bc26adc8f9304bf6a594b02e5a49b2ef9c11", size = 6653765, upload-time = "2025-07-24T18:52:51.094Z" }, - { url = "https://files.pythonhosted.org/packages/fd/75/a1991dd64b331d199935e096cc9daa3415ee5ccbe9f909aa48eded7bba34/grpcio-1.74.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3d7bd6e3929fd2ea7fbc3f562e4987229ead70c9ae5f01501a46701e08f1ad9", size = 6215172, upload-time = "2025-07-24T18:52:53.282Z" }, - { url = "https://files.pythonhosted.org/packages/01/a4/7cef3dbb3b073d0ce34fd507efc44ac4c9442a0ef9fba4fb3f5c551efef5/grpcio-1.74.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:136b53c91ac1d02c8c24201bfdeb56f8b3ac3278668cbb8e0ba49c88069e1bdc", size = 6329142, upload-time = "2025-07-24T18:52:54.927Z" }, - { url = "https://files.pythonhosted.org/packages/bf/d3/587920f882b46e835ad96014087054655312400e2f1f1446419e5179a383/grpcio-1.74.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fe0f540750a13fd8e5da4b3eaba91a785eea8dca5ccd2bc2ffe978caa403090e", size = 7018632, upload-time = "2025-07-24T18:52:56.523Z" }, - { url = "https://files.pythonhosted.org/packages/1f/95/c70a3b15a0bc83334b507e3d2ae20ee8fa38d419b8758a4d838f5c2a7d32/grpcio-1.74.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4e4181bfc24413d1e3a37a0b7889bea68d973d4b45dd2bc68bb766c140718f82", size = 6509641, upload-time = "2025-07-24T18:52:58.495Z" }, - { url = "https://files.pythonhosted.org/packages/4b/06/2e7042d06247d668ae69ea6998eca33f475fd4e2855f94dcb2aa5daef334/grpcio-1.74.0-cp310-cp310-win32.whl", hash = "sha256:1733969040989f7acc3d94c22f55b4a9501a30f6aaacdbccfaba0a3ffb255ab7", size = 3817478, upload-time = "2025-07-24T18:53:00.128Z" }, - { url = "https://files.pythonhosted.org/packages/93/20/e02b9dcca3ee91124060b65bbf5b8e1af80b3b76a30f694b44b964ab4d71/grpcio-1.74.0-cp310-cp310-win_amd64.whl", hash = "sha256:9e912d3c993a29df6c627459af58975b2e5c897d93287939b9d5065f000249b5", size = 4493971, upload-time = "2025-07-24T18:53:02.068Z" }, - { url = "https://files.pythonhosted.org/packages/e7/77/b2f06db9f240a5abeddd23a0e49eae2b6ac54d85f0e5267784ce02269c3b/grpcio-1.74.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:69e1a8180868a2576f02356565f16635b99088da7df3d45aaa7e24e73a054e31", size = 5487368, upload-time = "2025-07-24T18:53:03.548Z" }, - { url = "https://files.pythonhosted.org/packages/48/99/0ac8678a819c28d9a370a663007581744a9f2a844e32f0fa95e1ddda5b9e/grpcio-1.74.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8efe72fde5500f47aca1ef59495cb59c885afe04ac89dd11d810f2de87d935d4", size = 10999804, upload-time = "2025-07-24T18:53:05.095Z" }, - { url = "https://files.pythonhosted.org/packages/45/c6/a2d586300d9e14ad72e8dc211c7aecb45fe9846a51e558c5bca0c9102c7f/grpcio-1.74.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a8f0302f9ac4e9923f98d8e243939a6fb627cd048f5cd38595c97e38020dffce", size = 5987667, upload-time = "2025-07-24T18:53:07.157Z" }, - { url = "https://files.pythonhosted.org/packages/c9/57/5f338bf56a7f22584e68d669632e521f0de460bb3749d54533fc3d0fca4f/grpcio-1.74.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f609a39f62a6f6f05c7512746798282546358a37ea93c1fcbadf8b2fed162e3", size = 6655612, upload-time = "2025-07-24T18:53:09.244Z" }, - { url = "https://files.pythonhosted.org/packages/82/ea/a4820c4c44c8b35b1903a6c72a5bdccec92d0840cf5c858c498c66786ba5/grpcio-1.74.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c98e0b7434a7fa4e3e63f250456eaef52499fba5ae661c58cc5b5477d11e7182", size = 6219544, upload-time = "2025-07-24T18:53:11.221Z" }, - { url = "https://files.pythonhosted.org/packages/a4/17/0537630a921365928f5abb6d14c79ba4dcb3e662e0dbeede8af4138d9dcf/grpcio-1.74.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:662456c4513e298db6d7bd9c3b8df6f75f8752f0ba01fb653e252ed4a59b5a5d", size = 6334863, upload-time = "2025-07-24T18:53:12.925Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a6/85ca6cb9af3f13e1320d0a806658dca432ff88149d5972df1f7b51e87127/grpcio-1.74.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3d14e3c4d65e19d8430a4e28ceb71ace4728776fd6c3ce34016947474479683f", size = 7019320, upload-time = "2025-07-24T18:53:15.002Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a7/fe2beab970a1e25d2eff108b3cf4f7d9a53c185106377a3d1989216eba45/grpcio-1.74.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bf949792cee20d2078323a9b02bacbbae002b9e3b9e2433f2741c15bdeba1c4", size = 6514228, upload-time = "2025-07-24T18:53:16.999Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c2/2f9c945c8a248cebc3ccda1b7a1bf1775b9d7d59e444dbb18c0014e23da6/grpcio-1.74.0-cp311-cp311-win32.whl", hash = "sha256:55b453812fa7c7ce2f5c88be3018fb4a490519b6ce80788d5913f3f9d7da8c7b", size = 3817216, upload-time = "2025-07-24T18:53:20.564Z" }, - { url = "https://files.pythonhosted.org/packages/ff/d1/a9cf9c94b55becda2199299a12b9feef0c79946b0d9d34c989de6d12d05d/grpcio-1.74.0-cp311-cp311-win_amd64.whl", hash = "sha256:86ad489db097141a907c559988c29718719aa3e13370d40e20506f11b4de0d11", size = 4495380, upload-time = "2025-07-24T18:53:22.058Z" }, - { url = "https://files.pythonhosted.org/packages/4c/5d/e504d5d5c4469823504f65687d6c8fb97b7f7bf0b34873b7598f1df24630/grpcio-1.74.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8533e6e9c5bd630ca98062e3a1326249e6ada07d05acf191a77bc33f8948f3d8", size = 5445551, upload-time = "2025-07-24T18:53:23.641Z" }, - { url = "https://files.pythonhosted.org/packages/43/01/730e37056f96f2f6ce9f17999af1556df62ee8dab7fa48bceeaab5fd3008/grpcio-1.74.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:2918948864fec2a11721d91568effffbe0a02b23ecd57f281391d986847982f6", size = 10979810, upload-time = "2025-07-24T18:53:25.349Z" }, - { url = "https://files.pythonhosted.org/packages/79/3d/09fd100473ea5c47083889ca47ffd356576173ec134312f6aa0e13111dee/grpcio-1.74.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:60d2d48b0580e70d2e1954d0d19fa3c2e60dd7cbed826aca104fff518310d1c5", size = 5941946, upload-time = "2025-07-24T18:53:27.387Z" }, - { url = "https://files.pythonhosted.org/packages/8a/99/12d2cca0a63c874c6d3d195629dcd85cdf5d6f98a30d8db44271f8a97b93/grpcio-1.74.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3601274bc0523f6dc07666c0e01682c94472402ac2fd1226fd96e079863bfa49", size = 6621763, upload-time = "2025-07-24T18:53:29.193Z" }, - { url = "https://files.pythonhosted.org/packages/9d/2c/930b0e7a2f1029bbc193443c7bc4dc2a46fedb0203c8793dcd97081f1520/grpcio-1.74.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:176d60a5168d7948539def20b2a3adcce67d72454d9ae05969a2e73f3a0feee7", size = 6180664, upload-time = "2025-07-24T18:53:30.823Z" }, - { url = "https://files.pythonhosted.org/packages/db/d5/ff8a2442180ad0867717e670f5ec42bfd8d38b92158ad6bcd864e6d4b1ed/grpcio-1.74.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e759f9e8bc908aaae0412642afe5416c9f983a80499448fcc7fab8692ae044c3", size = 6301083, upload-time = "2025-07-24T18:53:32.454Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ba/b361d390451a37ca118e4ec7dccec690422e05bc85fba2ec72b06cefec9f/grpcio-1.74.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9e7c4389771855a92934b2846bd807fc25a3dfa820fd912fe6bd8136026b2707", size = 6994132, upload-time = "2025-07-24T18:53:34.506Z" }, - { url = "https://files.pythonhosted.org/packages/3b/0c/3a5fa47d2437a44ced74141795ac0251bbddeae74bf81df3447edd767d27/grpcio-1.74.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cce634b10aeab37010449124814b05a62fb5f18928ca878f1bf4750d1f0c815b", size = 6489616, upload-time = "2025-07-24T18:53:36.217Z" }, - { url = "https://files.pythonhosted.org/packages/ae/95/ab64703b436d99dc5217228babc76047d60e9ad14df129e307b5fec81fd0/grpcio-1.74.0-cp312-cp312-win32.whl", hash = "sha256:885912559974df35d92219e2dc98f51a16a48395f37b92865ad45186f294096c", size = 3807083, upload-time = "2025-07-24T18:53:37.911Z" }, - { url = "https://files.pythonhosted.org/packages/84/59/900aa2445891fc47a33f7d2f76e00ca5d6ae6584b20d19af9c06fa09bf9a/grpcio-1.74.0-cp312-cp312-win_amd64.whl", hash = "sha256:42f8fee287427b94be63d916c90399ed310ed10aadbf9e2e5538b3e497d269bc", size = 4490123, upload-time = "2025-07-24T18:53:39.528Z" }, - { url = "https://files.pythonhosted.org/packages/d4/d8/1004a5f468715221450e66b051c839c2ce9a985aa3ee427422061fcbb6aa/grpcio-1.74.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:2bc2d7d8d184e2362b53905cb1708c84cb16354771c04b490485fa07ce3a1d89", size = 5449488, upload-time = "2025-07-24T18:53:41.174Z" }, - { url = "https://files.pythonhosted.org/packages/94/0e/33731a03f63740d7743dced423846c831d8e6da808fcd02821a4416df7fa/grpcio-1.74.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:c14e803037e572c177ba54a3e090d6eb12efd795d49327c5ee2b3bddb836bf01", size = 10974059, upload-time = "2025-07-24T18:53:43.066Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c6/3d2c14d87771a421205bdca991467cfe473ee4c6a1231c1ede5248c62ab8/grpcio-1.74.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f6ec94f0e50eb8fa1744a731088b966427575e40c2944a980049798b127a687e", size = 5945647, upload-time = "2025-07-24T18:53:45.269Z" }, - { url = "https://files.pythonhosted.org/packages/c5/83/5a354c8aaff58594eef7fffebae41a0f8995a6258bbc6809b800c33d4c13/grpcio-1.74.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:566b9395b90cc3d0d0c6404bc8572c7c18786ede549cdb540ae27b58afe0fb91", size = 6626101, upload-time = "2025-07-24T18:53:47.015Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ca/4fdc7bf59bf6994aa45cbd4ef1055cd65e2884de6113dbd49f75498ddb08/grpcio-1.74.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1ea6176d7dfd5b941ea01c2ec34de9531ba494d541fe2057c904e601879f249", size = 6182562, upload-time = "2025-07-24T18:53:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/fd/48/2869e5b2c1922583686f7ae674937986807c2f676d08be70d0a541316270/grpcio-1.74.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:64229c1e9cea079420527fa8ac45d80fc1e8d3f94deaa35643c381fa8d98f362", size = 6303425, upload-time = "2025-07-24T18:53:50.847Z" }, - { url = "https://files.pythonhosted.org/packages/a6/0e/bac93147b9a164f759497bc6913e74af1cb632c733c7af62c0336782bd38/grpcio-1.74.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:0f87bddd6e27fc776aacf7ebfec367b6d49cad0455123951e4488ea99d9b9b8f", size = 6996533, upload-time = "2025-07-24T18:53:52.747Z" }, - { url = "https://files.pythonhosted.org/packages/84/35/9f6b2503c1fd86d068b46818bbd7329db26a87cdd8c01e0d1a9abea1104c/grpcio-1.74.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3b03d8f2a07f0fea8c8f74deb59f8352b770e3900d143b3d1475effcb08eec20", size = 6491489, upload-time = "2025-07-24T18:53:55.06Z" }, - { url = "https://files.pythonhosted.org/packages/75/33/a04e99be2a82c4cbc4039eb3a76f6c3632932b9d5d295221389d10ac9ca7/grpcio-1.74.0-cp313-cp313-win32.whl", hash = "sha256:b6a73b2ba83e663b2480a90b82fdae6a7aa6427f62bf43b29912c0cfd1aa2bfa", size = 3805811, upload-time = "2025-07-24T18:53:56.798Z" }, - { url = "https://files.pythonhosted.org/packages/34/80/de3eb55eb581815342d097214bed4c59e806b05f1b3110df03b2280d6dfd/grpcio-1.74.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd3c71aeee838299c5887230b8a1822795325ddfea635edd82954c1eaa831e24", size = 4489214, upload-time = "2025-07-24T18:53:59.771Z" }, + { url = "https://files.pythonhosted.org/packages/66/54/68e51a90797ad7afc5b0a7881426c337f6a9168ebab73c3210b76aa7c90d/grpcio-1.74.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:85bd5cdf4ed7b2d6438871adf6afff9af7096486fcf51818a81b77ef4dd30907", size = 5481935 }, + { url = "https://files.pythonhosted.org/packages/32/2a/af817c7e9843929e93e54d09c9aee2555c2e8d81b93102a9426b36e91833/grpcio-1.74.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:68c8ebcca945efff9d86d8d6d7bfb0841cf0071024417e2d7f45c5e46b5b08eb", size = 10986796 }, + { url = "https://files.pythonhosted.org/packages/d5/94/d67756638d7bb07750b07d0826c68e414124574b53840ba1ff777abcd388/grpcio-1.74.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:e154d230dc1bbbd78ad2fdc3039fa50ad7ffcf438e4eb2fa30bce223a70c7486", size = 5983663 }, + { url = "https://files.pythonhosted.org/packages/35/f5/c5e4853bf42148fea8532d49e919426585b73eafcf379a712934652a8de9/grpcio-1.74.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8978003816c7b9eabe217f88c78bc26adc8f9304bf6a594b02e5a49b2ef9c11", size = 6653765 }, + { url = "https://files.pythonhosted.org/packages/fd/75/a1991dd64b331d199935e096cc9daa3415ee5ccbe9f909aa48eded7bba34/grpcio-1.74.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3d7bd6e3929fd2ea7fbc3f562e4987229ead70c9ae5f01501a46701e08f1ad9", size = 6215172 }, + { url = "https://files.pythonhosted.org/packages/01/a4/7cef3dbb3b073d0ce34fd507efc44ac4c9442a0ef9fba4fb3f5c551efef5/grpcio-1.74.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:136b53c91ac1d02c8c24201bfdeb56f8b3ac3278668cbb8e0ba49c88069e1bdc", size = 6329142 }, + { url = "https://files.pythonhosted.org/packages/bf/d3/587920f882b46e835ad96014087054655312400e2f1f1446419e5179a383/grpcio-1.74.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fe0f540750a13fd8e5da4b3eaba91a785eea8dca5ccd2bc2ffe978caa403090e", size = 7018632 }, + { url = "https://files.pythonhosted.org/packages/1f/95/c70a3b15a0bc83334b507e3d2ae20ee8fa38d419b8758a4d838f5c2a7d32/grpcio-1.74.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4e4181bfc24413d1e3a37a0b7889bea68d973d4b45dd2bc68bb766c140718f82", size = 6509641 }, + { url = "https://files.pythonhosted.org/packages/4b/06/2e7042d06247d668ae69ea6998eca33f475fd4e2855f94dcb2aa5daef334/grpcio-1.74.0-cp310-cp310-win32.whl", hash = "sha256:1733969040989f7acc3d94c22f55b4a9501a30f6aaacdbccfaba0a3ffb255ab7", size = 3817478 }, + { url = "https://files.pythonhosted.org/packages/93/20/e02b9dcca3ee91124060b65bbf5b8e1af80b3b76a30f694b44b964ab4d71/grpcio-1.74.0-cp310-cp310-win_amd64.whl", hash = "sha256:9e912d3c993a29df6c627459af58975b2e5c897d93287939b9d5065f000249b5", size = 4493971 }, + { url = "https://files.pythonhosted.org/packages/e7/77/b2f06db9f240a5abeddd23a0e49eae2b6ac54d85f0e5267784ce02269c3b/grpcio-1.74.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:69e1a8180868a2576f02356565f16635b99088da7df3d45aaa7e24e73a054e31", size = 5487368 }, + { url = "https://files.pythonhosted.org/packages/48/99/0ac8678a819c28d9a370a663007581744a9f2a844e32f0fa95e1ddda5b9e/grpcio-1.74.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8efe72fde5500f47aca1ef59495cb59c885afe04ac89dd11d810f2de87d935d4", size = 10999804 }, + { url = "https://files.pythonhosted.org/packages/45/c6/a2d586300d9e14ad72e8dc211c7aecb45fe9846a51e558c5bca0c9102c7f/grpcio-1.74.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a8f0302f9ac4e9923f98d8e243939a6fb627cd048f5cd38595c97e38020dffce", size = 5987667 }, + { url = "https://files.pythonhosted.org/packages/c9/57/5f338bf56a7f22584e68d669632e521f0de460bb3749d54533fc3d0fca4f/grpcio-1.74.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f609a39f62a6f6f05c7512746798282546358a37ea93c1fcbadf8b2fed162e3", size = 6655612 }, + { url = "https://files.pythonhosted.org/packages/82/ea/a4820c4c44c8b35b1903a6c72a5bdccec92d0840cf5c858c498c66786ba5/grpcio-1.74.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c98e0b7434a7fa4e3e63f250456eaef52499fba5ae661c58cc5b5477d11e7182", size = 6219544 }, + { url = "https://files.pythonhosted.org/packages/a4/17/0537630a921365928f5abb6d14c79ba4dcb3e662e0dbeede8af4138d9dcf/grpcio-1.74.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:662456c4513e298db6d7bd9c3b8df6f75f8752f0ba01fb653e252ed4a59b5a5d", size = 6334863 }, + { url = "https://files.pythonhosted.org/packages/e2/a6/85ca6cb9af3f13e1320d0a806658dca432ff88149d5972df1f7b51e87127/grpcio-1.74.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3d14e3c4d65e19d8430a4e28ceb71ace4728776fd6c3ce34016947474479683f", size = 7019320 }, + { url = "https://files.pythonhosted.org/packages/4f/a7/fe2beab970a1e25d2eff108b3cf4f7d9a53c185106377a3d1989216eba45/grpcio-1.74.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bf949792cee20d2078323a9b02bacbbae002b9e3b9e2433f2741c15bdeba1c4", size = 6514228 }, + { url = "https://files.pythonhosted.org/packages/6a/c2/2f9c945c8a248cebc3ccda1b7a1bf1775b9d7d59e444dbb18c0014e23da6/grpcio-1.74.0-cp311-cp311-win32.whl", hash = "sha256:55b453812fa7c7ce2f5c88be3018fb4a490519b6ce80788d5913f3f9d7da8c7b", size = 3817216 }, + { url = "https://files.pythonhosted.org/packages/ff/d1/a9cf9c94b55becda2199299a12b9feef0c79946b0d9d34c989de6d12d05d/grpcio-1.74.0-cp311-cp311-win_amd64.whl", hash = "sha256:86ad489db097141a907c559988c29718719aa3e13370d40e20506f11b4de0d11", size = 4495380 }, + { url = "https://files.pythonhosted.org/packages/4c/5d/e504d5d5c4469823504f65687d6c8fb97b7f7bf0b34873b7598f1df24630/grpcio-1.74.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8533e6e9c5bd630ca98062e3a1326249e6ada07d05acf191a77bc33f8948f3d8", size = 5445551 }, + { url = "https://files.pythonhosted.org/packages/43/01/730e37056f96f2f6ce9f17999af1556df62ee8dab7fa48bceeaab5fd3008/grpcio-1.74.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:2918948864fec2a11721d91568effffbe0a02b23ecd57f281391d986847982f6", size = 10979810 }, + { url = "https://files.pythonhosted.org/packages/79/3d/09fd100473ea5c47083889ca47ffd356576173ec134312f6aa0e13111dee/grpcio-1.74.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:60d2d48b0580e70d2e1954d0d19fa3c2e60dd7cbed826aca104fff518310d1c5", size = 5941946 }, + { url = "https://files.pythonhosted.org/packages/8a/99/12d2cca0a63c874c6d3d195629dcd85cdf5d6f98a30d8db44271f8a97b93/grpcio-1.74.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3601274bc0523f6dc07666c0e01682c94472402ac2fd1226fd96e079863bfa49", size = 6621763 }, + { url = "https://files.pythonhosted.org/packages/9d/2c/930b0e7a2f1029bbc193443c7bc4dc2a46fedb0203c8793dcd97081f1520/grpcio-1.74.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:176d60a5168d7948539def20b2a3adcce67d72454d9ae05969a2e73f3a0feee7", size = 6180664 }, + { url = "https://files.pythonhosted.org/packages/db/d5/ff8a2442180ad0867717e670f5ec42bfd8d38b92158ad6bcd864e6d4b1ed/grpcio-1.74.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e759f9e8bc908aaae0412642afe5416c9f983a80499448fcc7fab8692ae044c3", size = 6301083 }, + { url = "https://files.pythonhosted.org/packages/b0/ba/b361d390451a37ca118e4ec7dccec690422e05bc85fba2ec72b06cefec9f/grpcio-1.74.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9e7c4389771855a92934b2846bd807fc25a3dfa820fd912fe6bd8136026b2707", size = 6994132 }, + { url = "https://files.pythonhosted.org/packages/3b/0c/3a5fa47d2437a44ced74141795ac0251bbddeae74bf81df3447edd767d27/grpcio-1.74.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cce634b10aeab37010449124814b05a62fb5f18928ca878f1bf4750d1f0c815b", size = 6489616 }, + { url = "https://files.pythonhosted.org/packages/ae/95/ab64703b436d99dc5217228babc76047d60e9ad14df129e307b5fec81fd0/grpcio-1.74.0-cp312-cp312-win32.whl", hash = "sha256:885912559974df35d92219e2dc98f51a16a48395f37b92865ad45186f294096c", size = 3807083 }, + { url = "https://files.pythonhosted.org/packages/84/59/900aa2445891fc47a33f7d2f76e00ca5d6ae6584b20d19af9c06fa09bf9a/grpcio-1.74.0-cp312-cp312-win_amd64.whl", hash = "sha256:42f8fee287427b94be63d916c90399ed310ed10aadbf9e2e5538b3e497d269bc", size = 4490123 }, + { url = "https://files.pythonhosted.org/packages/d4/d8/1004a5f468715221450e66b051c839c2ce9a985aa3ee427422061fcbb6aa/grpcio-1.74.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:2bc2d7d8d184e2362b53905cb1708c84cb16354771c04b490485fa07ce3a1d89", size = 5449488 }, + { url = "https://files.pythonhosted.org/packages/94/0e/33731a03f63740d7743dced423846c831d8e6da808fcd02821a4416df7fa/grpcio-1.74.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:c14e803037e572c177ba54a3e090d6eb12efd795d49327c5ee2b3bddb836bf01", size = 10974059 }, + { url = "https://files.pythonhosted.org/packages/0d/c6/3d2c14d87771a421205bdca991467cfe473ee4c6a1231c1ede5248c62ab8/grpcio-1.74.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f6ec94f0e50eb8fa1744a731088b966427575e40c2944a980049798b127a687e", size = 5945647 }, + { url = "https://files.pythonhosted.org/packages/c5/83/5a354c8aaff58594eef7fffebae41a0f8995a6258bbc6809b800c33d4c13/grpcio-1.74.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:566b9395b90cc3d0d0c6404bc8572c7c18786ede549cdb540ae27b58afe0fb91", size = 6626101 }, + { url = "https://files.pythonhosted.org/packages/3f/ca/4fdc7bf59bf6994aa45cbd4ef1055cd65e2884de6113dbd49f75498ddb08/grpcio-1.74.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1ea6176d7dfd5b941ea01c2ec34de9531ba494d541fe2057c904e601879f249", size = 6182562 }, + { url = "https://files.pythonhosted.org/packages/fd/48/2869e5b2c1922583686f7ae674937986807c2f676d08be70d0a541316270/grpcio-1.74.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:64229c1e9cea079420527fa8ac45d80fc1e8d3f94deaa35643c381fa8d98f362", size = 6303425 }, + { url = "https://files.pythonhosted.org/packages/a6/0e/bac93147b9a164f759497bc6913e74af1cb632c733c7af62c0336782bd38/grpcio-1.74.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:0f87bddd6e27fc776aacf7ebfec367b6d49cad0455123951e4488ea99d9b9b8f", size = 6996533 }, + { url = "https://files.pythonhosted.org/packages/84/35/9f6b2503c1fd86d068b46818bbd7329db26a87cdd8c01e0d1a9abea1104c/grpcio-1.74.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3b03d8f2a07f0fea8c8f74deb59f8352b770e3900d143b3d1475effcb08eec20", size = 6491489 }, + { url = "https://files.pythonhosted.org/packages/75/33/a04e99be2a82c4cbc4039eb3a76f6c3632932b9d5d295221389d10ac9ca7/grpcio-1.74.0-cp313-cp313-win32.whl", hash = "sha256:b6a73b2ba83e663b2480a90b82fdae6a7aa6427f62bf43b29912c0cfd1aa2bfa", size = 3805811 }, + { url = "https://files.pythonhosted.org/packages/34/80/de3eb55eb581815342d097214bed4c59e806b05f1b3110df03b2280d6dfd/grpcio-1.74.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd3c71aeee838299c5887230b8a1822795325ddfea635edd82954c1eaa831e24", size = 4489214 }, ] [[package]] name = "h11" version = "0.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, +] + +[[package]] +name = "h2" +version = "4.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hpack", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "hyperframe", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779 }, +] + +[[package]] +name = "hpack" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357 }, ] [[package]] @@ -1278,9 +1355,9 @@ dependencies = [ { name = "certifi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "h11", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, ] [[package]] @@ -1293,45 +1370,59 @@ dependencies = [ { name = "httpcore", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "idna", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[package.optional-dependencies] +http2 = [ + { name = "h2", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] [[package]] name = "httpx-sse" version = "0.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6e/fa/66bd985dd0b7c109a3bcb89272ee0bfb7e2b4d06309ad7b38ff866734b2a/httpx_sse-0.4.1.tar.gz", hash = "sha256:8f44d34414bc7b21bf3602713005c5df4917884f76072479b21f68befa4ea26e", size = 12998, upload-time = "2025-06-24T13:21:05.71Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/fa/66bd985dd0b7c109a3bcb89272ee0bfb7e2b4d06309ad7b38ff866734b2a/httpx_sse-0.4.1.tar.gz", hash = "sha256:8f44d34414bc7b21bf3602713005c5df4917884f76072479b21f68befa4ea26e", size = 12998 } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/0a/6269e3473b09aed2dab8aa1a600c70f31f00ae1349bee30658f7e358a159/httpx_sse-0.4.1-py3-none-any.whl", hash = "sha256:cba42174344c3a5b06f255ce65b350880f962d99ead85e776f23c6618a377a37", size = 8054, upload-time = "2025-06-24T13:21:04.772Z" }, + { url = "https://files.pythonhosted.org/packages/25/0a/6269e3473b09aed2dab8aa1a600c70f31f00ae1349bee30658f7e358a159/httpx_sse-0.4.1-py3-none-any.whl", hash = "sha256:cba42174344c3a5b06f255ce65b350880f962d99ead85e776f23c6618a377a37", size = 8054 }, +] + +[[package]] +name = "hyperframe" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007 }, ] [[package]] name = "identify" -version = "2.6.13" +version = "2.6.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/ca/ffbabe3635bb839aa36b3a893c91a9b0d368cb4d8073e03a12896970af82/identify-2.6.13.tar.gz", hash = "sha256:da8d6c828e773620e13bfa86ea601c5a5310ba4bcd65edf378198b56a1f9fb32", size = 99243, upload-time = "2025-08-09T19:35:00.6Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/c4/62963f25a678f6a050fb0505a65e9e726996171e6dbe1547f79619eefb15/identify-2.6.14.tar.gz", hash = "sha256:663494103b4f717cb26921c52f8751363dc89db64364cd836a9bf1535f53cd6a", size = 99283 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/ce/461b60a3ee109518c055953729bf9ed089a04db895d47e95444071dcdef2/identify-2.6.13-py2.py3-none-any.whl", hash = "sha256:60381139b3ae39447482ecc406944190f690d4a2997f2584062089848361b33b", size = 99153, upload-time = "2025-08-09T19:34:59.1Z" }, + { url = "https://files.pythonhosted.org/packages/e5/ae/2ad30f4652712c82f1c23423d79136fbce338932ad166d70c1efb86a5998/identify-2.6.14-py2.py3-none-any.whl", hash = "sha256:11a073da82212c6646b1f39bb20d4483bfb9543bd5566fec60053c4bb309bf2e", size = 99172 }, ] [[package]] name = "idna" version = "3.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, ] [[package]] name = "imagesize" version = "1.4.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026, upload-time = "2022-07-01T12:21:05.687Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769, upload-time = "2022-07-01T12:21:02.467Z" }, + { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 }, ] [[package]] @@ -1341,18 +1432,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "zipp", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } +sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641 } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656 }, ] [[package]] name = "iniconfig" version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, ] [[package]] @@ -1375,9 +1466,9 @@ dependencies = [ { name = "tornado", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "traitlets", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bb/76/11082e338e0daadc89c8ff866185de11daf67d181901038f9e139d109761/ipykernel-6.30.1.tar.gz", hash = "sha256:6abb270161896402e76b91394fcdce5d1be5d45f456671e5080572f8505be39b", size = 166260, upload-time = "2025-08-04T15:47:35.018Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/76/11082e338e0daadc89c8ff866185de11daf67d181901038f9e139d109761/ipykernel-6.30.1.tar.gz", hash = "sha256:6abb270161896402e76b91394fcdce5d1be5d45f456671e5080572f8505be39b", size = 166260 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/c7/b445faca8deb954fe536abebff4ece5b097b923de482b26e78448c89d1dd/ipykernel-6.30.1-py3-none-any.whl", hash = "sha256:aa6b9fb93dca949069d8b85b6c79b2518e32ac583ae9c7d37c51d119e18b3fb4", size = 117484, upload-time = "2025-08-04T15:47:32.622Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c7/b445faca8deb954fe536abebff4ece5b097b923de482b26e78448c89d1dd/ipykernel-6.30.1-py3-none-any.whl", hash = "sha256:aa6b9fb93dca949069d8b85b6c79b2518e32ac583ae9c7d37c51d119e18b3fb4", size = 117484 }, ] [[package]] @@ -1402,9 +1493,9 @@ dependencies = [ { name = "traitlets", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/31/10ac88f3357fc276dc8a64e8880c82e80e7459326ae1d0a211b40abf6665/ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216", size = 5606088, upload-time = "2025-05-31T16:39:09.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/85/31/10ac88f3357fc276dc8a64e8880c82e80e7459326ae1d0a211b40abf6665/ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216", size = 5606088 } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864, upload-time = "2025-05-31T16:39:06.38Z" }, + { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864 }, ] [[package]] @@ -1413,11 +1504,14 @@ version = "9.5.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.13' and sys_platform == 'darwin'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", "python_full_version >= '3.13' and sys_platform == 'linux'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and sys_platform == 'linux'", "python_full_version >= '3.13' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", ] dependencies = [ { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, @@ -1432,9 +1526,9 @@ dependencies = [ { name = "traitlets", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, { name = "typing-extensions", marker = "(python_full_version == '3.11.*' and sys_platform == 'darwin') or (python_full_version == '3.11.*' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6e/71/a86262bf5a68bf211bcc71fe302af7e05f18a2852fdc610a854d20d085e6/ipython-9.5.0.tar.gz", hash = "sha256:129c44b941fe6d9b82d36fc7a7c18127ddb1d6f02f78f867f402e2e3adde3113", size = 4389137, upload-time = "2025-08-29T12:15:21.519Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/71/a86262bf5a68bf211bcc71fe302af7e05f18a2852fdc610a854d20d085e6/ipython-9.5.0.tar.gz", hash = "sha256:129c44b941fe6d9b82d36fc7a7c18127ddb1d6f02f78f867f402e2e3adde3113", size = 4389137 } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/2a/5628a99d04acb2d2f2e749cdf4ea571d2575e898df0528a090948018b726/ipython-9.5.0-py3-none-any.whl", hash = "sha256:88369ffa1d5817d609120daa523a6da06d02518e582347c29f8451732a9c5e72", size = 612426, upload-time = "2025-08-29T12:15:18.866Z" }, + { url = "https://files.pythonhosted.org/packages/08/2a/5628a99d04acb2d2f2e749cdf4ea571d2575e898df0528a090948018b726/ipython-9.5.0-py3-none-any.whl", hash = "sha256:88369ffa1d5817d609120daa523a6da06d02518e582347c29f8451732a9c5e72", size = 612426 }, ] [[package]] @@ -1444,18 +1538,18 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pygments", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074 }, ] [[package]] name = "isodate" version = "0.7.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705, upload-time = "2024-10-08T23:04:11.5Z" } +sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705 } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320, upload-time = "2024-10-08T23:04:09.501Z" }, + { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320 }, ] [[package]] @@ -1465,9 +1559,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "parso", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, ] [[package]] @@ -1477,81 +1571,81 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, ] [[package]] name = "jiter" version = "0.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500", size = 162759, upload-time = "2025-05-18T19:04:59.73Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500", size = 162759 } wheels = [ - { url = "https://files.pythonhosted.org/packages/be/7e/4011b5c77bec97cb2b572f566220364e3e21b51c48c5bd9c4a9c26b41b67/jiter-0.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cd2fb72b02478f06a900a5782de2ef47e0396b3e1f7d5aba30daeb1fce66f303", size = 317215, upload-time = "2025-05-18T19:03:04.303Z" }, - { url = "https://files.pythonhosted.org/packages/8a/4f/144c1b57c39692efc7ea7d8e247acf28e47d0912800b34d0ad815f6b2824/jiter-0.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32bb468e3af278f095d3fa5b90314728a6916d89ba3d0ffb726dd9bf7367285e", size = 322814, upload-time = "2025-05-18T19:03:06.433Z" }, - { url = "https://files.pythonhosted.org/packages/63/1f/db977336d332a9406c0b1f0b82be6f71f72526a806cbb2281baf201d38e3/jiter-0.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8b3e0068c26ddedc7abc6fac37da2d0af16b921e288a5a613f4b86f050354f", size = 345237, upload-time = "2025-05-18T19:03:07.833Z" }, - { url = "https://files.pythonhosted.org/packages/d7/1c/aa30a4a775e8a672ad7f21532bdbfb269f0706b39c6ff14e1f86bdd9e5ff/jiter-0.10.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:286299b74cc49e25cd42eea19b72aa82c515d2f2ee12d11392c56d8701f52224", size = 370999, upload-time = "2025-05-18T19:03:09.338Z" }, - { url = "https://files.pythonhosted.org/packages/35/df/f8257abc4207830cb18880781b5f5b716bad5b2a22fb4330cfd357407c5b/jiter-0.10.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ed5649ceeaeffc28d87fb012d25a4cd356dcd53eff5acff1f0466b831dda2a7", size = 491109, upload-time = "2025-05-18T19:03:11.13Z" }, - { url = "https://files.pythonhosted.org/packages/06/76/9e1516fd7b4278aa13a2cc7f159e56befbea9aa65c71586305e7afa8b0b3/jiter-0.10.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2ab0051160cb758a70716448908ef14ad476c3774bd03ddce075f3c1f90a3d6", size = 388608, upload-time = "2025-05-18T19:03:12.911Z" }, - { url = "https://files.pythonhosted.org/packages/6d/64/67750672b4354ca20ca18d3d1ccf2c62a072e8a2d452ac3cf8ced73571ef/jiter-0.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03997d2f37f6b67d2f5c475da4412be584e1cec273c1cfc03d642c46db43f8cf", size = 352454, upload-time = "2025-05-18T19:03:14.741Z" }, - { url = "https://files.pythonhosted.org/packages/96/4d/5c4e36d48f169a54b53a305114be3efa2bbffd33b648cd1478a688f639c1/jiter-0.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c404a99352d839fed80d6afd6c1d66071f3bacaaa5c4268983fc10f769112e90", size = 391833, upload-time = "2025-05-18T19:03:16.426Z" }, - { url = "https://files.pythonhosted.org/packages/0b/de/ce4a6166a78810bd83763d2fa13f85f73cbd3743a325469a4a9289af6dae/jiter-0.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66e989410b6666d3ddb27a74c7e50d0829704ede652fd4c858e91f8d64b403d0", size = 523646, upload-time = "2025-05-18T19:03:17.704Z" }, - { url = "https://files.pythonhosted.org/packages/a2/a6/3bc9acce53466972964cf4ad85efecb94f9244539ab6da1107f7aed82934/jiter-0.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b532d3af9ef4f6374609a3bcb5e05a1951d3bf6190dc6b176fdb277c9bbf15ee", size = 514735, upload-time = "2025-05-18T19:03:19.44Z" }, - { url = "https://files.pythonhosted.org/packages/b4/d8/243c2ab8426a2a4dea85ba2a2ba43df379ccece2145320dfd4799b9633c5/jiter-0.10.0-cp310-cp310-win32.whl", hash = "sha256:da9be20b333970e28b72edc4dff63d4fec3398e05770fb3205f7fb460eb48dd4", size = 210747, upload-time = "2025-05-18T19:03:21.184Z" }, - { url = "https://files.pythonhosted.org/packages/37/7a/8021bd615ef7788b98fc76ff533eaac846322c170e93cbffa01979197a45/jiter-0.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:f59e533afed0c5b0ac3eba20d2548c4a550336d8282ee69eb07b37ea526ee4e5", size = 207484, upload-time = "2025-05-18T19:03:23.046Z" }, - { url = "https://files.pythonhosted.org/packages/1b/dd/6cefc6bd68b1c3c979cecfa7029ab582b57690a31cd2f346c4d0ce7951b6/jiter-0.10.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3bebe0c558e19902c96e99217e0b8e8b17d570906e72ed8a87170bc290b1e978", size = 317473, upload-time = "2025-05-18T19:03:25.942Z" }, - { url = "https://files.pythonhosted.org/packages/be/cf/fc33f5159ce132be1d8dd57251a1ec7a631c7df4bd11e1cd198308c6ae32/jiter-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:558cc7e44fd8e507a236bee6a02fa17199ba752874400a0ca6cd6e2196cdb7dc", size = 321971, upload-time = "2025-05-18T19:03:27.255Z" }, - { url = "https://files.pythonhosted.org/packages/68/a4/da3f150cf1d51f6c472616fb7650429c7ce053e0c962b41b68557fdf6379/jiter-0.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d613e4b379a07d7c8453c5712ce7014e86c6ac93d990a0b8e7377e18505e98d", size = 345574, upload-time = "2025-05-18T19:03:28.63Z" }, - { url = "https://files.pythonhosted.org/packages/84/34/6e8d412e60ff06b186040e77da5f83bc158e9735759fcae65b37d681f28b/jiter-0.10.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f62cf8ba0618eda841b9bf61797f21c5ebd15a7a1e19daab76e4e4b498d515b2", size = 371028, upload-time = "2025-05-18T19:03:30.292Z" }, - { url = "https://files.pythonhosted.org/packages/fb/d9/9ee86173aae4576c35a2f50ae930d2ccb4c4c236f6cb9353267aa1d626b7/jiter-0.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:919d139cdfa8ae8945112398511cb7fca58a77382617d279556b344867a37e61", size = 491083, upload-time = "2025-05-18T19:03:31.654Z" }, - { url = "https://files.pythonhosted.org/packages/d9/2c/f955de55e74771493ac9e188b0f731524c6a995dffdcb8c255b89c6fb74b/jiter-0.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13ddbc6ae311175a3b03bd8994881bc4635c923754932918e18da841632349db", size = 388821, upload-time = "2025-05-18T19:03:33.184Z" }, - { url = "https://files.pythonhosted.org/packages/81/5a/0e73541b6edd3f4aada586c24e50626c7815c561a7ba337d6a7eb0a915b4/jiter-0.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c440ea003ad10927a30521a9062ce10b5479592e8a70da27f21eeb457b4a9c5", size = 352174, upload-time = "2025-05-18T19:03:34.965Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c0/61eeec33b8c75b31cae42be14d44f9e6fe3ac15a4e58010256ac3abf3638/jiter-0.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc347c87944983481e138dea467c0551080c86b9d21de6ea9306efb12ca8f606", size = 391869, upload-time = "2025-05-18T19:03:36.436Z" }, - { url = "https://files.pythonhosted.org/packages/41/22/5beb5ee4ad4ef7d86f5ea5b4509f680a20706c4a7659e74344777efb7739/jiter-0.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:13252b58c1f4d8c5b63ab103c03d909e8e1e7842d302473f482915d95fefd605", size = 523741, upload-time = "2025-05-18T19:03:38.168Z" }, - { url = "https://files.pythonhosted.org/packages/ea/10/768e8818538e5817c637b0df52e54366ec4cebc3346108a4457ea7a98f32/jiter-0.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7d1bbf3c465de4a24ab12fb7766a0003f6f9bce48b8b6a886158c4d569452dc5", size = 514527, upload-time = "2025-05-18T19:03:39.577Z" }, - { url = "https://files.pythonhosted.org/packages/73/6d/29b7c2dc76ce93cbedabfd842fc9096d01a0550c52692dfc33d3cc889815/jiter-0.10.0-cp311-cp311-win32.whl", hash = "sha256:db16e4848b7e826edca4ccdd5b145939758dadf0dc06e7007ad0e9cfb5928ae7", size = 210765, upload-time = "2025-05-18T19:03:41.271Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c9/d394706deb4c660137caf13e33d05a031d734eb99c051142e039d8ceb794/jiter-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c9c1d5f10e18909e993f9641f12fe1c77b3e9b533ee94ffa970acc14ded3812", size = 209234, upload-time = "2025-05-18T19:03:42.918Z" }, - { url = "https://files.pythonhosted.org/packages/6d/b5/348b3313c58f5fbfb2194eb4d07e46a35748ba6e5b3b3046143f3040bafa/jiter-0.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1e274728e4a5345a6dde2d343c8da018b9d4bd4350f5a472fa91f66fda44911b", size = 312262, upload-time = "2025-05-18T19:03:44.637Z" }, - { url = "https://files.pythonhosted.org/packages/9c/4a/6a2397096162b21645162825f058d1709a02965606e537e3304b02742e9b/jiter-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7202ae396446c988cb2a5feb33a543ab2165b786ac97f53b59aafb803fef0744", size = 320124, upload-time = "2025-05-18T19:03:46.341Z" }, - { url = "https://files.pythonhosted.org/packages/2a/85/1ce02cade7516b726dd88f59a4ee46914bf79d1676d1228ef2002ed2f1c9/jiter-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ba7722d6748b6920ed02a8f1726fb4b33e0fd2f3f621816a8b486c66410ab2", size = 345330, upload-time = "2025-05-18T19:03:47.596Z" }, - { url = "https://files.pythonhosted.org/packages/75/d0/bb6b4f209a77190ce10ea8d7e50bf3725fc16d3372d0a9f11985a2b23eff/jiter-0.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:371eab43c0a288537d30e1f0b193bc4eca90439fc08a022dd83e5e07500ed026", size = 369670, upload-time = "2025-05-18T19:03:49.334Z" }, - { url = "https://files.pythonhosted.org/packages/a0/f5/a61787da9b8847a601e6827fbc42ecb12be2c925ced3252c8ffcb56afcaf/jiter-0.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c675736059020365cebc845a820214765162728b51ab1e03a1b7b3abb70f74c", size = 489057, upload-time = "2025-05-18T19:03:50.66Z" }, - { url = "https://files.pythonhosted.org/packages/12/e4/6f906272810a7b21406c760a53aadbe52e99ee070fc5c0cb191e316de30b/jiter-0.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c5867d40ab716e4684858e4887489685968a47e3ba222e44cde6e4a2154f959", size = 389372, upload-time = "2025-05-18T19:03:51.98Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ba/77013b0b8ba904bf3762f11e0129b8928bff7f978a81838dfcc958ad5728/jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395bb9a26111b60141757d874d27fdea01b17e8fac958b91c20128ba8f4acc8a", size = 352038, upload-time = "2025-05-18T19:03:53.703Z" }, - { url = "https://files.pythonhosted.org/packages/67/27/c62568e3ccb03368dbcc44a1ef3a423cb86778a4389e995125d3d1aaa0a4/jiter-0.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6842184aed5cdb07e0c7e20e5bdcfafe33515ee1741a6835353bb45fe5d1bd95", size = 391538, upload-time = "2025-05-18T19:03:55.046Z" }, - { url = "https://files.pythonhosted.org/packages/c0/72/0d6b7e31fc17a8fdce76164884edef0698ba556b8eb0af9546ae1a06b91d/jiter-0.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62755d1bcea9876770d4df713d82606c8c1a3dca88ff39046b85a048566d56ea", size = 523557, upload-time = "2025-05-18T19:03:56.386Z" }, - { url = "https://files.pythonhosted.org/packages/2f/09/bc1661fbbcbeb6244bd2904ff3a06f340aa77a2b94e5a7373fd165960ea3/jiter-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533efbce2cacec78d5ba73a41756beff8431dfa1694b6346ce7af3a12c42202b", size = 514202, upload-time = "2025-05-18T19:03:57.675Z" }, - { url = "https://files.pythonhosted.org/packages/1b/84/5a5d5400e9d4d54b8004c9673bbe4403928a00d28529ff35b19e9d176b19/jiter-0.10.0-cp312-cp312-win32.whl", hash = "sha256:8be921f0cadd245e981b964dfbcd6fd4bc4e254cdc069490416dd7a2632ecc01", size = 211781, upload-time = "2025-05-18T19:03:59.025Z" }, - { url = "https://files.pythonhosted.org/packages/9b/52/7ec47455e26f2d6e5f2ea4951a0652c06e5b995c291f723973ae9e724a65/jiter-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7c7d785ae9dda68c2678532a5a1581347e9c15362ae9f6e68f3fdbfb64f2e49", size = 206176, upload-time = "2025-05-18T19:04:00.305Z" }, - { url = "https://files.pythonhosted.org/packages/2e/b0/279597e7a270e8d22623fea6c5d4eeac328e7d95c236ed51a2b884c54f70/jiter-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0588107ec8e11b6f5ef0e0d656fb2803ac6cf94a96b2b9fc675c0e3ab5e8644", size = 311617, upload-time = "2025-05-18T19:04:02.078Z" }, - { url = "https://files.pythonhosted.org/packages/91/e3/0916334936f356d605f54cc164af4060e3e7094364add445a3bc79335d46/jiter-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cafc4628b616dc32530c20ee53d71589816cf385dd9449633e910d596b1f5c8a", size = 318947, upload-time = "2025-05-18T19:04:03.347Z" }, - { url = "https://files.pythonhosted.org/packages/6a/8e/fd94e8c02d0e94539b7d669a7ebbd2776e51f329bb2c84d4385e8063a2ad/jiter-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520ef6d981172693786a49ff5b09eda72a42e539f14788124a07530f785c3ad6", size = 344618, upload-time = "2025-05-18T19:04:04.709Z" }, - { url = "https://files.pythonhosted.org/packages/6f/b0/f9f0a2ec42c6e9c2e61c327824687f1e2415b767e1089c1d9135f43816bd/jiter-0.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dedfd05937f8fc45d17ebdf298fe7e0c77458232bcb73d9fbbf4c6455f5b3", size = 368829, upload-time = "2025-05-18T19:04:06.912Z" }, - { url = "https://files.pythonhosted.org/packages/e8/57/5bbcd5331910595ad53b9fd0c610392ac68692176f05ae48d6ce5c852967/jiter-0.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc299da7789deacf95f64052d97f75c16d4fc8c4c214a22bf8d859a4288a1c2", size = 491034, upload-time = "2025-05-18T19:04:08.222Z" }, - { url = "https://files.pythonhosted.org/packages/9b/be/c393df00e6e6e9e623a73551774449f2f23b6ec6a502a3297aeeece2c65a/jiter-0.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5161e201172de298a8a1baad95eb85db4fb90e902353b1f6a41d64ea64644e25", size = 388529, upload-time = "2025-05-18T19:04:09.566Z" }, - { url = "https://files.pythonhosted.org/packages/42/3e/df2235c54d365434c7f150b986a6e35f41ebdc2f95acea3036d99613025d/jiter-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2227db6ba93cb3e2bf67c87e594adde0609f146344e8207e8730364db27041", size = 350671, upload-time = "2025-05-18T19:04:10.98Z" }, - { url = "https://files.pythonhosted.org/packages/c6/77/71b0b24cbcc28f55ab4dbfe029f9a5b73aeadaba677843fc6dc9ed2b1d0a/jiter-0.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15acb267ea5e2c64515574b06a8bf393fbfee6a50eb1673614aa45f4613c0cca", size = 390864, upload-time = "2025-05-18T19:04:12.722Z" }, - { url = "https://files.pythonhosted.org/packages/6a/d3/ef774b6969b9b6178e1d1e7a89a3bd37d241f3d3ec5f8deb37bbd203714a/jiter-0.10.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:901b92f2e2947dc6dfcb52fd624453862e16665ea909a08398dde19c0731b7f4", size = 522989, upload-time = "2025-05-18T19:04:14.261Z" }, - { url = "https://files.pythonhosted.org/packages/0c/41/9becdb1d8dd5d854142f45a9d71949ed7e87a8e312b0bede2de849388cb9/jiter-0.10.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d0cb9a125d5a3ec971a094a845eadde2db0de85b33c9f13eb94a0c63d463879e", size = 513495, upload-time = "2025-05-18T19:04:15.603Z" }, - { url = "https://files.pythonhosted.org/packages/9c/36/3468e5a18238bdedae7c4d19461265b5e9b8e288d3f86cd89d00cbb48686/jiter-0.10.0-cp313-cp313-win32.whl", hash = "sha256:48a403277ad1ee208fb930bdf91745e4d2d6e47253eedc96e2559d1e6527006d", size = 211289, upload-time = "2025-05-18T19:04:17.541Z" }, - { url = "https://files.pythonhosted.org/packages/7e/07/1c96b623128bcb913706e294adb5f768fb7baf8db5e1338ce7b4ee8c78ef/jiter-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:75f9eb72ecb640619c29bf714e78c9c46c9c4eaafd644bf78577ede459f330d4", size = 205074, upload-time = "2025-05-18T19:04:19.21Z" }, - { url = "https://files.pythonhosted.org/packages/54/46/caa2c1342655f57d8f0f2519774c6d67132205909c65e9aa8255e1d7b4f4/jiter-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:28ed2a4c05a1f32ef0e1d24c2611330219fed727dae01789f4a335617634b1ca", size = 318225, upload-time = "2025-05-18T19:04:20.583Z" }, - { url = "https://files.pythonhosted.org/packages/43/84/c7d44c75767e18946219ba2d703a5a32ab37b0bc21886a97bc6062e4da42/jiter-0.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a4c418b1ec86a195f1ca69da8b23e8926c752b685af665ce30777233dfe070", size = 350235, upload-time = "2025-05-18T19:04:22.363Z" }, - { url = "https://files.pythonhosted.org/packages/01/16/f5a0135ccd968b480daad0e6ab34b0c7c5ba3bc447e5088152696140dcb3/jiter-0.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d7bfed2fe1fe0e4dda6ef682cee888ba444b21e7a6553e03252e4feb6cf0adca", size = 207278, upload-time = "2025-05-18T19:04:23.627Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9b/1d646da42c3de6c2188fdaa15bce8ecb22b635904fc68be025e21249ba44/jiter-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e9251a5e83fab8d87799d3e1a46cb4b7f2919b895c6f4483629ed2446f66522", size = 310866, upload-time = "2025-05-18T19:04:24.891Z" }, - { url = "https://files.pythonhosted.org/packages/ad/0e/26538b158e8a7c7987e94e7aeb2999e2e82b1f9d2e1f6e9874ddf71ebda0/jiter-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:023aa0204126fe5b87ccbcd75c8a0d0261b9abdbbf46d55e7ae9f8e22424eeb8", size = 318772, upload-time = "2025-05-18T19:04:26.161Z" }, - { url = "https://files.pythonhosted.org/packages/7b/fb/d302893151caa1c2636d6574d213e4b34e31fd077af6050a9c5cbb42f6fb/jiter-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c189c4f1779c05f75fc17c0c1267594ed918996a231593a21a5ca5438445216", size = 344534, upload-time = "2025-05-18T19:04:27.495Z" }, - { url = "https://files.pythonhosted.org/packages/01/d8/5780b64a149d74e347c5128d82176eb1e3241b1391ac07935693466d6219/jiter-0.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15720084d90d1098ca0229352607cd68256c76991f6b374af96f36920eae13c4", size = 369087, upload-time = "2025-05-18T19:04:28.896Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5b/f235a1437445160e777544f3ade57544daf96ba7e96c1a5b24a6f7ac7004/jiter-0.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f2fb68e5f1cfee30e2b2a09549a00683e0fde4c6a2ab88c94072fc33cb7426", size = 490694, upload-time = "2025-05-18T19:04:30.183Z" }, - { url = "https://files.pythonhosted.org/packages/85/a9/9c3d4617caa2ff89cf61b41e83820c27ebb3f7b5fae8a72901e8cd6ff9be/jiter-0.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce541693355fc6da424c08b7edf39a2895f58d6ea17d92cc2b168d20907dee12", size = 388992, upload-time = "2025-05-18T19:04:32.028Z" }, - { url = "https://files.pythonhosted.org/packages/68/b1/344fd14049ba5c94526540af7eb661871f9c54d5f5601ff41a959b9a0bbd/jiter-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31c50c40272e189d50006ad5c73883caabb73d4e9748a688b216e85a9a9ca3b9", size = 351723, upload-time = "2025-05-18T19:04:33.467Z" }, - { url = "https://files.pythonhosted.org/packages/41/89/4c0e345041186f82a31aee7b9d4219a910df672b9fef26f129f0cda07a29/jiter-0.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa3402a2ff9815960e0372a47b75c76979d74402448509ccd49a275fa983ef8a", size = 392215, upload-time = "2025-05-18T19:04:34.827Z" }, - { url = "https://files.pythonhosted.org/packages/55/58/ee607863e18d3f895feb802154a2177d7e823a7103f000df182e0f718b38/jiter-0.10.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:1956f934dca32d7bb647ea21d06d93ca40868b505c228556d3373cbd255ce853", size = 522762, upload-time = "2025-05-18T19:04:36.19Z" }, - { url = "https://files.pythonhosted.org/packages/15/d0/9123fb41825490d16929e73c212de9a42913d68324a8ce3c8476cae7ac9d/jiter-0.10.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:fcedb049bdfc555e261d6f65a6abe1d5ad68825b7202ccb9692636c70fcced86", size = 513427, upload-time = "2025-05-18T19:04:37.544Z" }, - { url = "https://files.pythonhosted.org/packages/d8/b3/2bd02071c5a2430d0b70403a34411fc519c2f227da7b03da9ba6a956f931/jiter-0.10.0-cp314-cp314-win32.whl", hash = "sha256:ac509f7eccca54b2a29daeb516fb95b6f0bd0d0d8084efaf8ed5dfc7b9f0b357", size = 210127, upload-time = "2025-05-18T19:04:38.837Z" }, - { url = "https://files.pythonhosted.org/packages/03/0c/5fe86614ea050c3ecd728ab4035534387cd41e7c1855ef6c031f1ca93e3f/jiter-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5ed975b83a2b8639356151cef5c0d597c68376fc4922b45d0eb384ac058cfa00", size = 318527, upload-time = "2025-05-18T19:04:40.612Z" }, - { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213, upload-time = "2025-05-18T19:04:41.894Z" }, + { url = "https://files.pythonhosted.org/packages/be/7e/4011b5c77bec97cb2b572f566220364e3e21b51c48c5bd9c4a9c26b41b67/jiter-0.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cd2fb72b02478f06a900a5782de2ef47e0396b3e1f7d5aba30daeb1fce66f303", size = 317215 }, + { url = "https://files.pythonhosted.org/packages/8a/4f/144c1b57c39692efc7ea7d8e247acf28e47d0912800b34d0ad815f6b2824/jiter-0.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32bb468e3af278f095d3fa5b90314728a6916d89ba3d0ffb726dd9bf7367285e", size = 322814 }, + { url = "https://files.pythonhosted.org/packages/63/1f/db977336d332a9406c0b1f0b82be6f71f72526a806cbb2281baf201d38e3/jiter-0.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8b3e0068c26ddedc7abc6fac37da2d0af16b921e288a5a613f4b86f050354f", size = 345237 }, + { url = "https://files.pythonhosted.org/packages/d7/1c/aa30a4a775e8a672ad7f21532bdbfb269f0706b39c6ff14e1f86bdd9e5ff/jiter-0.10.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:286299b74cc49e25cd42eea19b72aa82c515d2f2ee12d11392c56d8701f52224", size = 370999 }, + { url = "https://files.pythonhosted.org/packages/35/df/f8257abc4207830cb18880781b5f5b716bad5b2a22fb4330cfd357407c5b/jiter-0.10.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ed5649ceeaeffc28d87fb012d25a4cd356dcd53eff5acff1f0466b831dda2a7", size = 491109 }, + { url = "https://files.pythonhosted.org/packages/06/76/9e1516fd7b4278aa13a2cc7f159e56befbea9aa65c71586305e7afa8b0b3/jiter-0.10.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2ab0051160cb758a70716448908ef14ad476c3774bd03ddce075f3c1f90a3d6", size = 388608 }, + { url = "https://files.pythonhosted.org/packages/6d/64/67750672b4354ca20ca18d3d1ccf2c62a072e8a2d452ac3cf8ced73571ef/jiter-0.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03997d2f37f6b67d2f5c475da4412be584e1cec273c1cfc03d642c46db43f8cf", size = 352454 }, + { url = "https://files.pythonhosted.org/packages/96/4d/5c4e36d48f169a54b53a305114be3efa2bbffd33b648cd1478a688f639c1/jiter-0.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c404a99352d839fed80d6afd6c1d66071f3bacaaa5c4268983fc10f769112e90", size = 391833 }, + { url = "https://files.pythonhosted.org/packages/0b/de/ce4a6166a78810bd83763d2fa13f85f73cbd3743a325469a4a9289af6dae/jiter-0.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66e989410b6666d3ddb27a74c7e50d0829704ede652fd4c858e91f8d64b403d0", size = 523646 }, + { url = "https://files.pythonhosted.org/packages/a2/a6/3bc9acce53466972964cf4ad85efecb94f9244539ab6da1107f7aed82934/jiter-0.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b532d3af9ef4f6374609a3bcb5e05a1951d3bf6190dc6b176fdb277c9bbf15ee", size = 514735 }, + { url = "https://files.pythonhosted.org/packages/b4/d8/243c2ab8426a2a4dea85ba2a2ba43df379ccece2145320dfd4799b9633c5/jiter-0.10.0-cp310-cp310-win32.whl", hash = "sha256:da9be20b333970e28b72edc4dff63d4fec3398e05770fb3205f7fb460eb48dd4", size = 210747 }, + { url = "https://files.pythonhosted.org/packages/37/7a/8021bd615ef7788b98fc76ff533eaac846322c170e93cbffa01979197a45/jiter-0.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:f59e533afed0c5b0ac3eba20d2548c4a550336d8282ee69eb07b37ea526ee4e5", size = 207484 }, + { url = "https://files.pythonhosted.org/packages/1b/dd/6cefc6bd68b1c3c979cecfa7029ab582b57690a31cd2f346c4d0ce7951b6/jiter-0.10.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3bebe0c558e19902c96e99217e0b8e8b17d570906e72ed8a87170bc290b1e978", size = 317473 }, + { url = "https://files.pythonhosted.org/packages/be/cf/fc33f5159ce132be1d8dd57251a1ec7a631c7df4bd11e1cd198308c6ae32/jiter-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:558cc7e44fd8e507a236bee6a02fa17199ba752874400a0ca6cd6e2196cdb7dc", size = 321971 }, + { url = "https://files.pythonhosted.org/packages/68/a4/da3f150cf1d51f6c472616fb7650429c7ce053e0c962b41b68557fdf6379/jiter-0.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d613e4b379a07d7c8453c5712ce7014e86c6ac93d990a0b8e7377e18505e98d", size = 345574 }, + { url = "https://files.pythonhosted.org/packages/84/34/6e8d412e60ff06b186040e77da5f83bc158e9735759fcae65b37d681f28b/jiter-0.10.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f62cf8ba0618eda841b9bf61797f21c5ebd15a7a1e19daab76e4e4b498d515b2", size = 371028 }, + { url = "https://files.pythonhosted.org/packages/fb/d9/9ee86173aae4576c35a2f50ae930d2ccb4c4c236f6cb9353267aa1d626b7/jiter-0.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:919d139cdfa8ae8945112398511cb7fca58a77382617d279556b344867a37e61", size = 491083 }, + { url = "https://files.pythonhosted.org/packages/d9/2c/f955de55e74771493ac9e188b0f731524c6a995dffdcb8c255b89c6fb74b/jiter-0.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13ddbc6ae311175a3b03bd8994881bc4635c923754932918e18da841632349db", size = 388821 }, + { url = "https://files.pythonhosted.org/packages/81/5a/0e73541b6edd3f4aada586c24e50626c7815c561a7ba337d6a7eb0a915b4/jiter-0.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c440ea003ad10927a30521a9062ce10b5479592e8a70da27f21eeb457b4a9c5", size = 352174 }, + { url = "https://files.pythonhosted.org/packages/1c/c0/61eeec33b8c75b31cae42be14d44f9e6fe3ac15a4e58010256ac3abf3638/jiter-0.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc347c87944983481e138dea467c0551080c86b9d21de6ea9306efb12ca8f606", size = 391869 }, + { url = "https://files.pythonhosted.org/packages/41/22/5beb5ee4ad4ef7d86f5ea5b4509f680a20706c4a7659e74344777efb7739/jiter-0.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:13252b58c1f4d8c5b63ab103c03d909e8e1e7842d302473f482915d95fefd605", size = 523741 }, + { url = "https://files.pythonhosted.org/packages/ea/10/768e8818538e5817c637b0df52e54366ec4cebc3346108a4457ea7a98f32/jiter-0.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7d1bbf3c465de4a24ab12fb7766a0003f6f9bce48b8b6a886158c4d569452dc5", size = 514527 }, + { url = "https://files.pythonhosted.org/packages/73/6d/29b7c2dc76ce93cbedabfd842fc9096d01a0550c52692dfc33d3cc889815/jiter-0.10.0-cp311-cp311-win32.whl", hash = "sha256:db16e4848b7e826edca4ccdd5b145939758dadf0dc06e7007ad0e9cfb5928ae7", size = 210765 }, + { url = "https://files.pythonhosted.org/packages/c2/c9/d394706deb4c660137caf13e33d05a031d734eb99c051142e039d8ceb794/jiter-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c9c1d5f10e18909e993f9641f12fe1c77b3e9b533ee94ffa970acc14ded3812", size = 209234 }, + { url = "https://files.pythonhosted.org/packages/6d/b5/348b3313c58f5fbfb2194eb4d07e46a35748ba6e5b3b3046143f3040bafa/jiter-0.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1e274728e4a5345a6dde2d343c8da018b9d4bd4350f5a472fa91f66fda44911b", size = 312262 }, + { url = "https://files.pythonhosted.org/packages/9c/4a/6a2397096162b21645162825f058d1709a02965606e537e3304b02742e9b/jiter-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7202ae396446c988cb2a5feb33a543ab2165b786ac97f53b59aafb803fef0744", size = 320124 }, + { url = "https://files.pythonhosted.org/packages/2a/85/1ce02cade7516b726dd88f59a4ee46914bf79d1676d1228ef2002ed2f1c9/jiter-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ba7722d6748b6920ed02a8f1726fb4b33e0fd2f3f621816a8b486c66410ab2", size = 345330 }, + { url = "https://files.pythonhosted.org/packages/75/d0/bb6b4f209a77190ce10ea8d7e50bf3725fc16d3372d0a9f11985a2b23eff/jiter-0.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:371eab43c0a288537d30e1f0b193bc4eca90439fc08a022dd83e5e07500ed026", size = 369670 }, + { url = "https://files.pythonhosted.org/packages/a0/f5/a61787da9b8847a601e6827fbc42ecb12be2c925ced3252c8ffcb56afcaf/jiter-0.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c675736059020365cebc845a820214765162728b51ab1e03a1b7b3abb70f74c", size = 489057 }, + { url = "https://files.pythonhosted.org/packages/12/e4/6f906272810a7b21406c760a53aadbe52e99ee070fc5c0cb191e316de30b/jiter-0.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c5867d40ab716e4684858e4887489685968a47e3ba222e44cde6e4a2154f959", size = 389372 }, + { url = "https://files.pythonhosted.org/packages/e2/ba/77013b0b8ba904bf3762f11e0129b8928bff7f978a81838dfcc958ad5728/jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395bb9a26111b60141757d874d27fdea01b17e8fac958b91c20128ba8f4acc8a", size = 352038 }, + { url = "https://files.pythonhosted.org/packages/67/27/c62568e3ccb03368dbcc44a1ef3a423cb86778a4389e995125d3d1aaa0a4/jiter-0.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6842184aed5cdb07e0c7e20e5bdcfafe33515ee1741a6835353bb45fe5d1bd95", size = 391538 }, + { url = "https://files.pythonhosted.org/packages/c0/72/0d6b7e31fc17a8fdce76164884edef0698ba556b8eb0af9546ae1a06b91d/jiter-0.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62755d1bcea9876770d4df713d82606c8c1a3dca88ff39046b85a048566d56ea", size = 523557 }, + { url = "https://files.pythonhosted.org/packages/2f/09/bc1661fbbcbeb6244bd2904ff3a06f340aa77a2b94e5a7373fd165960ea3/jiter-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533efbce2cacec78d5ba73a41756beff8431dfa1694b6346ce7af3a12c42202b", size = 514202 }, + { url = "https://files.pythonhosted.org/packages/1b/84/5a5d5400e9d4d54b8004c9673bbe4403928a00d28529ff35b19e9d176b19/jiter-0.10.0-cp312-cp312-win32.whl", hash = "sha256:8be921f0cadd245e981b964dfbcd6fd4bc4e254cdc069490416dd7a2632ecc01", size = 211781 }, + { url = "https://files.pythonhosted.org/packages/9b/52/7ec47455e26f2d6e5f2ea4951a0652c06e5b995c291f723973ae9e724a65/jiter-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7c7d785ae9dda68c2678532a5a1581347e9c15362ae9f6e68f3fdbfb64f2e49", size = 206176 }, + { url = "https://files.pythonhosted.org/packages/2e/b0/279597e7a270e8d22623fea6c5d4eeac328e7d95c236ed51a2b884c54f70/jiter-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0588107ec8e11b6f5ef0e0d656fb2803ac6cf94a96b2b9fc675c0e3ab5e8644", size = 311617 }, + { url = "https://files.pythonhosted.org/packages/91/e3/0916334936f356d605f54cc164af4060e3e7094364add445a3bc79335d46/jiter-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cafc4628b616dc32530c20ee53d71589816cf385dd9449633e910d596b1f5c8a", size = 318947 }, + { url = "https://files.pythonhosted.org/packages/6a/8e/fd94e8c02d0e94539b7d669a7ebbd2776e51f329bb2c84d4385e8063a2ad/jiter-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520ef6d981172693786a49ff5b09eda72a42e539f14788124a07530f785c3ad6", size = 344618 }, + { url = "https://files.pythonhosted.org/packages/6f/b0/f9f0a2ec42c6e9c2e61c327824687f1e2415b767e1089c1d9135f43816bd/jiter-0.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dedfd05937f8fc45d17ebdf298fe7e0c77458232bcb73d9fbbf4c6455f5b3", size = 368829 }, + { url = "https://files.pythonhosted.org/packages/e8/57/5bbcd5331910595ad53b9fd0c610392ac68692176f05ae48d6ce5c852967/jiter-0.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc299da7789deacf95f64052d97f75c16d4fc8c4c214a22bf8d859a4288a1c2", size = 491034 }, + { url = "https://files.pythonhosted.org/packages/9b/be/c393df00e6e6e9e623a73551774449f2f23b6ec6a502a3297aeeece2c65a/jiter-0.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5161e201172de298a8a1baad95eb85db4fb90e902353b1f6a41d64ea64644e25", size = 388529 }, + { url = "https://files.pythonhosted.org/packages/42/3e/df2235c54d365434c7f150b986a6e35f41ebdc2f95acea3036d99613025d/jiter-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2227db6ba93cb3e2bf67c87e594adde0609f146344e8207e8730364db27041", size = 350671 }, + { url = "https://files.pythonhosted.org/packages/c6/77/71b0b24cbcc28f55ab4dbfe029f9a5b73aeadaba677843fc6dc9ed2b1d0a/jiter-0.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15acb267ea5e2c64515574b06a8bf393fbfee6a50eb1673614aa45f4613c0cca", size = 390864 }, + { url = "https://files.pythonhosted.org/packages/6a/d3/ef774b6969b9b6178e1d1e7a89a3bd37d241f3d3ec5f8deb37bbd203714a/jiter-0.10.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:901b92f2e2947dc6dfcb52fd624453862e16665ea909a08398dde19c0731b7f4", size = 522989 }, + { url = "https://files.pythonhosted.org/packages/0c/41/9becdb1d8dd5d854142f45a9d71949ed7e87a8e312b0bede2de849388cb9/jiter-0.10.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d0cb9a125d5a3ec971a094a845eadde2db0de85b33c9f13eb94a0c63d463879e", size = 513495 }, + { url = "https://files.pythonhosted.org/packages/9c/36/3468e5a18238bdedae7c4d19461265b5e9b8e288d3f86cd89d00cbb48686/jiter-0.10.0-cp313-cp313-win32.whl", hash = "sha256:48a403277ad1ee208fb930bdf91745e4d2d6e47253eedc96e2559d1e6527006d", size = 211289 }, + { url = "https://files.pythonhosted.org/packages/7e/07/1c96b623128bcb913706e294adb5f768fb7baf8db5e1338ce7b4ee8c78ef/jiter-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:75f9eb72ecb640619c29bf714e78c9c46c9c4eaafd644bf78577ede459f330d4", size = 205074 }, + { url = "https://files.pythonhosted.org/packages/54/46/caa2c1342655f57d8f0f2519774c6d67132205909c65e9aa8255e1d7b4f4/jiter-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:28ed2a4c05a1f32ef0e1d24c2611330219fed727dae01789f4a335617634b1ca", size = 318225 }, + { url = "https://files.pythonhosted.org/packages/43/84/c7d44c75767e18946219ba2d703a5a32ab37b0bc21886a97bc6062e4da42/jiter-0.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a4c418b1ec86a195f1ca69da8b23e8926c752b685af665ce30777233dfe070", size = 350235 }, + { url = "https://files.pythonhosted.org/packages/01/16/f5a0135ccd968b480daad0e6ab34b0c7c5ba3bc447e5088152696140dcb3/jiter-0.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d7bfed2fe1fe0e4dda6ef682cee888ba444b21e7a6553e03252e4feb6cf0adca", size = 207278 }, + { url = "https://files.pythonhosted.org/packages/1c/9b/1d646da42c3de6c2188fdaa15bce8ecb22b635904fc68be025e21249ba44/jiter-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e9251a5e83fab8d87799d3e1a46cb4b7f2919b895c6f4483629ed2446f66522", size = 310866 }, + { url = "https://files.pythonhosted.org/packages/ad/0e/26538b158e8a7c7987e94e7aeb2999e2e82b1f9d2e1f6e9874ddf71ebda0/jiter-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:023aa0204126fe5b87ccbcd75c8a0d0261b9abdbbf46d55e7ae9f8e22424eeb8", size = 318772 }, + { url = "https://files.pythonhosted.org/packages/7b/fb/d302893151caa1c2636d6574d213e4b34e31fd077af6050a9c5cbb42f6fb/jiter-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c189c4f1779c05f75fc17c0c1267594ed918996a231593a21a5ca5438445216", size = 344534 }, + { url = "https://files.pythonhosted.org/packages/01/d8/5780b64a149d74e347c5128d82176eb1e3241b1391ac07935693466d6219/jiter-0.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15720084d90d1098ca0229352607cd68256c76991f6b374af96f36920eae13c4", size = 369087 }, + { url = "https://files.pythonhosted.org/packages/e8/5b/f235a1437445160e777544f3ade57544daf96ba7e96c1a5b24a6f7ac7004/jiter-0.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f2fb68e5f1cfee30e2b2a09549a00683e0fde4c6a2ab88c94072fc33cb7426", size = 490694 }, + { url = "https://files.pythonhosted.org/packages/85/a9/9c3d4617caa2ff89cf61b41e83820c27ebb3f7b5fae8a72901e8cd6ff9be/jiter-0.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce541693355fc6da424c08b7edf39a2895f58d6ea17d92cc2b168d20907dee12", size = 388992 }, + { url = "https://files.pythonhosted.org/packages/68/b1/344fd14049ba5c94526540af7eb661871f9c54d5f5601ff41a959b9a0bbd/jiter-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31c50c40272e189d50006ad5c73883caabb73d4e9748a688b216e85a9a9ca3b9", size = 351723 }, + { url = "https://files.pythonhosted.org/packages/41/89/4c0e345041186f82a31aee7b9d4219a910df672b9fef26f129f0cda07a29/jiter-0.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa3402a2ff9815960e0372a47b75c76979d74402448509ccd49a275fa983ef8a", size = 392215 }, + { url = "https://files.pythonhosted.org/packages/55/58/ee607863e18d3f895feb802154a2177d7e823a7103f000df182e0f718b38/jiter-0.10.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:1956f934dca32d7bb647ea21d06d93ca40868b505c228556d3373cbd255ce853", size = 522762 }, + { url = "https://files.pythonhosted.org/packages/15/d0/9123fb41825490d16929e73c212de9a42913d68324a8ce3c8476cae7ac9d/jiter-0.10.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:fcedb049bdfc555e261d6f65a6abe1d5ad68825b7202ccb9692636c70fcced86", size = 513427 }, + { url = "https://files.pythonhosted.org/packages/d8/b3/2bd02071c5a2430d0b70403a34411fc519c2f227da7b03da9ba6a956f931/jiter-0.10.0-cp314-cp314-win32.whl", hash = "sha256:ac509f7eccca54b2a29daeb516fb95b6f0bd0d0d8084efaf8ed5dfc7b9f0b357", size = 210127 }, + { url = "https://files.pythonhosted.org/packages/03/0c/5fe86614ea050c3ecd728ab4035534387cd41e7c1855ef6c031f1ca93e3f/jiter-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5ed975b83a2b8639356151cef5c0d597c68376fc4922b45d0eb384ac058cfa00", size = 318527 }, + { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213 }, ] [[package]] @@ -1564,21 +1658,21 @@ dependencies = [ { name = "referencing", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "rpds-py", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342, upload-time = "2025-08-18T17:03:50.038Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" }, + { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040 }, ] [[package]] name = "jsonschema-specifications" -version = "2025.4.1" +version = "2025.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "referencing", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513, upload-time = "2025-04-23T12:34:07.418Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" }, + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 }, ] [[package]] @@ -1595,9 +1689,9 @@ dependencies = [ { name = "sqlalchemy", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "tabulate", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bb/f7/3627358075f183956e8c4974603232b03afd4ddc7baf72c2bc9fff522291/jupyter_cache-1.0.1.tar.gz", hash = "sha256:16e808eb19e3fb67a223db906e131ea6e01f03aa27f49a7214ce6a5fec186fb9", size = 32048, upload-time = "2024-11-15T16:03:55.322Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/f7/3627358075f183956e8c4974603232b03afd4ddc7baf72c2bc9fff522291/jupyter_cache-1.0.1.tar.gz", hash = "sha256:16e808eb19e3fb67a223db906e131ea6e01f03aa27f49a7214ce6a5fec186fb9", size = 32048 } wheels = [ - { url = "https://files.pythonhosted.org/packages/64/6b/67b87da9d36bff9df7d0efbd1a325fa372a43be7158effaf43ed7b22341d/jupyter_cache-1.0.1-py3-none-any.whl", hash = "sha256:9c3cafd825ba7da8b5830485343091143dff903e4d8c69db9349b728b140abf6", size = 33907, upload-time = "2024-11-15T16:03:54.021Z" }, + { url = "https://files.pythonhosted.org/packages/64/6b/67b87da9d36bff9df7d0efbd1a325fa372a43be7158effaf43ed7b22341d/jupyter_cache-1.0.1-py3-none-any.whl", hash = "sha256:9c3cafd825ba7da8b5830485343091143dff903e4d8c69db9349b728b140abf6", size = 33907 }, ] [[package]] @@ -1611,9 +1705,9 @@ dependencies = [ { name = "tornado", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "traitlets", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/22/bf9f12fdaeae18019a468b68952a60fe6dbab5d67cd2a103cac7659b41ca/jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419", size = 342019, upload-time = "2024-09-17T10:44:17.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/71/22/bf9f12fdaeae18019a468b68952a60fe6dbab5d67cd2a103cac7659b41ca/jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419", size = 342019 } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/85/b0394e0b6fcccd2c1eeefc230978a6f8cb0c5df1e4cd3e7625735a0d7d1e/jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f", size = 106105, upload-time = "2024-09-17T10:44:15.218Z" }, + { url = "https://files.pythonhosted.org/packages/11/85/b0394e0b6fcccd2c1eeefc230978a6f8cb0c5df1e4cd3e7625735a0d7d1e/jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f", size = 106105 }, ] [[package]] @@ -1625,9 +1719,9 @@ dependencies = [ { name = "pywin32", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'win32'" }, { name = "traitlets", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/1b/72906d554acfeb588332eaaa6f61577705e9ec752ddb486f302dafa292d9/jupyter_core-5.8.1.tar.gz", hash = "sha256:0a5f9706f70e64786b75acba995988915ebd4601c8a52e534a40b51c95f59941", size = 88923, upload-time = "2025-05-27T07:38:16.655Z" } +sdist = { url = "https://files.pythonhosted.org/packages/99/1b/72906d554acfeb588332eaaa6f61577705e9ec752ddb486f302dafa292d9/jupyter_core-5.8.1.tar.gz", hash = "sha256:0a5f9706f70e64786b75acba995988915ebd4601c8a52e534a40b51c95f59941", size = 88923 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/57/6bffd4b20b88da3800c5d691e0337761576ee688eb01299eae865689d2df/jupyter_core-5.8.1-py3-none-any.whl", hash = "sha256:c28d268fc90fb53f1338ded2eb410704c5449a358406e8a948b75706e24863d0", size = 28880, upload-time = "2025-05-27T07:38:15.137Z" }, + { url = "https://files.pythonhosted.org/packages/2f/57/6bffd4b20b88da3800c5d691e0337761576ee688eb01299eae865689d2df/jupyter_core-5.8.1-py3-none-any.whl", hash = "sha256:c28d268fc90fb53f1338ded2eb410704c5449a358406e8a948b75706e24863d0", size = 28880 }, ] [[package]] @@ -1637,9 +1731,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "uc-micro-py", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/ae/bb56c6828e4797ba5a4821eec7c43b8bf40f69cda4d4f5f8c8a2810ec96a/linkify-it-py-2.0.3.tar.gz", hash = "sha256:68cda27e162e9215c17d786649d1da0021a451bdc436ef9e0fa0ba5234b9b048", size = 27946, upload-time = "2024-02-04T14:48:04.179Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/ae/bb56c6828e4797ba5a4821eec7c43b8bf40f69cda4d4f5f8c8a2810ec96a/linkify-it-py-2.0.3.tar.gz", hash = "sha256:68cda27e162e9215c17d786649d1da0021a451bdc436ef9e0fa0ba5234b9b048", size = 27946 } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/1e/b832de447dee8b582cac175871d2f6c3d5077cc56d5575cadba1fd1cccfa/linkify_it_py-2.0.3-py3-none-any.whl", hash = "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79", size = 19820, upload-time = "2024-02-04T14:48:02.496Z" }, + { url = "https://files.pythonhosted.org/packages/04/1e/b832de447dee8b582cac175871d2f6c3d5077cc56d5575cadba1fd1cccfa/linkify_it_py-2.0.3-py3-none-any.whl", hash = "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79", size = 19820 }, ] [[package]] @@ -1649,9 +1743,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mdurl", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, ] [package.optional-dependencies] @@ -1667,67 +1761,67 @@ dependencies = [ { name = "beautifulsoup4", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "six", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/83/1b/6f2697b51eaca81f08852fd2734745af15718fea10222a1d40f8a239c4ea/markdownify-1.2.0.tar.gz", hash = "sha256:f6c367c54eb24ee953921804dfe6d6575c5e5b42c643955e7242034435de634c", size = 18771, upload-time = "2025-08-09T17:44:15.302Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/1b/6f2697b51eaca81f08852fd2734745af15718fea10222a1d40f8a239c4ea/markdownify-1.2.0.tar.gz", hash = "sha256:f6c367c54eb24ee953921804dfe6d6575c5e5b42c643955e7242034435de634c", size = 18771 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/e2/7af643acb4cae0741dffffaa7f3f7c9e7ab4046724543ba1777c401d821c/markdownify-1.2.0-py3-none-any.whl", hash = "sha256:48e150a1c4993d4d50f282f725c0111bd9eb25645d41fa2f543708fd44161351", size = 15561, upload-time = "2025-08-09T17:44:14.074Z" }, + { url = "https://files.pythonhosted.org/packages/6a/e2/7af643acb4cae0741dffffaa7f3f7c9e7ab4046724543ba1777c401d821c/markdownify-1.2.0-py3-none-any.whl", hash = "sha256:48e150a1c4993d4d50f282f725c0111bd9eb25645d41fa2f543708fd44161351", size = 15561 }, ] [[package]] name = "markupsafe" version = "3.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" }, - { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393, upload-time = "2024-10-18T15:20:52.426Z" }, - { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732, upload-time = "2024-10-18T15:20:53.578Z" }, - { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866, upload-time = "2024-10-18T15:20:55.06Z" }, - { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964, upload-time = "2024-10-18T15:20:55.906Z" }, - { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977, upload-time = "2024-10-18T15:20:57.189Z" }, - { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366, upload-time = "2024-10-18T15:20:58.235Z" }, - { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091, upload-time = "2024-10-18T15:20:59.235Z" }, - { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065, upload-time = "2024-10-18T15:21:00.307Z" }, - { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514, upload-time = "2024-10-18T15:21:01.122Z" }, - { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" }, - { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" }, - { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" }, - { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" }, - { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032, upload-time = "2024-10-18T15:21:07.295Z" }, - { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359, upload-time = "2024-10-18T15:21:09.318Z" }, - { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" }, - { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" }, - { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" }, - { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, - { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, - { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, - { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, - { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, - { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, - { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, - { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, - { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, - { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, - { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, - { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, - { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, - { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, - { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, - { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, - { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, - { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, - { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, - { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, - { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, - { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, - { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, - { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, - { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, - { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, - { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, ] [[package]] @@ -1737,9 +1831,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "traitlets", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159, upload-time = "2024-04-15T13:44:44.803Z" } +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899, upload-time = "2024-04-15T13:44:43.265Z" }, + { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 }, ] [[package]] @@ -1759,9 +1853,9 @@ dependencies = [ { name = "starlette", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "uvicorn", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/3c/82c400c2d50afdac4fbefb5b4031fd327e2ad1f23ccef8eee13c5909aa48/mcp-1.13.1.tar.gz", hash = "sha256:165306a8fd7991dc80334edd2de07798175a56461043b7ae907b279794a834c5", size = 438198, upload-time = "2025-08-22T09:22:16.061Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/3c/82c400c2d50afdac4fbefb5b4031fd327e2ad1f23ccef8eee13c5909aa48/mcp-1.13.1.tar.gz", hash = "sha256:165306a8fd7991dc80334edd2de07798175a56461043b7ae907b279794a834c5", size = 438198 } wheels = [ - { url = "https://files.pythonhosted.org/packages/19/3f/d085c7f49ade6d273b185d61ec9405e672b6433f710ea64a90135a8dd445/mcp-1.13.1-py3-none-any.whl", hash = "sha256:c314e7c8bd477a23ba3ef472ee5a32880316c42d03e06dcfa31a1cc7a73b65df", size = 161494, upload-time = "2025-08-22T09:22:14.705Z" }, + { url = "https://files.pythonhosted.org/packages/19/3f/d085c7f49ade6d273b185d61ec9405e672b6433f710ea64a90135a8dd445/mcp-1.13.1-py3-none-any.whl", hash = "sha256:c314e7c8bd477a23ba3ef472ee5a32880316c42d03e06dcfa31a1cc7a73b65df", size = 161494 }, ] [[package]] @@ -1771,18 +1865,36 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b2/fd/a756d36c0bfba5f6e39a1cdbdbfdd448dc02692467d83816dff4592a1ebc/mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6", size = 44655, upload-time = "2025-08-11T07:25:49.083Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/fd/a756d36c0bfba5f6e39a1cdbdbfdd448dc02692467d83816dff4592a1ebc/mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6", size = 44655 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f", size = 57205, upload-time = "2025-08-11T07:25:47.597Z" }, + { url = "https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f", size = 57205 }, ] [[package]] name = "mdurl" version = "0.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "mem0ai" +version = "0.1.117" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "openai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "posthog", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pytz", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "qdrant-client", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "sqlalchemy", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/bb/8e5c5a197ee2103a4a73ae65e8ada5c19ebc505e43c00518b5c8da4dcc91/mem0ai-0.1.117.tar.gz", hash = "sha256:aaf27fcc86a83c5906491bb0bf0588d7fe4db96e5af94e331280b18ae1e99092", size = 137816 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/f4/7fb9974bb1f81afb363a6b5a74f64609bf8e021d0cfe486909c6db6a8f3b/mem0ai-0.1.117-py3-none-any.whl", hash = "sha256:12159d6b9fef22a155f9b8b305999c52dfad20a46e7ccbced68677b6f48d4b57", size = 213063 }, ] [[package]] @@ -1794,9 +1906,9 @@ dependencies = [ { name = "pyjwt", extra = ["crypto"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d5/da/81acbe0c1fd7e9e4ec35f55dadeba9833a847b9a6ba2e2d1e4432da901dd/msal-1.33.0.tar.gz", hash = "sha256:836ad80faa3e25a7d71015c990ce61f704a87328b1e73bcbb0623a18cbf17510", size = 153801, upload-time = "2025-07-22T19:36:33.693Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/da/81acbe0c1fd7e9e4ec35f55dadeba9833a847b9a6ba2e2d1e4432da901dd/msal-1.33.0.tar.gz", hash = "sha256:836ad80faa3e25a7d71015c990ce61f704a87328b1e73bcbb0623a18cbf17510", size = 153801 } wheels = [ - { url = "https://files.pythonhosted.org/packages/86/5b/fbc73e91f7727ae1e79b21ed833308e99dc11cc1cd3d4717f579775de5e9/msal-1.33.0-py3-none-any.whl", hash = "sha256:c0cd41cecf8eaed733ee7e3be9e040291eba53b0f262d3ae9c58f38b04244273", size = 116853, upload-time = "2025-07-22T19:36:32.403Z" }, + { url = "https://files.pythonhosted.org/packages/86/5b/fbc73e91f7727ae1e79b21ed833308e99dc11cc1cd3d4717f579775de5e9/msal-1.33.0-py3-none-any.whl", hash = "sha256:c0cd41cecf8eaed733ee7e3be9e040291eba53b0f262d3ae9c58f38b04244273", size = 116853 }, ] [[package]] @@ -1806,9 +1918,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "msal", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4", size = 23315, upload-time = "2025-03-14T23:51:03.902Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4", size = 23315 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583, upload-time = "2025-03-14T23:51:03.016Z" }, + { url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583 }, ] [[package]] @@ -1822,9 +1934,9 @@ dependencies = [ { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "requests-oauthlib", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/77/8397c8fb8fc257d8ea0fa66f8068e073278c65f05acb17dcb22a02bfdc42/msrest-0.7.1.zip", hash = "sha256:6e7661f46f3afd88b75667b7187a92829924446c7ea1d169be8c4bb7eeb788b9", size = 175332, upload-time = "2022-06-13T22:41:25.111Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/77/8397c8fb8fc257d8ea0fa66f8068e073278c65f05acb17dcb22a02bfdc42/msrest-0.7.1.zip", hash = "sha256:6e7661f46f3afd88b75667b7187a92829924446c7ea1d169be8c4bb7eeb788b9", size = 175332 } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/cf/f2966a2638144491f8696c27320d5219f48a072715075d168b31d3237720/msrest-0.7.1-py3-none-any.whl", hash = "sha256:21120a810e1233e5e6cc7fe40b474eeb4ec6f757a15d7cf86702c369f9567c32", size = 85384, upload-time = "2022-06-13T22:41:22.42Z" }, + { url = "https://files.pythonhosted.org/packages/15/cf/f2966a2638144491f8696c27320d5219f48a072715075d168b31d3237720/msrest-0.7.1-py3-none-any.whl", hash = "sha256:21120a810e1233e5e6cc7fe40b474eeb4ec6f757a15d7cf86702c369f9567c32", size = 85384 }, ] [[package]] @@ -1834,99 +1946,99 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/69/7f/0652e6ed47ab288e3756ea9c0df8b14950781184d4bd7883f4d87dd41245/multidict-6.6.4.tar.gz", hash = "sha256:d2d4e4787672911b48350df02ed3fa3fffdc2f2e8ca06dd6afdf34189b76a9dd", size = 101843, upload-time = "2025-08-11T12:08:48.217Z" } +sdist = { url = "https://files.pythonhosted.org/packages/69/7f/0652e6ed47ab288e3756ea9c0df8b14950781184d4bd7883f4d87dd41245/multidict-6.6.4.tar.gz", hash = "sha256:d2d4e4787672911b48350df02ed3fa3fffdc2f2e8ca06dd6afdf34189b76a9dd", size = 101843 } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/6b/86f353088c1358e76fd30b0146947fddecee812703b604ee901e85cd2a80/multidict-6.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b8aa6f0bd8125ddd04a6593437bad6a7e70f300ff4180a531654aa2ab3f6d58f", size = 77054, upload-time = "2025-08-11T12:06:02.99Z" }, - { url = "https://files.pythonhosted.org/packages/19/5d/c01dc3d3788bb877bd7f5753ea6eb23c1beeca8044902a8f5bfb54430f63/multidict-6.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9e5853bbd7264baca42ffc53391b490d65fe62849bf2c690fa3f6273dbcd0cb", size = 44914, upload-time = "2025-08-11T12:06:05.264Z" }, - { url = "https://files.pythonhosted.org/packages/46/44/964dae19ea42f7d3e166474d8205f14bb811020e28bc423d46123ddda763/multidict-6.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0af5f9dee472371e36d6ae38bde009bd8ce65ac7335f55dcc240379d7bed1495", size = 44601, upload-time = "2025-08-11T12:06:06.627Z" }, - { url = "https://files.pythonhosted.org/packages/31/20/0616348a1dfb36cb2ab33fc9521de1f27235a397bf3f59338e583afadd17/multidict-6.6.4-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:d24f351e4d759f5054b641c81e8291e5d122af0fca5c72454ff77f7cbe492de8", size = 224821, upload-time = "2025-08-11T12:06:08.06Z" }, - { url = "https://files.pythonhosted.org/packages/14/26/5d8923c69c110ff51861af05bd27ca6783011b96725d59ccae6d9daeb627/multidict-6.6.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db6a3810eec08280a172a6cd541ff4a5f6a97b161d93ec94e6c4018917deb6b7", size = 242608, upload-time = "2025-08-11T12:06:09.697Z" }, - { url = "https://files.pythonhosted.org/packages/5c/cc/e2ad3ba9459aa34fa65cf1f82a5c4a820a2ce615aacfb5143b8817f76504/multidict-6.6.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a1b20a9d56b2d81e2ff52ecc0670d583eaabaa55f402e8d16dd062373dbbe796", size = 222324, upload-time = "2025-08-11T12:06:10.905Z" }, - { url = "https://files.pythonhosted.org/packages/19/db/4ed0f65701afbc2cb0c140d2d02928bb0fe38dd044af76e58ad7c54fd21f/multidict-6.6.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8c9854df0eaa610a23494c32a6f44a3a550fb398b6b51a56e8c6b9b3689578db", size = 253234, upload-time = "2025-08-11T12:06:12.658Z" }, - { url = "https://files.pythonhosted.org/packages/94/c1/5160c9813269e39ae14b73debb907bfaaa1beee1762da8c4fb95df4764ed/multidict-6.6.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4bb7627fd7a968f41905a4d6343b0d63244a0623f006e9ed989fa2b78f4438a0", size = 251613, upload-time = "2025-08-11T12:06:13.97Z" }, - { url = "https://files.pythonhosted.org/packages/05/a9/48d1bd111fc2f8fb98b2ed7f9a115c55a9355358432a19f53c0b74d8425d/multidict-6.6.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caebafea30ed049c57c673d0b36238b1748683be2593965614d7b0e99125c877", size = 241649, upload-time = "2025-08-11T12:06:15.204Z" }, - { url = "https://files.pythonhosted.org/packages/85/2a/f7d743df0019408768af8a70d2037546a2be7b81fbb65f040d76caafd4c5/multidict-6.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ad887a8250eb47d3ab083d2f98db7f48098d13d42eb7a3b67d8a5c795f224ace", size = 239238, upload-time = "2025-08-11T12:06:16.467Z" }, - { url = "https://files.pythonhosted.org/packages/cb/b8/4f4bb13323c2d647323f7919201493cf48ebe7ded971717bfb0f1a79b6bf/multidict-6.6.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ed8358ae7d94ffb7c397cecb62cbac9578a83ecefc1eba27b9090ee910e2efb6", size = 233517, upload-time = "2025-08-11T12:06:18.107Z" }, - { url = "https://files.pythonhosted.org/packages/33/29/4293c26029ebfbba4f574febd2ed01b6f619cfa0d2e344217d53eef34192/multidict-6.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ecab51ad2462197a4c000b6d5701fc8585b80eecb90583635d7e327b7b6923eb", size = 243122, upload-time = "2025-08-11T12:06:19.361Z" }, - { url = "https://files.pythonhosted.org/packages/20/60/a1c53628168aa22447bfde3a8730096ac28086704a0d8c590f3b63388d0c/multidict-6.6.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c5c97aa666cf70e667dfa5af945424ba1329af5dd988a437efeb3a09430389fb", size = 248992, upload-time = "2025-08-11T12:06:20.661Z" }, - { url = "https://files.pythonhosted.org/packages/a3/3b/55443a0c372f33cae5d9ec37a6a973802884fa0ab3586659b197cf8cc5e9/multidict-6.6.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9a950b7cf54099c1209f455ac5970b1ea81410f2af60ed9eb3c3f14f0bfcf987", size = 243708, upload-time = "2025-08-11T12:06:21.891Z" }, - { url = "https://files.pythonhosted.org/packages/7c/60/a18c6900086769312560b2626b18e8cca22d9e85b1186ba77f4755b11266/multidict-6.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:163c7ea522ea9365a8a57832dea7618e6cbdc3cd75f8c627663587459a4e328f", size = 237498, upload-time = "2025-08-11T12:06:23.206Z" }, - { url = "https://files.pythonhosted.org/packages/11/3d/8bdd8bcaff2951ce2affccca107a404925a2beafedd5aef0b5e4a71120a6/multidict-6.6.4-cp310-cp310-win32.whl", hash = "sha256:17d2cbbfa6ff20821396b25890f155f40c986f9cfbce5667759696d83504954f", size = 41415, upload-time = "2025-08-11T12:06:24.77Z" }, - { url = "https://files.pythonhosted.org/packages/c0/53/cab1ad80356a4cd1b685a254b680167059b433b573e53872fab245e9fc95/multidict-6.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:ce9a40fbe52e57e7edf20113a4eaddfacac0561a0879734e636aa6d4bb5e3fb0", size = 46046, upload-time = "2025-08-11T12:06:25.893Z" }, - { url = "https://files.pythonhosted.org/packages/cf/9a/874212b6f5c1c2d870d0a7adc5bb4cfe9b0624fa15cdf5cf757c0f5087ae/multidict-6.6.4-cp310-cp310-win_arm64.whl", hash = "sha256:01d0959807a451fe9fdd4da3e139cb5b77f7328baf2140feeaf233e1d777b729", size = 43147, upload-time = "2025-08-11T12:06:27.534Z" }, - { url = "https://files.pythonhosted.org/packages/6b/7f/90a7f01e2d005d6653c689039977f6856718c75c5579445effb7e60923d1/multidict-6.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c7a0e9b561e6460484318a7612e725df1145d46b0ef57c6b9866441bf6e27e0c", size = 76472, upload-time = "2025-08-11T12:06:29.006Z" }, - { url = "https://files.pythonhosted.org/packages/54/a3/bed07bc9e2bb302ce752f1dabc69e884cd6a676da44fb0e501b246031fdd/multidict-6.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6bf2f10f70acc7a2446965ffbc726e5fc0b272c97a90b485857e5c70022213eb", size = 44634, upload-time = "2025-08-11T12:06:30.374Z" }, - { url = "https://files.pythonhosted.org/packages/a7/4b/ceeb4f8f33cf81277da464307afeaf164fb0297947642585884f5cad4f28/multidict-6.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66247d72ed62d5dd29752ffc1d3b88f135c6a8de8b5f63b7c14e973ef5bda19e", size = 44282, upload-time = "2025-08-11T12:06:31.958Z" }, - { url = "https://files.pythonhosted.org/packages/03/35/436a5da8702b06866189b69f655ffdb8f70796252a8772a77815f1812679/multidict-6.6.4-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:105245cc6b76f51e408451a844a54e6823bbd5a490ebfe5bdfc79798511ceded", size = 229696, upload-time = "2025-08-11T12:06:33.087Z" }, - { url = "https://files.pythonhosted.org/packages/b6/0e/915160be8fecf1fca35f790c08fb74ca684d752fcba62c11daaf3d92c216/multidict-6.6.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cbbc54e58b34c3bae389ef00046be0961f30fef7cb0dd9c7756aee376a4f7683", size = 246665, upload-time = "2025-08-11T12:06:34.448Z" }, - { url = "https://files.pythonhosted.org/packages/08/ee/2f464330acd83f77dcc346f0b1a0eaae10230291450887f96b204b8ac4d3/multidict-6.6.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:56c6b3652f945c9bc3ac6c8178cd93132b8d82dd581fcbc3a00676c51302bc1a", size = 225485, upload-time = "2025-08-11T12:06:35.672Z" }, - { url = "https://files.pythonhosted.org/packages/71/cc/9a117f828b4d7fbaec6adeed2204f211e9caf0a012692a1ee32169f846ae/multidict-6.6.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b95494daf857602eccf4c18ca33337dd2be705bccdb6dddbfc9d513e6addb9d9", size = 257318, upload-time = "2025-08-11T12:06:36.98Z" }, - { url = "https://files.pythonhosted.org/packages/25/77/62752d3dbd70e27fdd68e86626c1ae6bccfebe2bb1f84ae226363e112f5a/multidict-6.6.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e5b1413361cef15340ab9dc61523e653d25723e82d488ef7d60a12878227ed50", size = 254689, upload-time = "2025-08-11T12:06:38.233Z" }, - { url = "https://files.pythonhosted.org/packages/00/6e/fac58b1072a6fc59af5e7acb245e8754d3e1f97f4f808a6559951f72a0d4/multidict-6.6.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e167bf899c3d724f9662ef00b4f7fef87a19c22b2fead198a6f68b263618df52", size = 246709, upload-time = "2025-08-11T12:06:39.517Z" }, - { url = "https://files.pythonhosted.org/packages/01/ef/4698d6842ef5e797c6db7744b0081e36fb5de3d00002cc4c58071097fac3/multidict-6.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aaea28ba20a9026dfa77f4b80369e51cb767c61e33a2d4043399c67bd95fb7c6", size = 243185, upload-time = "2025-08-11T12:06:40.796Z" }, - { url = "https://files.pythonhosted.org/packages/aa/c9/d82e95ae1d6e4ef396934e9b0e942dfc428775f9554acf04393cce66b157/multidict-6.6.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8c91cdb30809a96d9ecf442ec9bc45e8cfaa0f7f8bdf534e082c2443a196727e", size = 237838, upload-time = "2025-08-11T12:06:42.595Z" }, - { url = "https://files.pythonhosted.org/packages/57/cf/f94af5c36baaa75d44fab9f02e2a6bcfa0cd90acb44d4976a80960759dbc/multidict-6.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a0ccbfe93ca114c5d65a2471d52d8829e56d467c97b0e341cf5ee45410033b3", size = 246368, upload-time = "2025-08-11T12:06:44.304Z" }, - { url = "https://files.pythonhosted.org/packages/4a/fe/29f23460c3d995f6a4b678cb2e9730e7277231b981f0b234702f0177818a/multidict-6.6.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:55624b3f321d84c403cb7d8e6e982f41ae233d85f85db54ba6286f7295dc8a9c", size = 253339, upload-time = "2025-08-11T12:06:45.597Z" }, - { url = "https://files.pythonhosted.org/packages/29/b6/fd59449204426187b82bf8a75f629310f68c6adc9559dc922d5abe34797b/multidict-6.6.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4a1fb393a2c9d202cb766c76208bd7945bc194eba8ac920ce98c6e458f0b524b", size = 246933, upload-time = "2025-08-11T12:06:46.841Z" }, - { url = "https://files.pythonhosted.org/packages/19/52/d5d6b344f176a5ac3606f7a61fb44dc746e04550e1a13834dff722b8d7d6/multidict-6.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:43868297a5759a845fa3a483fb4392973a95fb1de891605a3728130c52b8f40f", size = 242225, upload-time = "2025-08-11T12:06:48.588Z" }, - { url = "https://files.pythonhosted.org/packages/ec/d3/5b2281ed89ff4d5318d82478a2a2450fcdfc3300da48ff15c1778280ad26/multidict-6.6.4-cp311-cp311-win32.whl", hash = "sha256:ed3b94c5e362a8a84d69642dbeac615452e8af9b8eb825b7bc9f31a53a1051e2", size = 41306, upload-time = "2025-08-11T12:06:49.95Z" }, - { url = "https://files.pythonhosted.org/packages/74/7d/36b045c23a1ab98507aefd44fd8b264ee1dd5e5010543c6fccf82141ccef/multidict-6.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:d8c112f7a90d8ca5d20213aa41eac690bb50a76da153e3afb3886418e61cb22e", size = 46029, upload-time = "2025-08-11T12:06:51.082Z" }, - { url = "https://files.pythonhosted.org/packages/0f/5e/553d67d24432c5cd52b49047f2d248821843743ee6d29a704594f656d182/multidict-6.6.4-cp311-cp311-win_arm64.whl", hash = "sha256:3bb0eae408fa1996d87247ca0d6a57b7fc1dcf83e8a5c47ab82c558c250d4adf", size = 43017, upload-time = "2025-08-11T12:06:52.243Z" }, - { url = "https://files.pythonhosted.org/packages/05/f6/512ffd8fd8b37fb2680e5ac35d788f1d71bbaf37789d21a820bdc441e565/multidict-6.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0ffb87be160942d56d7b87b0fdf098e81ed565add09eaa1294268c7f3caac4c8", size = 76516, upload-time = "2025-08-11T12:06:53.393Z" }, - { url = "https://files.pythonhosted.org/packages/99/58/45c3e75deb8855c36bd66cc1658007589662ba584dbf423d01df478dd1c5/multidict-6.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d191de6cbab2aff5de6c5723101705fd044b3e4c7cfd587a1929b5028b9714b3", size = 45394, upload-time = "2025-08-11T12:06:54.555Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ca/e8c4472a93a26e4507c0b8e1f0762c0d8a32de1328ef72fd704ef9cc5447/multidict-6.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38a0956dd92d918ad5feff3db8fcb4a5eb7dba114da917e1a88475619781b57b", size = 43591, upload-time = "2025-08-11T12:06:55.672Z" }, - { url = "https://files.pythonhosted.org/packages/05/51/edf414f4df058574a7265034d04c935aa84a89e79ce90fcf4df211f47b16/multidict-6.6.4-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6865f6d3b7900ae020b495d599fcf3765653bc927951c1abb959017f81ae8287", size = 237215, upload-time = "2025-08-11T12:06:57.213Z" }, - { url = "https://files.pythonhosted.org/packages/c8/45/8b3d6dbad8cf3252553cc41abea09ad527b33ce47a5e199072620b296902/multidict-6.6.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a2088c126b6f72db6c9212ad827d0ba088c01d951cee25e758c450da732c138", size = 258299, upload-time = "2025-08-11T12:06:58.946Z" }, - { url = "https://files.pythonhosted.org/packages/3c/e8/8ca2e9a9f5a435fc6db40438a55730a4bf4956b554e487fa1b9ae920f825/multidict-6.6.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0f37bed7319b848097085d7d48116f545985db988e2256b2e6f00563a3416ee6", size = 242357, upload-time = "2025-08-11T12:07:00.301Z" }, - { url = "https://files.pythonhosted.org/packages/0f/84/80c77c99df05a75c28490b2af8f7cba2a12621186e0a8b0865d8e745c104/multidict-6.6.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:01368e3c94032ba6ca0b78e7ccb099643466cf24f8dc8eefcfdc0571d56e58f9", size = 268369, upload-time = "2025-08-11T12:07:01.638Z" }, - { url = "https://files.pythonhosted.org/packages/0d/e9/920bfa46c27b05fb3e1ad85121fd49f441492dca2449c5bcfe42e4565d8a/multidict-6.6.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fe323540c255db0bffee79ad7f048c909f2ab0edb87a597e1c17da6a54e493c", size = 269341, upload-time = "2025-08-11T12:07:02.943Z" }, - { url = "https://files.pythonhosted.org/packages/af/65/753a2d8b05daf496f4a9c367fe844e90a1b2cac78e2be2c844200d10cc4c/multidict-6.6.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8eb3025f17b0a4c3cd08cda49acf312a19ad6e8a4edd9dbd591e6506d999402", size = 256100, upload-time = "2025-08-11T12:07:04.564Z" }, - { url = "https://files.pythonhosted.org/packages/09/54/655be13ae324212bf0bc15d665a4e34844f34c206f78801be42f7a0a8aaa/multidict-6.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bbc14f0365534d35a06970d6a83478b249752e922d662dc24d489af1aa0d1be7", size = 253584, upload-time = "2025-08-11T12:07:05.914Z" }, - { url = "https://files.pythonhosted.org/packages/5c/74/ab2039ecc05264b5cec73eb018ce417af3ebb384ae9c0e9ed42cb33f8151/multidict-6.6.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:75aa52fba2d96bf972e85451b99d8e19cc37ce26fd016f6d4aa60da9ab2b005f", size = 251018, upload-time = "2025-08-11T12:07:08.301Z" }, - { url = "https://files.pythonhosted.org/packages/af/0a/ccbb244ac848e56c6427f2392741c06302bbfba49c0042f1eb3c5b606497/multidict-6.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fefd4a815e362d4f011919d97d7b4a1e566f1dde83dc4ad8cfb5b41de1df68d", size = 251477, upload-time = "2025-08-11T12:07:10.248Z" }, - { url = "https://files.pythonhosted.org/packages/0e/b0/0ed49bba775b135937f52fe13922bc64a7eaf0a3ead84a36e8e4e446e096/multidict-6.6.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:db9801fe021f59a5b375ab778973127ca0ac52429a26e2fd86aa9508f4d26eb7", size = 263575, upload-time = "2025-08-11T12:07:11.928Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d9/7fb85a85e14de2e44dfb6a24f03c41e2af8697a6df83daddb0e9b7569f73/multidict-6.6.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a650629970fa21ac1fb06ba25dabfc5b8a2054fcbf6ae97c758aa956b8dba802", size = 259649, upload-time = "2025-08-11T12:07:13.244Z" }, - { url = "https://files.pythonhosted.org/packages/03/9e/b3a459bcf9b6e74fa461a5222a10ff9b544cb1cd52fd482fb1b75ecda2a2/multidict-6.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:452ff5da78d4720d7516a3a2abd804957532dd69296cb77319c193e3ffb87e24", size = 251505, upload-time = "2025-08-11T12:07:14.57Z" }, - { url = "https://files.pythonhosted.org/packages/86/a2/8022f78f041dfe6d71e364001a5cf987c30edfc83c8a5fb7a3f0974cff39/multidict-6.6.4-cp312-cp312-win32.whl", hash = "sha256:8c2fcb12136530ed19572bbba61b407f655e3953ba669b96a35036a11a485793", size = 41888, upload-time = "2025-08-11T12:07:15.904Z" }, - { url = "https://files.pythonhosted.org/packages/c7/eb/d88b1780d43a56db2cba24289fa744a9d216c1a8546a0dc3956563fd53ea/multidict-6.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:047d9425860a8c9544fed1b9584f0c8bcd31bcde9568b047c5e567a1025ecd6e", size = 46072, upload-time = "2025-08-11T12:07:17.045Z" }, - { url = "https://files.pythonhosted.org/packages/9f/16/b929320bf5750e2d9d4931835a4c638a19d2494a5b519caaaa7492ebe105/multidict-6.6.4-cp312-cp312-win_arm64.whl", hash = "sha256:14754eb72feaa1e8ae528468f24250dd997b8e2188c3d2f593f9eba259e4b364", size = 43222, upload-time = "2025-08-11T12:07:18.328Z" }, - { url = "https://files.pythonhosted.org/packages/3a/5d/e1db626f64f60008320aab00fbe4f23fc3300d75892a3381275b3d284580/multidict-6.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f46a6e8597f9bd71b31cc708195d42b634c8527fecbcf93febf1052cacc1f16e", size = 75848, upload-time = "2025-08-11T12:07:19.912Z" }, - { url = "https://files.pythonhosted.org/packages/4c/aa/8b6f548d839b6c13887253af4e29c939af22a18591bfb5d0ee6f1931dae8/multidict-6.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:22e38b2bc176c5eb9c0a0e379f9d188ae4cd8b28c0f53b52bce7ab0a9e534657", size = 45060, upload-time = "2025-08-11T12:07:21.163Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c6/f5e97e5d99a729bc2aa58eb3ebfa9f1e56a9b517cc38c60537c81834a73f/multidict-6.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5df8afd26f162da59e218ac0eefaa01b01b2e6cd606cffa46608f699539246da", size = 43269, upload-time = "2025-08-11T12:07:22.392Z" }, - { url = "https://files.pythonhosted.org/packages/dc/31/d54eb0c62516776f36fe67f84a732f97e0b0e12f98d5685bebcc6d396910/multidict-6.6.4-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:49517449b58d043023720aa58e62b2f74ce9b28f740a0b5d33971149553d72aa", size = 237158, upload-time = "2025-08-11T12:07:23.636Z" }, - { url = "https://files.pythonhosted.org/packages/c4/1c/8a10c1c25b23156e63b12165a929d8eb49a6ed769fdbefb06e6f07c1e50d/multidict-6.6.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9408439537c5afdca05edd128a63f56a62680f4b3c234301055d7a2000220f", size = 257076, upload-time = "2025-08-11T12:07:25.049Z" }, - { url = "https://files.pythonhosted.org/packages/ad/86/90e20b5771d6805a119e483fd3d1e8393e745a11511aebca41f0da38c3e2/multidict-6.6.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:87a32d20759dc52a9e850fe1061b6e41ab28e2998d44168a8a341b99ded1dba0", size = 240694, upload-time = "2025-08-11T12:07:26.458Z" }, - { url = "https://files.pythonhosted.org/packages/e7/49/484d3e6b535bc0555b52a0a26ba86e4d8d03fd5587d4936dc59ba7583221/multidict-6.6.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:52e3c8d43cdfff587ceedce9deb25e6ae77daba560b626e97a56ddcad3756879", size = 266350, upload-time = "2025-08-11T12:07:27.94Z" }, - { url = "https://files.pythonhosted.org/packages/bf/b4/aa4c5c379b11895083d50021e229e90c408d7d875471cb3abf721e4670d6/multidict-6.6.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ad8850921d3a8d8ff6fbef790e773cecfc260bbfa0566998980d3fa8f520bc4a", size = 267250, upload-time = "2025-08-11T12:07:29.303Z" }, - { url = "https://files.pythonhosted.org/packages/80/e5/5e22c5bf96a64bdd43518b1834c6d95a4922cc2066b7d8e467dae9b6cee6/multidict-6.6.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:497a2954adc25c08daff36f795077f63ad33e13f19bfff7736e72c785391534f", size = 254900, upload-time = "2025-08-11T12:07:30.764Z" }, - { url = "https://files.pythonhosted.org/packages/17/38/58b27fed927c07035abc02befacab42491e7388ca105e087e6e0215ead64/multidict-6.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:024ce601f92d780ca1617ad4be5ac15b501cc2414970ffa2bb2bbc2bd5a68fa5", size = 252355, upload-time = "2025-08-11T12:07:32.205Z" }, - { url = "https://files.pythonhosted.org/packages/d0/a1/dad75d23a90c29c02b5d6f3d7c10ab36c3197613be5d07ec49c7791e186c/multidict-6.6.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a693fc5ed9bdd1c9e898013e0da4dcc640de7963a371c0bd458e50e046bf6438", size = 250061, upload-time = "2025-08-11T12:07:33.623Z" }, - { url = "https://files.pythonhosted.org/packages/b8/1a/ac2216b61c7f116edab6dc3378cca6c70dc019c9a457ff0d754067c58b20/multidict-6.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:190766dac95aab54cae5b152a56520fd99298f32a1266d66d27fdd1b5ac00f4e", size = 249675, upload-time = "2025-08-11T12:07:34.958Z" }, - { url = "https://files.pythonhosted.org/packages/d4/79/1916af833b800d13883e452e8e0977c065c4ee3ab7a26941fbfdebc11895/multidict-6.6.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8f2a5ffdceab9dcd97c7a016deb2308531d5f0fced2bb0c9e1df45b3363d7", size = 261247, upload-time = "2025-08-11T12:07:36.588Z" }, - { url = "https://files.pythonhosted.org/packages/c5/65/d1f84fe08ac44a5fc7391cbc20a7cedc433ea616b266284413fd86062f8c/multidict-6.6.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:59e8d40ab1f5a8597abcef00d04845155a5693b5da00d2c93dbe88f2050f2812", size = 257960, upload-time = "2025-08-11T12:07:39.735Z" }, - { url = "https://files.pythonhosted.org/packages/13/b5/29ec78057d377b195ac2c5248c773703a6b602e132a763e20ec0457e7440/multidict-6.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:467fe64138cfac771f0e949b938c2e1ada2b5af22f39692aa9258715e9ea613a", size = 250078, upload-time = "2025-08-11T12:07:41.525Z" }, - { url = "https://files.pythonhosted.org/packages/c4/0e/7e79d38f70a872cae32e29b0d77024bef7834b0afb406ddae6558d9e2414/multidict-6.6.4-cp313-cp313-win32.whl", hash = "sha256:14616a30fe6d0a48d0a48d1a633ab3b8bec4cf293aac65f32ed116f620adfd69", size = 41708, upload-time = "2025-08-11T12:07:43.405Z" }, - { url = "https://files.pythonhosted.org/packages/9d/34/746696dffff742e97cd6a23da953e55d0ea51fa601fa2ff387b3edcfaa2c/multidict-6.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:40cd05eaeb39e2bc8939451f033e57feaa2ac99e07dbca8afe2be450a4a3b6cf", size = 45912, upload-time = "2025-08-11T12:07:45.082Z" }, - { url = "https://files.pythonhosted.org/packages/c7/87/3bac136181e271e29170d8d71929cdeddeb77f3e8b6a0c08da3a8e9da114/multidict-6.6.4-cp313-cp313-win_arm64.whl", hash = "sha256:f6eb37d511bfae9e13e82cb4d1af36b91150466f24d9b2b8a9785816deb16605", size = 43076, upload-time = "2025-08-11T12:07:46.746Z" }, - { url = "https://files.pythonhosted.org/packages/64/94/0a8e63e36c049b571c9ae41ee301ada29c3fee9643d9c2548d7d558a1d99/multidict-6.6.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6c84378acd4f37d1b507dfa0d459b449e2321b3ba5f2338f9b085cf7a7ba95eb", size = 82812, upload-time = "2025-08-11T12:07:48.402Z" }, - { url = "https://files.pythonhosted.org/packages/25/1a/be8e369dfcd260d2070a67e65dd3990dd635cbd735b98da31e00ea84cd4e/multidict-6.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0e0558693063c75f3d952abf645c78f3c5dfdd825a41d8c4d8156fc0b0da6e7e", size = 48313, upload-time = "2025-08-11T12:07:49.679Z" }, - { url = "https://files.pythonhosted.org/packages/26/5a/dd4ade298674b2f9a7b06a32c94ffbc0497354df8285f27317c66433ce3b/multidict-6.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3f8e2384cb83ebd23fd07e9eada8ba64afc4c759cd94817433ab8c81ee4b403f", size = 46777, upload-time = "2025-08-11T12:07:51.318Z" }, - { url = "https://files.pythonhosted.org/packages/89/db/98aa28bc7e071bfba611ac2ae803c24e96dd3a452b4118c587d3d872c64c/multidict-6.6.4-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f996b87b420995a9174b2a7c1a8daf7db4750be6848b03eb5e639674f7963773", size = 229321, upload-time = "2025-08-11T12:07:52.965Z" }, - { url = "https://files.pythonhosted.org/packages/c7/bc/01ddda2a73dd9d167bd85d0e8ef4293836a8f82b786c63fb1a429bc3e678/multidict-6.6.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc356250cffd6e78416cf5b40dc6a74f1edf3be8e834cf8862d9ed5265cf9b0e", size = 249954, upload-time = "2025-08-11T12:07:54.423Z" }, - { url = "https://files.pythonhosted.org/packages/06/78/6b7c0f020f9aa0acf66d0ab4eb9f08375bac9a50ff5e3edb1c4ccd59eafc/multidict-6.6.4-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:dadf95aa862714ea468a49ad1e09fe00fcc9ec67d122f6596a8d40caf6cec7d0", size = 228612, upload-time = "2025-08-11T12:07:55.914Z" }, - { url = "https://files.pythonhosted.org/packages/00/44/3faa416f89b2d5d76e9d447296a81521e1c832ad6e40b92f990697b43192/multidict-6.6.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7dd57515bebffd8ebd714d101d4c434063322e4fe24042e90ced41f18b6d3395", size = 257528, upload-time = "2025-08-11T12:07:57.371Z" }, - { url = "https://files.pythonhosted.org/packages/05/5f/77c03b89af0fcb16f018f668207768191fb9dcfb5e3361a5e706a11db2c9/multidict-6.6.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:967af5f238ebc2eb1da4e77af5492219fbd9b4b812347da39a7b5f5c72c0fa45", size = 256329, upload-time = "2025-08-11T12:07:58.844Z" }, - { url = "https://files.pythonhosted.org/packages/cf/e9/ed750a2a9afb4f8dc6f13dc5b67b514832101b95714f1211cd42e0aafc26/multidict-6.6.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a4c6875c37aae9794308ec43e3530e4aa0d36579ce38d89979bbf89582002bb", size = 247928, upload-time = "2025-08-11T12:08:01.037Z" }, - { url = "https://files.pythonhosted.org/packages/1f/b5/e0571bc13cda277db7e6e8a532791d4403dacc9850006cb66d2556e649c0/multidict-6.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f683a551e92bdb7fac545b9c6f9fa2aebdeefa61d607510b3533286fcab67f5", size = 245228, upload-time = "2025-08-11T12:08:02.96Z" }, - { url = "https://files.pythonhosted.org/packages/f3/a3/69a84b0eccb9824491f06368f5b86e72e4af54c3067c37c39099b6687109/multidict-6.6.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:3ba5aaf600edaf2a868a391779f7a85d93bed147854925f34edd24cc70a3e141", size = 235869, upload-time = "2025-08-11T12:08:04.746Z" }, - { url = "https://files.pythonhosted.org/packages/a9/9d/28802e8f9121a6a0804fa009debf4e753d0a59969ea9f70be5f5fdfcb18f/multidict-6.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:580b643b7fd2c295d83cad90d78419081f53fd532d1f1eb67ceb7060f61cff0d", size = 243446, upload-time = "2025-08-11T12:08:06.332Z" }, - { url = "https://files.pythonhosted.org/packages/38/ea/6c98add069b4878c1d66428a5f5149ddb6d32b1f9836a826ac764b9940be/multidict-6.6.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:37b7187197da6af3ee0b044dbc9625afd0c885f2800815b228a0e70f9a7f473d", size = 252299, upload-time = "2025-08-11T12:08:07.931Z" }, - { url = "https://files.pythonhosted.org/packages/3a/09/8fe02d204473e14c0af3affd50af9078839dfca1742f025cca765435d6b4/multidict-6.6.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e1b93790ed0bc26feb72e2f08299691ceb6da5e9e14a0d13cc74f1869af327a0", size = 246926, upload-time = "2025-08-11T12:08:09.467Z" }, - { url = "https://files.pythonhosted.org/packages/37/3d/7b1e10d774a6df5175ecd3c92bff069e77bed9ec2a927fdd4ff5fe182f67/multidict-6.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a506a77ddee1efcca81ecbeae27ade3e09cdf21a8ae854d766c2bb4f14053f92", size = 243383, upload-time = "2025-08-11T12:08:10.981Z" }, - { url = "https://files.pythonhosted.org/packages/50/b0/a6fae46071b645ae98786ab738447de1ef53742eaad949f27e960864bb49/multidict-6.6.4-cp313-cp313t-win32.whl", hash = "sha256:f93b2b2279883d1d0a9e1bd01f312d6fc315c5e4c1f09e112e4736e2f650bc4e", size = 47775, upload-time = "2025-08-11T12:08:12.439Z" }, - { url = "https://files.pythonhosted.org/packages/b2/0a/2436550b1520091af0600dff547913cb2d66fbac27a8c33bc1b1bccd8d98/multidict-6.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:6d46a180acdf6e87cc41dc15d8f5c2986e1e8739dc25dbb7dac826731ef381a4", size = 53100, upload-time = "2025-08-11T12:08:13.823Z" }, - { url = "https://files.pythonhosted.org/packages/97/ea/43ac51faff934086db9c072a94d327d71b7d8b40cd5dcb47311330929ef0/multidict-6.6.4-cp313-cp313t-win_arm64.whl", hash = "sha256:756989334015e3335d087a27331659820d53ba432befdef6a718398b0a8493ad", size = 45501, upload-time = "2025-08-11T12:08:15.173Z" }, - { url = "https://files.pythonhosted.org/packages/fd/69/b547032297c7e63ba2af494edba695d781af8a0c6e89e4d06cf848b21d80/multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c", size = 12313, upload-time = "2025-08-11T12:08:46.891Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6b/86f353088c1358e76fd30b0146947fddecee812703b604ee901e85cd2a80/multidict-6.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b8aa6f0bd8125ddd04a6593437bad6a7e70f300ff4180a531654aa2ab3f6d58f", size = 77054 }, + { url = "https://files.pythonhosted.org/packages/19/5d/c01dc3d3788bb877bd7f5753ea6eb23c1beeca8044902a8f5bfb54430f63/multidict-6.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9e5853bbd7264baca42ffc53391b490d65fe62849bf2c690fa3f6273dbcd0cb", size = 44914 }, + { url = "https://files.pythonhosted.org/packages/46/44/964dae19ea42f7d3e166474d8205f14bb811020e28bc423d46123ddda763/multidict-6.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0af5f9dee472371e36d6ae38bde009bd8ce65ac7335f55dcc240379d7bed1495", size = 44601 }, + { url = "https://files.pythonhosted.org/packages/31/20/0616348a1dfb36cb2ab33fc9521de1f27235a397bf3f59338e583afadd17/multidict-6.6.4-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:d24f351e4d759f5054b641c81e8291e5d122af0fca5c72454ff77f7cbe492de8", size = 224821 }, + { url = "https://files.pythonhosted.org/packages/14/26/5d8923c69c110ff51861af05bd27ca6783011b96725d59ccae6d9daeb627/multidict-6.6.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db6a3810eec08280a172a6cd541ff4a5f6a97b161d93ec94e6c4018917deb6b7", size = 242608 }, + { url = "https://files.pythonhosted.org/packages/5c/cc/e2ad3ba9459aa34fa65cf1f82a5c4a820a2ce615aacfb5143b8817f76504/multidict-6.6.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a1b20a9d56b2d81e2ff52ecc0670d583eaabaa55f402e8d16dd062373dbbe796", size = 222324 }, + { url = "https://files.pythonhosted.org/packages/19/db/4ed0f65701afbc2cb0c140d2d02928bb0fe38dd044af76e58ad7c54fd21f/multidict-6.6.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8c9854df0eaa610a23494c32a6f44a3a550fb398b6b51a56e8c6b9b3689578db", size = 253234 }, + { url = "https://files.pythonhosted.org/packages/94/c1/5160c9813269e39ae14b73debb907bfaaa1beee1762da8c4fb95df4764ed/multidict-6.6.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4bb7627fd7a968f41905a4d6343b0d63244a0623f006e9ed989fa2b78f4438a0", size = 251613 }, + { url = "https://files.pythonhosted.org/packages/05/a9/48d1bd111fc2f8fb98b2ed7f9a115c55a9355358432a19f53c0b74d8425d/multidict-6.6.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caebafea30ed049c57c673d0b36238b1748683be2593965614d7b0e99125c877", size = 241649 }, + { url = "https://files.pythonhosted.org/packages/85/2a/f7d743df0019408768af8a70d2037546a2be7b81fbb65f040d76caafd4c5/multidict-6.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ad887a8250eb47d3ab083d2f98db7f48098d13d42eb7a3b67d8a5c795f224ace", size = 239238 }, + { url = "https://files.pythonhosted.org/packages/cb/b8/4f4bb13323c2d647323f7919201493cf48ebe7ded971717bfb0f1a79b6bf/multidict-6.6.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ed8358ae7d94ffb7c397cecb62cbac9578a83ecefc1eba27b9090ee910e2efb6", size = 233517 }, + { url = "https://files.pythonhosted.org/packages/33/29/4293c26029ebfbba4f574febd2ed01b6f619cfa0d2e344217d53eef34192/multidict-6.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ecab51ad2462197a4c000b6d5701fc8585b80eecb90583635d7e327b7b6923eb", size = 243122 }, + { url = "https://files.pythonhosted.org/packages/20/60/a1c53628168aa22447bfde3a8730096ac28086704a0d8c590f3b63388d0c/multidict-6.6.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c5c97aa666cf70e667dfa5af945424ba1329af5dd988a437efeb3a09430389fb", size = 248992 }, + { url = "https://files.pythonhosted.org/packages/a3/3b/55443a0c372f33cae5d9ec37a6a973802884fa0ab3586659b197cf8cc5e9/multidict-6.6.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9a950b7cf54099c1209f455ac5970b1ea81410f2af60ed9eb3c3f14f0bfcf987", size = 243708 }, + { url = "https://files.pythonhosted.org/packages/7c/60/a18c6900086769312560b2626b18e8cca22d9e85b1186ba77f4755b11266/multidict-6.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:163c7ea522ea9365a8a57832dea7618e6cbdc3cd75f8c627663587459a4e328f", size = 237498 }, + { url = "https://files.pythonhosted.org/packages/11/3d/8bdd8bcaff2951ce2affccca107a404925a2beafedd5aef0b5e4a71120a6/multidict-6.6.4-cp310-cp310-win32.whl", hash = "sha256:17d2cbbfa6ff20821396b25890f155f40c986f9cfbce5667759696d83504954f", size = 41415 }, + { url = "https://files.pythonhosted.org/packages/c0/53/cab1ad80356a4cd1b685a254b680167059b433b573e53872fab245e9fc95/multidict-6.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:ce9a40fbe52e57e7edf20113a4eaddfacac0561a0879734e636aa6d4bb5e3fb0", size = 46046 }, + { url = "https://files.pythonhosted.org/packages/cf/9a/874212b6f5c1c2d870d0a7adc5bb4cfe9b0624fa15cdf5cf757c0f5087ae/multidict-6.6.4-cp310-cp310-win_arm64.whl", hash = "sha256:01d0959807a451fe9fdd4da3e139cb5b77f7328baf2140feeaf233e1d777b729", size = 43147 }, + { url = "https://files.pythonhosted.org/packages/6b/7f/90a7f01e2d005d6653c689039977f6856718c75c5579445effb7e60923d1/multidict-6.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c7a0e9b561e6460484318a7612e725df1145d46b0ef57c6b9866441bf6e27e0c", size = 76472 }, + { url = "https://files.pythonhosted.org/packages/54/a3/bed07bc9e2bb302ce752f1dabc69e884cd6a676da44fb0e501b246031fdd/multidict-6.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6bf2f10f70acc7a2446965ffbc726e5fc0b272c97a90b485857e5c70022213eb", size = 44634 }, + { url = "https://files.pythonhosted.org/packages/a7/4b/ceeb4f8f33cf81277da464307afeaf164fb0297947642585884f5cad4f28/multidict-6.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66247d72ed62d5dd29752ffc1d3b88f135c6a8de8b5f63b7c14e973ef5bda19e", size = 44282 }, + { url = "https://files.pythonhosted.org/packages/03/35/436a5da8702b06866189b69f655ffdb8f70796252a8772a77815f1812679/multidict-6.6.4-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:105245cc6b76f51e408451a844a54e6823bbd5a490ebfe5bdfc79798511ceded", size = 229696 }, + { url = "https://files.pythonhosted.org/packages/b6/0e/915160be8fecf1fca35f790c08fb74ca684d752fcba62c11daaf3d92c216/multidict-6.6.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cbbc54e58b34c3bae389ef00046be0961f30fef7cb0dd9c7756aee376a4f7683", size = 246665 }, + { url = "https://files.pythonhosted.org/packages/08/ee/2f464330acd83f77dcc346f0b1a0eaae10230291450887f96b204b8ac4d3/multidict-6.6.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:56c6b3652f945c9bc3ac6c8178cd93132b8d82dd581fcbc3a00676c51302bc1a", size = 225485 }, + { url = "https://files.pythonhosted.org/packages/71/cc/9a117f828b4d7fbaec6adeed2204f211e9caf0a012692a1ee32169f846ae/multidict-6.6.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b95494daf857602eccf4c18ca33337dd2be705bccdb6dddbfc9d513e6addb9d9", size = 257318 }, + { url = "https://files.pythonhosted.org/packages/25/77/62752d3dbd70e27fdd68e86626c1ae6bccfebe2bb1f84ae226363e112f5a/multidict-6.6.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e5b1413361cef15340ab9dc61523e653d25723e82d488ef7d60a12878227ed50", size = 254689 }, + { url = "https://files.pythonhosted.org/packages/00/6e/fac58b1072a6fc59af5e7acb245e8754d3e1f97f4f808a6559951f72a0d4/multidict-6.6.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e167bf899c3d724f9662ef00b4f7fef87a19c22b2fead198a6f68b263618df52", size = 246709 }, + { url = "https://files.pythonhosted.org/packages/01/ef/4698d6842ef5e797c6db7744b0081e36fb5de3d00002cc4c58071097fac3/multidict-6.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aaea28ba20a9026dfa77f4b80369e51cb767c61e33a2d4043399c67bd95fb7c6", size = 243185 }, + { url = "https://files.pythonhosted.org/packages/aa/c9/d82e95ae1d6e4ef396934e9b0e942dfc428775f9554acf04393cce66b157/multidict-6.6.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8c91cdb30809a96d9ecf442ec9bc45e8cfaa0f7f8bdf534e082c2443a196727e", size = 237838 }, + { url = "https://files.pythonhosted.org/packages/57/cf/f94af5c36baaa75d44fab9f02e2a6bcfa0cd90acb44d4976a80960759dbc/multidict-6.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a0ccbfe93ca114c5d65a2471d52d8829e56d467c97b0e341cf5ee45410033b3", size = 246368 }, + { url = "https://files.pythonhosted.org/packages/4a/fe/29f23460c3d995f6a4b678cb2e9730e7277231b981f0b234702f0177818a/multidict-6.6.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:55624b3f321d84c403cb7d8e6e982f41ae233d85f85db54ba6286f7295dc8a9c", size = 253339 }, + { url = "https://files.pythonhosted.org/packages/29/b6/fd59449204426187b82bf8a75f629310f68c6adc9559dc922d5abe34797b/multidict-6.6.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4a1fb393a2c9d202cb766c76208bd7945bc194eba8ac920ce98c6e458f0b524b", size = 246933 }, + { url = "https://files.pythonhosted.org/packages/19/52/d5d6b344f176a5ac3606f7a61fb44dc746e04550e1a13834dff722b8d7d6/multidict-6.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:43868297a5759a845fa3a483fb4392973a95fb1de891605a3728130c52b8f40f", size = 242225 }, + { url = "https://files.pythonhosted.org/packages/ec/d3/5b2281ed89ff4d5318d82478a2a2450fcdfc3300da48ff15c1778280ad26/multidict-6.6.4-cp311-cp311-win32.whl", hash = "sha256:ed3b94c5e362a8a84d69642dbeac615452e8af9b8eb825b7bc9f31a53a1051e2", size = 41306 }, + { url = "https://files.pythonhosted.org/packages/74/7d/36b045c23a1ab98507aefd44fd8b264ee1dd5e5010543c6fccf82141ccef/multidict-6.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:d8c112f7a90d8ca5d20213aa41eac690bb50a76da153e3afb3886418e61cb22e", size = 46029 }, + { url = "https://files.pythonhosted.org/packages/0f/5e/553d67d24432c5cd52b49047f2d248821843743ee6d29a704594f656d182/multidict-6.6.4-cp311-cp311-win_arm64.whl", hash = "sha256:3bb0eae408fa1996d87247ca0d6a57b7fc1dcf83e8a5c47ab82c558c250d4adf", size = 43017 }, + { url = "https://files.pythonhosted.org/packages/05/f6/512ffd8fd8b37fb2680e5ac35d788f1d71bbaf37789d21a820bdc441e565/multidict-6.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0ffb87be160942d56d7b87b0fdf098e81ed565add09eaa1294268c7f3caac4c8", size = 76516 }, + { url = "https://files.pythonhosted.org/packages/99/58/45c3e75deb8855c36bd66cc1658007589662ba584dbf423d01df478dd1c5/multidict-6.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d191de6cbab2aff5de6c5723101705fd044b3e4c7cfd587a1929b5028b9714b3", size = 45394 }, + { url = "https://files.pythonhosted.org/packages/fd/ca/e8c4472a93a26e4507c0b8e1f0762c0d8a32de1328ef72fd704ef9cc5447/multidict-6.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38a0956dd92d918ad5feff3db8fcb4a5eb7dba114da917e1a88475619781b57b", size = 43591 }, + { url = "https://files.pythonhosted.org/packages/05/51/edf414f4df058574a7265034d04c935aa84a89e79ce90fcf4df211f47b16/multidict-6.6.4-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6865f6d3b7900ae020b495d599fcf3765653bc927951c1abb959017f81ae8287", size = 237215 }, + { url = "https://files.pythonhosted.org/packages/c8/45/8b3d6dbad8cf3252553cc41abea09ad527b33ce47a5e199072620b296902/multidict-6.6.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a2088c126b6f72db6c9212ad827d0ba088c01d951cee25e758c450da732c138", size = 258299 }, + { url = "https://files.pythonhosted.org/packages/3c/e8/8ca2e9a9f5a435fc6db40438a55730a4bf4956b554e487fa1b9ae920f825/multidict-6.6.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0f37bed7319b848097085d7d48116f545985db988e2256b2e6f00563a3416ee6", size = 242357 }, + { url = "https://files.pythonhosted.org/packages/0f/84/80c77c99df05a75c28490b2af8f7cba2a12621186e0a8b0865d8e745c104/multidict-6.6.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:01368e3c94032ba6ca0b78e7ccb099643466cf24f8dc8eefcfdc0571d56e58f9", size = 268369 }, + { url = "https://files.pythonhosted.org/packages/0d/e9/920bfa46c27b05fb3e1ad85121fd49f441492dca2449c5bcfe42e4565d8a/multidict-6.6.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fe323540c255db0bffee79ad7f048c909f2ab0edb87a597e1c17da6a54e493c", size = 269341 }, + { url = "https://files.pythonhosted.org/packages/af/65/753a2d8b05daf496f4a9c367fe844e90a1b2cac78e2be2c844200d10cc4c/multidict-6.6.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8eb3025f17b0a4c3cd08cda49acf312a19ad6e8a4edd9dbd591e6506d999402", size = 256100 }, + { url = "https://files.pythonhosted.org/packages/09/54/655be13ae324212bf0bc15d665a4e34844f34c206f78801be42f7a0a8aaa/multidict-6.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bbc14f0365534d35a06970d6a83478b249752e922d662dc24d489af1aa0d1be7", size = 253584 }, + { url = "https://files.pythonhosted.org/packages/5c/74/ab2039ecc05264b5cec73eb018ce417af3ebb384ae9c0e9ed42cb33f8151/multidict-6.6.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:75aa52fba2d96bf972e85451b99d8e19cc37ce26fd016f6d4aa60da9ab2b005f", size = 251018 }, + { url = "https://files.pythonhosted.org/packages/af/0a/ccbb244ac848e56c6427f2392741c06302bbfba49c0042f1eb3c5b606497/multidict-6.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fefd4a815e362d4f011919d97d7b4a1e566f1dde83dc4ad8cfb5b41de1df68d", size = 251477 }, + { url = "https://files.pythonhosted.org/packages/0e/b0/0ed49bba775b135937f52fe13922bc64a7eaf0a3ead84a36e8e4e446e096/multidict-6.6.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:db9801fe021f59a5b375ab778973127ca0ac52429a26e2fd86aa9508f4d26eb7", size = 263575 }, + { url = "https://files.pythonhosted.org/packages/3e/d9/7fb85a85e14de2e44dfb6a24f03c41e2af8697a6df83daddb0e9b7569f73/multidict-6.6.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a650629970fa21ac1fb06ba25dabfc5b8a2054fcbf6ae97c758aa956b8dba802", size = 259649 }, + { url = "https://files.pythonhosted.org/packages/03/9e/b3a459bcf9b6e74fa461a5222a10ff9b544cb1cd52fd482fb1b75ecda2a2/multidict-6.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:452ff5da78d4720d7516a3a2abd804957532dd69296cb77319c193e3ffb87e24", size = 251505 }, + { url = "https://files.pythonhosted.org/packages/86/a2/8022f78f041dfe6d71e364001a5cf987c30edfc83c8a5fb7a3f0974cff39/multidict-6.6.4-cp312-cp312-win32.whl", hash = "sha256:8c2fcb12136530ed19572bbba61b407f655e3953ba669b96a35036a11a485793", size = 41888 }, + { url = "https://files.pythonhosted.org/packages/c7/eb/d88b1780d43a56db2cba24289fa744a9d216c1a8546a0dc3956563fd53ea/multidict-6.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:047d9425860a8c9544fed1b9584f0c8bcd31bcde9568b047c5e567a1025ecd6e", size = 46072 }, + { url = "https://files.pythonhosted.org/packages/9f/16/b929320bf5750e2d9d4931835a4c638a19d2494a5b519caaaa7492ebe105/multidict-6.6.4-cp312-cp312-win_arm64.whl", hash = "sha256:14754eb72feaa1e8ae528468f24250dd997b8e2188c3d2f593f9eba259e4b364", size = 43222 }, + { url = "https://files.pythonhosted.org/packages/3a/5d/e1db626f64f60008320aab00fbe4f23fc3300d75892a3381275b3d284580/multidict-6.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f46a6e8597f9bd71b31cc708195d42b634c8527fecbcf93febf1052cacc1f16e", size = 75848 }, + { url = "https://files.pythonhosted.org/packages/4c/aa/8b6f548d839b6c13887253af4e29c939af22a18591bfb5d0ee6f1931dae8/multidict-6.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:22e38b2bc176c5eb9c0a0e379f9d188ae4cd8b28c0f53b52bce7ab0a9e534657", size = 45060 }, + { url = "https://files.pythonhosted.org/packages/eb/c6/f5e97e5d99a729bc2aa58eb3ebfa9f1e56a9b517cc38c60537c81834a73f/multidict-6.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5df8afd26f162da59e218ac0eefaa01b01b2e6cd606cffa46608f699539246da", size = 43269 }, + { url = "https://files.pythonhosted.org/packages/dc/31/d54eb0c62516776f36fe67f84a732f97e0b0e12f98d5685bebcc6d396910/multidict-6.6.4-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:49517449b58d043023720aa58e62b2f74ce9b28f740a0b5d33971149553d72aa", size = 237158 }, + { url = "https://files.pythonhosted.org/packages/c4/1c/8a10c1c25b23156e63b12165a929d8eb49a6ed769fdbefb06e6f07c1e50d/multidict-6.6.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9408439537c5afdca05edd128a63f56a62680f4b3c234301055d7a2000220f", size = 257076 }, + { url = "https://files.pythonhosted.org/packages/ad/86/90e20b5771d6805a119e483fd3d1e8393e745a11511aebca41f0da38c3e2/multidict-6.6.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:87a32d20759dc52a9e850fe1061b6e41ab28e2998d44168a8a341b99ded1dba0", size = 240694 }, + { url = "https://files.pythonhosted.org/packages/e7/49/484d3e6b535bc0555b52a0a26ba86e4d8d03fd5587d4936dc59ba7583221/multidict-6.6.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:52e3c8d43cdfff587ceedce9deb25e6ae77daba560b626e97a56ddcad3756879", size = 266350 }, + { url = "https://files.pythonhosted.org/packages/bf/b4/aa4c5c379b11895083d50021e229e90c408d7d875471cb3abf721e4670d6/multidict-6.6.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ad8850921d3a8d8ff6fbef790e773cecfc260bbfa0566998980d3fa8f520bc4a", size = 267250 }, + { url = "https://files.pythonhosted.org/packages/80/e5/5e22c5bf96a64bdd43518b1834c6d95a4922cc2066b7d8e467dae9b6cee6/multidict-6.6.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:497a2954adc25c08daff36f795077f63ad33e13f19bfff7736e72c785391534f", size = 254900 }, + { url = "https://files.pythonhosted.org/packages/17/38/58b27fed927c07035abc02befacab42491e7388ca105e087e6e0215ead64/multidict-6.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:024ce601f92d780ca1617ad4be5ac15b501cc2414970ffa2bb2bbc2bd5a68fa5", size = 252355 }, + { url = "https://files.pythonhosted.org/packages/d0/a1/dad75d23a90c29c02b5d6f3d7c10ab36c3197613be5d07ec49c7791e186c/multidict-6.6.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a693fc5ed9bdd1c9e898013e0da4dcc640de7963a371c0bd458e50e046bf6438", size = 250061 }, + { url = "https://files.pythonhosted.org/packages/b8/1a/ac2216b61c7f116edab6dc3378cca6c70dc019c9a457ff0d754067c58b20/multidict-6.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:190766dac95aab54cae5b152a56520fd99298f32a1266d66d27fdd1b5ac00f4e", size = 249675 }, + { url = "https://files.pythonhosted.org/packages/d4/79/1916af833b800d13883e452e8e0977c065c4ee3ab7a26941fbfdebc11895/multidict-6.6.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8f2a5ffdceab9dcd97c7a016deb2308531d5f0fced2bb0c9e1df45b3363d7", size = 261247 }, + { url = "https://files.pythonhosted.org/packages/c5/65/d1f84fe08ac44a5fc7391cbc20a7cedc433ea616b266284413fd86062f8c/multidict-6.6.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:59e8d40ab1f5a8597abcef00d04845155a5693b5da00d2c93dbe88f2050f2812", size = 257960 }, + { url = "https://files.pythonhosted.org/packages/13/b5/29ec78057d377b195ac2c5248c773703a6b602e132a763e20ec0457e7440/multidict-6.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:467fe64138cfac771f0e949b938c2e1ada2b5af22f39692aa9258715e9ea613a", size = 250078 }, + { url = "https://files.pythonhosted.org/packages/c4/0e/7e79d38f70a872cae32e29b0d77024bef7834b0afb406ddae6558d9e2414/multidict-6.6.4-cp313-cp313-win32.whl", hash = "sha256:14616a30fe6d0a48d0a48d1a633ab3b8bec4cf293aac65f32ed116f620adfd69", size = 41708 }, + { url = "https://files.pythonhosted.org/packages/9d/34/746696dffff742e97cd6a23da953e55d0ea51fa601fa2ff387b3edcfaa2c/multidict-6.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:40cd05eaeb39e2bc8939451f033e57feaa2ac99e07dbca8afe2be450a4a3b6cf", size = 45912 }, + { url = "https://files.pythonhosted.org/packages/c7/87/3bac136181e271e29170d8d71929cdeddeb77f3e8b6a0c08da3a8e9da114/multidict-6.6.4-cp313-cp313-win_arm64.whl", hash = "sha256:f6eb37d511bfae9e13e82cb4d1af36b91150466f24d9b2b8a9785816deb16605", size = 43076 }, + { url = "https://files.pythonhosted.org/packages/64/94/0a8e63e36c049b571c9ae41ee301ada29c3fee9643d9c2548d7d558a1d99/multidict-6.6.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6c84378acd4f37d1b507dfa0d459b449e2321b3ba5f2338f9b085cf7a7ba95eb", size = 82812 }, + { url = "https://files.pythonhosted.org/packages/25/1a/be8e369dfcd260d2070a67e65dd3990dd635cbd735b98da31e00ea84cd4e/multidict-6.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0e0558693063c75f3d952abf645c78f3c5dfdd825a41d8c4d8156fc0b0da6e7e", size = 48313 }, + { url = "https://files.pythonhosted.org/packages/26/5a/dd4ade298674b2f9a7b06a32c94ffbc0497354df8285f27317c66433ce3b/multidict-6.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3f8e2384cb83ebd23fd07e9eada8ba64afc4c759cd94817433ab8c81ee4b403f", size = 46777 }, + { url = "https://files.pythonhosted.org/packages/89/db/98aa28bc7e071bfba611ac2ae803c24e96dd3a452b4118c587d3d872c64c/multidict-6.6.4-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f996b87b420995a9174b2a7c1a8daf7db4750be6848b03eb5e639674f7963773", size = 229321 }, + { url = "https://files.pythonhosted.org/packages/c7/bc/01ddda2a73dd9d167bd85d0e8ef4293836a8f82b786c63fb1a429bc3e678/multidict-6.6.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc356250cffd6e78416cf5b40dc6a74f1edf3be8e834cf8862d9ed5265cf9b0e", size = 249954 }, + { url = "https://files.pythonhosted.org/packages/06/78/6b7c0f020f9aa0acf66d0ab4eb9f08375bac9a50ff5e3edb1c4ccd59eafc/multidict-6.6.4-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:dadf95aa862714ea468a49ad1e09fe00fcc9ec67d122f6596a8d40caf6cec7d0", size = 228612 }, + { url = "https://files.pythonhosted.org/packages/00/44/3faa416f89b2d5d76e9d447296a81521e1c832ad6e40b92f990697b43192/multidict-6.6.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7dd57515bebffd8ebd714d101d4c434063322e4fe24042e90ced41f18b6d3395", size = 257528 }, + { url = "https://files.pythonhosted.org/packages/05/5f/77c03b89af0fcb16f018f668207768191fb9dcfb5e3361a5e706a11db2c9/multidict-6.6.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:967af5f238ebc2eb1da4e77af5492219fbd9b4b812347da39a7b5f5c72c0fa45", size = 256329 }, + { url = "https://files.pythonhosted.org/packages/cf/e9/ed750a2a9afb4f8dc6f13dc5b67b514832101b95714f1211cd42e0aafc26/multidict-6.6.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a4c6875c37aae9794308ec43e3530e4aa0d36579ce38d89979bbf89582002bb", size = 247928 }, + { url = "https://files.pythonhosted.org/packages/1f/b5/e0571bc13cda277db7e6e8a532791d4403dacc9850006cb66d2556e649c0/multidict-6.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f683a551e92bdb7fac545b9c6f9fa2aebdeefa61d607510b3533286fcab67f5", size = 245228 }, + { url = "https://files.pythonhosted.org/packages/f3/a3/69a84b0eccb9824491f06368f5b86e72e4af54c3067c37c39099b6687109/multidict-6.6.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:3ba5aaf600edaf2a868a391779f7a85d93bed147854925f34edd24cc70a3e141", size = 235869 }, + { url = "https://files.pythonhosted.org/packages/a9/9d/28802e8f9121a6a0804fa009debf4e753d0a59969ea9f70be5f5fdfcb18f/multidict-6.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:580b643b7fd2c295d83cad90d78419081f53fd532d1f1eb67ceb7060f61cff0d", size = 243446 }, + { url = "https://files.pythonhosted.org/packages/38/ea/6c98add069b4878c1d66428a5f5149ddb6d32b1f9836a826ac764b9940be/multidict-6.6.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:37b7187197da6af3ee0b044dbc9625afd0c885f2800815b228a0e70f9a7f473d", size = 252299 }, + { url = "https://files.pythonhosted.org/packages/3a/09/8fe02d204473e14c0af3affd50af9078839dfca1742f025cca765435d6b4/multidict-6.6.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e1b93790ed0bc26feb72e2f08299691ceb6da5e9e14a0d13cc74f1869af327a0", size = 246926 }, + { url = "https://files.pythonhosted.org/packages/37/3d/7b1e10d774a6df5175ecd3c92bff069e77bed9ec2a927fdd4ff5fe182f67/multidict-6.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a506a77ddee1efcca81ecbeae27ade3e09cdf21a8ae854d766c2bb4f14053f92", size = 243383 }, + { url = "https://files.pythonhosted.org/packages/50/b0/a6fae46071b645ae98786ab738447de1ef53742eaad949f27e960864bb49/multidict-6.6.4-cp313-cp313t-win32.whl", hash = "sha256:f93b2b2279883d1d0a9e1bd01f312d6fc315c5e4c1f09e112e4736e2f650bc4e", size = 47775 }, + { url = "https://files.pythonhosted.org/packages/b2/0a/2436550b1520091af0600dff547913cb2d66fbac27a8c33bc1b1bccd8d98/multidict-6.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:6d46a180acdf6e87cc41dc15d8f5c2986e1e8739dc25dbb7dac826731ef381a4", size = 53100 }, + { url = "https://files.pythonhosted.org/packages/97/ea/43ac51faff934086db9c072a94d327d71b7d8b40cd5dcb47311330929ef0/multidict-6.6.4-cp313-cp313t-win_arm64.whl", hash = "sha256:756989334015e3335d087a27331659820d53ba432befdef6a718398b0a8493ad", size = 45501 }, + { url = "https://files.pythonhosted.org/packages/fd/69/b547032297c7e63ba2af494edba695d781af8a0c6e89e4d06cf848b21d80/multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c", size = 12313 }, ] [[package]] @@ -1939,48 +2051,48 @@ dependencies = [ { name = "tomli", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570 } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/a9/3d7aa83955617cdf02f94e50aab5c830d205cfa4320cf124ff64acce3a8e/mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972", size = 11003299, upload-time = "2025-07-31T07:54:06.425Z" }, - { url = "https://files.pythonhosted.org/packages/83/e8/72e62ff837dd5caaac2b4a5c07ce769c8e808a00a65e5d8f94ea9c6f20ab/mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7", size = 10125451, upload-time = "2025-07-31T07:53:52.974Z" }, - { url = "https://files.pythonhosted.org/packages/7d/10/f3f3543f6448db11881776f26a0ed079865926b0c841818ee22de2c6bbab/mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df", size = 11916211, upload-time = "2025-07-31T07:53:18.879Z" }, - { url = "https://files.pythonhosted.org/packages/06/bf/63e83ed551282d67bb3f7fea2cd5561b08d2bb6eb287c096539feb5ddbc5/mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390", size = 12652687, upload-time = "2025-07-31T07:53:30.544Z" }, - { url = "https://files.pythonhosted.org/packages/69/66/68f2eeef11facf597143e85b694a161868b3b006a5fbad50e09ea117ef24/mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94", size = 12896322, upload-time = "2025-07-31T07:53:50.74Z" }, - { url = "https://files.pythonhosted.org/packages/a3/87/8e3e9c2c8bd0d7e071a89c71be28ad088aaecbadf0454f46a540bda7bca6/mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b", size = 9507962, upload-time = "2025-07-31T07:53:08.431Z" }, - { url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009, upload-time = "2025-07-31T07:53:23.037Z" }, - { url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482, upload-time = "2025-07-31T07:53:26.151Z" }, - { url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883, upload-time = "2025-07-31T07:53:47.948Z" }, - { url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215, upload-time = "2025-07-31T07:54:04.031Z" }, - { url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956, upload-time = "2025-07-31T07:53:36.263Z" }, - { url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307, upload-time = "2025-07-31T07:53:59.734Z" }, - { url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295, upload-time = "2025-07-31T07:53:28.124Z" }, - { url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355, upload-time = "2025-07-31T07:53:21.121Z" }, - { url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285, upload-time = "2025-07-31T07:53:55.293Z" }, - { url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895, upload-time = "2025-07-31T07:53:43.623Z" }, - { url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025, upload-time = "2025-07-31T07:54:17.125Z" }, - { url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664, upload-time = "2025-07-31T07:54:12.842Z" }, - { url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338, upload-time = "2025-07-31T07:53:38.873Z" }, - { url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066, upload-time = "2025-07-31T07:54:14.707Z" }, - { url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473, upload-time = "2025-07-31T07:53:14.504Z" }, - { url = "https://files.pythonhosted.org/packages/9f/0f/478b4dce1cb4f43cf0f0d00fba3030b21ca04a01b74d1cd272a528cf446f/mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849", size = 12744296, upload-time = "2025-07-31T07:53:03.896Z" }, - { url = "https://files.pythonhosted.org/packages/ca/70/afa5850176379d1b303f992a828de95fc14487429a7139a4e0bdd17a8279/mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14", size = 12914657, upload-time = "2025-07-31T07:54:08.576Z" }, - { url = "https://files.pythonhosted.org/packages/53/f9/4a83e1c856a3d9c8f6edaa4749a4864ee98486e9b9dbfbc93842891029c2/mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a", size = 9593320, upload-time = "2025-07-31T07:53:01.341Z" }, - { url = "https://files.pythonhosted.org/packages/38/56/79c2fac86da57c7d8c48622a05873eaab40b905096c33597462713f5af90/mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733", size = 11040037, upload-time = "2025-07-31T07:54:10.942Z" }, - { url = "https://files.pythonhosted.org/packages/4d/c3/adabe6ff53638e3cad19e3547268482408323b1e68bf082c9119000cd049/mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd", size = 10131550, upload-time = "2025-07-31T07:53:41.307Z" }, - { url = "https://files.pythonhosted.org/packages/b8/c5/2e234c22c3bdeb23a7817af57a58865a39753bde52c74e2c661ee0cfc640/mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0", size = 11872963, upload-time = "2025-07-31T07:53:16.878Z" }, - { url = "https://files.pythonhosted.org/packages/ab/26/c13c130f35ca8caa5f2ceab68a247775648fdcd6c9a18f158825f2bc2410/mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a", size = 12710189, upload-time = "2025-07-31T07:54:01.962Z" }, - { url = "https://files.pythonhosted.org/packages/82/df/c7d79d09f6de8383fe800521d066d877e54d30b4fb94281c262be2df84ef/mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91", size = 12900322, upload-time = "2025-07-31T07:53:10.551Z" }, - { url = "https://files.pythonhosted.org/packages/b8/98/3d5a48978b4f708c55ae832619addc66d677f6dc59f3ebad71bae8285ca6/mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed", size = 9751879, upload-time = "2025-07-31T07:52:56.683Z" }, - { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" }, + { url = "https://files.pythonhosted.org/packages/77/a9/3d7aa83955617cdf02f94e50aab5c830d205cfa4320cf124ff64acce3a8e/mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972", size = 11003299 }, + { url = "https://files.pythonhosted.org/packages/83/e8/72e62ff837dd5caaac2b4a5c07ce769c8e808a00a65e5d8f94ea9c6f20ab/mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7", size = 10125451 }, + { url = "https://files.pythonhosted.org/packages/7d/10/f3f3543f6448db11881776f26a0ed079865926b0c841818ee22de2c6bbab/mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df", size = 11916211 }, + { url = "https://files.pythonhosted.org/packages/06/bf/63e83ed551282d67bb3f7fea2cd5561b08d2bb6eb287c096539feb5ddbc5/mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390", size = 12652687 }, + { url = "https://files.pythonhosted.org/packages/69/66/68f2eeef11facf597143e85b694a161868b3b006a5fbad50e09ea117ef24/mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94", size = 12896322 }, + { url = "https://files.pythonhosted.org/packages/a3/87/8e3e9c2c8bd0d7e071a89c71be28ad088aaecbadf0454f46a540bda7bca6/mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b", size = 9507962 }, + { url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009 }, + { url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482 }, + { url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883 }, + { url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215 }, + { url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956 }, + { url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307 }, + { url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295 }, + { url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355 }, + { url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285 }, + { url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895 }, + { url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025 }, + { url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664 }, + { url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338 }, + { url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066 }, + { url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473 }, + { url = "https://files.pythonhosted.org/packages/9f/0f/478b4dce1cb4f43cf0f0d00fba3030b21ca04a01b74d1cd272a528cf446f/mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849", size = 12744296 }, + { url = "https://files.pythonhosted.org/packages/ca/70/afa5850176379d1b303f992a828de95fc14487429a7139a4e0bdd17a8279/mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14", size = 12914657 }, + { url = "https://files.pythonhosted.org/packages/53/f9/4a83e1c856a3d9c8f6edaa4749a4864ee98486e9b9dbfbc93842891029c2/mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a", size = 9593320 }, + { url = "https://files.pythonhosted.org/packages/38/56/79c2fac86da57c7d8c48622a05873eaab40b905096c33597462713f5af90/mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733", size = 11040037 }, + { url = "https://files.pythonhosted.org/packages/4d/c3/adabe6ff53638e3cad19e3547268482408323b1e68bf082c9119000cd049/mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd", size = 10131550 }, + { url = "https://files.pythonhosted.org/packages/b8/c5/2e234c22c3bdeb23a7817af57a58865a39753bde52c74e2c661ee0cfc640/mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0", size = 11872963 }, + { url = "https://files.pythonhosted.org/packages/ab/26/c13c130f35ca8caa5f2ceab68a247775648fdcd6c9a18f158825f2bc2410/mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a", size = 12710189 }, + { url = "https://files.pythonhosted.org/packages/82/df/c7d79d09f6de8383fe800521d066d877e54d30b4fb94281c262be2df84ef/mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91", size = 12900322 }, + { url = "https://files.pythonhosted.org/packages/b8/98/3d5a48978b4f708c55ae832619addc66d677f6dc59f3ebad71bae8285ca6/mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed", size = 9751879 }, + { url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411 }, ] [[package]] name = "mypy-extensions" version = "1.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343 } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963 }, ] [[package]] @@ -2001,9 +2113,9 @@ dependencies = [ { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/e3/01c093f6a46be2edc0fd370cbf6d227495ea19452939b2810b36657c63d4/myst_nb-1.1.2.tar.gz", hash = "sha256:961b4005657029ca89892a4c75edbf0856c54ceaf6172368b46bf7676c1f7700", size = 78036, upload-time = "2024-09-24T13:29:47.299Z" } +sdist = { url = "https://files.pythonhosted.org/packages/04/e3/01c093f6a46be2edc0fd370cbf6d227495ea19452939b2810b36657c63d4/myst_nb-1.1.2.tar.gz", hash = "sha256:961b4005657029ca89892a4c75edbf0856c54ceaf6172368b46bf7676c1f7700", size = 78036 } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/45/cf78b2f09c46b36f486b75c34a8b48580e53b543bd9a467b3c7eb9054b70/myst_nb-1.1.2-py3-none-any.whl", hash = "sha256:9b7034e5d62640cb6daf03f9ca16ef45d0462fced27944c77aa3f98c7cdcd566", size = 80281, upload-time = "2024-09-24T13:29:45.73Z" }, + { url = "https://files.pythonhosted.org/packages/04/45/cf78b2f09c46b36f486b75c34a8b48580e53b543bd9a467b3c7eb9054b70/myst_nb-1.1.2-py3-none-any.whl", hash = "sha256:9b7034e5d62640cb6daf03f9ca16ef45d0462fced27944c77aa3f98c7cdcd566", size = 80281 }, ] [[package]] @@ -2019,9 +2131,9 @@ dependencies = [ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/a5/9626ba4f73555b3735ad86247a8077d4603aa8628537687c839ab08bfe44/myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4", size = 93985, upload-time = "2025-02-12T10:53:03.833Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/a5/9626ba4f73555b3735ad86247a8077d4603aa8628537687c839ab08bfe44/myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4", size = 93985 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/df/76d0321c3797b54b60fef9ec3bd6f4cfd124b9e422182156a1dd418722cf/myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d", size = 84579, upload-time = "2025-02-12T10:53:02.078Z" }, + { url = "https://files.pythonhosted.org/packages/5f/df/76d0321c3797b54b60fef9ec3bd6f4cfd124b9e422182156a1dd418722cf/myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d", size = 84579 }, ] [[package]] @@ -2034,9 +2146,9 @@ dependencies = [ { name = "nbformat", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "traitlets", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/87/66/7ffd18d58eae90d5721f9f39212327695b749e23ad44b3881744eaf4d9e8/nbclient-0.10.2.tar.gz", hash = "sha256:90b7fc6b810630db87a6d0c2250b1f0ab4cf4d3c27a299b0cde78a4ed3fd9193", size = 62424, upload-time = "2024-12-19T10:32:27.164Z" } +sdist = { url = "https://files.pythonhosted.org/packages/87/66/7ffd18d58eae90d5721f9f39212327695b749e23ad44b3881744eaf4d9e8/nbclient-0.10.2.tar.gz", hash = "sha256:90b7fc6b810630db87a6d0c2250b1f0ab4cf4d3c27a299b0cde78a4ed3fd9193", size = 62424 } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/6d/e7fa07f03a4a7b221d94b4d586edb754a9b0dc3c9e2c93353e9fa4e0d117/nbclient-0.10.2-py3-none-any.whl", hash = "sha256:4ffee11e788b4a27fabeb7955547e4318a5298f34342a4bfd01f2e1faaeadc3d", size = 25434, upload-time = "2024-12-19T10:32:24.139Z" }, + { url = "https://files.pythonhosted.org/packages/34/6d/e7fa07f03a4a7b221d94b4d586edb754a9b0dc3c9e2c93353e9fa4e0d117/nbclient-0.10.2-py3-none-any.whl", hash = "sha256:4ffee11e788b4a27fabeb7955547e4318a5298f34342a4bfd01f2e1faaeadc3d", size = 25434 }, ] [[package]] @@ -2049,41 +2161,200 @@ dependencies = [ { name = "jupyter-core", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "traitlets", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749, upload-time = "2024-04-04T11:20:37.371Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454, upload-time = "2024-04-04T11:20:34.895Z" }, + { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454 }, ] [[package]] name = "nest-asyncio" version = "1.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, ] [[package]] name = "nodeenv" version = "1.9.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11' and sys_platform == 'darwin'", + "python_full_version < '3.11' and sys_platform == 'linux'", + "python_full_version < '3.11' and sys_platform == 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245 }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048 }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542 }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301 }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320 }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050 }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034 }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185 }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149 }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620 }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963 }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743 }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616 }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579 }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005 }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570 }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548 }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521 }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866 }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455 }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348 }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362 }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103 }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382 }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462 }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618 }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511 }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783 }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506 }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190 }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828 }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006 }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765 }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736 }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719 }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072 }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213 }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632 }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532 }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885 }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467 }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144 }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217 }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014 }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935 }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122 }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143 }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260 }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225 }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374 }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391 }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754 }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476 }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666 }, +] + +[[package]] +name = "numpy" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and sys_platform == 'linux'", + "python_full_version >= '3.13' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029", size = 20576648 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/45/e80d203ef6b267aa29b22714fb558930b27960a0c5ce3c19c999232bb3eb/numpy-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ffc4f5caba7dfcbe944ed674b7eef683c7e94874046454bb79ed7ee0236f59d", size = 21259253 }, + { url = "https://files.pythonhosted.org/packages/52/18/cf2c648fccf339e59302e00e5f2bc87725a3ce1992f30f3f78c9044d7c43/numpy-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7e946c7170858a0295f79a60214424caac2ffdb0063d4d79cb681f9aa0aa569", size = 14450980 }, + { url = "https://files.pythonhosted.org/packages/93/fb/9af1082bec870188c42a1c239839915b74a5099c392389ff04215dcee812/numpy-2.3.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:cd4260f64bc794c3390a63bf0728220dd1a68170c169088a1e0dfa2fde1be12f", size = 5379709 }, + { url = "https://files.pythonhosted.org/packages/75/0f/bfd7abca52bcbf9a4a65abc83fe18ef01ccdeb37bfb28bbd6ad613447c79/numpy-2.3.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:f0ddb4b96a87b6728df9362135e764eac3cfa674499943ebc44ce96c478ab125", size = 6913923 }, + { url = "https://files.pythonhosted.org/packages/79/55/d69adad255e87ab7afda1caf93ca997859092afeb697703e2f010f7c2e55/numpy-2.3.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:afd07d377f478344ec6ca2b8d4ca08ae8bd44706763d1efb56397de606393f48", size = 14589591 }, + { url = "https://files.pythonhosted.org/packages/10/a2/010b0e27ddeacab7839957d7a8f00e91206e0c2c47abbb5f35a2630e5387/numpy-2.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc92a5dedcc53857249ca51ef29f5e5f2f8c513e22cfb90faeb20343b8c6f7a6", size = 16938714 }, + { url = "https://files.pythonhosted.org/packages/1c/6b/12ce8ede632c7126eb2762b9e15e18e204b81725b81f35176eac14dc5b82/numpy-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7af05ed4dc19f308e1d9fc759f36f21921eb7bbfc82843eeec6b2a2863a0aefa", size = 16370592 }, + { url = "https://files.pythonhosted.org/packages/b4/35/aba8568b2593067bb6a8fe4c52babb23b4c3b9c80e1b49dff03a09925e4a/numpy-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:433bf137e338677cebdd5beac0199ac84712ad9d630b74eceeb759eaa45ddf30", size = 18884474 }, + { url = "https://files.pythonhosted.org/packages/45/fa/7f43ba10c77575e8be7b0138d107e4f44ca4a1ef322cd16980ea3e8b8222/numpy-2.3.3-cp311-cp311-win32.whl", hash = "sha256:eb63d443d7b4ffd1e873f8155260d7f58e7e4b095961b01c91062935c2491e57", size = 6599794 }, + { url = "https://files.pythonhosted.org/packages/0a/a2/a4f78cb2241fe5664a22a10332f2be886dcdea8784c9f6a01c272da9b426/numpy-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:ec9d249840f6a565f58d8f913bccac2444235025bbb13e9a4681783572ee3caa", size = 13088104 }, + { url = "https://files.pythonhosted.org/packages/79/64/e424e975adbd38282ebcd4891661965b78783de893b381cbc4832fb9beb2/numpy-2.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:74c2a948d02f88c11a3c075d9733f1ae67d97c6bdb97f2bb542f980458b257e7", size = 10460772 }, + { url = "https://files.pythonhosted.org/packages/51/5d/bb7fc075b762c96329147799e1bcc9176ab07ca6375ea976c475482ad5b3/numpy-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cfdd09f9c84a1a934cde1eec2267f0a43a7cd44b2cca4ff95b7c0d14d144b0bf", size = 20957014 }, + { url = "https://files.pythonhosted.org/packages/6b/0e/c6211bb92af26517acd52125a237a92afe9c3124c6a68d3b9f81b62a0568/numpy-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb32e3cf0f762aee47ad1ddc6672988f7f27045b0783c887190545baba73aa25", size = 14185220 }, + { url = "https://files.pythonhosted.org/packages/22/f2/07bb754eb2ede9073f4054f7c0286b0d9d2e23982e090a80d478b26d35ca/numpy-2.3.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:396b254daeb0a57b1fe0ecb5e3cff6fa79a380fa97c8f7781a6d08cd429418fe", size = 5113918 }, + { url = "https://files.pythonhosted.org/packages/81/0a/afa51697e9fb74642f231ea36aca80fa17c8fb89f7a82abd5174023c3960/numpy-2.3.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:067e3d7159a5d8f8a0b46ee11148fc35ca9b21f61e3c49fbd0a027450e65a33b", size = 6647922 }, + { url = "https://files.pythonhosted.org/packages/5d/f5/122d9cdb3f51c520d150fef6e87df9279e33d19a9611a87c0d2cf78a89f4/numpy-2.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c02d0629d25d426585fb2e45a66154081b9fa677bc92a881ff1d216bc9919a8", size = 14281991 }, + { url = "https://files.pythonhosted.org/packages/51/64/7de3c91e821a2debf77c92962ea3fe6ac2bc45d0778c1cbe15d4fce2fd94/numpy-2.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9192da52b9745f7f0766531dcfa978b7763916f158bb63bdb8a1eca0068ab20", size = 16641643 }, + { url = "https://files.pythonhosted.org/packages/30/e4/961a5fa681502cd0d68907818b69f67542695b74e3ceaa513918103b7e80/numpy-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cd7de500a5b66319db419dc3c345244404a164beae0d0937283b907d8152e6ea", size = 16056787 }, + { url = "https://files.pythonhosted.org/packages/99/26/92c912b966e47fbbdf2ad556cb17e3a3088e2e1292b9833be1dfa5361a1a/numpy-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93d4962d8f82af58f0b2eb85daaf1b3ca23fe0a85d0be8f1f2b7bb46034e56d7", size = 18579598 }, + { url = "https://files.pythonhosted.org/packages/17/b6/fc8f82cb3520768718834f310c37d96380d9dc61bfdaf05fe5c0b7653e01/numpy-2.3.3-cp312-cp312-win32.whl", hash = "sha256:5534ed6b92f9b7dca6c0a19d6df12d41c68b991cef051d108f6dbff3babc4ebf", size = 6320800 }, + { url = "https://files.pythonhosted.org/packages/32/ee/de999f2625b80d043d6d2d628c07d0d5555a677a3cf78fdf868d409b8766/numpy-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:497d7cad08e7092dba36e3d296fe4c97708c93daf26643a1ae4b03f6294d30eb", size = 12786615 }, + { url = "https://files.pythonhosted.org/packages/49/6e/b479032f8a43559c383acb20816644f5f91c88f633d9271ee84f3b3a996c/numpy-2.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:ca0309a18d4dfea6fc6262a66d06c26cfe4640c3926ceec90e57791a82b6eee5", size = 10195936 }, + { url = "https://files.pythonhosted.org/packages/7d/b9/984c2b1ee61a8b803bf63582b4ac4242cf76e2dbd663efeafcb620cc0ccb/numpy-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f5415fb78995644253370985342cd03572ef8620b934da27d77377a2285955bf", size = 20949588 }, + { url = "https://files.pythonhosted.org/packages/a6/e4/07970e3bed0b1384d22af1e9912527ecbeb47d3b26e9b6a3bced068b3bea/numpy-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d00de139a3324e26ed5b95870ce63be7ec7352171bc69a4cf1f157a48e3eb6b7", size = 14177802 }, + { url = "https://files.pythonhosted.org/packages/35/c7/477a83887f9de61f1203bad89cf208b7c19cc9fef0cebef65d5a1a0619f2/numpy-2.3.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9dc13c6a5829610cc07422bc74d3ac083bd8323f14e2827d992f9e52e22cd6a6", size = 5106537 }, + { url = "https://files.pythonhosted.org/packages/52/47/93b953bd5866a6f6986344d045a207d3f1cfbad99db29f534ea9cee5108c/numpy-2.3.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d79715d95f1894771eb4e60fb23f065663b2298f7d22945d66877aadf33d00c7", size = 6640743 }, + { url = "https://files.pythonhosted.org/packages/23/83/377f84aaeb800b64c0ef4de58b08769e782edcefa4fea712910b6f0afd3c/numpy-2.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:952cfd0748514ea7c3afc729a0fc639e61655ce4c55ab9acfab14bda4f402b4c", size = 14278881 }, + { url = "https://files.pythonhosted.org/packages/9a/a5/bf3db6e66c4b160d6ea10b534c381a1955dfab34cb1017ea93aa33c70ed3/numpy-2.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b83648633d46f77039c29078751f80da65aa64d5622a3cd62aaef9d835b6c93", size = 16636301 }, + { url = "https://files.pythonhosted.org/packages/a2/59/1287924242eb4fa3f9b3a2c30400f2e17eb2707020d1c5e3086fe7330717/numpy-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b001bae8cea1c7dfdb2ae2b017ed0a6f2102d7a70059df1e338e307a4c78a8ae", size = 16053645 }, + { url = "https://files.pythonhosted.org/packages/e6/93/b3d47ed882027c35e94ac2320c37e452a549f582a5e801f2d34b56973c97/numpy-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e9aced64054739037d42fb84c54dd38b81ee238816c948c8f3ed134665dcd86", size = 18578179 }, + { url = "https://files.pythonhosted.org/packages/20/d9/487a2bccbf7cc9d4bfc5f0f197761a5ef27ba870f1e3bbb9afc4bbe3fcc2/numpy-2.3.3-cp313-cp313-win32.whl", hash = "sha256:9591e1221db3f37751e6442850429b3aabf7026d3b05542d102944ca7f00c8a8", size = 6312250 }, + { url = "https://files.pythonhosted.org/packages/1b/b5/263ebbbbcede85028f30047eab3d58028d7ebe389d6493fc95ae66c636ab/numpy-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f0dadeb302887f07431910f67a14d57209ed91130be0adea2f9793f1a4f817cf", size = 12783269 }, + { url = "https://files.pythonhosted.org/packages/fa/75/67b8ca554bbeaaeb3fac2e8bce46967a5a06544c9108ec0cf5cece559b6c/numpy-2.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:3c7cf302ac6e0b76a64c4aecf1a09e51abd9b01fc7feee80f6c43e3ab1b1dbc5", size = 10195314 }, + { url = "https://files.pythonhosted.org/packages/11/d0/0d1ddec56b162042ddfafeeb293bac672de9b0cfd688383590090963720a/numpy-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eda59e44957d272846bb407aad19f89dc6f58fecf3504bd144f4c5cf81a7eacc", size = 21048025 }, + { url = "https://files.pythonhosted.org/packages/36/9e/1996ca6b6d00415b6acbdd3c42f7f03ea256e2c3f158f80bd7436a8a19f3/numpy-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:823d04112bc85ef5c4fda73ba24e6096c8f869931405a80aa8b0e604510a26bc", size = 14301053 }, + { url = "https://files.pythonhosted.org/packages/05/24/43da09aa764c68694b76e84b3d3f0c44cb7c18cdc1ba80e48b0ac1d2cd39/numpy-2.3.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:40051003e03db4041aa325da2a0971ba41cf65714e65d296397cc0e32de6018b", size = 5229444 }, + { url = "https://files.pythonhosted.org/packages/bc/14/50ffb0f22f7218ef8af28dd089f79f68289a7a05a208db9a2c5dcbe123c1/numpy-2.3.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ee9086235dd6ab7ae75aba5662f582a81ced49f0f1c6de4260a78d8f2d91a19", size = 6738039 }, + { url = "https://files.pythonhosted.org/packages/55/52/af46ac0795e09657d45a7f4db961917314377edecf66db0e39fa7ab5c3d3/numpy-2.3.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94fcaa68757c3e2e668ddadeaa86ab05499a70725811e582b6a9858dd472fb30", size = 14352314 }, + { url = "https://files.pythonhosted.org/packages/a7/b1/dc226b4c90eb9f07a3fff95c2f0db3268e2e54e5cce97c4ac91518aee71b/numpy-2.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da1a74b90e7483d6ce5244053399a614b1d6b7bc30a60d2f570e5071f8959d3e", size = 16701722 }, + { url = "https://files.pythonhosted.org/packages/9d/9d/9d8d358f2eb5eced14dba99f110d83b5cd9a4460895230f3b396ad19a323/numpy-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2990adf06d1ecee3b3dcbb4977dfab6e9f09807598d647f04d385d29e7a3c3d3", size = 16132755 }, + { url = "https://files.pythonhosted.org/packages/b6/27/b3922660c45513f9377b3fb42240bec63f203c71416093476ec9aa0719dc/numpy-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ed635ff692483b8e3f0fcaa8e7eb8a75ee71aa6d975388224f70821421800cea", size = 18651560 }, + { url = "https://files.pythonhosted.org/packages/5b/8e/3ab61a730bdbbc201bb245a71102aa609f0008b9ed15255500a99cd7f780/numpy-2.3.3-cp313-cp313t-win32.whl", hash = "sha256:a333b4ed33d8dc2b373cc955ca57babc00cd6f9009991d9edc5ddbc1bac36bcd", size = 6442776 }, + { url = "https://files.pythonhosted.org/packages/1c/3a/e22b766b11f6030dc2decdeff5c2fb1610768055603f9f3be88b6d192fb2/numpy-2.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4384a169c4d8f97195980815d6fcad04933a7e1ab3b530921c3fef7a1c63426d", size = 12927281 }, + { url = "https://files.pythonhosted.org/packages/7b/42/c2e2bc48c5e9b2a83423f99733950fbefd86f165b468a3d85d52b30bf782/numpy-2.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:75370986cc0bc66f4ce5110ad35aae6d182cc4ce6433c40ad151f53690130bf1", size = 10265275 }, + { url = "https://files.pythonhosted.org/packages/6b/01/342ad585ad82419b99bcf7cebe99e61da6bedb89e213c5fd71acc467faee/numpy-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cd052f1fa6a78dee696b58a914b7229ecfa41f0a6d96dc663c1220a55e137593", size = 20951527 }, + { url = "https://files.pythonhosted.org/packages/ef/d8/204e0d73fc1b7a9ee80ab1fe1983dd33a4d64a4e30a05364b0208e9a241a/numpy-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:414a97499480067d305fcac9716c29cf4d0d76db6ebf0bf3cbce666677f12652", size = 14186159 }, + { url = "https://files.pythonhosted.org/packages/22/af/f11c916d08f3a18fb8ba81ab72b5b74a6e42ead4c2846d270eb19845bf74/numpy-2.3.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:50a5fe69f135f88a2be9b6ca0481a68a136f6febe1916e4920e12f1a34e708a7", size = 5114624 }, + { url = "https://files.pythonhosted.org/packages/fb/11/0ed919c8381ac9d2ffacd63fd1f0c34d27e99cab650f0eb6f110e6ae4858/numpy-2.3.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:b912f2ed2b67a129e6a601e9d93d4fa37bef67e54cac442a2f588a54afe5c67a", size = 6642627 }, + { url = "https://files.pythonhosted.org/packages/ee/83/deb5f77cb0f7ba6cb52b91ed388b47f8f3c2e9930d4665c600408d9b90b9/numpy-2.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e318ee0596d76d4cb3d78535dc005fa60e5ea348cd131a51e99d0bdbe0b54fe", size = 14296926 }, + { url = "https://files.pythonhosted.org/packages/77/cc/70e59dcb84f2b005d4f306310ff0a892518cc0c8000a33d0e6faf7ca8d80/numpy-2.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce020080e4a52426202bdb6f7691c65bb55e49f261f31a8f506c9f6bc7450421", size = 16638958 }, + { url = "https://files.pythonhosted.org/packages/b6/5a/b2ab6c18b4257e099587d5b7f903317bd7115333ad8d4ec4874278eafa61/numpy-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6687dc183aa55dae4a705b35f9c0f8cb178bcaa2f029b241ac5356221d5c021", size = 16071920 }, + { url = "https://files.pythonhosted.org/packages/b8/f1/8b3fdc44324a259298520dd82147ff648979bed085feeacc1250ef1656c0/numpy-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d8f3b1080782469fdc1718c4ed1d22549b5fb12af0d57d35e992158a772a37cf", size = 18577076 }, + { url = "https://files.pythonhosted.org/packages/f0/a1/b87a284fb15a42e9274e7fcea0dad259d12ddbf07c1595b26883151ca3b4/numpy-2.3.3-cp314-cp314-win32.whl", hash = "sha256:cb248499b0bc3be66ebd6578b83e5acacf1d6cb2a77f2248ce0e40fbec5a76d0", size = 6366952 }, + { url = "https://files.pythonhosted.org/packages/70/5f/1816f4d08f3b8f66576d8433a66f8fa35a5acfb3bbd0bf6c31183b003f3d/numpy-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:691808c2b26b0f002a032c73255d0bd89751425f379f7bcd22d140db593a96e8", size = 12919322 }, + { url = "https://files.pythonhosted.org/packages/8c/de/072420342e46a8ea41c324a555fa90fcc11637583fb8df722936aed1736d/numpy-2.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:9ad12e976ca7b10f1774b03615a2a4bab8addce37ecc77394d8e986927dc0dfe", size = 10478630 }, + { url = "https://files.pythonhosted.org/packages/d5/df/ee2f1c0a9de7347f14da5dd3cd3c3b034d1b8607ccb6883d7dd5c035d631/numpy-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9cc48e09feb11e1db00b320e9d30a4151f7369afb96bd0e48d942d09da3a0d00", size = 21047987 }, + { url = "https://files.pythonhosted.org/packages/d6/92/9453bdc5a4e9e69cf4358463f25e8260e2ffc126d52e10038b9077815989/numpy-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:901bf6123879b7f251d3631967fd574690734236075082078e0571977c6a8e6a", size = 14301076 }, + { url = "https://files.pythonhosted.org/packages/13/77/1447b9eb500f028bb44253105bd67534af60499588a5149a94f18f2ca917/numpy-2.3.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:7f025652034199c301049296b59fa7d52c7e625017cae4c75d8662e377bf487d", size = 5229491 }, + { url = "https://files.pythonhosted.org/packages/3d/f9/d72221b6ca205f9736cb4b2ce3b002f6e45cd67cd6a6d1c8af11a2f0b649/numpy-2.3.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:533ca5f6d325c80b6007d4d7fb1984c303553534191024ec6a524a4c92a5935a", size = 6737913 }, + { url = "https://files.pythonhosted.org/packages/3c/5f/d12834711962ad9c46af72f79bb31e73e416ee49d17f4c797f72c96b6ca5/numpy-2.3.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0edd58682a399824633b66885d699d7de982800053acf20be1eaa46d92009c54", size = 14352811 }, + { url = "https://files.pythonhosted.org/packages/a1/0d/fdbec6629d97fd1bebed56cd742884e4eead593611bbe1abc3eb40d304b2/numpy-2.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:367ad5d8fbec5d9296d18478804a530f1191e24ab4d75ab408346ae88045d25e", size = 16702689 }, + { url = "https://files.pythonhosted.org/packages/9b/09/0a35196dc5575adde1eb97ddfbc3e1687a814f905377621d18ca9bc2b7dd/numpy-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8f6ac61a217437946a1fa48d24c47c91a0c4f725237871117dea264982128097", size = 16133855 }, + { url = "https://files.pythonhosted.org/packages/7a/ca/c9de3ea397d576f1b6753eaa906d4cdef1bf97589a6d9825a349b4729cc2/numpy-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:179a42101b845a816d464b6fe9a845dfaf308fdfc7925387195570789bb2c970", size = 18652520 }, + { url = "https://files.pythonhosted.org/packages/fd/c2/e5ed830e08cd0196351db55db82f65bc0ab05da6ef2b72a836dcf1936d2f/numpy-2.3.3-cp314-cp314t-win32.whl", hash = "sha256:1250c5d3d2562ec4174bce2e3a1523041595f9b651065e4a4473f5f48a6bc8a5", size = 6515371 }, + { url = "https://files.pythonhosted.org/packages/47/c7/b0f6b5b67f6788a0725f744496badbb604d226bf233ba716683ebb47b570/numpy-2.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b37a0b2e5935409daebe82c1e42274d30d9dd355852529eab91dab8dcca7419f", size = 13112576 }, + { url = "https://files.pythonhosted.org/packages/06/b9/33bba5ff6fb679aa0b1f8a07e853f002a6b04b9394db3069a1270a7784ca/numpy-2.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:78c9f6560dc7e6b3990e32df7ea1a50bbd0e2a111e05209963f5ddcab7073b0b", size = 10545953 }, + { url = "https://files.pythonhosted.org/packages/b8/f2/7e0a37cfced2644c9563c529f29fa28acbd0960dde32ece683aafa6f4949/numpy-2.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1e02c7159791cd481e1e6d5ddd766b62a4d5acf8df4d4d1afe35ee9c5c33a41e", size = 21131019 }, + { url = "https://files.pythonhosted.org/packages/1a/7e/3291f505297ed63831135a6cc0f474da0c868a1f31b0dd9a9f03a7a0d2ed/numpy-2.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:dca2d0fc80b3893ae72197b39f69d55a3cd8b17ea1b50aa4c62de82419936150", size = 14376288 }, + { url = "https://files.pythonhosted.org/packages/bf/4b/ae02e985bdeee73d7b5abdefeb98aef1207e96d4c0621ee0cf228ddfac3c/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:99683cbe0658f8271b333a1b1b4bb3173750ad59c0c61f5bbdc5b318918fffe3", size = 5305425 }, + { url = "https://files.pythonhosted.org/packages/8b/eb/9df215d6d7250db32007941500dc51c48190be25f2401d5b2b564e467247/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d9d537a39cc9de668e5cd0e25affb17aec17b577c6b3ae8a3d866b479fbe88d0", size = 6819053 }, + { url = "https://files.pythonhosted.org/packages/57/62/208293d7d6b2a8998a4a1f23ac758648c3c32182d4ce4346062018362e29/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8596ba2f8af5f93b01d97563832686d20206d303024777f6dfc2e7c7c3f1850e", size = 14420354 }, + { url = "https://files.pythonhosted.org/packages/ed/0c/8e86e0ff7072e14a71b4c6af63175e40d1e7e933ce9b9e9f765a95b4e0c3/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1ec5615b05369925bd1125f27df33f3b6c8bc10d788d5999ecd8769a1fa04db", size = 16760413 }, + { url = "https://files.pythonhosted.org/packages/af/11/0cc63f9f321ccf63886ac203336777140011fb669e739da36d8db3c53b98/numpy-2.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2e267c7da5bf7309670523896df97f93f6e469fb931161f483cd6882b3b1a5dc", size = 12971844 }, ] [[package]] name = "oauthlib" version = "3.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918 } wheels = [ - { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" }, + { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065 }, ] [[package]] name = "openai" -version = "1.106.1" +version = "1.99.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -2095,9 +2366,9 @@ dependencies = [ { name = "tqdm", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/79/b6/1aff7d6b8e9f0c3ac26bfbb57b9861a6711d5d60bd7dd5f7eebbf80509b7/openai-1.106.1.tar.gz", hash = "sha256:5f575967e3a05555825c43829cdcd50be6e49ab6a3e5262f0937a3f791f917f1", size = 561095, upload-time = "2025-09-04T18:17:15.303Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/d2/ef89c6f3f36b13b06e271d3cc984ddd2f62508a0972c1cbcc8485a6644ff/openai-1.99.9.tar.gz", hash = "sha256:f2082d155b1ad22e83247c3de3958eb4255b20ccf4a1de2e6681b6957b554e92", size = 506992 } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/e1/47887212baa7bc0532880d33d5eafbdb46fcc4b53789b903282a74a85b5b/openai-1.106.1-py3-none-any.whl", hash = "sha256:bfdef37c949f80396c59f2c17e0eda35414979bc07ef3379596a93c9ed044f3a", size = 930768, upload-time = "2025-09-04T18:17:13.349Z" }, + { url = "https://files.pythonhosted.org/packages/e8/fb/df274ca10698ee77b07bff952f302ea627cc12dac6b85289485dd77db6de/openai-1.99.9-py3-none-any.whl", hash = "sha256:9dbcdb425553bae1ac5d947147bebbd630d91bbfc7788394d4c4f3a35682ab3a", size = 786816 }, ] [[package]] @@ -2108,9 +2379,9 @@ dependencies = [ { name = "importlib-metadata", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/27/d2/c782c88b8afbf961d6972428821c302bd1e9e7bc361352172f0ca31296e2/opentelemetry_api-1.36.0.tar.gz", hash = "sha256:9a72572b9c416d004d492cbc6e61962c0501eaf945ece9b5a0f56597d8348aa0", size = 64780, upload-time = "2025-07-29T15:12:06.02Z" } +sdist = { url = "https://files.pythonhosted.org/packages/27/d2/c782c88b8afbf961d6972428821c302bd1e9e7bc361352172f0ca31296e2/opentelemetry_api-1.36.0.tar.gz", hash = "sha256:9a72572b9c416d004d492cbc6e61962c0501eaf945ece9b5a0f56597d8348aa0", size = 64780 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/ee/6b08dde0a022c463b88f55ae81149584b125a42183407dc1045c486cc870/opentelemetry_api-1.36.0-py3-none-any.whl", hash = "sha256:02f20bcacf666e1333b6b1f04e647dc1d5111f86b8e510238fcc56d7762cda8c", size = 65564, upload-time = "2025-07-29T15:11:47.998Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ee/6b08dde0a022c463b88f55ae81149584b125a42183407dc1045c486cc870/opentelemetry_api-1.36.0-py3-none-any.whl", hash = "sha256:02f20bcacf666e1333b6b1f04e647dc1d5111f86b8e510238fcc56d7762cda8c", size = 65564 }, ] [[package]] @@ -2120,9 +2391,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-proto", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/34/da/7747e57eb341c59886052d733072bc878424bf20f1d8cf203d508bbece5b/opentelemetry_exporter_otlp_proto_common-1.36.0.tar.gz", hash = "sha256:6c496ccbcbe26b04653cecadd92f73659b814c6e3579af157d8716e5f9f25cbf", size = 20302, upload-time = "2025-07-29T15:12:07.71Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/da/7747e57eb341c59886052d733072bc878424bf20f1d8cf203d508bbece5b/opentelemetry_exporter_otlp_proto_common-1.36.0.tar.gz", hash = "sha256:6c496ccbcbe26b04653cecadd92f73659b814c6e3579af157d8716e5f9f25cbf", size = 20302 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/ed/22290dca7db78eb32e0101738366b5bbda00d0407f00feffb9bf8c3fdf87/opentelemetry_exporter_otlp_proto_common-1.36.0-py3-none-any.whl", hash = "sha256:0fc002a6ed63eac235ada9aa7056e5492e9a71728214a61745f6ad04b923f840", size = 18349, upload-time = "2025-07-29T15:11:51.327Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ed/22290dca7db78eb32e0101738366b5bbda00d0407f00feffb9bf8c3fdf87/opentelemetry_exporter_otlp_proto_common-1.36.0-py3-none-any.whl", hash = "sha256:0fc002a6ed63eac235ada9aa7056e5492e9a71728214a61745f6ad04b923f840", size = 18349 }, ] [[package]] @@ -2138,9 +2409,9 @@ dependencies = [ { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/6f/6c1b0bdd0446e5532294d1d41bf11fbaea39c8a2423a4cdfe4fe6b708127/opentelemetry_exporter_otlp_proto_grpc-1.36.0.tar.gz", hash = "sha256:b281afbf7036b325b3588b5b6c8bb175069e3978d1bd24071f4a59d04c1e5bbf", size = 23822, upload-time = "2025-07-29T15:12:08.292Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/6f/6c1b0bdd0446e5532294d1d41bf11fbaea39c8a2423a4cdfe4fe6b708127/opentelemetry_exporter_otlp_proto_grpc-1.36.0.tar.gz", hash = "sha256:b281afbf7036b325b3588b5b6c8bb175069e3978d1bd24071f4a59d04c1e5bbf", size = 23822 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/67/5f6bd188d66d0fd8e81e681bbf5822e53eb150034e2611dd2b935d3ab61a/opentelemetry_exporter_otlp_proto_grpc-1.36.0-py3-none-any.whl", hash = "sha256:734e841fc6a5d6f30e7be4d8053adb703c70ca80c562ae24e8083a28fadef211", size = 18828, upload-time = "2025-07-29T15:11:52.235Z" }, + { url = "https://files.pythonhosted.org/packages/0c/67/5f6bd188d66d0fd8e81e681bbf5822e53eb150034e2611dd2b935d3ab61a/opentelemetry_exporter_otlp_proto_grpc-1.36.0-py3-none-any.whl", hash = "sha256:734e841fc6a5d6f30e7be4d8053adb703c70ca80c562ae24e8083a28fadef211", size = 18828 }, ] [[package]] @@ -2153,9 +2424,9 @@ dependencies = [ { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "wrapt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/12/37/cf17cf28f945a3aca5a038cfbb45ee01317d4f7f3a0e5209920883fe9b08/opentelemetry_instrumentation-0.57b0.tar.gz", hash = "sha256:f2a30135ba77cdea2b0e1df272f4163c154e978f57214795d72f40befd4fcf05", size = 30807, upload-time = "2025-07-29T15:42:44.746Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/37/cf17cf28f945a3aca5a038cfbb45ee01317d4f7f3a0e5209920883fe9b08/opentelemetry_instrumentation-0.57b0.tar.gz", hash = "sha256:f2a30135ba77cdea2b0e1df272f4163c154e978f57214795d72f40befd4fcf05", size = 30807 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/6f/f20cd1542959f43fb26a5bf9bb18cd81a1ea0700e8870c8f369bd07f5c65/opentelemetry_instrumentation-0.57b0-py3-none-any.whl", hash = "sha256:9109280f44882e07cec2850db28210b90600ae9110b42824d196de357cbddf7e", size = 32460, upload-time = "2025-07-29T15:41:40.883Z" }, + { url = "https://files.pythonhosted.org/packages/d0/6f/f20cd1542959f43fb26a5bf9bb18cd81a1ea0700e8870c8f369bd07f5c65/opentelemetry_instrumentation-0.57b0-py3-none-any.whl", hash = "sha256:9109280f44882e07cec2850db28210b90600ae9110b42824d196de357cbddf7e", size = 32460 }, ] [[package]] @@ -2169,9 +2440,9 @@ dependencies = [ { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/10/7ba59b586eb099fa0155521b387d857de476687c670096597f618d889323/opentelemetry_instrumentation_asgi-0.57b0.tar.gz", hash = "sha256:a6f880b5d1838f65688fc992c65fbb1d3571f319d370990c32e759d3160e510b", size = 24654, upload-time = "2025-07-29T15:42:48.199Z" } +sdist = { url = "https://files.pythonhosted.org/packages/97/10/7ba59b586eb099fa0155521b387d857de476687c670096597f618d889323/opentelemetry_instrumentation_asgi-0.57b0.tar.gz", hash = "sha256:a6f880b5d1838f65688fc992c65fbb1d3571f319d370990c32e759d3160e510b", size = 24654 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/07/ab97dd7e8bc680b479203f7d3b2771b7a097468135a669a38da3208f96cb/opentelemetry_instrumentation_asgi-0.57b0-py3-none-any.whl", hash = "sha256:47debbde6af066a7e8e911f7193730d5e40d62effc1ac2e1119908347790a3ea", size = 16599, upload-time = "2025-07-29T15:41:48.332Z" }, + { url = "https://files.pythonhosted.org/packages/9e/07/ab97dd7e8bc680b479203f7d3b2771b7a097468135a669a38da3208f96cb/opentelemetry_instrumentation_asgi-0.57b0-py3-none-any.whl", hash = "sha256:47debbde6af066a7e8e911f7193730d5e40d62effc1ac2e1119908347790a3ea", size = 16599 }, ] [[package]] @@ -2184,9 +2455,9 @@ dependencies = [ { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "wrapt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/15/dc/5a17b2fb593901ba5257278073b28d0ed31497e56985990c26046e4da2d9/opentelemetry_instrumentation_dbapi-0.57b0.tar.gz", hash = "sha256:7ad9e39c91f6212f118435fd6fab842a1f78b2cbad1167f228c025bba2a8fc2d", size = 14176, upload-time = "2025-07-29T15:42:56.249Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/dc/5a17b2fb593901ba5257278073b28d0ed31497e56985990c26046e4da2d9/opentelemetry_instrumentation_dbapi-0.57b0.tar.gz", hash = "sha256:7ad9e39c91f6212f118435fd6fab842a1f78b2cbad1167f228c025bba2a8fc2d", size = 14176 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/71/21a7e862dead70267b7c7bd5aa4e0b61fbc9fa9b4be57f4e183766abbad9/opentelemetry_instrumentation_dbapi-0.57b0-py3-none-any.whl", hash = "sha256:c1b110a5e86ec9b52b970460917523f47afa0c73f131e7f03c6a7c1921822dc4", size = 12466, upload-time = "2025-07-29T15:41:59.775Z" }, + { url = "https://files.pythonhosted.org/packages/2c/71/21a7e862dead70267b7c7bd5aa4e0b61fbc9fa9b4be57f4e183766abbad9/opentelemetry_instrumentation_dbapi-0.57b0-py3-none-any.whl", hash = "sha256:c1b110a5e86ec9b52b970460917523f47afa0c73f131e7f03c6a7c1921822dc4", size = 12466 }, ] [[package]] @@ -2200,9 +2471,9 @@ dependencies = [ { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/88/d88268c37aabbd2bcc54f4f868394316fa6fdfd3b91e011d229617d862d3/opentelemetry_instrumentation_django-0.57b0.tar.gz", hash = "sha256:df4116d2ea2c6bbbbf8853b843deb74d66bd0d573ddd372ec84fd60adaf977c6", size = 25005, upload-time = "2025-07-29T15:42:56.88Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/88/d88268c37aabbd2bcc54f4f868394316fa6fdfd3b91e011d229617d862d3/opentelemetry_instrumentation_django-0.57b0.tar.gz", hash = "sha256:df4116d2ea2c6bbbbf8853b843deb74d66bd0d573ddd372ec84fd60adaf977c6", size = 25005 } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/f0/1d5022f2fe16d50b79d9f1f5b70bd08d0e59819e0f6b237cff82c3dbda0f/opentelemetry_instrumentation_django-0.57b0-py3-none-any.whl", hash = "sha256:3d702d79a9ec0c836ccf733becf34630c6afb3c86c25c330c5b7601debe1e7c5", size = 19597, upload-time = "2025-07-29T15:42:00.657Z" }, + { url = "https://files.pythonhosted.org/packages/97/f0/1d5022f2fe16d50b79d9f1f5b70bd08d0e59819e0f6b237cff82c3dbda0f/opentelemetry_instrumentation_django-0.57b0-py3-none-any.whl", hash = "sha256:3d702d79a9ec0c836ccf733becf34630c6afb3c86c25c330c5b7601debe1e7c5", size = 19597 }, ] [[package]] @@ -2216,9 +2487,9 @@ dependencies = [ { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/47/a8/7c22a33ff5986523a7f9afcb5f4d749533842c3cc77ef55b46727580edd0/opentelemetry_instrumentation_fastapi-0.57b0.tar.gz", hash = "sha256:73ac22f3c472a8f9cb21d1fbe5a4bf2797690c295fff4a1c040e9b1b1688a105", size = 20277, upload-time = "2025-07-29T15:42:58.68Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/a8/7c22a33ff5986523a7f9afcb5f4d749533842c3cc77ef55b46727580edd0/opentelemetry_instrumentation_fastapi-0.57b0.tar.gz", hash = "sha256:73ac22f3c472a8f9cb21d1fbe5a4bf2797690c295fff4a1c040e9b1b1688a105", size = 20277 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/df/f20fc21c88c7af5311bfefc15fc4e606bab5edb7c193aa8c73c354904c35/opentelemetry_instrumentation_fastapi-0.57b0-py3-none-any.whl", hash = "sha256:61e6402749ffe0bfec582e58155e0d81dd38723cd9bc4562bca1acca80334006", size = 12712, upload-time = "2025-07-29T15:42:03.332Z" }, + { url = "https://files.pythonhosted.org/packages/3b/df/f20fc21c88c7af5311bfefc15fc4e606bab5edb7c193aa8c73c354904c35/opentelemetry_instrumentation_fastapi-0.57b0-py3-none-any.whl", hash = "sha256:61e6402749ffe0bfec582e58155e0d81dd38723cd9bc4562bca1acca80334006", size = 12712 }, ] [[package]] @@ -2233,9 +2504,9 @@ dependencies = [ { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5f/98/8a8fa41f624069ac2912141b65bd528fd345d65e14a359c4d896fc3dc291/opentelemetry_instrumentation_flask-0.57b0.tar.gz", hash = "sha256:c5244a40b03664db966d844a32f43c900181431b77929be62a68d4907e86ed25", size = 19381, upload-time = "2025-07-29T15:42:59.38Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/98/8a8fa41f624069ac2912141b65bd528fd345d65e14a359c4d896fc3dc291/opentelemetry_instrumentation_flask-0.57b0.tar.gz", hash = "sha256:c5244a40b03664db966d844a32f43c900181431b77929be62a68d4907e86ed25", size = 19381 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/3f/79b6c9a240221f5614a143eab6a0ecacdcb23b93cc35ff2b78234f68804f/opentelemetry_instrumentation_flask-0.57b0-py3-none-any.whl", hash = "sha256:5ecd614f194825725b61ee9ba8e37dcd4d3f9b5d40fef759df8650d6a91b1cb9", size = 14688, upload-time = "2025-07-29T15:42:04.162Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3f/79b6c9a240221f5614a143eab6a0ecacdcb23b93cc35ff2b78234f68804f/opentelemetry_instrumentation_flask-0.57b0-py3-none-any.whl", hash = "sha256:5ecd614f194825725b61ee9ba8e37dcd4d3f9b5d40fef759df8650d6a91b1cb9", size = 14688 }, ] [[package]] @@ -2248,9 +2519,9 @@ dependencies = [ { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "opentelemetry-semantic-conventions-ai", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/75/42/3ceb2b1a685897c7c3e5e08f3006f5f805a98c23659e1bbfd41a035679b6/opentelemetry_instrumentation_openai-0.46.2.tar.gz", hash = "sha256:5f32380d9018dce3c9af42eaa25a163d20825e66193d57f5a5c4876ec6bf8444", size = 25406, upload-time = "2025-08-29T18:07:57.021Z" } +sdist = { url = "https://files.pythonhosted.org/packages/75/42/3ceb2b1a685897c7c3e5e08f3006f5f805a98c23659e1bbfd41a035679b6/opentelemetry_instrumentation_openai-0.46.2.tar.gz", hash = "sha256:5f32380d9018dce3c9af42eaa25a163d20825e66193d57f5a5c4876ec6bf8444", size = 25406 } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/db/f6637a16f15763f12e727405a8ed0caaaca3f2d786b283fff0cd33d599d5/opentelemetry_instrumentation_openai-0.46.2-py3-none-any.whl", hash = "sha256:0880685a00752c31fdc4c6d9b959342156d62257515e9a8410431fcf7febe2a2", size = 35269, upload-time = "2025-08-29T18:07:30.132Z" }, + { url = "https://files.pythonhosted.org/packages/da/db/f6637a16f15763f12e727405a8ed0caaaca3f2d786b283fff0cd33d599d5/opentelemetry_instrumentation_openai-0.46.2-py3-none-any.whl", hash = "sha256:0880685a00752c31fdc4c6d9b959342156d62257515e9a8410431fcf7febe2a2", size = 35269 }, ] [[package]] @@ -2262,9 +2533,9 @@ dependencies = [ { name = "opentelemetry-instrumentation", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "opentelemetry-instrumentation-dbapi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/66/f2004cde131663810e62b47bb48b684660632876f120c6b1d400a04ccb06/opentelemetry_instrumentation_psycopg2-0.57b0.tar.gz", hash = "sha256:4e9d05d661c50985f0a5d7f090a7f399d453b467c9912c7611fcef693d15b038", size = 10722, upload-time = "2025-07-29T15:43:05.644Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/66/f2004cde131663810e62b47bb48b684660632876f120c6b1d400a04ccb06/opentelemetry_instrumentation_psycopg2-0.57b0.tar.gz", hash = "sha256:4e9d05d661c50985f0a5d7f090a7f399d453b467c9912c7611fcef693d15b038", size = 10722 } wheels = [ - { url = "https://files.pythonhosted.org/packages/02/40/00f9c1334fb0c9d74c99d37c4a730cbe6dc941eea5fae6f9bc36e5a53d19/opentelemetry_instrumentation_psycopg2-0.57b0-py3-none-any.whl", hash = "sha256:94fdde02b7451c8e85d43b4b9dd13a34fee96ffd43324d1b3567f47d2903b99f", size = 10721, upload-time = "2025-07-29T15:42:15.698Z" }, + { url = "https://files.pythonhosted.org/packages/02/40/00f9c1334fb0c9d74c99d37c4a730cbe6dc941eea5fae6f9bc36e5a53d19/opentelemetry_instrumentation_psycopg2-0.57b0-py3-none-any.whl", hash = "sha256:94fdde02b7451c8e85d43b4b9dd13a34fee96ffd43324d1b3567f47d2903b99f", size = 10721 }, ] [[package]] @@ -2277,9 +2548,9 @@ dependencies = [ { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/e1/01f5c28a60ffbc4c04946ad35bc8bf16382d333e41afaa042b31c35364b9/opentelemetry_instrumentation_requests-0.57b0.tar.gz", hash = "sha256:193bd3fd1f14737721876fb1952dffc7d43795586118df633a91ecd9057446ff", size = 15182, upload-time = "2025-07-29T15:43:11.812Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/e1/01f5c28a60ffbc4c04946ad35bc8bf16382d333e41afaa042b31c35364b9/opentelemetry_instrumentation_requests-0.57b0.tar.gz", hash = "sha256:193bd3fd1f14737721876fb1952dffc7d43795586118df633a91ecd9057446ff", size = 15182 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/7d/40144701fa22521e3b3fce23e2f0a5684a9385c90b119b70e7598b3cb607/opentelemetry_instrumentation_requests-0.57b0-py3-none-any.whl", hash = "sha256:66a576ac8080724ddc8a14c39d16bb5f430991bd504fdbea844c7a063f555971", size = 12966, upload-time = "2025-07-29T15:42:24.608Z" }, + { url = "https://files.pythonhosted.org/packages/b5/7d/40144701fa22521e3b3fce23e2f0a5684a9385c90b119b70e7598b3cb607/opentelemetry_instrumentation_requests-0.57b0-py3-none-any.whl", hash = "sha256:66a576ac8080724ddc8a14c39d16bb5f430991bd504fdbea844c7a063f555971", size = 12966 }, ] [[package]] @@ -2292,9 +2563,9 @@ dependencies = [ { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/a5/9d400dd978ac5e81356fe8435ca264e140a7d4cf77a88db43791d62311d5/opentelemetry_instrumentation_urllib-0.57b0.tar.gz", hash = "sha256:657225ceae8bb52b67bd5c26dcb8a33f0efb041f1baea4c59dbd1adbc63a4162", size = 13929, upload-time = "2025-07-29T15:43:16.498Z" } +sdist = { url = "https://files.pythonhosted.org/packages/86/a5/9d400dd978ac5e81356fe8435ca264e140a7d4cf77a88db43791d62311d5/opentelemetry_instrumentation_urllib-0.57b0.tar.gz", hash = "sha256:657225ceae8bb52b67bd5c26dcb8a33f0efb041f1baea4c59dbd1adbc63a4162", size = 13929 } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/47/3c9535a68b9dd125eb6a25c086984e5cee7285e4f36bfa37eeb40e95d2b5/opentelemetry_instrumentation_urllib-0.57b0-py3-none-any.whl", hash = "sha256:bb3a01172109a6f56bfcc38ea83b9d4a61c4c2cac6b9a190e757063daadf545c", size = 12671, upload-time = "2025-07-29T15:42:34.561Z" }, + { url = "https://files.pythonhosted.org/packages/79/47/3c9535a68b9dd125eb6a25c086984e5cee7285e4f36bfa37eeb40e95d2b5/opentelemetry_instrumentation_urllib-0.57b0-py3-none-any.whl", hash = "sha256:bb3a01172109a6f56bfcc38ea83b9d4a61c4c2cac6b9a190e757063daadf545c", size = 12671 }, ] [[package]] @@ -2308,9 +2579,9 @@ dependencies = [ { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "wrapt", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/2d/c241e9716c94704dbddf64e2c7367b57642425455befdbc622936bec78e9/opentelemetry_instrumentation_urllib3-0.57b0.tar.gz", hash = "sha256:f49d8c3d1d81ae56304a08b14a7f564d250733ed75cd2210ccef815b5af2eea1", size = 15790, upload-time = "2025-07-29T15:43:17.05Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/2d/c241e9716c94704dbddf64e2c7367b57642425455befdbc622936bec78e9/opentelemetry_instrumentation_urllib3-0.57b0.tar.gz", hash = "sha256:f49d8c3d1d81ae56304a08b14a7f564d250733ed75cd2210ccef815b5af2eea1", size = 15790 } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/0e/a5467ab57d815caa58cbabb3a7f3906c3718c599221ac770482d13187306/opentelemetry_instrumentation_urllib3-0.57b0-py3-none-any.whl", hash = "sha256:337ecac6df3ff92026b51c64df7dd4a3fff52f2dc96036ea9371670243bf83c6", size = 13186, upload-time = "2025-07-29T15:42:35.775Z" }, + { url = "https://files.pythonhosted.org/packages/06/0e/a5467ab57d815caa58cbabb3a7f3906c3718c599221ac770482d13187306/opentelemetry_instrumentation_urllib3-0.57b0-py3-none-any.whl", hash = "sha256:337ecac6df3ff92026b51c64df7dd4a3fff52f2dc96036ea9371670243bf83c6", size = 13186 }, ] [[package]] @@ -2323,9 +2594,9 @@ dependencies = [ { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "opentelemetry-util-http", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/3f/d1ab49d68f2f6ebbe3c2fa5ff609ee5603a9cc68915203c454afb3a38d5b/opentelemetry_instrumentation_wsgi-0.57b0.tar.gz", hash = "sha256:d7e16b3b87930c30fc4c1bbc8b58c5dd6eefade493a3a5e7343bc24d572bc5b7", size = 18376, upload-time = "2025-07-29T15:43:17.683Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/3f/d1ab49d68f2f6ebbe3c2fa5ff609ee5603a9cc68915203c454afb3a38d5b/opentelemetry_instrumentation_wsgi-0.57b0.tar.gz", hash = "sha256:d7e16b3b87930c30fc4c1bbc8b58c5dd6eefade493a3a5e7343bc24d572bc5b7", size = 18376 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/0c/7760f9e14f4f8128e4880b4fd5f232ef4eb00cb29ee560c972dbf7801369/opentelemetry_instrumentation_wsgi-0.57b0-py3-none-any.whl", hash = "sha256:b9cf0c6e61489f7503fc17ef04d169bd214e7a825650ee492f5d2b4d73b17b54", size = 14450, upload-time = "2025-07-29T15:42:37.351Z" }, + { url = "https://files.pythonhosted.org/packages/1f/0c/7760f9e14f4f8128e4880b4fd5f232ef4eb00cb29ee560c972dbf7801369/opentelemetry_instrumentation_wsgi-0.57b0-py3-none-any.whl", hash = "sha256:b9cf0c6e61489f7503fc17ef04d169bd214e7a825650ee492f5d2b4d73b17b54", size = 14450 }, ] [[package]] @@ -2335,9 +2606,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fd/02/f6556142301d136e3b7e95ab8ea6a5d9dc28d879a99f3dd673b5f97dca06/opentelemetry_proto-1.36.0.tar.gz", hash = "sha256:0f10b3c72f74c91e0764a5ec88fd8f1c368ea5d9c64639fb455e2854ef87dd2f", size = 46152, upload-time = "2025-07-29T15:12:15.717Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/02/f6556142301d136e3b7e95ab8ea6a5d9dc28d879a99f3dd673b5f97dca06/opentelemetry_proto-1.36.0.tar.gz", hash = "sha256:0f10b3c72f74c91e0764a5ec88fd8f1c368ea5d9c64639fb455e2854ef87dd2f", size = 46152 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/57/3361e06136225be8180e879199caea520f38026f8071366241ac458beb8d/opentelemetry_proto-1.36.0-py3-none-any.whl", hash = "sha256:151b3bf73a09f94afc658497cf77d45a565606f62ce0c17acb08cd9937ca206e", size = 72537, upload-time = "2025-07-29T15:12:02.243Z" }, + { url = "https://files.pythonhosted.org/packages/b3/57/3361e06136225be8180e879199caea520f38026f8071366241ac458beb8d/opentelemetry_proto-1.36.0-py3-none-any.whl", hash = "sha256:151b3bf73a09f94afc658497cf77d45a565606f62ce0c17acb08cd9937ca206e", size = 72537 }, ] [[package]] @@ -2347,9 +2618,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-sdk", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/67/e4/0d359d48d03d447225b30c3dd889d5d454e3b413763ff721f9b0e4ac2e59/opentelemetry_resource_detector_azure-0.1.5.tar.gz", hash = "sha256:e0ba658a87c69eebc806e75398cd0e9f68a8898ea62de99bc1b7083136403710", size = 11503, upload-time = "2024-05-16T21:54:58.994Z" } +sdist = { url = "https://files.pythonhosted.org/packages/67/e4/0d359d48d03d447225b30c3dd889d5d454e3b413763ff721f9b0e4ac2e59/opentelemetry_resource_detector_azure-0.1.5.tar.gz", hash = "sha256:e0ba658a87c69eebc806e75398cd0e9f68a8898ea62de99bc1b7083136403710", size = 11503 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/ae/c26d8da88ba2e438e9653a408b0c2ad6f17267801250a8f3cc6405a93a72/opentelemetry_resource_detector_azure-0.1.5-py3-none-any.whl", hash = "sha256:4dcc5d54ab5c3b11226af39509bc98979a8b9e0f8a24c1b888783755d3bf00eb", size = 14252, upload-time = "2024-05-16T21:54:57.208Z" }, + { url = "https://files.pythonhosted.org/packages/c3/ae/c26d8da88ba2e438e9653a408b0c2ad6f17267801250a8f3cc6405a93a72/opentelemetry_resource_detector_azure-0.1.5-py3-none-any.whl", hash = "sha256:4dcc5d54ab5c3b11226af39509bc98979a8b9e0f8a24c1b888783755d3bf00eb", size = 14252 }, ] [[package]] @@ -2361,9 +2632,9 @@ dependencies = [ { name = "opentelemetry-semantic-conventions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/85/8567a966b85a2d3f971c4d42f781c305b2b91c043724fa08fd37d158e9dc/opentelemetry_sdk-1.36.0.tar.gz", hash = "sha256:19c8c81599f51b71670661ff7495c905d8fdf6976e41622d5245b791b06fa581", size = 162557, upload-time = "2025-07-29T15:12:16.76Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/85/8567a966b85a2d3f971c4d42f781c305b2b91c043724fa08fd37d158e9dc/opentelemetry_sdk-1.36.0.tar.gz", hash = "sha256:19c8c81599f51b71670661ff7495c905d8fdf6976e41622d5245b791b06fa581", size = 162557 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/59/7bed362ad1137ba5886dac8439e84cd2df6d087be7c09574ece47ae9b22c/opentelemetry_sdk-1.36.0-py3-none-any.whl", hash = "sha256:19fe048b42e98c5c1ffe85b569b7073576ad4ce0bcb6e9b4c6a39e890a6c45fb", size = 119995, upload-time = "2025-07-29T15:12:03.181Z" }, + { url = "https://files.pythonhosted.org/packages/0b/59/7bed362ad1137ba5886dac8439e84cd2df6d087be7c09574ece47ae9b22c/opentelemetry_sdk-1.36.0-py3-none-any.whl", hash = "sha256:19fe048b42e98c5c1ffe85b569b7073576ad4ce0bcb6e9b4c6a39e890a6c45fb", size = 119995 }, ] [[package]] @@ -2374,63 +2645,63 @@ dependencies = [ { name = "opentelemetry-api", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7e/31/67dfa252ee88476a29200b0255bda8dfc2cf07b56ad66dc9a6221f7dc787/opentelemetry_semantic_conventions-0.57b0.tar.gz", hash = "sha256:609a4a79c7891b4620d64c7aac6898f872d790d75f22019913a660756f27ff32", size = 124225, upload-time = "2025-07-29T15:12:17.873Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/31/67dfa252ee88476a29200b0255bda8dfc2cf07b56ad66dc9a6221f7dc787/opentelemetry_semantic_conventions-0.57b0.tar.gz", hash = "sha256:609a4a79c7891b4620d64c7aac6898f872d790d75f22019913a660756f27ff32", size = 124225 } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/75/7d591371c6c39c73de5ce5da5a2cc7b72d1d1cd3f8f4638f553c01c37b11/opentelemetry_semantic_conventions-0.57b0-py3-none-any.whl", hash = "sha256:757f7e76293294f124c827e514c2a3144f191ef175b069ce8d1211e1e38e9e78", size = 201627, upload-time = "2025-07-29T15:12:04.174Z" }, + { url = "https://files.pythonhosted.org/packages/05/75/7d591371c6c39c73de5ce5da5a2cc7b72d1d1cd3f8f4638f553c01c37b11/opentelemetry_semantic_conventions-0.57b0-py3-none-any.whl", hash = "sha256:757f7e76293294f124c827e514c2a3144f191ef175b069ce8d1211e1e38e9e78", size = 201627 }, ] [[package]] name = "opentelemetry-semantic-conventions-ai" version = "0.4.13" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/e6/40b59eda51ac47009fb47afcdf37c6938594a0bd7f3b9fadcbc6058248e3/opentelemetry_semantic_conventions_ai-0.4.13.tar.gz", hash = "sha256:94efa9fb4ffac18c45f54a3a338ffeb7eedb7e1bb4d147786e77202e159f0036", size = 5368, upload-time = "2025-08-22T10:14:17.387Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e6/40b59eda51ac47009fb47afcdf37c6938594a0bd7f3b9fadcbc6058248e3/opentelemetry_semantic_conventions_ai-0.4.13.tar.gz", hash = "sha256:94efa9fb4ffac18c45f54a3a338ffeb7eedb7e1bb4d147786e77202e159f0036", size = 5368 } wheels = [ - { url = "https://files.pythonhosted.org/packages/35/b5/cf25da2218910f0d6cdf7f876a06bed118c4969eacaf60a887cbaef44f44/opentelemetry_semantic_conventions_ai-0.4.13-py3-none-any.whl", hash = "sha256:883a30a6bb5deaec0d646912b5f9f6dcbb9f6f72557b73d0f2560bf25d13e2d5", size = 6080, upload-time = "2025-08-22T10:14:16.477Z" }, + { url = "https://files.pythonhosted.org/packages/35/b5/cf25da2218910f0d6cdf7f876a06bed118c4969eacaf60a887cbaef44f44/opentelemetry_semantic_conventions_ai-0.4.13-py3-none-any.whl", hash = "sha256:883a30a6bb5deaec0d646912b5f9f6dcbb9f6f72557b73d0f2560bf25d13e2d5", size = 6080 }, ] [[package]] name = "opentelemetry-util-http" version = "0.57b0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9b/1b/6229c45445e08e798fa825f5376f6d6a4211d29052a4088eed6d577fa653/opentelemetry_util_http-0.57b0.tar.gz", hash = "sha256:f7417595ead0eb42ed1863ec9b2f839fc740368cd7bbbfc1d0a47bc1ab0aba11", size = 9405, upload-time = "2025-07-29T15:43:19.916Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/1b/6229c45445e08e798fa825f5376f6d6a4211d29052a4088eed6d577fa653/opentelemetry_util_http-0.57b0.tar.gz", hash = "sha256:f7417595ead0eb42ed1863ec9b2f839fc740368cd7bbbfc1d0a47bc1ab0aba11", size = 9405 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/a6/b98d508d189b9c208f5978d0906141747d7e6df7c7cafec03657ed1ed559/opentelemetry_util_http-0.57b0-py3-none-any.whl", hash = "sha256:e54c0df5543951e471c3d694f85474977cd5765a3b7654398c83bab3d2ffb8e9", size = 7643, upload-time = "2025-07-29T15:42:41.744Z" }, + { url = "https://files.pythonhosted.org/packages/0b/a6/b98d508d189b9c208f5978d0906141747d7e6df7c7cafec03657ed1ed559/opentelemetry_util_http-0.57b0-py3-none-any.whl", hash = "sha256:e54c0df5543951e471c3d694f85474977cd5765a3b7654398c83bab3d2ffb8e9", size = 7643 }, ] [[package]] name = "packaging" version = "25.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, ] [[package]] name = "parso" version = "0.8.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205 } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" }, + { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668 }, ] [[package]] name = "pastel" version = "0.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/76/f1/4594f5e0fcddb6953e5b8fe00da8c317b8b41b547e2b3ae2da7512943c62/pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d", size = 7555, upload-time = "2020-09-16T19:21:12.43Z" } +sdist = { url = "https://files.pythonhosted.org/packages/76/f1/4594f5e0fcddb6953e5b8fe00da8c317b8b41b547e2b3ae2da7512943c62/pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d", size = 7555 } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/18/a8444036c6dd65ba3624c63b734d3ba95ba63ace513078e1580590075d21/pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364", size = 5955, upload-time = "2020-09-16T19:21:11.409Z" }, + { url = "https://files.pythonhosted.org/packages/aa/18/a8444036c6dd65ba3624c63b734d3ba95ba63ace513078e1580590075d21/pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364", size = 5955 }, ] [[package]] name = "pathspec" version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, ] [[package]] @@ -2440,27 +2711,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ptyprocess", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, ] [[package]] name = "platformdirs" version = "4.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } +sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634 } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, + { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654 }, ] [[package]] name = "pluggy" version = "1.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412 } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, ] [[package]] @@ -2472,9 +2743,38 @@ dependencies = [ { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "tomli", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/f2/273fe54a78dc5c6c8dd63db71f5a6ceb95e4648516b5aeaeff4bde804e44/poethepoet-0.37.0.tar.gz", hash = "sha256:73edf458707c674a079baa46802e21455bda3a7f82a408e58c31b9f4fe8e933d", size = 68570, upload-time = "2025-08-11T18:00:29.103Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/f2/273fe54a78dc5c6c8dd63db71f5a6ceb95e4648516b5aeaeff4bde804e44/poethepoet-0.37.0.tar.gz", hash = "sha256:73edf458707c674a079baa46802e21455bda3a7f82a408e58c31b9f4fe8e933d", size = 68570 } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/1b/5337af1a6a478d25a3e3c56b9b4b42b0a160314e02f4a0498d5322c8dac4/poethepoet-0.37.0-py3-none-any.whl", hash = "sha256:861790276315abcc8df1b4bd60e28c3d48a06db273edd3092f3c94e1a46e5e22", size = 90062, upload-time = "2025-08-11T18:00:27.595Z" }, + { url = "https://files.pythonhosted.org/packages/92/1b/5337af1a6a478d25a3e3c56b9b4b42b0a160314e02f4a0498d5322c8dac4/poethepoet-0.37.0-py3-none-any.whl", hash = "sha256:861790276315abcc8df1b4bd60e28c3d48a06db273edd3092f3c94e1a46e5e22", size = 90062 }, +] + +[[package]] +name = "portalocker" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywin32", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/77/65b857a69ed876e1951e88aaba60f5ce6120c33703f7cb61a3c894b8c1b6/portalocker-3.2.0.tar.gz", hash = "sha256:1f3002956a54a8c3730586c5c77bf18fae4149e07eaf1c29fc3faf4d5a3f89ac", size = 95644 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/a6/38c8e2f318bf67d338f4d629e93b0b4b9af331f455f0390ea8ce4a099b26/portalocker-3.2.0-py3-none-any.whl", hash = "sha256:3cdc5f565312224bc570c49337bd21428bba0ef363bbcf58b9ef4a9f11779968", size = 22424 }, +] + +[[package]] +name = "posthog" +version = "6.7.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backoff", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "distro", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "python-dateutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "six", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/40/d7f585e09e47f492ebaeb8048a8e2ce5d9f49a3896856a7a975cbc1484fa/posthog-6.7.4.tar.gz", hash = "sha256:2bfa74f321ac18efe4a48a256d62034a506ca95477af7efa32292ed488a742c5", size = 118209 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/95/e795059ef73d480a7f11f1be201087f65207509525920897fb514a04914c/posthog-6.7.4-py3-none-any.whl", hash = "sha256:7f1872c53ec7e9a29b088a5a1ad03fa1be3b871d10d70c8bf6c2dafb91beaac5", size = 136409 }, ] [[package]] @@ -2488,9 +2788,9 @@ dependencies = [ { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "virtualenv", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792, upload-time = "2025-08-09T18:56:14.651Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965, upload-time = "2025-08-09T18:56:13.192Z" }, + { url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965 }, ] [[package]] @@ -2500,154 +2800,154 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wcwidth", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198 } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431 }, ] [[package]] name = "propcache" version = "0.3.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168", size = 44139, upload-time = "2025-06-09T22:56:06.081Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168", size = 44139 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/14/510deed325e262afeb8b360043c5d7c960da7d3ecd6d6f9496c9c56dc7f4/propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770", size = 73178, upload-time = "2025-06-09T22:53:40.126Z" }, - { url = "https://files.pythonhosted.org/packages/cd/4e/ad52a7925ff01c1325653a730c7ec3175a23f948f08626a534133427dcff/propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3", size = 43133, upload-time = "2025-06-09T22:53:41.965Z" }, - { url = "https://files.pythonhosted.org/packages/63/7c/e9399ba5da7780871db4eac178e9c2e204c23dd3e7d32df202092a1ed400/propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3", size = 43039, upload-time = "2025-06-09T22:53:43.268Z" }, - { url = "https://files.pythonhosted.org/packages/22/e1/58da211eb8fdc6fc854002387d38f415a6ca5f5c67c1315b204a5d3e9d7a/propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e", size = 201903, upload-time = "2025-06-09T22:53:44.872Z" }, - { url = "https://files.pythonhosted.org/packages/c4/0a/550ea0f52aac455cb90111c8bab995208443e46d925e51e2f6ebdf869525/propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220", size = 213362, upload-time = "2025-06-09T22:53:46.707Z" }, - { url = "https://files.pythonhosted.org/packages/5a/af/9893b7d878deda9bb69fcf54600b247fba7317761b7db11fede6e0f28bd0/propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb", size = 210525, upload-time = "2025-06-09T22:53:48.547Z" }, - { url = "https://files.pythonhosted.org/packages/7c/bb/38fd08b278ca85cde36d848091ad2b45954bc5f15cce494bb300b9285831/propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614", size = 198283, upload-time = "2025-06-09T22:53:50.067Z" }, - { url = "https://files.pythonhosted.org/packages/78/8c/9fe55bd01d362bafb413dfe508c48753111a1e269737fa143ba85693592c/propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50", size = 191872, upload-time = "2025-06-09T22:53:51.438Z" }, - { url = "https://files.pythonhosted.org/packages/54/14/4701c33852937a22584e08abb531d654c8bcf7948a8f87ad0a4822394147/propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339", size = 199452, upload-time = "2025-06-09T22:53:53.229Z" }, - { url = "https://files.pythonhosted.org/packages/16/44/447f2253d859602095356007657ee535e0093215ea0b3d1d6a41d16e5201/propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0", size = 191567, upload-time = "2025-06-09T22:53:54.541Z" }, - { url = "https://files.pythonhosted.org/packages/f2/b3/e4756258749bb2d3b46defcff606a2f47410bab82be5824a67e84015b267/propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2", size = 193015, upload-time = "2025-06-09T22:53:56.44Z" }, - { url = "https://files.pythonhosted.org/packages/1e/df/e6d3c7574233164b6330b9fd697beeac402afd367280e6dc377bb99b43d9/propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7", size = 204660, upload-time = "2025-06-09T22:53:57.839Z" }, - { url = "https://files.pythonhosted.org/packages/b2/53/e4d31dd5170b4a0e2e6b730f2385a96410633b4833dc25fe5dffd1f73294/propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b", size = 206105, upload-time = "2025-06-09T22:53:59.638Z" }, - { url = "https://files.pythonhosted.org/packages/7f/fe/74d54cf9fbe2a20ff786e5f7afcfde446588f0cf15fb2daacfbc267b866c/propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c", size = 196980, upload-time = "2025-06-09T22:54:01.071Z" }, - { url = "https://files.pythonhosted.org/packages/22/ec/c469c9d59dada8a7679625e0440b544fe72e99311a4679c279562051f6fc/propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70", size = 37679, upload-time = "2025-06-09T22:54:03.003Z" }, - { url = "https://files.pythonhosted.org/packages/38/35/07a471371ac89d418f8d0b699c75ea6dca2041fbda360823de21f6a9ce0a/propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9", size = 41459, upload-time = "2025-06-09T22:54:04.134Z" }, - { url = "https://files.pythonhosted.org/packages/80/8d/e8b436717ab9c2cfc23b116d2c297305aa4cd8339172a456d61ebf5669b8/propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be", size = 74207, upload-time = "2025-06-09T22:54:05.399Z" }, - { url = "https://files.pythonhosted.org/packages/d6/29/1e34000e9766d112171764b9fa3226fa0153ab565d0c242c70e9945318a7/propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f", size = 43648, upload-time = "2025-06-09T22:54:08.023Z" }, - { url = "https://files.pythonhosted.org/packages/46/92/1ad5af0df781e76988897da39b5f086c2bf0f028b7f9bd1f409bb05b6874/propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9", size = 43496, upload-time = "2025-06-09T22:54:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/ce/e96392460f9fb68461fabab3e095cb00c8ddf901205be4eae5ce246e5b7e/propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf", size = 217288, upload-time = "2025-06-09T22:54:10.466Z" }, - { url = "https://files.pythonhosted.org/packages/c5/2a/866726ea345299f7ceefc861a5e782b045545ae6940851930a6adaf1fca6/propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9", size = 227456, upload-time = "2025-06-09T22:54:11.828Z" }, - { url = "https://files.pythonhosted.org/packages/de/03/07d992ccb6d930398689187e1b3c718339a1c06b8b145a8d9650e4726166/propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66", size = 225429, upload-time = "2025-06-09T22:54:13.823Z" }, - { url = "https://files.pythonhosted.org/packages/5d/e6/116ba39448753b1330f48ab8ba927dcd6cf0baea8a0ccbc512dfb49ba670/propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df", size = 213472, upload-time = "2025-06-09T22:54:15.232Z" }, - { url = "https://files.pythonhosted.org/packages/a6/85/f01f5d97e54e428885a5497ccf7f54404cbb4f906688a1690cd51bf597dc/propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2", size = 204480, upload-time = "2025-06-09T22:54:17.104Z" }, - { url = "https://files.pythonhosted.org/packages/e3/79/7bf5ab9033b8b8194cc3f7cf1aaa0e9c3256320726f64a3e1f113a812dce/propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7", size = 214530, upload-time = "2025-06-09T22:54:18.512Z" }, - { url = "https://files.pythonhosted.org/packages/31/0b/bd3e0c00509b609317df4a18e6b05a450ef2d9a963e1d8bc9c9415d86f30/propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95", size = 205230, upload-time = "2025-06-09T22:54:19.947Z" }, - { url = "https://files.pythonhosted.org/packages/7a/23/fae0ff9b54b0de4e819bbe559508da132d5683c32d84d0dc2ccce3563ed4/propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e", size = 206754, upload-time = "2025-06-09T22:54:21.716Z" }, - { url = "https://files.pythonhosted.org/packages/b7/7f/ad6a3c22630aaa5f618b4dc3c3598974a72abb4c18e45a50b3cdd091eb2f/propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e", size = 218430, upload-time = "2025-06-09T22:54:23.17Z" }, - { url = "https://files.pythonhosted.org/packages/5b/2c/ba4f1c0e8a4b4c75910742f0d333759d441f65a1c7f34683b4a74c0ee015/propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf", size = 223884, upload-time = "2025-06-09T22:54:25.539Z" }, - { url = "https://files.pythonhosted.org/packages/88/e4/ebe30fc399e98572019eee82ad0caf512401661985cbd3da5e3140ffa1b0/propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e", size = 211480, upload-time = "2025-06-09T22:54:26.892Z" }, - { url = "https://files.pythonhosted.org/packages/96/0a/7d5260b914e01d1d0906f7f38af101f8d8ed0dc47426219eeaf05e8ea7c2/propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897", size = 37757, upload-time = "2025-06-09T22:54:28.241Z" }, - { url = "https://files.pythonhosted.org/packages/e1/2d/89fe4489a884bc0da0c3278c552bd4ffe06a1ace559db5ef02ef24ab446b/propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39", size = 41500, upload-time = "2025-06-09T22:54:29.4Z" }, - { url = "https://files.pythonhosted.org/packages/a8/42/9ca01b0a6f48e81615dca4765a8f1dd2c057e0540f6116a27dc5ee01dfb6/propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10", size = 73674, upload-time = "2025-06-09T22:54:30.551Z" }, - { url = "https://files.pythonhosted.org/packages/af/6e/21293133beb550f9c901bbece755d582bfaf2176bee4774000bd4dd41884/propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154", size = 43570, upload-time = "2025-06-09T22:54:32.296Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c8/0393a0a3a2b8760eb3bde3c147f62b20044f0ddac81e9d6ed7318ec0d852/propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615", size = 43094, upload-time = "2025-06-09T22:54:33.929Z" }, - { url = "https://files.pythonhosted.org/packages/37/2c/489afe311a690399d04a3e03b069225670c1d489eb7b044a566511c1c498/propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db", size = 226958, upload-time = "2025-06-09T22:54:35.186Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ca/63b520d2f3d418c968bf596839ae26cf7f87bead026b6192d4da6a08c467/propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1", size = 234894, upload-time = "2025-06-09T22:54:36.708Z" }, - { url = "https://files.pythonhosted.org/packages/11/60/1d0ed6fff455a028d678df30cc28dcee7af77fa2b0e6962ce1df95c9a2a9/propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c", size = 233672, upload-time = "2025-06-09T22:54:38.062Z" }, - { url = "https://files.pythonhosted.org/packages/37/7c/54fd5301ef38505ab235d98827207176a5c9b2aa61939b10a460ca53e123/propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67", size = 224395, upload-time = "2025-06-09T22:54:39.634Z" }, - { url = "https://files.pythonhosted.org/packages/ee/1a/89a40e0846f5de05fdc6779883bf46ba980e6df4d2ff8fb02643de126592/propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b", size = 212510, upload-time = "2025-06-09T22:54:41.565Z" }, - { url = "https://files.pythonhosted.org/packages/5e/33/ca98368586c9566a6b8d5ef66e30484f8da84c0aac3f2d9aec6d31a11bd5/propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8", size = 222949, upload-time = "2025-06-09T22:54:43.038Z" }, - { url = "https://files.pythonhosted.org/packages/ba/11/ace870d0aafe443b33b2f0b7efdb872b7c3abd505bfb4890716ad7865e9d/propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251", size = 217258, upload-time = "2025-06-09T22:54:44.376Z" }, - { url = "https://files.pythonhosted.org/packages/5b/d2/86fd6f7adffcfc74b42c10a6b7db721d1d9ca1055c45d39a1a8f2a740a21/propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474", size = 213036, upload-time = "2025-06-09T22:54:46.243Z" }, - { url = "https://files.pythonhosted.org/packages/07/94/2d7d1e328f45ff34a0a284cf5a2847013701e24c2a53117e7c280a4316b3/propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535", size = 227684, upload-time = "2025-06-09T22:54:47.63Z" }, - { url = "https://files.pythonhosted.org/packages/b7/05/37ae63a0087677e90b1d14710e532ff104d44bc1efa3b3970fff99b891dc/propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06", size = 234562, upload-time = "2025-06-09T22:54:48.982Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7c/3f539fcae630408d0bd8bf3208b9a647ccad10976eda62402a80adf8fc34/propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1", size = 222142, upload-time = "2025-06-09T22:54:50.424Z" }, - { url = "https://files.pythonhosted.org/packages/7c/d2/34b9eac8c35f79f8a962546b3e97e9d4b990c420ee66ac8255d5d9611648/propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1", size = 37711, upload-time = "2025-06-09T22:54:52.072Z" }, - { url = "https://files.pythonhosted.org/packages/19/61/d582be5d226cf79071681d1b46b848d6cb03d7b70af7063e33a2787eaa03/propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c", size = 41479, upload-time = "2025-06-09T22:54:53.234Z" }, - { url = "https://files.pythonhosted.org/packages/dc/d1/8c747fafa558c603c4ca19d8e20b288aa0c7cda74e9402f50f31eb65267e/propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945", size = 71286, upload-time = "2025-06-09T22:54:54.369Z" }, - { url = "https://files.pythonhosted.org/packages/61/99/d606cb7986b60d89c36de8a85d58764323b3a5ff07770a99d8e993b3fa73/propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252", size = 42425, upload-time = "2025-06-09T22:54:55.642Z" }, - { url = "https://files.pythonhosted.org/packages/8c/96/ef98f91bbb42b79e9bb82bdd348b255eb9d65f14dbbe3b1594644c4073f7/propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f", size = 41846, upload-time = "2025-06-09T22:54:57.246Z" }, - { url = "https://files.pythonhosted.org/packages/5b/ad/3f0f9a705fb630d175146cd7b1d2bf5555c9beaed54e94132b21aac098a6/propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33", size = 208871, upload-time = "2025-06-09T22:54:58.975Z" }, - { url = "https://files.pythonhosted.org/packages/3a/38/2085cda93d2c8b6ec3e92af2c89489a36a5886b712a34ab25de9fbca7992/propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e", size = 215720, upload-time = "2025-06-09T22:55:00.471Z" }, - { url = "https://files.pythonhosted.org/packages/61/c1/d72ea2dc83ac7f2c8e182786ab0fc2c7bd123a1ff9b7975bee671866fe5f/propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1", size = 215203, upload-time = "2025-06-09T22:55:01.834Z" }, - { url = "https://files.pythonhosted.org/packages/af/81/b324c44ae60c56ef12007105f1460d5c304b0626ab0cc6b07c8f2a9aa0b8/propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3", size = 206365, upload-time = "2025-06-09T22:55:03.199Z" }, - { url = "https://files.pythonhosted.org/packages/09/73/88549128bb89e66d2aff242488f62869014ae092db63ccea53c1cc75a81d/propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1", size = 196016, upload-time = "2025-06-09T22:55:04.518Z" }, - { url = "https://files.pythonhosted.org/packages/b9/3f/3bdd14e737d145114a5eb83cb172903afba7242f67c5877f9909a20d948d/propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6", size = 205596, upload-time = "2025-06-09T22:55:05.942Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ca/2f4aa819c357d3107c3763d7ef42c03980f9ed5c48c82e01e25945d437c1/propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387", size = 200977, upload-time = "2025-06-09T22:55:07.792Z" }, - { url = "https://files.pythonhosted.org/packages/cd/4a/e65276c7477533c59085251ae88505caf6831c0e85ff8b2e31ebcbb949b1/propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4", size = 197220, upload-time = "2025-06-09T22:55:09.173Z" }, - { url = "https://files.pythonhosted.org/packages/7c/54/fc7152e517cf5578278b242396ce4d4b36795423988ef39bb8cd5bf274c8/propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88", size = 210642, upload-time = "2025-06-09T22:55:10.62Z" }, - { url = "https://files.pythonhosted.org/packages/b9/80/abeb4a896d2767bf5f1ea7b92eb7be6a5330645bd7fb844049c0e4045d9d/propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206", size = 212789, upload-time = "2025-06-09T22:55:12.029Z" }, - { url = "https://files.pythonhosted.org/packages/b3/db/ea12a49aa7b2b6d68a5da8293dcf50068d48d088100ac016ad92a6a780e6/propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43", size = 205880, upload-time = "2025-06-09T22:55:13.45Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e5/9076a0bbbfb65d1198007059c65639dfd56266cf8e477a9707e4b1999ff4/propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02", size = 37220, upload-time = "2025-06-09T22:55:15.284Z" }, - { url = "https://files.pythonhosted.org/packages/d3/f5/b369e026b09a26cd77aa88d8fffd69141d2ae00a2abaaf5380d2603f4b7f/propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05", size = 40678, upload-time = "2025-06-09T22:55:16.445Z" }, - { url = "https://files.pythonhosted.org/packages/a4/3a/6ece377b55544941a08d03581c7bc400a3c8cd3c2865900a68d5de79e21f/propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b", size = 76560, upload-time = "2025-06-09T22:55:17.598Z" }, - { url = "https://files.pythonhosted.org/packages/0c/da/64a2bb16418740fa634b0e9c3d29edff1db07f56d3546ca2d86ddf0305e1/propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0", size = 44676, upload-time = "2025-06-09T22:55:18.922Z" }, - { url = "https://files.pythonhosted.org/packages/36/7b/f025e06ea51cb72c52fb87e9b395cced02786610b60a3ed51da8af017170/propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e", size = 44701, upload-time = "2025-06-09T22:55:20.106Z" }, - { url = "https://files.pythonhosted.org/packages/a4/00/faa1b1b7c3b74fc277f8642f32a4c72ba1d7b2de36d7cdfb676db7f4303e/propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28", size = 276934, upload-time = "2025-06-09T22:55:21.5Z" }, - { url = "https://files.pythonhosted.org/packages/74/ab/935beb6f1756e0476a4d5938ff44bf0d13a055fed880caf93859b4f1baf4/propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a", size = 278316, upload-time = "2025-06-09T22:55:22.918Z" }, - { url = "https://files.pythonhosted.org/packages/f8/9d/994a5c1ce4389610838d1caec74bdf0e98b306c70314d46dbe4fcf21a3e2/propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c", size = 282619, upload-time = "2025-06-09T22:55:24.651Z" }, - { url = "https://files.pythonhosted.org/packages/2b/00/a10afce3d1ed0287cef2e09506d3be9822513f2c1e96457ee369adb9a6cd/propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725", size = 265896, upload-time = "2025-06-09T22:55:26.049Z" }, - { url = "https://files.pythonhosted.org/packages/2e/a8/2aa6716ffa566ca57c749edb909ad27884680887d68517e4be41b02299f3/propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892", size = 252111, upload-time = "2025-06-09T22:55:27.381Z" }, - { url = "https://files.pythonhosted.org/packages/36/4f/345ca9183b85ac29c8694b0941f7484bf419c7f0fea2d1e386b4f7893eed/propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44", size = 268334, upload-time = "2025-06-09T22:55:28.747Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ca/fcd54f78b59e3f97b3b9715501e3147f5340167733d27db423aa321e7148/propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe", size = 255026, upload-time = "2025-06-09T22:55:30.184Z" }, - { url = "https://files.pythonhosted.org/packages/8b/95/8e6a6bbbd78ac89c30c225210a5c687790e532ba4088afb8c0445b77ef37/propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81", size = 250724, upload-time = "2025-06-09T22:55:31.646Z" }, - { url = "https://files.pythonhosted.org/packages/ee/b0/0dd03616142baba28e8b2d14ce5df6631b4673850a3d4f9c0f9dd714a404/propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba", size = 268868, upload-time = "2025-06-09T22:55:33.209Z" }, - { url = "https://files.pythonhosted.org/packages/c5/98/2c12407a7e4fbacd94ddd32f3b1e3d5231e77c30ef7162b12a60e2dd5ce3/propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770", size = 271322, upload-time = "2025-06-09T22:55:35.065Z" }, - { url = "https://files.pythonhosted.org/packages/35/91/9cb56efbb428b006bb85db28591e40b7736847b8331d43fe335acf95f6c8/propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330", size = 265778, upload-time = "2025-06-09T22:55:36.45Z" }, - { url = "https://files.pythonhosted.org/packages/9a/4c/b0fe775a2bdd01e176b14b574be679d84fc83958335790f7c9a686c1f468/propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394", size = 41175, upload-time = "2025-06-09T22:55:38.436Z" }, - { url = "https://files.pythonhosted.org/packages/a4/ff/47f08595e3d9b5e149c150f88d9714574f1a7cbd89fe2817158a952674bf/propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198", size = 44857, upload-time = "2025-06-09T22:55:39.687Z" }, - { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663, upload-time = "2025-06-09T22:56:04.484Z" }, + { url = "https://files.pythonhosted.org/packages/ab/14/510deed325e262afeb8b360043c5d7c960da7d3ecd6d6f9496c9c56dc7f4/propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770", size = 73178 }, + { url = "https://files.pythonhosted.org/packages/cd/4e/ad52a7925ff01c1325653a730c7ec3175a23f948f08626a534133427dcff/propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3", size = 43133 }, + { url = "https://files.pythonhosted.org/packages/63/7c/e9399ba5da7780871db4eac178e9c2e204c23dd3e7d32df202092a1ed400/propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3", size = 43039 }, + { url = "https://files.pythonhosted.org/packages/22/e1/58da211eb8fdc6fc854002387d38f415a6ca5f5c67c1315b204a5d3e9d7a/propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e", size = 201903 }, + { url = "https://files.pythonhosted.org/packages/c4/0a/550ea0f52aac455cb90111c8bab995208443e46d925e51e2f6ebdf869525/propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220", size = 213362 }, + { url = "https://files.pythonhosted.org/packages/5a/af/9893b7d878deda9bb69fcf54600b247fba7317761b7db11fede6e0f28bd0/propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb", size = 210525 }, + { url = "https://files.pythonhosted.org/packages/7c/bb/38fd08b278ca85cde36d848091ad2b45954bc5f15cce494bb300b9285831/propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614", size = 198283 }, + { url = "https://files.pythonhosted.org/packages/78/8c/9fe55bd01d362bafb413dfe508c48753111a1e269737fa143ba85693592c/propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50", size = 191872 }, + { url = "https://files.pythonhosted.org/packages/54/14/4701c33852937a22584e08abb531d654c8bcf7948a8f87ad0a4822394147/propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339", size = 199452 }, + { url = "https://files.pythonhosted.org/packages/16/44/447f2253d859602095356007657ee535e0093215ea0b3d1d6a41d16e5201/propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0", size = 191567 }, + { url = "https://files.pythonhosted.org/packages/f2/b3/e4756258749bb2d3b46defcff606a2f47410bab82be5824a67e84015b267/propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2", size = 193015 }, + { url = "https://files.pythonhosted.org/packages/1e/df/e6d3c7574233164b6330b9fd697beeac402afd367280e6dc377bb99b43d9/propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7", size = 204660 }, + { url = "https://files.pythonhosted.org/packages/b2/53/e4d31dd5170b4a0e2e6b730f2385a96410633b4833dc25fe5dffd1f73294/propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b", size = 206105 }, + { url = "https://files.pythonhosted.org/packages/7f/fe/74d54cf9fbe2a20ff786e5f7afcfde446588f0cf15fb2daacfbc267b866c/propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c", size = 196980 }, + { url = "https://files.pythonhosted.org/packages/22/ec/c469c9d59dada8a7679625e0440b544fe72e99311a4679c279562051f6fc/propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70", size = 37679 }, + { url = "https://files.pythonhosted.org/packages/38/35/07a471371ac89d418f8d0b699c75ea6dca2041fbda360823de21f6a9ce0a/propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9", size = 41459 }, + { url = "https://files.pythonhosted.org/packages/80/8d/e8b436717ab9c2cfc23b116d2c297305aa4cd8339172a456d61ebf5669b8/propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be", size = 74207 }, + { url = "https://files.pythonhosted.org/packages/d6/29/1e34000e9766d112171764b9fa3226fa0153ab565d0c242c70e9945318a7/propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f", size = 43648 }, + { url = "https://files.pythonhosted.org/packages/46/92/1ad5af0df781e76988897da39b5f086c2bf0f028b7f9bd1f409bb05b6874/propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9", size = 43496 }, + { url = "https://files.pythonhosted.org/packages/b3/ce/e96392460f9fb68461fabab3e095cb00c8ddf901205be4eae5ce246e5b7e/propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf", size = 217288 }, + { url = "https://files.pythonhosted.org/packages/c5/2a/866726ea345299f7ceefc861a5e782b045545ae6940851930a6adaf1fca6/propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9", size = 227456 }, + { url = "https://files.pythonhosted.org/packages/de/03/07d992ccb6d930398689187e1b3c718339a1c06b8b145a8d9650e4726166/propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66", size = 225429 }, + { url = "https://files.pythonhosted.org/packages/5d/e6/116ba39448753b1330f48ab8ba927dcd6cf0baea8a0ccbc512dfb49ba670/propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df", size = 213472 }, + { url = "https://files.pythonhosted.org/packages/a6/85/f01f5d97e54e428885a5497ccf7f54404cbb4f906688a1690cd51bf597dc/propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2", size = 204480 }, + { url = "https://files.pythonhosted.org/packages/e3/79/7bf5ab9033b8b8194cc3f7cf1aaa0e9c3256320726f64a3e1f113a812dce/propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7", size = 214530 }, + { url = "https://files.pythonhosted.org/packages/31/0b/bd3e0c00509b609317df4a18e6b05a450ef2d9a963e1d8bc9c9415d86f30/propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95", size = 205230 }, + { url = "https://files.pythonhosted.org/packages/7a/23/fae0ff9b54b0de4e819bbe559508da132d5683c32d84d0dc2ccce3563ed4/propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e", size = 206754 }, + { url = "https://files.pythonhosted.org/packages/b7/7f/ad6a3c22630aaa5f618b4dc3c3598974a72abb4c18e45a50b3cdd091eb2f/propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e", size = 218430 }, + { url = "https://files.pythonhosted.org/packages/5b/2c/ba4f1c0e8a4b4c75910742f0d333759d441f65a1c7f34683b4a74c0ee015/propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf", size = 223884 }, + { url = "https://files.pythonhosted.org/packages/88/e4/ebe30fc399e98572019eee82ad0caf512401661985cbd3da5e3140ffa1b0/propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e", size = 211480 }, + { url = "https://files.pythonhosted.org/packages/96/0a/7d5260b914e01d1d0906f7f38af101f8d8ed0dc47426219eeaf05e8ea7c2/propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897", size = 37757 }, + { url = "https://files.pythonhosted.org/packages/e1/2d/89fe4489a884bc0da0c3278c552bd4ffe06a1ace559db5ef02ef24ab446b/propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39", size = 41500 }, + { url = "https://files.pythonhosted.org/packages/a8/42/9ca01b0a6f48e81615dca4765a8f1dd2c057e0540f6116a27dc5ee01dfb6/propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10", size = 73674 }, + { url = "https://files.pythonhosted.org/packages/af/6e/21293133beb550f9c901bbece755d582bfaf2176bee4774000bd4dd41884/propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154", size = 43570 }, + { url = "https://files.pythonhosted.org/packages/0c/c8/0393a0a3a2b8760eb3bde3c147f62b20044f0ddac81e9d6ed7318ec0d852/propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615", size = 43094 }, + { url = "https://files.pythonhosted.org/packages/37/2c/489afe311a690399d04a3e03b069225670c1d489eb7b044a566511c1c498/propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db", size = 226958 }, + { url = "https://files.pythonhosted.org/packages/9d/ca/63b520d2f3d418c968bf596839ae26cf7f87bead026b6192d4da6a08c467/propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1", size = 234894 }, + { url = "https://files.pythonhosted.org/packages/11/60/1d0ed6fff455a028d678df30cc28dcee7af77fa2b0e6962ce1df95c9a2a9/propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c", size = 233672 }, + { url = "https://files.pythonhosted.org/packages/37/7c/54fd5301ef38505ab235d98827207176a5c9b2aa61939b10a460ca53e123/propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67", size = 224395 }, + { url = "https://files.pythonhosted.org/packages/ee/1a/89a40e0846f5de05fdc6779883bf46ba980e6df4d2ff8fb02643de126592/propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b", size = 212510 }, + { url = "https://files.pythonhosted.org/packages/5e/33/ca98368586c9566a6b8d5ef66e30484f8da84c0aac3f2d9aec6d31a11bd5/propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8", size = 222949 }, + { url = "https://files.pythonhosted.org/packages/ba/11/ace870d0aafe443b33b2f0b7efdb872b7c3abd505bfb4890716ad7865e9d/propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251", size = 217258 }, + { url = "https://files.pythonhosted.org/packages/5b/d2/86fd6f7adffcfc74b42c10a6b7db721d1d9ca1055c45d39a1a8f2a740a21/propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474", size = 213036 }, + { url = "https://files.pythonhosted.org/packages/07/94/2d7d1e328f45ff34a0a284cf5a2847013701e24c2a53117e7c280a4316b3/propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535", size = 227684 }, + { url = "https://files.pythonhosted.org/packages/b7/05/37ae63a0087677e90b1d14710e532ff104d44bc1efa3b3970fff99b891dc/propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06", size = 234562 }, + { url = "https://files.pythonhosted.org/packages/a4/7c/3f539fcae630408d0bd8bf3208b9a647ccad10976eda62402a80adf8fc34/propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1", size = 222142 }, + { url = "https://files.pythonhosted.org/packages/7c/d2/34b9eac8c35f79f8a962546b3e97e9d4b990c420ee66ac8255d5d9611648/propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1", size = 37711 }, + { url = "https://files.pythonhosted.org/packages/19/61/d582be5d226cf79071681d1b46b848d6cb03d7b70af7063e33a2787eaa03/propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c", size = 41479 }, + { url = "https://files.pythonhosted.org/packages/dc/d1/8c747fafa558c603c4ca19d8e20b288aa0c7cda74e9402f50f31eb65267e/propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945", size = 71286 }, + { url = "https://files.pythonhosted.org/packages/61/99/d606cb7986b60d89c36de8a85d58764323b3a5ff07770a99d8e993b3fa73/propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252", size = 42425 }, + { url = "https://files.pythonhosted.org/packages/8c/96/ef98f91bbb42b79e9bb82bdd348b255eb9d65f14dbbe3b1594644c4073f7/propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f", size = 41846 }, + { url = "https://files.pythonhosted.org/packages/5b/ad/3f0f9a705fb630d175146cd7b1d2bf5555c9beaed54e94132b21aac098a6/propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33", size = 208871 }, + { url = "https://files.pythonhosted.org/packages/3a/38/2085cda93d2c8b6ec3e92af2c89489a36a5886b712a34ab25de9fbca7992/propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e", size = 215720 }, + { url = "https://files.pythonhosted.org/packages/61/c1/d72ea2dc83ac7f2c8e182786ab0fc2c7bd123a1ff9b7975bee671866fe5f/propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1", size = 215203 }, + { url = "https://files.pythonhosted.org/packages/af/81/b324c44ae60c56ef12007105f1460d5c304b0626ab0cc6b07c8f2a9aa0b8/propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3", size = 206365 }, + { url = "https://files.pythonhosted.org/packages/09/73/88549128bb89e66d2aff242488f62869014ae092db63ccea53c1cc75a81d/propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1", size = 196016 }, + { url = "https://files.pythonhosted.org/packages/b9/3f/3bdd14e737d145114a5eb83cb172903afba7242f67c5877f9909a20d948d/propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6", size = 205596 }, + { url = "https://files.pythonhosted.org/packages/0f/ca/2f4aa819c357d3107c3763d7ef42c03980f9ed5c48c82e01e25945d437c1/propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387", size = 200977 }, + { url = "https://files.pythonhosted.org/packages/cd/4a/e65276c7477533c59085251ae88505caf6831c0e85ff8b2e31ebcbb949b1/propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4", size = 197220 }, + { url = "https://files.pythonhosted.org/packages/7c/54/fc7152e517cf5578278b242396ce4d4b36795423988ef39bb8cd5bf274c8/propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88", size = 210642 }, + { url = "https://files.pythonhosted.org/packages/b9/80/abeb4a896d2767bf5f1ea7b92eb7be6a5330645bd7fb844049c0e4045d9d/propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206", size = 212789 }, + { url = "https://files.pythonhosted.org/packages/b3/db/ea12a49aa7b2b6d68a5da8293dcf50068d48d088100ac016ad92a6a780e6/propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43", size = 205880 }, + { url = "https://files.pythonhosted.org/packages/d1/e5/9076a0bbbfb65d1198007059c65639dfd56266cf8e477a9707e4b1999ff4/propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02", size = 37220 }, + { url = "https://files.pythonhosted.org/packages/d3/f5/b369e026b09a26cd77aa88d8fffd69141d2ae00a2abaaf5380d2603f4b7f/propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05", size = 40678 }, + { url = "https://files.pythonhosted.org/packages/a4/3a/6ece377b55544941a08d03581c7bc400a3c8cd3c2865900a68d5de79e21f/propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b", size = 76560 }, + { url = "https://files.pythonhosted.org/packages/0c/da/64a2bb16418740fa634b0e9c3d29edff1db07f56d3546ca2d86ddf0305e1/propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0", size = 44676 }, + { url = "https://files.pythonhosted.org/packages/36/7b/f025e06ea51cb72c52fb87e9b395cced02786610b60a3ed51da8af017170/propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e", size = 44701 }, + { url = "https://files.pythonhosted.org/packages/a4/00/faa1b1b7c3b74fc277f8642f32a4c72ba1d7b2de36d7cdfb676db7f4303e/propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28", size = 276934 }, + { url = "https://files.pythonhosted.org/packages/74/ab/935beb6f1756e0476a4d5938ff44bf0d13a055fed880caf93859b4f1baf4/propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a", size = 278316 }, + { url = "https://files.pythonhosted.org/packages/f8/9d/994a5c1ce4389610838d1caec74bdf0e98b306c70314d46dbe4fcf21a3e2/propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c", size = 282619 }, + { url = "https://files.pythonhosted.org/packages/2b/00/a10afce3d1ed0287cef2e09506d3be9822513f2c1e96457ee369adb9a6cd/propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725", size = 265896 }, + { url = "https://files.pythonhosted.org/packages/2e/a8/2aa6716ffa566ca57c749edb909ad27884680887d68517e4be41b02299f3/propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892", size = 252111 }, + { url = "https://files.pythonhosted.org/packages/36/4f/345ca9183b85ac29c8694b0941f7484bf419c7f0fea2d1e386b4f7893eed/propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44", size = 268334 }, + { url = "https://files.pythonhosted.org/packages/3e/ca/fcd54f78b59e3f97b3b9715501e3147f5340167733d27db423aa321e7148/propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe", size = 255026 }, + { url = "https://files.pythonhosted.org/packages/8b/95/8e6a6bbbd78ac89c30c225210a5c687790e532ba4088afb8c0445b77ef37/propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81", size = 250724 }, + { url = "https://files.pythonhosted.org/packages/ee/b0/0dd03616142baba28e8b2d14ce5df6631b4673850a3d4f9c0f9dd714a404/propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba", size = 268868 }, + { url = "https://files.pythonhosted.org/packages/c5/98/2c12407a7e4fbacd94ddd32f3b1e3d5231e77c30ef7162b12a60e2dd5ce3/propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770", size = 271322 }, + { url = "https://files.pythonhosted.org/packages/35/91/9cb56efbb428b006bb85db28591e40b7736847b8331d43fe335acf95f6c8/propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330", size = 265778 }, + { url = "https://files.pythonhosted.org/packages/9a/4c/b0fe775a2bdd01e176b14b574be679d84fc83958335790f7c9a686c1f468/propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394", size = 41175 }, + { url = "https://files.pythonhosted.org/packages/a4/ff/47f08595e3d9b5e149c150f88d9714574f1a7cbd89fe2817158a952674bf/propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198", size = 44857 }, + { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663 }, ] [[package]] name = "protobuf" -version = "6.32.0" +version = "5.29.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c0/df/fb4a8eeea482eca989b51cffd274aac2ee24e825f0bf3cbce5281fa1567b/protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2", size = 440614, upload-time = "2025-08-14T21:21:25.015Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/29/d09e70352e4e88c9c7a198d5645d7277811448d76c23b00345670f7c8a38/protobuf-5.29.5.tar.gz", hash = "sha256:bc1463bafd4b0929216c35f437a8e28731a2b7fe3d98bb77a600efced5a15c84", size = 425226 } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/18/df8c87da2e47f4f1dcc5153a81cd6bca4e429803f4069a299e236e4dd510/protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741", size = 424409, upload-time = "2025-08-14T21:21:12.366Z" }, - { url = "https://files.pythonhosted.org/packages/e1/59/0a820b7310f8139bd8d5a9388e6a38e1786d179d6f33998448609296c229/protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e", size = 435735, upload-time = "2025-08-14T21:21:15.046Z" }, - { url = "https://files.pythonhosted.org/packages/cc/5b/0d421533c59c789e9c9894683efac582c06246bf24bb26b753b149bd88e4/protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0", size = 426449, upload-time = "2025-08-14T21:21:16.687Z" }, - { url = "https://files.pythonhosted.org/packages/ec/7b/607764ebe6c7a23dcee06e054fd1de3d5841b7648a90fd6def9a3bb58c5e/protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1", size = 322869, upload-time = "2025-08-14T21:21:18.282Z" }, - { url = "https://files.pythonhosted.org/packages/40/01/2e730bd1c25392fc32e3268e02446f0d77cb51a2c3a8486b1798e34d5805/protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c", size = 322009, upload-time = "2025-08-14T21:21:19.893Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f2/80ffc4677aac1bc3519b26bc7f7f5de7fce0ee2f7e36e59e27d8beb32dd1/protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783", size = 169287, upload-time = "2025-08-14T21:21:23.515Z" }, + { url = "https://files.pythonhosted.org/packages/5f/11/6e40e9fc5bba02988a214c07cf324595789ca7820160bfd1f8be96e48539/protobuf-5.29.5-cp310-abi3-win32.whl", hash = "sha256:3f1c6468a2cfd102ff4703976138844f78ebd1fb45f49011afc5139e9e283079", size = 422963 }, + { url = "https://files.pythonhosted.org/packages/81/7f/73cefb093e1a2a7c3ffd839e6f9fcafb7a427d300c7f8aef9c64405d8ac6/protobuf-5.29.5-cp310-abi3-win_amd64.whl", hash = "sha256:3f76e3a3675b4a4d867b52e4a5f5b78a2ef9565549d4037e06cf7b0942b1d3fc", size = 434818 }, + { url = "https://files.pythonhosted.org/packages/dd/73/10e1661c21f139f2c6ad9b23040ff36fee624310dc28fba20d33fdae124c/protobuf-5.29.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e38c5add5a311f2a6eb0340716ef9b039c1dfa428b28f25a7838ac329204a671", size = 418091 }, + { url = "https://files.pythonhosted.org/packages/6c/04/98f6f8cf5b07ab1294c13f34b4e69b3722bb609c5b701d6c169828f9f8aa/protobuf-5.29.5-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:fa18533a299d7ab6c55a238bf8629311439995f2e7eca5caaff08663606e9015", size = 319824 }, + { url = "https://files.pythonhosted.org/packages/85/e4/07c80521879c2d15f321465ac24c70efe2381378c00bf5e56a0f4fbac8cd/protobuf-5.29.5-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:63848923da3325e1bf7e9003d680ce6e14b07e55d0473253a690c3a8b8fd6e61", size = 319942 }, + { url = "https://files.pythonhosted.org/packages/7e/cc/7e77861000a0691aeea8f4566e5d3aa716f2b1dece4a24439437e41d3d25/protobuf-5.29.5-py3-none-any.whl", hash = "sha256:6cf42630262c59b2d8de33954443d94b746c952b01434fc58a417fdbd2e84bd5", size = 172823 }, ] [[package]] name = "psutil" version = "7.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload-time = "2025-02-13T21:54:07.946Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload-time = "2025-02-13T21:54:12.36Z" }, - { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload-time = "2025-02-13T21:54:16.07Z" }, - { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload-time = "2025-02-13T21:54:18.662Z" }, - { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload-time = "2025-02-13T21:54:21.811Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload-time = "2025-02-13T21:54:24.68Z" }, - { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload-time = "2025-02-13T21:54:34.31Z" }, - { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload-time = "2025-02-13T21:54:37.486Z" }, + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 }, + { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 }, + { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 }, ] [[package]] name = "ptyprocess" version = "0.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, ] [[package]] name = "pure-eval" version = "0.2.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, ] [[package]] name = "pycparser" -version = "2.22" +version = "2.23" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734 } wheels = [ - { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140 }, ] [[package]] @@ -2660,9 +2960,9 @@ dependencies = [ { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-inspection", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782 }, ] [[package]] @@ -2672,84 +2972,84 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" }, - { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" }, - { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" }, - { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" }, - { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" }, - { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" }, - { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" }, - { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" }, - { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" }, - { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" }, - { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" }, - { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" }, - { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" }, - { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" }, - { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" }, - { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" }, - { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" }, - { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" }, - { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" }, - { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" }, - { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" }, - { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" }, - { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" }, - { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" }, - { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" }, - { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, - { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, - { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, - { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, - { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, - { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, - { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, - { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, - { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, - { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, - { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, - { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, - { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, - { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, - { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, - { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, - { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, - { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, - { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, - { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, - { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, - { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, - { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, - { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, - { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, - { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, - { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, - { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" }, - { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" }, - { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" }, - { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" }, - { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" }, - { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" }, - { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" }, - { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" }, - { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" }, - { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" }, - { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" }, - { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" }, - { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" }, - { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" }, - { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" }, - { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, + { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817 }, + { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357 }, + { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011 }, + { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730 }, + { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178 }, + { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462 }, + { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652 }, + { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306 }, + { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720 }, + { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915 }, + { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884 }, + { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496 }, + { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019 }, + { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584 }, + { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071 }, + { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823 }, + { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792 }, + { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338 }, + { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998 }, + { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200 }, + { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890 }, + { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359 }, + { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883 }, + { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074 }, + { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538 }, + { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909 }, + { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786 }, + { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000 }, + { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996 }, + { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957 }, + { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199 }, + { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296 }, + { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109 }, + { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028 }, + { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044 }, + { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881 }, + { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034 }, + { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187 }, + { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628 }, + { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866 }, + { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894 }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688 }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808 }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580 }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859 }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810 }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498 }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611 }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924 }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196 }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389 }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223 }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162 }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560 }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 }, + { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982 }, + { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412 }, + { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749 }, + { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527 }, + { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225 }, + { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490 }, + { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525 }, + { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446 }, + { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678 }, + { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200 }, + { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123 }, + { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852 }, + { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484 }, + { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896 }, + { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475 }, + { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013 }, + { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715 }, + { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757 }, ] [[package]] @@ -2761,9 +3061,9 @@ dependencies = [ { name = "python-dotenv", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-inspection", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583, upload-time = "2025-06-24T13:26:46.841Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/85/1ea668bbab3c50071ca613c6ab30047fb36ab0da1b92fa8f17bbc38fd36c/pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee", size = 172583 } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235, upload-time = "2025-06-24T13:26:45.485Z" }, + { url = "https://files.pythonhosted.org/packages/58/f0/427018098906416f580e3cf1366d3b1abfb408a0652e9f31600c24a1903c/pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796", size = 45235 }, ] [[package]] @@ -2780,27 +3080,27 @@ dependencies = [ { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/91/c3/5240f2a5dc0b4856655c003466f70aa50d676b1709e5b04f0bee296bbd28/pydata_sphinx_theme-0.16.0.tar.gz", hash = "sha256:721dd26e05fa8b992d66ef545536e6cbe0110afb9865820a08894af1ad6f7707", size = 2407197, upload-time = "2024-10-22T14:24:41.245Z" } +sdist = { url = "https://files.pythonhosted.org/packages/91/c3/5240f2a5dc0b4856655c003466f70aa50d676b1709e5b04f0bee296bbd28/pydata_sphinx_theme-0.16.0.tar.gz", hash = "sha256:721dd26e05fa8b992d66ef545536e6cbe0110afb9865820a08894af1ad6f7707", size = 2407197 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/92/38f384061e1361fac7092c35e932c0e08026fb9080bf3fbf05f4c3bb6bda/pydata_sphinx_theme-0.16.0-py3-none-any.whl", hash = "sha256:18c810ee4e67e05281e371e156c1fb5bb0fa1f2747240461b225272f7d8d57d8", size = 6739948, upload-time = "2024-10-22T14:24:38.782Z" }, + { url = "https://files.pythonhosted.org/packages/ba/92/38f384061e1361fac7092c35e932c0e08026fb9080bf3fbf05f4c3bb6bda/pydata_sphinx_theme-0.16.0-py3-none-any.whl", hash = "sha256:18c810ee4e67e05281e371e156c1fb5bb0fa1f2747240461b225272f7d8d57d8", size = 6739948 }, ] [[package]] name = "pygments" version = "2.19.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, ] [[package]] name = "pyjwt" version = "2.10.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785 } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, + { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997 }, ] [package.optional-dependencies] @@ -2816,9 +3116,9 @@ dependencies = [ { name = "nodeenv", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/6c/ba4bbee22e76af700ea593a1d8701e3225080956753bee9750dcc25e2649/pyright-1.1.405.tar.gz", hash = "sha256:5c2a30e1037af27eb463a1cc0b9f6d65fec48478ccf092c1ac28385a15c55763", size = 4068319, upload-time = "2025-09-04T03:37:06.776Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/6c/ba4bbee22e76af700ea593a1d8701e3225080956753bee9750dcc25e2649/pyright-1.1.405.tar.gz", hash = "sha256:5c2a30e1037af27eb463a1cc0b9f6d65fec48478ccf092c1ac28385a15c55763", size = 4068319 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/1a/524f832e1ff1962a22a1accc775ca7b143ba2e9f5924bb6749dce566784a/pyright-1.1.405-py3-none-any.whl", hash = "sha256:a2cb13700b5508ce8e5d4546034cb7ea4aedb60215c6c33f56cec7f53996035a", size = 5905038, upload-time = "2025-09-04T03:37:04.913Z" }, + { url = "https://files.pythonhosted.org/packages/d5/1a/524f832e1ff1962a22a1accc775ca7b143ba2e9f5924bb6749dce566784a/pyright-1.1.405-py3-none-any.whl", hash = "sha256:a2cb13700b5508ce8e5d4546034cb7ea4aedb60215c6c33f56cec7f53996035a", size = 5905038 }, ] [[package]] @@ -2834,9 +3134,9 @@ dependencies = [ { name = "pygments", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "tomli", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750 }, ] [[package]] @@ -2847,23 +3147,23 @@ dependencies = [ { name = "backports-asyncio-runner", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652, upload-time = "2025-07-16T04:29:26.393Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157, upload-time = "2025-07-16T04:29:24.929Z" }, + { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157 }, ] [[package]] name = "pytest-cov" -version = "6.2.1" +version = "7.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "coverage", extra = ["toml"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "pluggy", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432, upload-time = "2025-06-12T10:47:47.684Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328 } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" }, + { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424 }, ] [[package]] @@ -2873,9 +3173,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973, upload-time = "2025-05-05T19:44:34.99Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/82/4c9ecabab13363e72d880f2fb504c5f750433b2b6f16e99f4ec21ada284c/pytest_timeout-2.4.0.tar.gz", hash = "sha256:7e68e90b01f9eff71332b25001f85c75495fc4e3a836701876183c4bcfd0540a", size = 17973 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382, upload-time = "2025-05-05T19:44:33.502Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", hash = "sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", size = 14382 }, ] [[package]] @@ -2886,9 +3186,9 @@ dependencies = [ { name = "execnet", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" } +sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396, upload-time = "2025-07-01T13:30:56.632Z" }, + { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396 }, ] [package.optional-dependencies] @@ -2903,27 +3203,36 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, ] [[package]] name = "python-dotenv" version = "1.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, + { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556 }, ] [[package]] name = "python-multipart" version = "0.0.20" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } +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/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, + { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 }, +] + +[[package]] +name = "pytz" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225 }, ] [[package]] @@ -2931,137 +3240,157 @@ name = "pywin32" version = "311" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" }, - { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" }, - { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" }, - { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" }, - { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" }, - { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" }, - { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, - { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, - { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, - { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, - { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, - { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, - { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, - { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, - { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, + { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432 }, + { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103 }, + { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557 }, + { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031 }, + { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308 }, + { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930 }, + { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543 }, + { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040 }, + { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102 }, + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700 }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700 }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318 }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714 }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800 }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540 }, ] [[package]] name = "pyyaml" version = "6.0.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, - { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, - { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, - { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, - { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, - { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, - { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, - { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, - { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, - { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, ] [[package]] name = "pyzmq" -version = "27.0.2" +version = "27.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "(implementation_name == 'pypy' and sys_platform == 'darwin') or (implementation_name == 'pypy' and sys_platform == 'linux') or (implementation_name == 'pypy' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/66/159f38d184f08b5f971b467f87b1ab142ab1320d5200825c824b32b84b66/pyzmq-27.0.2.tar.gz", hash = "sha256:b398dd713b18de89730447347e96a0240225e154db56e35b6bb8447ffdb07798", size = 281440, upload-time = "2025-08-21T04:23:26.334Z" } +sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750 } wheels = [ - { url = "https://files.pythonhosted.org/packages/83/4d/2081cd7e41e340004d2051821efe1d0d67d31bdb5ac33bffc7e628d5f1bd/pyzmq-27.0.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:8b32c4636ced87dce0ac3d671e578b3400215efab372f1b4be242e8cf0b11384", size = 1329839, upload-time = "2025-08-21T04:20:55.8Z" }, - { url = "https://files.pythonhosted.org/packages/ad/f1/1300b7e932671e31accb3512c19b43e6a3e8d08c54ab8b920308e53427ce/pyzmq-27.0.2-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f9528a4b3e24189cb333a9850fddbbafaa81df187297cfbddee50447cdb042cf", size = 906367, upload-time = "2025-08-21T04:20:58.476Z" }, - { url = "https://files.pythonhosted.org/packages/e6/80/61662db85eb3255a58c1bb59f6d4fc0d31c9c75b9a14983deafab12b2329/pyzmq-27.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b02ba0c0b2b9ebe74688002e6c56c903429924a25630804b9ede1f178aa5a3f", size = 666545, upload-time = "2025-08-21T04:20:59.775Z" }, - { url = "https://files.pythonhosted.org/packages/5c/6e/49fb9c75b039978cbb1f3657811d8056b0ebe6ecafd78a4457fc6de19799/pyzmq-27.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4dc5c9a6167617251dea0d024d67559795761aabb4b7ea015518be898be076", size = 854219, upload-time = "2025-08-21T04:21:01.807Z" }, - { url = "https://files.pythonhosted.org/packages/b0/3c/9951b302d221e471b7c659e70f9cb64db5f68fa3b7da45809ec4e6c6ef17/pyzmq-27.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f1151b33aaf3b4fa9da26f4d696e38eebab67d1b43c446184d733c700b3ff8ce", size = 1655103, upload-time = "2025-08-21T04:21:03.239Z" }, - { url = "https://files.pythonhosted.org/packages/88/ca/d7adea6100fdf7f87f3856db02d2a0a45ce2764b9f60ba08c48c655b762f/pyzmq-27.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4ecfc7999ac44c9ef92b5ae8f0b44fb935297977df54d8756b195a3cd12f38f0", size = 2033712, upload-time = "2025-08-21T04:21:05.121Z" }, - { url = "https://files.pythonhosted.org/packages/e9/63/b34e601b36ba4864d02ac1460443fc39bf533dedbdeead2a4e0df7dfc8ee/pyzmq-27.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:31c26a5d0b00befcaeeb600d8b15ad09f5604b6f44e2057ec5e521a9e18dcd9a", size = 1891847, upload-time = "2025-08-21T04:21:06.586Z" }, - { url = "https://files.pythonhosted.org/packages/cf/a2/9479e6af779da44f788d5fcda5f77dff1af988351ef91682b92524eab2db/pyzmq-27.0.2-cp310-cp310-win32.whl", hash = "sha256:25a100d2de2ac0c644ecf4ce0b509a720d12e559c77aff7e7e73aa684f0375bc", size = 567136, upload-time = "2025-08-21T04:21:07.885Z" }, - { url = "https://files.pythonhosted.org/packages/58/46/e1c2be469781fc56ba092fecb1bb336cedde0fd87d9e1a547aaeb5d1a968/pyzmq-27.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a1acf091f53bb406e9e5e7383e467d1dd1b94488b8415b890917d30111a1fef3", size = 631969, upload-time = "2025-08-21T04:21:09.5Z" }, - { url = "https://files.pythonhosted.org/packages/d5/8d/d20a62f1f77e3f04633a80bb83df085e4314f0e9404619cc458d0005d6ab/pyzmq-27.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:b38e01f11e9e95f6668dc8a62dccf9483f454fed78a77447507a0e8dcbd19a63", size = 559459, upload-time = "2025-08-21T04:21:11.208Z" }, - { url = "https://files.pythonhosted.org/packages/42/73/034429ab0f4316bf433eb6c20c3f49d1dc13b2ed4e4d951b283d300a0f35/pyzmq-27.0.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:063845960df76599ad4fad69fa4d884b3ba38304272104fdcd7e3af33faeeb1d", size = 1333169, upload-time = "2025-08-21T04:21:12.483Z" }, - { url = "https://files.pythonhosted.org/packages/35/02/c42b3b526eb03a570c889eea85a5602797f800a50ba8b09ddbf7db568b78/pyzmq-27.0.2-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:845a35fb21b88786aeb38af8b271d41ab0967985410f35411a27eebdc578a076", size = 909176, upload-time = "2025-08-21T04:21:13.835Z" }, - { url = "https://files.pythonhosted.org/packages/1b/35/a1c0b988fabbdf2dc5fe94b7c2bcfd61e3533e5109297b8e0daf1d7a8d2d/pyzmq-27.0.2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:515d20b5c3c86db95503faa989853a8ab692aab1e5336db011cd6d35626c4cb1", size = 668972, upload-time = "2025-08-21T04:21:15.315Z" }, - { url = "https://files.pythonhosted.org/packages/a0/63/908ac865da32ceaeecea72adceadad28ca25b23a2ca5ff018e5bff30116f/pyzmq-27.0.2-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:862aedec0b0684a5050cdb5ec13c2da96d2f8dffda48657ed35e312a4e31553b", size = 856962, upload-time = "2025-08-21T04:21:16.652Z" }, - { url = "https://files.pythonhosted.org/packages/2f/5a/90b3cc20b65cdf9391896fcfc15d8db21182eab810b7ea05a2986912fbe2/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cb5bcfc51c7a4fce335d3bc974fd1d6a916abbcdd2b25f6e89d37b8def25f57", size = 1657712, upload-time = "2025-08-21T04:21:18.666Z" }, - { url = "https://files.pythonhosted.org/packages/c4/3c/32a5a80f9be4759325b8d7b22ce674bb87e586b4c80c6a9d77598b60d6f0/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:38ff75b2a36e3a032e9fef29a5871e3e1301a37464e09ba364e3c3193f62982a", size = 2035054, upload-time = "2025-08-21T04:21:20.073Z" }, - { url = "https://files.pythonhosted.org/packages/13/61/71084fe2ff2d7dc5713f8740d735336e87544845dae1207a8e2e16d9af90/pyzmq-27.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7a5709abe8d23ca158a9d0a18c037f4193f5b6afeb53be37173a41e9fb885792", size = 1894010, upload-time = "2025-08-21T04:21:21.96Z" }, - { url = "https://files.pythonhosted.org/packages/cb/6b/77169cfb13b696e50112ca496b2ed23c4b7d8860a1ec0ff3e4b9f9926221/pyzmq-27.0.2-cp311-cp311-win32.whl", hash = "sha256:47c5dda2018c35d87be9b83de0890cb92ac0791fd59498847fc4eca6ff56671d", size = 566819, upload-time = "2025-08-21T04:21:23.31Z" }, - { url = "https://files.pythonhosted.org/packages/37/cd/86c4083e0f811f48f11bc0ddf1e7d13ef37adfd2fd4f78f2445f1cc5dec0/pyzmq-27.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:f54ca3e98f8f4d23e989c7d0edcf9da7a514ff261edaf64d1d8653dd5feb0a8b", size = 633264, upload-time = "2025-08-21T04:21:24.761Z" }, - { url = "https://files.pythonhosted.org/packages/a0/69/5b8bb6a19a36a569fac02153a9e083738785892636270f5f68a915956aea/pyzmq-27.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:2ef3067cb5b51b090fb853f423ad7ed63836ec154374282780a62eb866bf5768", size = 559316, upload-time = "2025-08-21T04:21:26.1Z" }, - { url = "https://files.pythonhosted.org/packages/68/69/b3a729e7b03e412bee2b1823ab8d22e20a92593634f664afd04c6c9d9ac0/pyzmq-27.0.2-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:5da05e3c22c95e23bfc4afeee6ff7d4be9ff2233ad6cb171a0e8257cd46b169a", size = 1305910, upload-time = "2025-08-21T04:21:27.609Z" }, - { url = "https://files.pythonhosted.org/packages/15/b7/f6a6a285193d489b223c340b38ee03a673467cb54914da21c3d7849f1b10/pyzmq-27.0.2-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4e4520577971d01d47e2559bb3175fce1be9103b18621bf0b241abe0a933d040", size = 895507, upload-time = "2025-08-21T04:21:29.005Z" }, - { url = "https://files.pythonhosted.org/packages/17/e6/c4ed2da5ef9182cde1b1f5d0051a986e76339d71720ec1a00be0b49275ad/pyzmq-27.0.2-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d7de7bf73165b90bd25a8668659ccb134dd28449116bf3c7e9bab5cf8a8ec9", size = 652670, upload-time = "2025-08-21T04:21:30.71Z" }, - { url = "https://files.pythonhosted.org/packages/0e/66/d781ab0636570d32c745c4e389b1c6b713115905cca69ab6233508622edd/pyzmq-27.0.2-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:340e7cddc32f147c6c00d116a3f284ab07ee63dbd26c52be13b590520434533c", size = 840581, upload-time = "2025-08-21T04:21:32.008Z" }, - { url = "https://files.pythonhosted.org/packages/a6/df/f24790caf565d72544f5c8d8500960b9562c1dc848d6f22f3c7e122e73d4/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba95693f9df8bb4a9826464fb0fe89033936f35fd4a8ff1edff09a473570afa0", size = 1641931, upload-time = "2025-08-21T04:21:33.371Z" }, - { url = "https://files.pythonhosted.org/packages/65/65/77d27b19fc5e845367f9100db90b9fce924f611b14770db480615944c9c9/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:ca42a6ce2d697537da34f77a1960d21476c6a4af3e539eddb2b114c3cf65a78c", size = 2021226, upload-time = "2025-08-21T04:21:35.301Z" }, - { url = "https://files.pythonhosted.org/packages/5b/65/1ed14421ba27a4207fa694772003a311d1142b7f543179e4d1099b7eb746/pyzmq-27.0.2-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3e44e665d78a07214b2772ccbd4b9bcc6d848d7895f1b2d7653f047b6318a4f6", size = 1878047, upload-time = "2025-08-21T04:21:36.749Z" }, - { url = "https://files.pythonhosted.org/packages/dd/dc/e578549b89b40dc78a387ec471c2a360766690c0a045cd8d1877d401012d/pyzmq-27.0.2-cp312-abi3-win32.whl", hash = "sha256:272d772d116615397d2be2b1417b3b8c8bc8671f93728c2f2c25002a4530e8f6", size = 558757, upload-time = "2025-08-21T04:21:38.2Z" }, - { url = "https://files.pythonhosted.org/packages/b5/89/06600980aefcc535c758414da969f37a5194ea4cdb73b745223f6af3acfb/pyzmq-27.0.2-cp312-abi3-win_amd64.whl", hash = "sha256:734be4f44efba0aa69bf5f015ed13eb69ff29bf0d17ea1e21588b095a3147b8e", size = 619281, upload-time = "2025-08-21T04:21:39.909Z" }, - { url = "https://files.pythonhosted.org/packages/30/84/df8a5c089552d17c9941d1aea4314b606edf1b1622361dae89aacedc6467/pyzmq-27.0.2-cp312-abi3-win_arm64.whl", hash = "sha256:41f0bd56d9279392810950feb2785a419c2920bbf007fdaaa7f4a07332ae492d", size = 552680, upload-time = "2025-08-21T04:21:41.571Z" }, - { url = "https://files.pythonhosted.org/packages/b4/7b/b79e976508517ab80dc800f7021ef1fb602a6d55e4caa2d47fb3dca5d8b6/pyzmq-27.0.2-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:7f01118133427cd7f34ee133b5098e2af5f70303fa7519785c007bca5aa6f96a", size = 1122259, upload-time = "2025-08-21T04:21:43.063Z" }, - { url = "https://files.pythonhosted.org/packages/59/7d/654657a4c6435f41538182e71b61eac386a789a2bbb6f30171915253a9a7/pyzmq-27.0.2-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:cb77923ea163156da14295c941930bd525df0d29c96c1ec2fe3c3806b1e17cb3", size = 1341437, upload-time = "2025-08-21T04:21:46.019Z" }, - { url = "https://files.pythonhosted.org/packages/20/a0/5ed7710037f9c096017adc748bcb1698674a2d297f8b9422d38816f7b56a/pyzmq-27.0.2-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:61678b7407b04df8f9423f188156355dc94d0fb52d360ae79d02ed7e0d431eea", size = 897888, upload-time = "2025-08-21T04:21:47.362Z" }, - { url = "https://files.pythonhosted.org/packages/2c/8a/6e4699a60931c17e7406641d201d7f2c121e2a38979bc83226a6d8f1ba32/pyzmq-27.0.2-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3c824b70925963bdc8e39a642672c15ffaa67e7d4b491f64662dd56d6271263", size = 660727, upload-time = "2025-08-21T04:21:48.734Z" }, - { url = "https://files.pythonhosted.org/packages/7b/d8/d761e438c186451bd89ce63a665cde5690c084b61cd8f5d7b51e966e875a/pyzmq-27.0.2-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c4833e02fcf2751975457be1dfa2f744d4d09901a8cc106acaa519d868232175", size = 848136, upload-time = "2025-08-21T04:21:50.416Z" }, - { url = "https://files.pythonhosted.org/packages/43/f1/a0f31684efdf3eb92f46b7dd2117e752208115e89d278f8ca5f413c5bb85/pyzmq-27.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b18045668d09cf0faa44918af2a67f0dbbef738c96f61c2f1b975b1ddb92ccfc", size = 1650402, upload-time = "2025-08-21T04:21:52.235Z" }, - { url = "https://files.pythonhosted.org/packages/41/fd/0d7f2a1732812df02c85002770da4a7864c79b210084bcdab01ea57e8d92/pyzmq-27.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:bbbb7e2f3ac5a22901324e7b086f398b8e16d343879a77b15ca3312e8cd8e6d5", size = 2024587, upload-time = "2025-08-21T04:21:54.07Z" }, - { url = "https://files.pythonhosted.org/packages/f1/73/358be69e279a382dd09e46dda29df8446365cddee4f79ef214e71e5b2b5a/pyzmq-27.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b751914a73604d40d88a061bab042a11d4511b3ddbb7624cd83c39c8a498564c", size = 1885493, upload-time = "2025-08-21T04:21:55.588Z" }, - { url = "https://files.pythonhosted.org/packages/c5/7b/e9951ad53b3dfed8cfb4c2cfd6e0097c9b454e5c0d0e6df5f2b60d7c8c3d/pyzmq-27.0.2-cp313-cp313t-win32.whl", hash = "sha256:3e8f833dd82af11db5321c414638045c70f61009f72dd61c88db4a713c1fb1d2", size = 574934, upload-time = "2025-08-21T04:21:57.52Z" }, - { url = "https://files.pythonhosted.org/packages/55/33/1a7fc3a92f2124a63e6e2a6afa0af471a5c0c713e776b476d4eda5111b13/pyzmq-27.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5b45153cb8eadcab14139970643a84f7a7b08dda541fbc1f6f4855c49334b549", size = 640932, upload-time = "2025-08-21T04:21:59.527Z" }, - { url = "https://files.pythonhosted.org/packages/2a/52/2598a94ac251a7c83f3887866225eea1952b0d4463a68df5032eb00ff052/pyzmq-27.0.2-cp313-cp313t-win_arm64.whl", hash = "sha256:86898f5c9730df23427c1ee0097d8aa41aa5f89539a79e48cd0d2c22d059f1b7", size = 561315, upload-time = "2025-08-21T04:22:01.295Z" }, - { url = "https://files.pythonhosted.org/packages/42/7d/10ef02ea36590b29d48ef88eb0831f0af3eb240cccca2752556faec55f59/pyzmq-27.0.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d2b4b261dce10762be5c116b6ad1f267a9429765b493c454f049f33791dd8b8a", size = 1341463, upload-time = "2025-08-21T04:22:02.712Z" }, - { url = "https://files.pythonhosted.org/packages/94/36/115d18dade9a3d4d3d08dd8bfe5459561b8e02815f99df040555fdd7768e/pyzmq-27.0.2-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4e4d88b6cff156fed468903006b24bbd85322612f9c2f7b96e72d5016fd3f543", size = 897840, upload-time = "2025-08-21T04:22:04.845Z" }, - { url = "https://files.pythonhosted.org/packages/39/66/083b37839b95c386a95f1537bb41bdbf0c002b7c55b75ee737949cecb11f/pyzmq-27.0.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8426c0ebbc11ed8416a6e9409c194142d677c2c5c688595f2743664e356d9e9b", size = 660704, upload-time = "2025-08-21T04:22:06.389Z" }, - { url = "https://files.pythonhosted.org/packages/76/5a/196ab46e549ba35bf3268f575e10cfac0dc86b78dcaa7a3e36407ecda752/pyzmq-27.0.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:565bee96a155fe6452caed5fb5f60c9862038e6b51a59f4f632562081cdb4004", size = 848037, upload-time = "2025-08-21T04:22:07.817Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/a27b9eb44b2e615a9ecb8510ebb023cc1d2d251181e4a1e50366bfbf94d6/pyzmq-27.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5de735c745ca5cefe9c2d1547d8f28cfe1b1926aecb7483ab1102fd0a746c093", size = 1650278, upload-time = "2025-08-21T04:22:09.269Z" }, - { url = "https://files.pythonhosted.org/packages/62/ac/3e9af036bfaf718ab5e69ded8f6332da392c5450ad43e8e3ca66797f145a/pyzmq-27.0.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ea4f498f8115fd90d7bf03a3e83ae3e9898e43362f8e8e8faec93597206e15cc", size = 2024504, upload-time = "2025-08-21T04:22:10.778Z" }, - { url = "https://files.pythonhosted.org/packages/ae/e9/3202d31788df8ebaa176b23d846335eb9c768d8b43c0506bbd6265ad36a0/pyzmq-27.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d00e81cb0afd672915257a3927124ee2ad117ace3c256d39cd97ca3f190152ad", size = 1885381, upload-time = "2025-08-21T04:22:12.718Z" }, - { url = "https://files.pythonhosted.org/packages/4b/ed/42de80b7ab4e8fcf13376f81206cf8041740672ac1fd2e1c598d63f595bf/pyzmq-27.0.2-cp314-cp314t-win32.whl", hash = "sha256:0f6e9b00d81b58f859fffc112365d50413954e02aefe36c5b4c8fb4af79f8cc3", size = 587526, upload-time = "2025-08-21T04:22:14.18Z" }, - { url = "https://files.pythonhosted.org/packages/ed/c8/8f3c72d6f0bfbf090aa5e283576073ca5c59839b85a5cc8c66ddb9b59801/pyzmq-27.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:2e73cf3b127a437fef4100eb3ac2ebe6b49e655bb721329f667f59eca0a26221", size = 661368, upload-time = "2025-08-21T04:22:15.677Z" }, - { url = "https://files.pythonhosted.org/packages/69/a4/7ee652ea1c77d872f5d99ed937fa8bbd1f6f4b7a39a6d3a0076c286e0c3e/pyzmq-27.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:4108785f2e5ac865d06f678a07a1901e3465611356df21a545eeea8b45f56265", size = 574901, upload-time = "2025-08-21T04:22:17.423Z" }, - { url = "https://files.pythonhosted.org/packages/19/d7/e388e80107b7c438c9698ce59c2a3b950021cd4ab3fe641485e4ed6b0960/pyzmq-27.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d67a0960803a37b60f51b460c58444bc7033a804c662f5735172e21e74ee4902", size = 836008, upload-time = "2025-08-21T04:22:51.842Z" }, - { url = "https://files.pythonhosted.org/packages/65/ef/58d3eb85f1b67a16e22adb07d084f975a7b9641463d18e27230550bb436a/pyzmq-27.0.2-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:dd4d3e6a567ffd0d232cfc667c49d0852d0ee7481458a2a1593b9b1bc5acba88", size = 799932, upload-time = "2025-08-21T04:22:53.529Z" }, - { url = "https://files.pythonhosted.org/packages/3c/63/66b9f6db19ee8c86105ffd4475a4f5d93cdd62b1edcb1e894d971df0728c/pyzmq-27.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e558be423631704803bc6a642e2caa96083df759e25fe6eb01f2d28725f80bd", size = 567458, upload-time = "2025-08-21T04:22:55.289Z" }, - { url = "https://files.pythonhosted.org/packages/10/af/d92207fe8b6e3d9f588d0591219a86dd7b4ed27bb3e825c1d9cf48467fc0/pyzmq-27.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c4c20ba8389f495c7b4f6b896bb1ca1e109a157d4f189267a902079699aaf787", size = 747087, upload-time = "2025-08-21T04:22:56.994Z" }, - { url = "https://files.pythonhosted.org/packages/82/e9/d9f8b4b191c6733e31de28974d608a2475a6598136ac901a8c5b67c11432/pyzmq-27.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c5be232f7219414ff672ff7ab8c5a7e8632177735186d8a42b57b491fafdd64e", size = 544641, upload-time = "2025-08-21T04:22:58.87Z" }, - { url = "https://files.pythonhosted.org/packages/c7/60/027d0032a1e3b1aabcef0e309b9ff8a4099bdd5a60ab38b36a676ff2bd7b/pyzmq-27.0.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e297784aea724294fe95e442e39a4376c2f08aa4fae4161c669f047051e31b02", size = 836007, upload-time = "2025-08-21T04:23:00.447Z" }, - { url = "https://files.pythonhosted.org/packages/25/20/2ed1e6168aaea323df9bb2c451309291f53ba3af372ffc16edd4ce15b9e5/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e3659a79ded9745bc9c2aef5b444ac8805606e7bc50d2d2eb16dc3ab5483d91f", size = 799932, upload-time = "2025-08-21T04:23:02.052Z" }, - { url = "https://files.pythonhosted.org/packages/fd/25/5c147307de546b502c9373688ce5b25dc22288d23a1ebebe5d587bf77610/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3dba49ff037d02373a9306b58d6c1e0be031438f822044e8767afccfdac4c6b", size = 567459, upload-time = "2025-08-21T04:23:03.593Z" }, - { url = "https://files.pythonhosted.org/packages/71/06/0dc56ffc615c8095cd089c9b98ce5c733e990f09ce4e8eea4aaf1041a532/pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de84e1694f9507b29e7b263453a2255a73e3d099d258db0f14539bad258abe41", size = 747088, upload-time = "2025-08-21T04:23:05.334Z" }, - { url = "https://files.pythonhosted.org/packages/06/f6/4a50187e023b8848edd3f0a8e197b1a7fb08d261d8c60aae7cb6c3d71612/pyzmq-27.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f0944d65ba2b872b9fcece08411d6347f15a874c775b4c3baae7f278550da0fb", size = 544639, upload-time = "2025-08-21T04:23:07.279Z" }, + { url = "https://files.pythonhosted.org/packages/67/b9/52aa9ec2867528b54f1e60846728d8b4d84726630874fee3a91e66c7df81/pyzmq-27.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4", size = 1329850 }, + { url = "https://files.pythonhosted.org/packages/99/64/5653e7b7425b169f994835a2b2abf9486264401fdef18df91ddae47ce2cc/pyzmq-27.1.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556", size = 906380 }, + { url = "https://files.pythonhosted.org/packages/73/78/7d713284dbe022f6440e391bd1f3c48d9185673878034cfb3939cdf333b2/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b", size = 666421 }, + { url = "https://files.pythonhosted.org/packages/30/76/8f099f9d6482450428b17c4d6b241281af7ce6a9de8149ca8c1c649f6792/pyzmq-27.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e", size = 854149 }, + { url = "https://files.pythonhosted.org/packages/59/f0/37fbfff06c68016019043897e4c969ceab18bde46cd2aca89821fcf4fb2e/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526", size = 1655070 }, + { url = "https://files.pythonhosted.org/packages/47/14/7254be73f7a8edc3587609554fcaa7bfd30649bf89cd260e4487ca70fdaa/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1", size = 2033441 }, + { url = "https://files.pythonhosted.org/packages/22/dc/49f2be26c6f86f347e796a4d99b19167fc94503f0af3fd010ad262158822/pyzmq-27.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386", size = 1891529 }, + { url = "https://files.pythonhosted.org/packages/a3/3e/154fb963ae25be70c0064ce97776c937ecc7d8b0259f22858154a9999769/pyzmq-27.1.0-cp310-cp310-win32.whl", hash = "sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda", size = 567276 }, + { url = "https://files.pythonhosted.org/packages/62/b2/f4ab56c8c595abcb26b2be5fd9fa9e6899c1e5ad54964e93ae8bb35482be/pyzmq-27.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f", size = 632208 }, + { url = "https://files.pythonhosted.org/packages/3b/e3/be2cc7ab8332bdac0522fdb64c17b1b6241a795bee02e0196636ec5beb79/pyzmq-27.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32", size = 559766 }, + { url = "https://files.pythonhosted.org/packages/06/5d/305323ba86b284e6fcb0d842d6adaa2999035f70f8c38a9b6d21ad28c3d4/pyzmq-27.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:226b091818d461a3bef763805e75685e478ac17e9008f49fce2d3e52b3d58b86", size = 1333328 }, + { url = "https://files.pythonhosted.org/packages/bd/a0/fc7e78a23748ad5443ac3275943457e8452da67fda347e05260261108cbc/pyzmq-27.1.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0790a0161c281ca9723f804871b4027f2e8b5a528d357c8952d08cd1a9c15581", size = 908803 }, + { url = "https://files.pythonhosted.org/packages/7e/22/37d15eb05f3bdfa4abea6f6d96eb3bb58585fbd3e4e0ded4e743bc650c97/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c895a6f35476b0c3a54e3eb6ccf41bf3018de937016e6e18748317f25d4e925f", size = 668836 }, + { url = "https://files.pythonhosted.org/packages/b1/c4/2a6fe5111a01005fc7af3878259ce17684fabb8852815eda6225620f3c59/pyzmq-27.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bbf8d3630bf96550b3be8e1fc0fea5cbdc8d5466c1192887bd94869da17a63e", size = 857038 }, + { url = "https://files.pythonhosted.org/packages/cb/eb/bfdcb41d0db9cd233d6fb22dc131583774135505ada800ebf14dfb0a7c40/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15c8bd0fe0dabf808e2d7a681398c4e5ded70a551ab47482067a572c054c8e2e", size = 1657531 }, + { url = "https://files.pythonhosted.org/packages/ab/21/e3180ca269ed4a0de5c34417dfe71a8ae80421198be83ee619a8a485b0c7/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bafcb3dd171b4ae9f19ee6380dfc71ce0390fefaf26b504c0e5f628d7c8c54f2", size = 2034786 }, + { url = "https://files.pythonhosted.org/packages/3b/b1/5e21d0b517434b7f33588ff76c177c5a167858cc38ef740608898cd329f2/pyzmq-27.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e829529fcaa09937189178115c49c504e69289abd39967cd8a4c215761373394", size = 1894220 }, + { url = "https://files.pythonhosted.org/packages/03/f2/44913a6ff6941905efc24a1acf3d3cb6146b636c546c7406c38c49c403d4/pyzmq-27.1.0-cp311-cp311-win32.whl", hash = "sha256:6df079c47d5902af6db298ec92151db82ecb557af663098b92f2508c398bb54f", size = 567155 }, + { url = "https://files.pythonhosted.org/packages/23/6d/d8d92a0eb270a925c9b4dd039c0b4dc10abc2fcbc48331788824ef113935/pyzmq-27.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:190cbf120fbc0fc4957b56866830def56628934a9d112aec0e2507aa6a032b97", size = 633428 }, + { url = "https://files.pythonhosted.org/packages/ae/14/01afebc96c5abbbd713ecfc7469cfb1bc801c819a74ed5c9fad9a48801cb/pyzmq-27.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:eca6b47df11a132d1745eb3b5b5e557a7dae2c303277aa0e69c6ba91b8736e07", size = 559497 }, + { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279 }, + { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645 }, + { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574 }, + { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995 }, + { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070 }, + { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121 }, + { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550 }, + { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184 }, + { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480 }, + { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993 }, + { url = "https://files.pythonhosted.org/packages/60/cb/84a13459c51da6cec1b7b1dc1a47e6db6da50b77ad7fd9c145842750a011/pyzmq-27.1.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:93ad4b0855a664229559e45c8d23797ceac03183c7b6f5b4428152a6b06684a5", size = 1122436 }, + { url = "https://files.pythonhosted.org/packages/dc/b6/94414759a69a26c3dd674570a81813c46a078767d931a6c70ad29fc585cb/pyzmq-27.1.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:fbb4f2400bfda24f12f009cba62ad5734148569ff4949b1b6ec3b519444342e6", size = 1156301 }, + { url = "https://files.pythonhosted.org/packages/a5/ad/15906493fd40c316377fd8a8f6b1f93104f97a752667763c9b9c1b71d42d/pyzmq-27.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:e343d067f7b151cfe4eb3bb796a7752c9d369eed007b91231e817071d2c2fec7", size = 1341197 }, + { url = "https://files.pythonhosted.org/packages/14/1d/d343f3ce13db53a54cb8946594e567410b2125394dafcc0268d8dda027e0/pyzmq-27.1.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:08363b2011dec81c354d694bdecaef4770e0ae96b9afea70b3f47b973655cc05", size = 897275 }, + { url = "https://files.pythonhosted.org/packages/69/2d/d83dd6d7ca929a2fc67d2c3005415cdf322af7751d773524809f9e585129/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d54530c8c8b5b8ddb3318f481297441af102517602b569146185fa10b63f4fa9", size = 660469 }, + { url = "https://files.pythonhosted.org/packages/3e/cd/9822a7af117f4bc0f1952dbe9ef8358eb50a24928efd5edf54210b850259/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3afa12c392f0a44a2414056d730eebc33ec0926aae92b5ad5cf26ebb6cc128", size = 847961 }, + { url = "https://files.pythonhosted.org/packages/9a/12/f003e824a19ed73be15542f172fd0ec4ad0b60cf37436652c93b9df7c585/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c65047adafe573ff023b3187bb93faa583151627bc9c51fc4fb2c561ed689d39", size = 1650282 }, + { url = "https://files.pythonhosted.org/packages/d5/4a/e82d788ed58e9a23995cee70dbc20c9aded3d13a92d30d57ec2291f1e8a3/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:90e6e9441c946a8b0a667356f7078d96411391a3b8f80980315455574177ec97", size = 2024468 }, + { url = "https://files.pythonhosted.org/packages/d9/94/2da0a60841f757481e402b34bf4c8bf57fa54a5466b965de791b1e6f747d/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:add071b2d25f84e8189aaf0882d39a285b42fa3853016ebab234a5e78c7a43db", size = 1885394 }, + { url = "https://files.pythonhosted.org/packages/4f/6f/55c10e2e49ad52d080dc24e37adb215e5b0d64990b57598abc2e3f01725b/pyzmq-27.1.0-cp313-cp313t-win32.whl", hash = "sha256:7ccc0700cfdf7bd487bea8d850ec38f204478681ea02a582a8da8171b7f90a1c", size = 574964 }, + { url = "https://files.pythonhosted.org/packages/87/4d/2534970ba63dd7c522d8ca80fb92777f362c0f321900667c615e2067cb29/pyzmq-27.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8085a9fba668216b9b4323be338ee5437a235fe275b9d1610e422ccc279733e2", size = 641029 }, + { url = "https://files.pythonhosted.org/packages/f6/fa/f8aea7a28b0641f31d40dea42d7ef003fded31e184ef47db696bc74cd610/pyzmq-27.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6bb54ca21bcfe361e445256c15eedf083f153811c37be87e0514934d6913061e", size = 561541 }, + { url = "https://files.pythonhosted.org/packages/87/45/19efbb3000956e82d0331bafca5d9ac19ea2857722fa2caacefb6042f39d/pyzmq-27.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a", size = 1341197 }, + { url = "https://files.pythonhosted.org/packages/48/43/d72ccdbf0d73d1343936296665826350cb1e825f92f2db9db3e61c2162a2/pyzmq-27.1.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea", size = 897175 }, + { url = "https://files.pythonhosted.org/packages/2f/2e/a483f73a10b65a9ef0161e817321d39a770b2acf8bcf3004a28d90d14a94/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96", size = 660427 }, + { url = "https://files.pythonhosted.org/packages/f5/d2/5f36552c2d3e5685abe60dfa56f91169f7a2d99bbaf67c5271022ab40863/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d", size = 847929 }, + { url = "https://files.pythonhosted.org/packages/c4/2a/404b331f2b7bf3198e9945f75c4c521f0c6a3a23b51f7a4a401b94a13833/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146", size = 1650193 }, + { url = "https://files.pythonhosted.org/packages/1c/0b/f4107e33f62a5acf60e3ded67ed33d79b4ce18de432625ce2fc5093d6388/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd", size = 2024388 }, + { url = "https://files.pythonhosted.org/packages/0d/01/add31fe76512642fd6e40e3a3bd21f4b47e242c8ba33efb6809e37076d9b/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a", size = 1885316 }, + { url = "https://files.pythonhosted.org/packages/c4/59/a5f38970f9bf07cee96128de79590bb354917914a9be11272cfc7ff26af0/pyzmq-27.1.0-cp314-cp314t-win32.whl", hash = "sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92", size = 587472 }, + { url = "https://files.pythonhosted.org/packages/70/d8/78b1bad170f93fcf5e3536e70e8fadac55030002275c9a29e8f5719185de/pyzmq-27.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0", size = 661401 }, + { url = "https://files.pythonhosted.org/packages/81/d6/4bfbb40c9a0b42fc53c7cf442f6385db70b40f74a783130c5d0a5aa62228/pyzmq-27.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7", size = 575170 }, + { url = "https://files.pythonhosted.org/packages/f3/81/a65e71c1552f74dec9dff91d95bafb6e0d33338a8dfefbc88aa562a20c92/pyzmq-27.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6", size = 836266 }, + { url = "https://files.pythonhosted.org/packages/58/ed/0202ca350f4f2b69faa95c6d931e3c05c3a397c184cacb84cb4f8f42f287/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90", size = 800206 }, + { url = "https://files.pythonhosted.org/packages/47/42/1ff831fa87fe8f0a840ddb399054ca0009605d820e2b44ea43114f5459f4/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62", size = 567747 }, + { url = "https://files.pythonhosted.org/packages/d1/db/5c4d6807434751e3f21231bee98109aa57b9b9b55e058e450d0aef59b70f/pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74", size = 747371 }, + { url = "https://files.pythonhosted.org/packages/26/af/78ce193dbf03567eb8c0dc30e3df2b9e56f12a670bf7eb20f9fb532c7e8a/pyzmq-27.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba", size = 544862 }, + { url = "https://files.pythonhosted.org/packages/4c/c6/c4dcdecdbaa70969ee1fdced6d7b8f60cfabe64d25361f27ac4665a70620/pyzmq-27.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:18770c8d3563715387139060d37859c02ce40718d1faf299abddcdcc6a649066", size = 836265 }, + { url = "https://files.pythonhosted.org/packages/3e/79/f38c92eeaeb03a2ccc2ba9866f0439593bb08c5e3b714ac1d553e5c96e25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ac25465d42f92e990f8d8b0546b01c391ad431c3bf447683fdc40565941d0604", size = 800208 }, + { url = "https://files.pythonhosted.org/packages/49/0e/3f0d0d335c6b3abb9b7b723776d0b21fa7f3a6c819a0db6097059aada160/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53b40f8ae006f2734ee7608d59ed661419f087521edbfc2149c3932e9c14808c", size = 567747 }, + { url = "https://files.pythonhosted.org/packages/a1/cf/f2b3784d536250ffd4be70e049f3b60981235d70c6e8ce7e3ef21e1adb25/pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f605d884e7c8be8fe1aa94e0a783bf3f591b84c24e4bc4f3e7564c82ac25e271", size = 747371 }, + { url = "https://files.pythonhosted.org/packages/01/1b/5dbe84eefc86f48473947e2f41711aded97eecef1231f4558f1f02713c12/pyzmq-27.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c9f7f6e13dff2e44a6afeaf2cf54cee5929ad64afaf4d40b50f93c58fc687355", size = 544862 }, +] + +[[package]] +name = "qdrant-client" +version = "1.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "grpcio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "httpx", extra = ["http2"], marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, + { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, + { name = "portalocker", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "protobuf", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "urllib3", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/8b/76c7d325e11d97cb8eb5e261c3759e9ed6664735afbf32fdded5b580690c/qdrant_client-1.15.1.tar.gz", hash = "sha256:631f1f3caebfad0fd0c1fba98f41be81d9962b7bf3ca653bed3b727c0e0cbe0e", size = 295297 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/33/d8df6a2b214ffbe4138db9a1efe3248f67dc3c671f82308bea1582ecbbb7/qdrant_client-1.15.1-py3-none-any.whl", hash = "sha256:2b975099b378382f6ca1cfb43f0d59e541be6e16a5892f282a4b8de7eff5cb63", size = 337331 }, ] [[package]] @@ -3071,9 +3400,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "async-timeout", marker = "(python_full_version < '3.11.3' and sys_platform == 'darwin') or (python_full_version < '3.11.3' and sys_platform == 'linux') or (python_full_version < '3.11.3' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/d6/e8b92798a5bd67d659d51a18170e91c16ac3b59738d91894651ee255ed49/redis-6.4.0.tar.gz", hash = "sha256:b01bc7282b8444e28ec36b261df5375183bb47a07eb9c603f284e89cbc5ef010", size = 4647399, upload-time = "2025-08-07T08:10:11.441Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/d6/e8b92798a5bd67d659d51a18170e91c16ac3b59738d91894651ee255ed49/redis-6.4.0.tar.gz", hash = "sha256:b01bc7282b8444e28ec36b261df5375183bb47a07eb9c603f284e89cbc5ef010", size = 4647399 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/02/89e2ed7e85db6c93dfa9e8f691c5087df4e3551ab39081a4d7c6d1f90e05/redis-6.4.0-py3-none-any.whl", hash = "sha256:f0544fa9604264e9464cdf4814e7d4830f74b165d52f2a330a760a88dd248b7f", size = 279847, upload-time = "2025-08-07T08:10:09.84Z" }, + { url = "https://files.pythonhosted.org/packages/e8/02/89e2ed7e85db6c93dfa9e8f691c5087df4e3551ab39081a4d7c6d1f90e05/redis-6.4.0-py3-none-any.whl", hash = "sha256:f0544fa9604264e9464cdf4814e7d4830f74b165d52f2a330a760a88dd248b7f", size = 279847 }, ] [[package]] @@ -3085,9 +3414,9 @@ dependencies = [ { name = "rpds-py", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775 }, ] [[package]] @@ -3100,9 +3429,9 @@ dependencies = [ { name = "idna", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "urllib3", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738 }, ] [[package]] @@ -3113,9 +3442,9 @@ dependencies = [ { name = "oauthlib", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, + { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179 }, ] [[package]] @@ -3126,215 +3455,215 @@ dependencies = [ { name = "markdown-it-py", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "pygments", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8", size = 224441, upload-time = "2025-07-25T07:32:58.125Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8", size = 224441 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f", size = 243368, upload-time = "2025-07-25T07:32:56.73Z" }, + { url = "https://files.pythonhosted.org/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f", size = 243368 }, ] [[package]] name = "roman-numerals-py" version = "3.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d", size = 9017, upload-time = "2025-02-22T07:34:54.333Z" } +sdist = { url = "https://files.pythonhosted.org/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d", size = 9017 } wheels = [ - { url = "https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742, upload-time = "2025-02-22T07:34:52.422Z" }, + { url = "https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742 }, ] [[package]] name = "rpds-py" version = "0.27.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479, upload-time = "2025-08-27T12:16:36.024Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/ed/3aef893e2dd30e77e35d20d4ddb45ca459db59cead748cad9796ad479411/rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef", size = 371606, upload-time = "2025-08-27T12:12:25.189Z" }, - { url = "https://files.pythonhosted.org/packages/6d/82/9818b443e5d3eb4c83c3994561387f116aae9833b35c484474769c4a8faf/rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be", size = 353452, upload-time = "2025-08-27T12:12:27.433Z" }, - { url = "https://files.pythonhosted.org/packages/99/c7/d2a110ffaaa397fc6793a83c7bd3545d9ab22658b7cdff05a24a4535cc45/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61", size = 381519, upload-time = "2025-08-27T12:12:28.719Z" }, - { url = "https://files.pythonhosted.org/packages/5a/bc/e89581d1f9d1be7d0247eaef602566869fdc0d084008ba139e27e775366c/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb", size = 394424, upload-time = "2025-08-27T12:12:30.207Z" }, - { url = "https://files.pythonhosted.org/packages/ac/2e/36a6861f797530e74bb6ed53495f8741f1ef95939eed01d761e73d559067/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657", size = 523467, upload-time = "2025-08-27T12:12:31.808Z" }, - { url = "https://files.pythonhosted.org/packages/c4/59/c1bc2be32564fa499f988f0a5c6505c2f4746ef96e58e4d7de5cf923d77e/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013", size = 402660, upload-time = "2025-08-27T12:12:33.444Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ec/ef8bf895f0628dd0a59e54d81caed6891663cb9c54a0f4bb7da918cb88cf/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a", size = 384062, upload-time = "2025-08-27T12:12:34.857Z" }, - { url = "https://files.pythonhosted.org/packages/69/f7/f47ff154be8d9a5e691c083a920bba89cef88d5247c241c10b9898f595a1/rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1", size = 401289, upload-time = "2025-08-27T12:12:36.085Z" }, - { url = "https://files.pythonhosted.org/packages/3b/d9/ca410363efd0615814ae579f6829cafb39225cd63e5ea5ed1404cb345293/rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10", size = 417718, upload-time = "2025-08-27T12:12:37.401Z" }, - { url = "https://files.pythonhosted.org/packages/e3/a0/8cb5c2ff38340f221cc067cc093d1270e10658ba4e8d263df923daa18e86/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808", size = 558333, upload-time = "2025-08-27T12:12:38.672Z" }, - { url = "https://files.pythonhosted.org/packages/6f/8c/1b0de79177c5d5103843774ce12b84caa7164dfc6cd66378768d37db11bf/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8", size = 589127, upload-time = "2025-08-27T12:12:41.48Z" }, - { url = "https://files.pythonhosted.org/packages/c8/5e/26abb098d5e01266b0f3a2488d299d19ccc26849735d9d2b95c39397e945/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9", size = 554899, upload-time = "2025-08-27T12:12:42.925Z" }, - { url = "https://files.pythonhosted.org/packages/de/41/905cc90ced13550db017f8f20c6d8e8470066c5738ba480d7ba63e3d136b/rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4", size = 217450, upload-time = "2025-08-27T12:12:44.813Z" }, - { url = "https://files.pythonhosted.org/packages/75/3d/6bef47b0e253616ccdf67c283e25f2d16e18ccddd38f92af81d5a3420206/rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1", size = 228447, upload-time = "2025-08-27T12:12:46.204Z" }, - { url = "https://files.pythonhosted.org/packages/b5/c1/7907329fbef97cbd49db6f7303893bd1dd5a4a3eae415839ffdfb0762cae/rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881", size = 371063, upload-time = "2025-08-27T12:12:47.856Z" }, - { url = "https://files.pythonhosted.org/packages/11/94/2aab4bc86228bcf7c48760990273653a4900de89c7537ffe1b0d6097ed39/rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5", size = 353210, upload-time = "2025-08-27T12:12:49.187Z" }, - { url = "https://files.pythonhosted.org/packages/3a/57/f5eb3ecf434342f4f1a46009530e93fd201a0b5b83379034ebdb1d7c1a58/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e", size = 381636, upload-time = "2025-08-27T12:12:50.492Z" }, - { url = "https://files.pythonhosted.org/packages/ae/f4/ef95c5945e2ceb5119571b184dd5a1cc4b8541bbdf67461998cfeac9cb1e/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c", size = 394341, upload-time = "2025-08-27T12:12:52.024Z" }, - { url = "https://files.pythonhosted.org/packages/5a/7e/4bd610754bf492d398b61725eb9598ddd5eb86b07d7d9483dbcd810e20bc/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195", size = 523428, upload-time = "2025-08-27T12:12:53.779Z" }, - { url = "https://files.pythonhosted.org/packages/9f/e5/059b9f65a8c9149361a8b75094864ab83b94718344db511fd6117936ed2a/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52", size = 402923, upload-time = "2025-08-27T12:12:55.15Z" }, - { url = "https://files.pythonhosted.org/packages/f5/48/64cabb7daced2968dd08e8a1b7988bf358d7bd5bcd5dc89a652f4668543c/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed", size = 384094, upload-time = "2025-08-27T12:12:57.194Z" }, - { url = "https://files.pythonhosted.org/packages/ae/e1/dc9094d6ff566bff87add8a510c89b9e158ad2ecd97ee26e677da29a9e1b/rpds_py-0.27.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a", size = 401093, upload-time = "2025-08-27T12:12:58.985Z" }, - { url = "https://files.pythonhosted.org/packages/37/8e/ac8577e3ecdd5593e283d46907d7011618994e1d7ab992711ae0f78b9937/rpds_py-0.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde", size = 417969, upload-time = "2025-08-27T12:13:00.367Z" }, - { url = "https://files.pythonhosted.org/packages/66/6d/87507430a8f74a93556fe55c6485ba9c259949a853ce407b1e23fea5ba31/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21", size = 558302, upload-time = "2025-08-27T12:13:01.737Z" }, - { url = "https://files.pythonhosted.org/packages/3a/bb/1db4781ce1dda3eecc735e3152659a27b90a02ca62bfeea17aee45cc0fbc/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9", size = 589259, upload-time = "2025-08-27T12:13:03.127Z" }, - { url = "https://files.pythonhosted.org/packages/7b/0e/ae1c8943d11a814d01b482e1f8da903f88047a962dff9bbdadf3bd6e6fd1/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948", size = 554983, upload-time = "2025-08-27T12:13:04.516Z" }, - { url = "https://files.pythonhosted.org/packages/b2/d5/0b2a55415931db4f112bdab072443ff76131b5ac4f4dc98d10d2d357eb03/rpds_py-0.27.1-cp311-cp311-win32.whl", hash = "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39", size = 217154, upload-time = "2025-08-27T12:13:06.278Z" }, - { url = "https://files.pythonhosted.org/packages/24/75/3b7ffe0d50dc86a6a964af0d1cc3a4a2cdf437cb7b099a4747bbb96d1819/rpds_py-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15", size = 228627, upload-time = "2025-08-27T12:13:07.625Z" }, - { url = "https://files.pythonhosted.org/packages/8d/3f/4fd04c32abc02c710f09a72a30c9a55ea3cc154ef8099078fd50a0596f8e/rpds_py-0.27.1-cp311-cp311-win_arm64.whl", hash = "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746", size = 220998, upload-time = "2025-08-27T12:13:08.972Z" }, - { url = "https://files.pythonhosted.org/packages/bd/fe/38de28dee5df58b8198c743fe2bea0c785c6d40941b9950bac4cdb71a014/rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90", size = 361887, upload-time = "2025-08-27T12:13:10.233Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/4b6c7eedc7dd90986bf0fab6ea2a091ec11c01b15f8ba0a14d3f80450468/rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5", size = 345795, upload-time = "2025-08-27T12:13:11.65Z" }, - { url = "https://files.pythonhosted.org/packages/6f/0e/e650e1b81922847a09cca820237b0edee69416a01268b7754d506ade11ad/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e", size = 385121, upload-time = "2025-08-27T12:13:13.008Z" }, - { url = "https://files.pythonhosted.org/packages/1b/ea/b306067a712988e2bff00dcc7c8f31d26c29b6d5931b461aa4b60a013e33/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881", size = 398976, upload-time = "2025-08-27T12:13:14.368Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0a/26dc43c8840cb8fe239fe12dbc8d8de40f2365e838f3d395835dde72f0e5/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec", size = 525953, upload-time = "2025-08-27T12:13:15.774Z" }, - { url = "https://files.pythonhosted.org/packages/22/14/c85e8127b573aaf3a0cbd7fbb8c9c99e735a4a02180c84da2a463b766e9e/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb", size = 407915, upload-time = "2025-08-27T12:13:17.379Z" }, - { url = "https://files.pythonhosted.org/packages/ed/7b/8f4fee9ba1fb5ec856eb22d725a4efa3deb47f769597c809e03578b0f9d9/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5", size = 386883, upload-time = "2025-08-27T12:13:18.704Z" }, - { url = "https://files.pythonhosted.org/packages/86/47/28fa6d60f8b74fcdceba81b272f8d9836ac0340570f68f5df6b41838547b/rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a", size = 405699, upload-time = "2025-08-27T12:13:20.089Z" }, - { url = "https://files.pythonhosted.org/packages/d0/fd/c5987b5e054548df56953a21fe2ebed51fc1ec7c8f24fd41c067b68c4a0a/rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444", size = 423713, upload-time = "2025-08-27T12:13:21.436Z" }, - { url = "https://files.pythonhosted.org/packages/ac/ba/3c4978b54a73ed19a7d74531be37a8bcc542d917c770e14d372b8daea186/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a", size = 562324, upload-time = "2025-08-27T12:13:22.789Z" }, - { url = "https://files.pythonhosted.org/packages/b5/6c/6943a91768fec16db09a42b08644b960cff540c66aab89b74be6d4a144ba/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1", size = 593646, upload-time = "2025-08-27T12:13:24.122Z" }, - { url = "https://files.pythonhosted.org/packages/11/73/9d7a8f4be5f4396f011a6bb7a19fe26303a0dac9064462f5651ced2f572f/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998", size = 558137, upload-time = "2025-08-27T12:13:25.557Z" }, - { url = "https://files.pythonhosted.org/packages/6e/96/6772cbfa0e2485bcceef8071de7821f81aeac8bb45fbfd5542a3e8108165/rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39", size = 221343, upload-time = "2025-08-27T12:13:26.967Z" }, - { url = "https://files.pythonhosted.org/packages/67/b6/c82f0faa9af1c6a64669f73a17ee0eeef25aff30bb9a1c318509efe45d84/rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594", size = 232497, upload-time = "2025-08-27T12:13:28.326Z" }, - { url = "https://files.pythonhosted.org/packages/e1/96/2817b44bd2ed11aebacc9251da03689d56109b9aba5e311297b6902136e2/rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502", size = 222790, upload-time = "2025-08-27T12:13:29.71Z" }, - { url = "https://files.pythonhosted.org/packages/cc/77/610aeee8d41e39080c7e14afa5387138e3c9fa9756ab893d09d99e7d8e98/rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b", size = 361741, upload-time = "2025-08-27T12:13:31.039Z" }, - { url = "https://files.pythonhosted.org/packages/3a/fc/c43765f201c6a1c60be2043cbdb664013def52460a4c7adace89d6682bf4/rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf", size = 345574, upload-time = "2025-08-27T12:13:32.902Z" }, - { url = "https://files.pythonhosted.org/packages/20/42/ee2b2ca114294cd9847d0ef9c26d2b0851b2e7e00bf14cc4c0b581df0fc3/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83", size = 385051, upload-time = "2025-08-27T12:13:34.228Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e8/1e430fe311e4799e02e2d1af7c765f024e95e17d651612425b226705f910/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf", size = 398395, upload-time = "2025-08-27T12:13:36.132Z" }, - { url = "https://files.pythonhosted.org/packages/82/95/9dc227d441ff2670651c27a739acb2535ccaf8b351a88d78c088965e5996/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2", size = 524334, upload-time = "2025-08-27T12:13:37.562Z" }, - { url = "https://files.pythonhosted.org/packages/87/01/a670c232f401d9ad461d9a332aa4080cd3cb1d1df18213dbd0d2a6a7ab51/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0", size = 407691, upload-time = "2025-08-27T12:13:38.94Z" }, - { url = "https://files.pythonhosted.org/packages/03/36/0a14aebbaa26fe7fab4780c76f2239e76cc95a0090bdb25e31d95c492fcd/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418", size = 386868, upload-time = "2025-08-27T12:13:40.192Z" }, - { url = "https://files.pythonhosted.org/packages/3b/03/8c897fb8b5347ff6c1cc31239b9611c5bf79d78c984430887a353e1409a1/rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d", size = 405469, upload-time = "2025-08-27T12:13:41.496Z" }, - { url = "https://files.pythonhosted.org/packages/da/07/88c60edc2df74850d496d78a1fdcdc7b54360a7f610a4d50008309d41b94/rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274", size = 422125, upload-time = "2025-08-27T12:13:42.802Z" }, - { url = "https://files.pythonhosted.org/packages/6b/86/5f4c707603e41b05f191a749984f390dabcbc467cf833769b47bf14ba04f/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd", size = 562341, upload-time = "2025-08-27T12:13:44.472Z" }, - { url = "https://files.pythonhosted.org/packages/b2/92/3c0cb2492094e3cd9baf9e49bbb7befeceb584ea0c1a8b5939dca4da12e5/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2", size = 592511, upload-time = "2025-08-27T12:13:45.898Z" }, - { url = "https://files.pythonhosted.org/packages/10/bb/82e64fbb0047c46a168faa28d0d45a7851cd0582f850b966811d30f67ad8/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002", size = 557736, upload-time = "2025-08-27T12:13:47.408Z" }, - { url = "https://files.pythonhosted.org/packages/00/95/3c863973d409210da7fb41958172c6b7dbe7fc34e04d3cc1f10bb85e979f/rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3", size = 221462, upload-time = "2025-08-27T12:13:48.742Z" }, - { url = "https://files.pythonhosted.org/packages/ce/2c/5867b14a81dc217b56d95a9f2a40fdbc56a1ab0181b80132beeecbd4b2d6/rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83", size = 232034, upload-time = "2025-08-27T12:13:50.11Z" }, - { url = "https://files.pythonhosted.org/packages/c7/78/3958f3f018c01923823f1e47f1cc338e398814b92d83cd278364446fac66/rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d", size = 222392, upload-time = "2025-08-27T12:13:52.587Z" }, - { url = "https://files.pythonhosted.org/packages/01/76/1cdf1f91aed5c3a7bf2eba1f1c4e4d6f57832d73003919a20118870ea659/rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228", size = 358355, upload-time = "2025-08-27T12:13:54.012Z" }, - { url = "https://files.pythonhosted.org/packages/c3/6f/bf142541229374287604caf3bb2a4ae17f0a580798fd72d3b009b532db4e/rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92", size = 342138, upload-time = "2025-08-27T12:13:55.791Z" }, - { url = "https://files.pythonhosted.org/packages/1a/77/355b1c041d6be40886c44ff5e798b4e2769e497b790f0f7fd1e78d17e9a8/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2", size = 380247, upload-time = "2025-08-27T12:13:57.683Z" }, - { url = "https://files.pythonhosted.org/packages/d6/a4/d9cef5c3946ea271ce2243c51481971cd6e34f21925af2783dd17b26e815/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723", size = 390699, upload-time = "2025-08-27T12:13:59.137Z" }, - { url = "https://files.pythonhosted.org/packages/3a/06/005106a7b8c6c1a7e91b73169e49870f4af5256119d34a361ae5240a0c1d/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802", size = 521852, upload-time = "2025-08-27T12:14:00.583Z" }, - { url = "https://files.pythonhosted.org/packages/e5/3e/50fb1dac0948e17a02eb05c24510a8fe12d5ce8561c6b7b7d1339ab7ab9c/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f", size = 402582, upload-time = "2025-08-27T12:14:02.034Z" }, - { url = "https://files.pythonhosted.org/packages/cb/b0/f4e224090dc5b0ec15f31a02d746ab24101dd430847c4d99123798661bfc/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2", size = 384126, upload-time = "2025-08-27T12:14:03.437Z" }, - { url = "https://files.pythonhosted.org/packages/54/77/ac339d5f82b6afff1df8f0fe0d2145cc827992cb5f8eeb90fc9f31ef7a63/rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21", size = 399486, upload-time = "2025-08-27T12:14:05.443Z" }, - { url = "https://files.pythonhosted.org/packages/d6/29/3e1c255eee6ac358c056a57d6d6869baa00a62fa32eea5ee0632039c50a3/rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef", size = 414832, upload-time = "2025-08-27T12:14:06.902Z" }, - { url = "https://files.pythonhosted.org/packages/3f/db/6d498b844342deb3fa1d030598db93937a9964fcf5cb4da4feb5f17be34b/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081", size = 557249, upload-time = "2025-08-27T12:14:08.37Z" }, - { url = "https://files.pythonhosted.org/packages/60/f3/690dd38e2310b6f68858a331399b4d6dbb9132c3e8ef8b4333b96caf403d/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd", size = 587356, upload-time = "2025-08-27T12:14:10.034Z" }, - { url = "https://files.pythonhosted.org/packages/86/e3/84507781cccd0145f35b1dc32c72675200c5ce8d5b30f813e49424ef68fc/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7", size = 555300, upload-time = "2025-08-27T12:14:11.783Z" }, - { url = "https://files.pythonhosted.org/packages/e5/ee/375469849e6b429b3516206b4580a79e9ef3eb12920ddbd4492b56eaacbe/rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688", size = 216714, upload-time = "2025-08-27T12:14:13.629Z" }, - { url = "https://files.pythonhosted.org/packages/21/87/3fc94e47c9bd0742660e84706c311a860dcae4374cf4a03c477e23ce605a/rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797", size = 228943, upload-time = "2025-08-27T12:14:14.937Z" }, - { url = "https://files.pythonhosted.org/packages/70/36/b6e6066520a07cf029d385de869729a895917b411e777ab1cde878100a1d/rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334", size = 362472, upload-time = "2025-08-27T12:14:16.333Z" }, - { url = "https://files.pythonhosted.org/packages/af/07/b4646032e0dcec0df9c73a3bd52f63bc6c5f9cda992f06bd0e73fe3fbebd/rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33", size = 345676, upload-time = "2025-08-27T12:14:17.764Z" }, - { url = "https://files.pythonhosted.org/packages/b0/16/2f1003ee5d0af4bcb13c0cf894957984c32a6751ed7206db2aee7379a55e/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a", size = 385313, upload-time = "2025-08-27T12:14:19.829Z" }, - { url = "https://files.pythonhosted.org/packages/05/cd/7eb6dd7b232e7f2654d03fa07f1414d7dfc980e82ba71e40a7c46fd95484/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b", size = 399080, upload-time = "2025-08-27T12:14:21.531Z" }, - { url = "https://files.pythonhosted.org/packages/20/51/5829afd5000ec1cb60f304711f02572d619040aa3ec033d8226817d1e571/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7", size = 523868, upload-time = "2025-08-27T12:14:23.485Z" }, - { url = "https://files.pythonhosted.org/packages/05/2c/30eebca20d5db95720ab4d2faec1b5e4c1025c473f703738c371241476a2/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136", size = 408750, upload-time = "2025-08-27T12:14:24.924Z" }, - { url = "https://files.pythonhosted.org/packages/90/1a/cdb5083f043597c4d4276eae4e4c70c55ab5accec078da8611f24575a367/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff", size = 387688, upload-time = "2025-08-27T12:14:27.537Z" }, - { url = "https://files.pythonhosted.org/packages/7c/92/cf786a15320e173f945d205ab31585cc43969743bb1a48b6888f7a2b0a2d/rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9", size = 407225, upload-time = "2025-08-27T12:14:28.981Z" }, - { url = "https://files.pythonhosted.org/packages/33/5c/85ee16df5b65063ef26017bef33096557a4c83fbe56218ac7cd8c235f16d/rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60", size = 423361, upload-time = "2025-08-27T12:14:30.469Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8e/1c2741307fcabd1a334ecf008e92c4f47bb6f848712cf15c923becfe82bb/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e", size = 562493, upload-time = "2025-08-27T12:14:31.987Z" }, - { url = "https://files.pythonhosted.org/packages/04/03/5159321baae9b2222442a70c1f988cbbd66b9be0675dd3936461269be360/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212", size = 592623, upload-time = "2025-08-27T12:14:33.543Z" }, - { url = "https://files.pythonhosted.org/packages/ff/39/c09fd1ad28b85bc1d4554a8710233c9f4cefd03d7717a1b8fbfd171d1167/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675", size = 558800, upload-time = "2025-08-27T12:14:35.436Z" }, - { url = "https://files.pythonhosted.org/packages/c5/d6/99228e6bbcf4baa764b18258f519a9035131d91b538d4e0e294313462a98/rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3", size = 221943, upload-time = "2025-08-27T12:14:36.898Z" }, - { url = "https://files.pythonhosted.org/packages/be/07/c802bc6b8e95be83b79bdf23d1aa61d68324cb1006e245d6c58e959e314d/rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456", size = 233739, upload-time = "2025-08-27T12:14:38.386Z" }, - { url = "https://files.pythonhosted.org/packages/c8/89/3e1b1c16d4c2d547c5717377a8df99aee8099ff050f87c45cb4d5fa70891/rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3", size = 223120, upload-time = "2025-08-27T12:14:39.82Z" }, - { url = "https://files.pythonhosted.org/packages/62/7e/dc7931dc2fa4a6e46b2a4fa744a9fe5c548efd70e0ba74f40b39fa4a8c10/rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2", size = 358944, upload-time = "2025-08-27T12:14:41.199Z" }, - { url = "https://files.pythonhosted.org/packages/e6/22/4af76ac4e9f336bfb1a5f240d18a33c6b2fcaadb7472ac7680576512b49a/rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4", size = 342283, upload-time = "2025-08-27T12:14:42.699Z" }, - { url = "https://files.pythonhosted.org/packages/1c/15/2a7c619b3c2272ea9feb9ade67a45c40b3eeb500d503ad4c28c395dc51b4/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e", size = 380320, upload-time = "2025-08-27T12:14:44.157Z" }, - { url = "https://files.pythonhosted.org/packages/a2/7d/4c6d243ba4a3057e994bb5bedd01b5c963c12fe38dde707a52acdb3849e7/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817", size = 391760, upload-time = "2025-08-27T12:14:45.845Z" }, - { url = "https://files.pythonhosted.org/packages/b4/71/b19401a909b83bcd67f90221330bc1ef11bc486fe4e04c24388d28a618ae/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec", size = 522476, upload-time = "2025-08-27T12:14:47.364Z" }, - { url = "https://files.pythonhosted.org/packages/e4/44/1a3b9715c0455d2e2f0f6df5ee6d6f5afdc423d0773a8a682ed2b43c566c/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a", size = 403418, upload-time = "2025-08-27T12:14:49.991Z" }, - { url = "https://files.pythonhosted.org/packages/1c/4b/fb6c4f14984eb56673bc868a66536f53417ddb13ed44b391998100a06a96/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8", size = 384771, upload-time = "2025-08-27T12:14:52.159Z" }, - { url = "https://files.pythonhosted.org/packages/c0/56/d5265d2d28b7420d7b4d4d85cad8ef891760f5135102e60d5c970b976e41/rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48", size = 400022, upload-time = "2025-08-27T12:14:53.859Z" }, - { url = "https://files.pythonhosted.org/packages/8f/e9/9f5fc70164a569bdd6ed9046486c3568d6926e3a49bdefeeccfb18655875/rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb", size = 416787, upload-time = "2025-08-27T12:14:55.673Z" }, - { url = "https://files.pythonhosted.org/packages/d4/64/56dd03430ba491db943a81dcdef115a985aac5f44f565cd39a00c766d45c/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734", size = 557538, upload-time = "2025-08-27T12:14:57.245Z" }, - { url = "https://files.pythonhosted.org/packages/3f/36/92cc885a3129993b1d963a2a42ecf64e6a8e129d2c7cc980dbeba84e55fb/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb", size = 588512, upload-time = "2025-08-27T12:14:58.728Z" }, - { url = "https://files.pythonhosted.org/packages/dd/10/6b283707780a81919f71625351182b4f98932ac89a09023cb61865136244/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0", size = 555813, upload-time = "2025-08-27T12:15:00.334Z" }, - { url = "https://files.pythonhosted.org/packages/04/2e/30b5ea18c01379da6272a92825dd7e53dc9d15c88a19e97932d35d430ef7/rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a", size = 217385, upload-time = "2025-08-27T12:15:01.937Z" }, - { url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097, upload-time = "2025-08-27T12:15:03.961Z" }, - { url = "https://files.pythonhosted.org/packages/d5/63/b7cc415c345625d5e62f694ea356c58fb964861409008118f1245f8c3347/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf", size = 371360, upload-time = "2025-08-27T12:15:29.218Z" }, - { url = "https://files.pythonhosted.org/packages/e5/8c/12e1b24b560cf378b8ffbdb9dc73abd529e1adcfcf82727dfd29c4a7b88d/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3", size = 353933, upload-time = "2025-08-27T12:15:30.837Z" }, - { url = "https://files.pythonhosted.org/packages/9b/85/1bb2210c1f7a1b99e91fea486b9f0f894aa5da3a5ec7097cbad7dec6d40f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636", size = 382962, upload-time = "2025-08-27T12:15:32.348Z" }, - { url = "https://files.pythonhosted.org/packages/cc/c9/a839b9f219cf80ed65f27a7f5ddbb2809c1b85c966020ae2dff490e0b18e/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8", size = 394412, upload-time = "2025-08-27T12:15:33.839Z" }, - { url = "https://files.pythonhosted.org/packages/02/2d/b1d7f928b0b1f4fc2e0133e8051d199b01d7384875adc63b6ddadf3de7e5/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc", size = 523972, upload-time = "2025-08-27T12:15:35.377Z" }, - { url = "https://files.pythonhosted.org/packages/a9/af/2cbf56edd2d07716df1aec8a726b3159deb47cb5c27e1e42b71d705a7c2f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8", size = 403273, upload-time = "2025-08-27T12:15:37.051Z" }, - { url = "https://files.pythonhosted.org/packages/c0/93/425e32200158d44ff01da5d9612c3b6711fe69f606f06e3895511f17473b/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc", size = 385278, upload-time = "2025-08-27T12:15:38.571Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1a/1a04a915ecd0551bfa9e77b7672d1937b4b72a0fc204a17deef76001cfb2/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71", size = 402084, upload-time = "2025-08-27T12:15:40.529Z" }, - { url = "https://files.pythonhosted.org/packages/51/f7/66585c0fe5714368b62951d2513b684e5215beaceab2c6629549ddb15036/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad", size = 419041, upload-time = "2025-08-27T12:15:42.191Z" }, - { url = "https://files.pythonhosted.org/packages/8e/7e/83a508f6b8e219bba2d4af077c35ba0e0cdd35a751a3be6a7cba5a55ad71/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab", size = 560084, upload-time = "2025-08-27T12:15:43.839Z" }, - { url = "https://files.pythonhosted.org/packages/66/66/bb945683b958a1b19eb0fe715594630d0f36396ebdef4d9b89c2fa09aa56/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059", size = 590115, upload-time = "2025-08-27T12:15:46.647Z" }, - { url = "https://files.pythonhosted.org/packages/12/00/ccfaafaf7db7e7adace915e5c2f2c2410e16402561801e9c7f96683002d3/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b", size = 556561, upload-time = "2025-08-27T12:15:48.219Z" }, - { url = "https://files.pythonhosted.org/packages/e1/b7/92b6ed9aad103bfe1c45df98453dfae40969eef2cb6c6239c58d7e96f1b3/rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819", size = 229125, upload-time = "2025-08-27T12:15:49.956Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ed/e1fba02de17f4f76318b834425257c8ea297e415e12c68b4361f63e8ae92/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df", size = 371402, upload-time = "2025-08-27T12:15:51.561Z" }, - { url = "https://files.pythonhosted.org/packages/af/7c/e16b959b316048b55585a697e94add55a4ae0d984434d279ea83442e460d/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3", size = 354084, upload-time = "2025-08-27T12:15:53.219Z" }, - { url = "https://files.pythonhosted.org/packages/de/c1/ade645f55de76799fdd08682d51ae6724cb46f318573f18be49b1e040428/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9", size = 383090, upload-time = "2025-08-27T12:15:55.158Z" }, - { url = "https://files.pythonhosted.org/packages/1f/27/89070ca9b856e52960da1472efcb6c20ba27cfe902f4f23ed095b9cfc61d/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc", size = 394519, upload-time = "2025-08-27T12:15:57.238Z" }, - { url = "https://files.pythonhosted.org/packages/b3/28/be120586874ef906aa5aeeae95ae8df4184bc757e5b6bd1c729ccff45ed5/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4", size = 523817, upload-time = "2025-08-27T12:15:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/70cc197bc11cfcde02a86f36ac1eed15c56667c2ebddbdb76a47e90306da/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66", size = 403240, upload-time = "2025-08-27T12:16:00.923Z" }, - { url = "https://files.pythonhosted.org/packages/cf/35/46936cca449f7f518f2f4996e0e8344db4b57e2081e752441154089d2a5f/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e", size = 385194, upload-time = "2025-08-27T12:16:02.802Z" }, - { url = "https://files.pythonhosted.org/packages/e1/62/29c0d3e5125c3270b51415af7cbff1ec587379c84f55a5761cc9efa8cd06/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c", size = 402086, upload-time = "2025-08-27T12:16:04.806Z" }, - { url = "https://files.pythonhosted.org/packages/8f/66/03e1087679227785474466fdd04157fb793b3b76e3fcf01cbf4c693c1949/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf", size = 419272, upload-time = "2025-08-27T12:16:06.471Z" }, - { url = "https://files.pythonhosted.org/packages/6a/24/e3e72d265121e00b063aef3e3501e5b2473cf1b23511d56e529531acf01e/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf", size = 560003, upload-time = "2025-08-27T12:16:08.06Z" }, - { url = "https://files.pythonhosted.org/packages/26/ca/f5a344c534214cc2d41118c0699fffbdc2c1bc7046f2a2b9609765ab9c92/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6", size = 590482, upload-time = "2025-08-27T12:16:10.137Z" }, - { url = "https://files.pythonhosted.org/packages/ce/08/4349bdd5c64d9d193c360aa9db89adeee6f6682ab8825dca0a3f535f434f/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a", size = 556523, upload-time = "2025-08-27T12:16:12.188Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ed/3aef893e2dd30e77e35d20d4ddb45ca459db59cead748cad9796ad479411/rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef", size = 371606 }, + { url = "https://files.pythonhosted.org/packages/6d/82/9818b443e5d3eb4c83c3994561387f116aae9833b35c484474769c4a8faf/rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be", size = 353452 }, + { url = "https://files.pythonhosted.org/packages/99/c7/d2a110ffaaa397fc6793a83c7bd3545d9ab22658b7cdff05a24a4535cc45/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61", size = 381519 }, + { url = "https://files.pythonhosted.org/packages/5a/bc/e89581d1f9d1be7d0247eaef602566869fdc0d084008ba139e27e775366c/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb", size = 394424 }, + { url = "https://files.pythonhosted.org/packages/ac/2e/36a6861f797530e74bb6ed53495f8741f1ef95939eed01d761e73d559067/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657", size = 523467 }, + { url = "https://files.pythonhosted.org/packages/c4/59/c1bc2be32564fa499f988f0a5c6505c2f4746ef96e58e4d7de5cf923d77e/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013", size = 402660 }, + { url = "https://files.pythonhosted.org/packages/0a/ec/ef8bf895f0628dd0a59e54d81caed6891663cb9c54a0f4bb7da918cb88cf/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a", size = 384062 }, + { url = "https://files.pythonhosted.org/packages/69/f7/f47ff154be8d9a5e691c083a920bba89cef88d5247c241c10b9898f595a1/rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1", size = 401289 }, + { url = "https://files.pythonhosted.org/packages/3b/d9/ca410363efd0615814ae579f6829cafb39225cd63e5ea5ed1404cb345293/rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10", size = 417718 }, + { url = "https://files.pythonhosted.org/packages/e3/a0/8cb5c2ff38340f221cc067cc093d1270e10658ba4e8d263df923daa18e86/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808", size = 558333 }, + { url = "https://files.pythonhosted.org/packages/6f/8c/1b0de79177c5d5103843774ce12b84caa7164dfc6cd66378768d37db11bf/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8", size = 589127 }, + { url = "https://files.pythonhosted.org/packages/c8/5e/26abb098d5e01266b0f3a2488d299d19ccc26849735d9d2b95c39397e945/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9", size = 554899 }, + { url = "https://files.pythonhosted.org/packages/de/41/905cc90ced13550db017f8f20c6d8e8470066c5738ba480d7ba63e3d136b/rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4", size = 217450 }, + { url = "https://files.pythonhosted.org/packages/75/3d/6bef47b0e253616ccdf67c283e25f2d16e18ccddd38f92af81d5a3420206/rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1", size = 228447 }, + { url = "https://files.pythonhosted.org/packages/b5/c1/7907329fbef97cbd49db6f7303893bd1dd5a4a3eae415839ffdfb0762cae/rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881", size = 371063 }, + { url = "https://files.pythonhosted.org/packages/11/94/2aab4bc86228bcf7c48760990273653a4900de89c7537ffe1b0d6097ed39/rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5", size = 353210 }, + { url = "https://files.pythonhosted.org/packages/3a/57/f5eb3ecf434342f4f1a46009530e93fd201a0b5b83379034ebdb1d7c1a58/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e", size = 381636 }, + { url = "https://files.pythonhosted.org/packages/ae/f4/ef95c5945e2ceb5119571b184dd5a1cc4b8541bbdf67461998cfeac9cb1e/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c", size = 394341 }, + { url = "https://files.pythonhosted.org/packages/5a/7e/4bd610754bf492d398b61725eb9598ddd5eb86b07d7d9483dbcd810e20bc/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195", size = 523428 }, + { url = "https://files.pythonhosted.org/packages/9f/e5/059b9f65a8c9149361a8b75094864ab83b94718344db511fd6117936ed2a/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52", size = 402923 }, + { url = "https://files.pythonhosted.org/packages/f5/48/64cabb7daced2968dd08e8a1b7988bf358d7bd5bcd5dc89a652f4668543c/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed", size = 384094 }, + { url = "https://files.pythonhosted.org/packages/ae/e1/dc9094d6ff566bff87add8a510c89b9e158ad2ecd97ee26e677da29a9e1b/rpds_py-0.27.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a", size = 401093 }, + { url = "https://files.pythonhosted.org/packages/37/8e/ac8577e3ecdd5593e283d46907d7011618994e1d7ab992711ae0f78b9937/rpds_py-0.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde", size = 417969 }, + { url = "https://files.pythonhosted.org/packages/66/6d/87507430a8f74a93556fe55c6485ba9c259949a853ce407b1e23fea5ba31/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21", size = 558302 }, + { url = "https://files.pythonhosted.org/packages/3a/bb/1db4781ce1dda3eecc735e3152659a27b90a02ca62bfeea17aee45cc0fbc/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9", size = 589259 }, + { url = "https://files.pythonhosted.org/packages/7b/0e/ae1c8943d11a814d01b482e1f8da903f88047a962dff9bbdadf3bd6e6fd1/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948", size = 554983 }, + { url = "https://files.pythonhosted.org/packages/b2/d5/0b2a55415931db4f112bdab072443ff76131b5ac4f4dc98d10d2d357eb03/rpds_py-0.27.1-cp311-cp311-win32.whl", hash = "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39", size = 217154 }, + { url = "https://files.pythonhosted.org/packages/24/75/3b7ffe0d50dc86a6a964af0d1cc3a4a2cdf437cb7b099a4747bbb96d1819/rpds_py-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15", size = 228627 }, + { url = "https://files.pythonhosted.org/packages/8d/3f/4fd04c32abc02c710f09a72a30c9a55ea3cc154ef8099078fd50a0596f8e/rpds_py-0.27.1-cp311-cp311-win_arm64.whl", hash = "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746", size = 220998 }, + { url = "https://files.pythonhosted.org/packages/bd/fe/38de28dee5df58b8198c743fe2bea0c785c6d40941b9950bac4cdb71a014/rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90", size = 361887 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/4b6c7eedc7dd90986bf0fab6ea2a091ec11c01b15f8ba0a14d3f80450468/rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5", size = 345795 }, + { url = "https://files.pythonhosted.org/packages/6f/0e/e650e1b81922847a09cca820237b0edee69416a01268b7754d506ade11ad/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e", size = 385121 }, + { url = "https://files.pythonhosted.org/packages/1b/ea/b306067a712988e2bff00dcc7c8f31d26c29b6d5931b461aa4b60a013e33/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881", size = 398976 }, + { url = "https://files.pythonhosted.org/packages/2c/0a/26dc43c8840cb8fe239fe12dbc8d8de40f2365e838f3d395835dde72f0e5/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec", size = 525953 }, + { url = "https://files.pythonhosted.org/packages/22/14/c85e8127b573aaf3a0cbd7fbb8c9c99e735a4a02180c84da2a463b766e9e/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb", size = 407915 }, + { url = "https://files.pythonhosted.org/packages/ed/7b/8f4fee9ba1fb5ec856eb22d725a4efa3deb47f769597c809e03578b0f9d9/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5", size = 386883 }, + { url = "https://files.pythonhosted.org/packages/86/47/28fa6d60f8b74fcdceba81b272f8d9836ac0340570f68f5df6b41838547b/rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a", size = 405699 }, + { url = "https://files.pythonhosted.org/packages/d0/fd/c5987b5e054548df56953a21fe2ebed51fc1ec7c8f24fd41c067b68c4a0a/rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444", size = 423713 }, + { url = "https://files.pythonhosted.org/packages/ac/ba/3c4978b54a73ed19a7d74531be37a8bcc542d917c770e14d372b8daea186/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a", size = 562324 }, + { url = "https://files.pythonhosted.org/packages/b5/6c/6943a91768fec16db09a42b08644b960cff540c66aab89b74be6d4a144ba/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1", size = 593646 }, + { url = "https://files.pythonhosted.org/packages/11/73/9d7a8f4be5f4396f011a6bb7a19fe26303a0dac9064462f5651ced2f572f/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998", size = 558137 }, + { url = "https://files.pythonhosted.org/packages/6e/96/6772cbfa0e2485bcceef8071de7821f81aeac8bb45fbfd5542a3e8108165/rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39", size = 221343 }, + { url = "https://files.pythonhosted.org/packages/67/b6/c82f0faa9af1c6a64669f73a17ee0eeef25aff30bb9a1c318509efe45d84/rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594", size = 232497 }, + { url = "https://files.pythonhosted.org/packages/e1/96/2817b44bd2ed11aebacc9251da03689d56109b9aba5e311297b6902136e2/rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502", size = 222790 }, + { url = "https://files.pythonhosted.org/packages/cc/77/610aeee8d41e39080c7e14afa5387138e3c9fa9756ab893d09d99e7d8e98/rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b", size = 361741 }, + { url = "https://files.pythonhosted.org/packages/3a/fc/c43765f201c6a1c60be2043cbdb664013def52460a4c7adace89d6682bf4/rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf", size = 345574 }, + { url = "https://files.pythonhosted.org/packages/20/42/ee2b2ca114294cd9847d0ef9c26d2b0851b2e7e00bf14cc4c0b581df0fc3/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83", size = 385051 }, + { url = "https://files.pythonhosted.org/packages/fd/e8/1e430fe311e4799e02e2d1af7c765f024e95e17d651612425b226705f910/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf", size = 398395 }, + { url = "https://files.pythonhosted.org/packages/82/95/9dc227d441ff2670651c27a739acb2535ccaf8b351a88d78c088965e5996/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2", size = 524334 }, + { url = "https://files.pythonhosted.org/packages/87/01/a670c232f401d9ad461d9a332aa4080cd3cb1d1df18213dbd0d2a6a7ab51/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0", size = 407691 }, + { url = "https://files.pythonhosted.org/packages/03/36/0a14aebbaa26fe7fab4780c76f2239e76cc95a0090bdb25e31d95c492fcd/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418", size = 386868 }, + { url = "https://files.pythonhosted.org/packages/3b/03/8c897fb8b5347ff6c1cc31239b9611c5bf79d78c984430887a353e1409a1/rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d", size = 405469 }, + { url = "https://files.pythonhosted.org/packages/da/07/88c60edc2df74850d496d78a1fdcdc7b54360a7f610a4d50008309d41b94/rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274", size = 422125 }, + { url = "https://files.pythonhosted.org/packages/6b/86/5f4c707603e41b05f191a749984f390dabcbc467cf833769b47bf14ba04f/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd", size = 562341 }, + { url = "https://files.pythonhosted.org/packages/b2/92/3c0cb2492094e3cd9baf9e49bbb7befeceb584ea0c1a8b5939dca4da12e5/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2", size = 592511 }, + { url = "https://files.pythonhosted.org/packages/10/bb/82e64fbb0047c46a168faa28d0d45a7851cd0582f850b966811d30f67ad8/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002", size = 557736 }, + { url = "https://files.pythonhosted.org/packages/00/95/3c863973d409210da7fb41958172c6b7dbe7fc34e04d3cc1f10bb85e979f/rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3", size = 221462 }, + { url = "https://files.pythonhosted.org/packages/ce/2c/5867b14a81dc217b56d95a9f2a40fdbc56a1ab0181b80132beeecbd4b2d6/rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83", size = 232034 }, + { url = "https://files.pythonhosted.org/packages/c7/78/3958f3f018c01923823f1e47f1cc338e398814b92d83cd278364446fac66/rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d", size = 222392 }, + { url = "https://files.pythonhosted.org/packages/01/76/1cdf1f91aed5c3a7bf2eba1f1c4e4d6f57832d73003919a20118870ea659/rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228", size = 358355 }, + { url = "https://files.pythonhosted.org/packages/c3/6f/bf142541229374287604caf3bb2a4ae17f0a580798fd72d3b009b532db4e/rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92", size = 342138 }, + { url = "https://files.pythonhosted.org/packages/1a/77/355b1c041d6be40886c44ff5e798b4e2769e497b790f0f7fd1e78d17e9a8/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2", size = 380247 }, + { url = "https://files.pythonhosted.org/packages/d6/a4/d9cef5c3946ea271ce2243c51481971cd6e34f21925af2783dd17b26e815/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723", size = 390699 }, + { url = "https://files.pythonhosted.org/packages/3a/06/005106a7b8c6c1a7e91b73169e49870f4af5256119d34a361ae5240a0c1d/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802", size = 521852 }, + { url = "https://files.pythonhosted.org/packages/e5/3e/50fb1dac0948e17a02eb05c24510a8fe12d5ce8561c6b7b7d1339ab7ab9c/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f", size = 402582 }, + { url = "https://files.pythonhosted.org/packages/cb/b0/f4e224090dc5b0ec15f31a02d746ab24101dd430847c4d99123798661bfc/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2", size = 384126 }, + { url = "https://files.pythonhosted.org/packages/54/77/ac339d5f82b6afff1df8f0fe0d2145cc827992cb5f8eeb90fc9f31ef7a63/rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21", size = 399486 }, + { url = "https://files.pythonhosted.org/packages/d6/29/3e1c255eee6ac358c056a57d6d6869baa00a62fa32eea5ee0632039c50a3/rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef", size = 414832 }, + { url = "https://files.pythonhosted.org/packages/3f/db/6d498b844342deb3fa1d030598db93937a9964fcf5cb4da4feb5f17be34b/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081", size = 557249 }, + { url = "https://files.pythonhosted.org/packages/60/f3/690dd38e2310b6f68858a331399b4d6dbb9132c3e8ef8b4333b96caf403d/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd", size = 587356 }, + { url = "https://files.pythonhosted.org/packages/86/e3/84507781cccd0145f35b1dc32c72675200c5ce8d5b30f813e49424ef68fc/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7", size = 555300 }, + { url = "https://files.pythonhosted.org/packages/e5/ee/375469849e6b429b3516206b4580a79e9ef3eb12920ddbd4492b56eaacbe/rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688", size = 216714 }, + { url = "https://files.pythonhosted.org/packages/21/87/3fc94e47c9bd0742660e84706c311a860dcae4374cf4a03c477e23ce605a/rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797", size = 228943 }, + { url = "https://files.pythonhosted.org/packages/70/36/b6e6066520a07cf029d385de869729a895917b411e777ab1cde878100a1d/rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334", size = 362472 }, + { url = "https://files.pythonhosted.org/packages/af/07/b4646032e0dcec0df9c73a3bd52f63bc6c5f9cda992f06bd0e73fe3fbebd/rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33", size = 345676 }, + { url = "https://files.pythonhosted.org/packages/b0/16/2f1003ee5d0af4bcb13c0cf894957984c32a6751ed7206db2aee7379a55e/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a", size = 385313 }, + { url = "https://files.pythonhosted.org/packages/05/cd/7eb6dd7b232e7f2654d03fa07f1414d7dfc980e82ba71e40a7c46fd95484/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b", size = 399080 }, + { url = "https://files.pythonhosted.org/packages/20/51/5829afd5000ec1cb60f304711f02572d619040aa3ec033d8226817d1e571/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7", size = 523868 }, + { url = "https://files.pythonhosted.org/packages/05/2c/30eebca20d5db95720ab4d2faec1b5e4c1025c473f703738c371241476a2/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136", size = 408750 }, + { url = "https://files.pythonhosted.org/packages/90/1a/cdb5083f043597c4d4276eae4e4c70c55ab5accec078da8611f24575a367/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff", size = 387688 }, + { url = "https://files.pythonhosted.org/packages/7c/92/cf786a15320e173f945d205ab31585cc43969743bb1a48b6888f7a2b0a2d/rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9", size = 407225 }, + { url = "https://files.pythonhosted.org/packages/33/5c/85ee16df5b65063ef26017bef33096557a4c83fbe56218ac7cd8c235f16d/rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60", size = 423361 }, + { url = "https://files.pythonhosted.org/packages/4b/8e/1c2741307fcabd1a334ecf008e92c4f47bb6f848712cf15c923becfe82bb/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e", size = 562493 }, + { url = "https://files.pythonhosted.org/packages/04/03/5159321baae9b2222442a70c1f988cbbd66b9be0675dd3936461269be360/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212", size = 592623 }, + { url = "https://files.pythonhosted.org/packages/ff/39/c09fd1ad28b85bc1d4554a8710233c9f4cefd03d7717a1b8fbfd171d1167/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675", size = 558800 }, + { url = "https://files.pythonhosted.org/packages/c5/d6/99228e6bbcf4baa764b18258f519a9035131d91b538d4e0e294313462a98/rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3", size = 221943 }, + { url = "https://files.pythonhosted.org/packages/be/07/c802bc6b8e95be83b79bdf23d1aa61d68324cb1006e245d6c58e959e314d/rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456", size = 233739 }, + { url = "https://files.pythonhosted.org/packages/c8/89/3e1b1c16d4c2d547c5717377a8df99aee8099ff050f87c45cb4d5fa70891/rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3", size = 223120 }, + { url = "https://files.pythonhosted.org/packages/62/7e/dc7931dc2fa4a6e46b2a4fa744a9fe5c548efd70e0ba74f40b39fa4a8c10/rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2", size = 358944 }, + { url = "https://files.pythonhosted.org/packages/e6/22/4af76ac4e9f336bfb1a5f240d18a33c6b2fcaadb7472ac7680576512b49a/rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4", size = 342283 }, + { url = "https://files.pythonhosted.org/packages/1c/15/2a7c619b3c2272ea9feb9ade67a45c40b3eeb500d503ad4c28c395dc51b4/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e", size = 380320 }, + { url = "https://files.pythonhosted.org/packages/a2/7d/4c6d243ba4a3057e994bb5bedd01b5c963c12fe38dde707a52acdb3849e7/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817", size = 391760 }, + { url = "https://files.pythonhosted.org/packages/b4/71/b19401a909b83bcd67f90221330bc1ef11bc486fe4e04c24388d28a618ae/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec", size = 522476 }, + { url = "https://files.pythonhosted.org/packages/e4/44/1a3b9715c0455d2e2f0f6df5ee6d6f5afdc423d0773a8a682ed2b43c566c/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a", size = 403418 }, + { url = "https://files.pythonhosted.org/packages/1c/4b/fb6c4f14984eb56673bc868a66536f53417ddb13ed44b391998100a06a96/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8", size = 384771 }, + { url = "https://files.pythonhosted.org/packages/c0/56/d5265d2d28b7420d7b4d4d85cad8ef891760f5135102e60d5c970b976e41/rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48", size = 400022 }, + { url = "https://files.pythonhosted.org/packages/8f/e9/9f5fc70164a569bdd6ed9046486c3568d6926e3a49bdefeeccfb18655875/rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb", size = 416787 }, + { url = "https://files.pythonhosted.org/packages/d4/64/56dd03430ba491db943a81dcdef115a985aac5f44f565cd39a00c766d45c/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734", size = 557538 }, + { url = "https://files.pythonhosted.org/packages/3f/36/92cc885a3129993b1d963a2a42ecf64e6a8e129d2c7cc980dbeba84e55fb/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb", size = 588512 }, + { url = "https://files.pythonhosted.org/packages/dd/10/6b283707780a81919f71625351182b4f98932ac89a09023cb61865136244/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0", size = 555813 }, + { url = "https://files.pythonhosted.org/packages/04/2e/30b5ea18c01379da6272a92825dd7e53dc9d15c88a19e97932d35d430ef7/rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a", size = 217385 }, + { url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097 }, + { url = "https://files.pythonhosted.org/packages/d5/63/b7cc415c345625d5e62f694ea356c58fb964861409008118f1245f8c3347/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf", size = 371360 }, + { url = "https://files.pythonhosted.org/packages/e5/8c/12e1b24b560cf378b8ffbdb9dc73abd529e1adcfcf82727dfd29c4a7b88d/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3", size = 353933 }, + { url = "https://files.pythonhosted.org/packages/9b/85/1bb2210c1f7a1b99e91fea486b9f0f894aa5da3a5ec7097cbad7dec6d40f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636", size = 382962 }, + { url = "https://files.pythonhosted.org/packages/cc/c9/a839b9f219cf80ed65f27a7f5ddbb2809c1b85c966020ae2dff490e0b18e/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8", size = 394412 }, + { url = "https://files.pythonhosted.org/packages/02/2d/b1d7f928b0b1f4fc2e0133e8051d199b01d7384875adc63b6ddadf3de7e5/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc", size = 523972 }, + { url = "https://files.pythonhosted.org/packages/a9/af/2cbf56edd2d07716df1aec8a726b3159deb47cb5c27e1e42b71d705a7c2f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8", size = 403273 }, + { url = "https://files.pythonhosted.org/packages/c0/93/425e32200158d44ff01da5d9612c3b6711fe69f606f06e3895511f17473b/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc", size = 385278 }, + { url = "https://files.pythonhosted.org/packages/eb/1a/1a04a915ecd0551bfa9e77b7672d1937b4b72a0fc204a17deef76001cfb2/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71", size = 402084 }, + { url = "https://files.pythonhosted.org/packages/51/f7/66585c0fe5714368b62951d2513b684e5215beaceab2c6629549ddb15036/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad", size = 419041 }, + { url = "https://files.pythonhosted.org/packages/8e/7e/83a508f6b8e219bba2d4af077c35ba0e0cdd35a751a3be6a7cba5a55ad71/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab", size = 560084 }, + { url = "https://files.pythonhosted.org/packages/66/66/bb945683b958a1b19eb0fe715594630d0f36396ebdef4d9b89c2fa09aa56/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059", size = 590115 }, + { url = "https://files.pythonhosted.org/packages/12/00/ccfaafaf7db7e7adace915e5c2f2c2410e16402561801e9c7f96683002d3/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b", size = 556561 }, + { url = "https://files.pythonhosted.org/packages/e1/b7/92b6ed9aad103bfe1c45df98453dfae40969eef2cb6c6239c58d7e96f1b3/rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819", size = 229125 }, + { url = "https://files.pythonhosted.org/packages/0c/ed/e1fba02de17f4f76318b834425257c8ea297e415e12c68b4361f63e8ae92/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df", size = 371402 }, + { url = "https://files.pythonhosted.org/packages/af/7c/e16b959b316048b55585a697e94add55a4ae0d984434d279ea83442e460d/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3", size = 354084 }, + { url = "https://files.pythonhosted.org/packages/de/c1/ade645f55de76799fdd08682d51ae6724cb46f318573f18be49b1e040428/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9", size = 383090 }, + { url = "https://files.pythonhosted.org/packages/1f/27/89070ca9b856e52960da1472efcb6c20ba27cfe902f4f23ed095b9cfc61d/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc", size = 394519 }, + { url = "https://files.pythonhosted.org/packages/b3/28/be120586874ef906aa5aeeae95ae8df4184bc757e5b6bd1c729ccff45ed5/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4", size = 523817 }, + { url = "https://files.pythonhosted.org/packages/a8/ef/70cc197bc11cfcde02a86f36ac1eed15c56667c2ebddbdb76a47e90306da/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66", size = 403240 }, + { url = "https://files.pythonhosted.org/packages/cf/35/46936cca449f7f518f2f4996e0e8344db4b57e2081e752441154089d2a5f/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e", size = 385194 }, + { url = "https://files.pythonhosted.org/packages/e1/62/29c0d3e5125c3270b51415af7cbff1ec587379c84f55a5761cc9efa8cd06/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c", size = 402086 }, + { url = "https://files.pythonhosted.org/packages/8f/66/03e1087679227785474466fdd04157fb793b3b76e3fcf01cbf4c693c1949/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf", size = 419272 }, + { url = "https://files.pythonhosted.org/packages/6a/24/e3e72d265121e00b063aef3e3501e5b2473cf1b23511d56e529531acf01e/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf", size = 560003 }, + { url = "https://files.pythonhosted.org/packages/26/ca/f5a344c534214cc2d41118c0699fffbdc2c1bc7046f2a2b9609765ab9c92/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6", size = 590482 }, + { url = "https://files.pythonhosted.org/packages/ce/08/4349bdd5c64d9d193c360aa9db89adeee6f6682ab8825dca0a3f535f434f/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a", size = 556523 }, ] [[package]] name = "ruff" version = "0.12.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a8/f0/e0965dd709b8cabe6356811c0ee8c096806bb57d20b5019eb4e48a117410/ruff-0.12.12.tar.gz", hash = "sha256:b86cd3415dbe31b3b46a71c598f4c4b2f550346d1ccf6326b347cc0c8fd063d6", size = 5359915, upload-time = "2025-09-04T16:50:18.273Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/f0/e0965dd709b8cabe6356811c0ee8c096806bb57d20b5019eb4e48a117410/ruff-0.12.12.tar.gz", hash = "sha256:b86cd3415dbe31b3b46a71c598f4c4b2f550346d1ccf6326b347cc0c8fd063d6", size = 5359915 } wheels = [ - { url = "https://files.pythonhosted.org/packages/09/79/8d3d687224d88367b51c7974cec1040c4b015772bfbeffac95face14c04a/ruff-0.12.12-py3-none-linux_armv6l.whl", hash = "sha256:de1c4b916d98ab289818e55ce481e2cacfaad7710b01d1f990c497edf217dafc", size = 12116602, upload-time = "2025-09-04T16:49:18.892Z" }, - { url = "https://files.pythonhosted.org/packages/c3/c3/6e599657fe192462f94861a09aae935b869aea8a1da07f47d6eae471397c/ruff-0.12.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7acd6045e87fac75a0b0cdedacf9ab3e1ad9d929d149785903cff9bb69ad9727", size = 12868393, upload-time = "2025-09-04T16:49:23.043Z" }, - { url = "https://files.pythonhosted.org/packages/e8/d2/9e3e40d399abc95336b1843f52fc0daaceb672d0e3c9290a28ff1a96f79d/ruff-0.12.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:abf4073688d7d6da16611f2f126be86523a8ec4343d15d276c614bda8ec44edb", size = 12036967, upload-time = "2025-09-04T16:49:26.04Z" }, - { url = "https://files.pythonhosted.org/packages/e9/03/6816b2ed08836be272e87107d905f0908be5b4a40c14bfc91043e76631b8/ruff-0.12.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:968e77094b1d7a576992ac078557d1439df678a34c6fe02fd979f973af167577", size = 12276038, upload-time = "2025-09-04T16:49:29.056Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d5/707b92a61310edf358a389477eabd8af68f375c0ef858194be97ca5b6069/ruff-0.12.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42a67d16e5b1ffc6d21c5f67851e0e769517fb57a8ebad1d0781b30888aa704e", size = 11901110, upload-time = "2025-09-04T16:49:32.07Z" }, - { url = "https://files.pythonhosted.org/packages/9d/3d/f8b1038f4b9822e26ec3d5b49cf2bc313e3c1564cceb4c1a42820bf74853/ruff-0.12.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b216ec0a0674e4b1214dcc998a5088e54eaf39417327b19ffefba1c4a1e4971e", size = 13668352, upload-time = "2025-09-04T16:49:35.148Z" }, - { url = "https://files.pythonhosted.org/packages/98/0e/91421368ae6c4f3765dd41a150f760c5f725516028a6be30e58255e3c668/ruff-0.12.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:59f909c0fdd8f1dcdbfed0b9569b8bf428cf144bec87d9de298dcd4723f5bee8", size = 14638365, upload-time = "2025-09-04T16:49:38.892Z" }, - { url = "https://files.pythonhosted.org/packages/74/5d/88f3f06a142f58ecc8ecb0c2fe0b82343e2a2b04dcd098809f717cf74b6c/ruff-0.12.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ac93d87047e765336f0c18eacad51dad0c1c33c9df7484c40f98e1d773876f5", size = 14060812, upload-time = "2025-09-04T16:49:42.732Z" }, - { url = "https://files.pythonhosted.org/packages/13/fc/8962e7ddd2e81863d5c92400820f650b86f97ff919c59836fbc4c1a6d84c/ruff-0.12.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01543c137fd3650d322922e8b14cc133b8ea734617c4891c5a9fccf4bfc9aa92", size = 13050208, upload-time = "2025-09-04T16:49:46.434Z" }, - { url = "https://files.pythonhosted.org/packages/53/06/8deb52d48a9a624fd37390555d9589e719eac568c020b27e96eed671f25f/ruff-0.12.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afc2fa864197634e549d87fb1e7b6feb01df0a80fd510d6489e1ce8c0b1cc45", size = 13311444, upload-time = "2025-09-04T16:49:49.931Z" }, - { url = "https://files.pythonhosted.org/packages/2a/81/de5a29af7eb8f341f8140867ffb93f82e4fde7256dadee79016ac87c2716/ruff-0.12.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0c0945246f5ad776cb8925e36af2438e66188d2b57d9cf2eed2c382c58b371e5", size = 13279474, upload-time = "2025-09-04T16:49:53.465Z" }, - { url = "https://files.pythonhosted.org/packages/7f/14/d9577fdeaf791737ada1b4f5c6b59c21c3326f3f683229096cccd7674e0c/ruff-0.12.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a0fbafe8c58e37aae28b84a80ba1817f2ea552e9450156018a478bf1fa80f4e4", size = 12070204, upload-time = "2025-09-04T16:49:56.882Z" }, - { url = "https://files.pythonhosted.org/packages/77/04/a910078284b47fad54506dc0af13839c418ff704e341c176f64e1127e461/ruff-0.12.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b9c456fb2fc8e1282affa932c9e40f5ec31ec9cbb66751a316bd131273b57c23", size = 11880347, upload-time = "2025-09-04T16:49:59.729Z" }, - { url = "https://files.pythonhosted.org/packages/df/58/30185fcb0e89f05e7ea82e5817b47798f7fa7179863f9d9ba6fd4fe1b098/ruff-0.12.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5f12856123b0ad0147d90b3961f5c90e7427f9acd4b40050705499c98983f489", size = 12891844, upload-time = "2025-09-04T16:50:02.591Z" }, - { url = "https://files.pythonhosted.org/packages/21/9c/28a8dacce4855e6703dcb8cdf6c1705d0b23dd01d60150786cd55aa93b16/ruff-0.12.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:26a1b5a2bf7dd2c47e3b46d077cd9c0fc3b93e6c6cc9ed750bd312ae9dc302ee", size = 13360687, upload-time = "2025-09-04T16:50:05.8Z" }, - { url = "https://files.pythonhosted.org/packages/c8/fa/05b6428a008e60f79546c943e54068316f32ec8ab5c4f73e4563934fbdc7/ruff-0.12.12-py3-none-win32.whl", hash = "sha256:173be2bfc142af07a01e3a759aba6f7791aa47acf3604f610b1c36db888df7b1", size = 12052870, upload-time = "2025-09-04T16:50:09.121Z" }, - { url = "https://files.pythonhosted.org/packages/85/60/d1e335417804df452589271818749d061b22772b87efda88354cf35cdb7a/ruff-0.12.12-py3-none-win_amd64.whl", hash = "sha256:e99620bf01884e5f38611934c09dd194eb665b0109104acae3ba6102b600fd0d", size = 13178016, upload-time = "2025-09-04T16:50:12.559Z" }, - { url = "https://files.pythonhosted.org/packages/28/7e/61c42657f6e4614a4258f1c3b0c5b93adc4d1f8575f5229d1906b483099b/ruff-0.12.12-py3-none-win_arm64.whl", hash = "sha256:2a8199cab4ce4d72d158319b63370abf60991495fb733db96cd923a34c52d093", size = 12256762, upload-time = "2025-09-04T16:50:15.737Z" }, + { url = "https://files.pythonhosted.org/packages/09/79/8d3d687224d88367b51c7974cec1040c4b015772bfbeffac95face14c04a/ruff-0.12.12-py3-none-linux_armv6l.whl", hash = "sha256:de1c4b916d98ab289818e55ce481e2cacfaad7710b01d1f990c497edf217dafc", size = 12116602 }, + { url = "https://files.pythonhosted.org/packages/c3/c3/6e599657fe192462f94861a09aae935b869aea8a1da07f47d6eae471397c/ruff-0.12.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7acd6045e87fac75a0b0cdedacf9ab3e1ad9d929d149785903cff9bb69ad9727", size = 12868393 }, + { url = "https://files.pythonhosted.org/packages/e8/d2/9e3e40d399abc95336b1843f52fc0daaceb672d0e3c9290a28ff1a96f79d/ruff-0.12.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:abf4073688d7d6da16611f2f126be86523a8ec4343d15d276c614bda8ec44edb", size = 12036967 }, + { url = "https://files.pythonhosted.org/packages/e9/03/6816b2ed08836be272e87107d905f0908be5b4a40c14bfc91043e76631b8/ruff-0.12.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:968e77094b1d7a576992ac078557d1439df678a34c6fe02fd979f973af167577", size = 12276038 }, + { url = "https://files.pythonhosted.org/packages/9f/d5/707b92a61310edf358a389477eabd8af68f375c0ef858194be97ca5b6069/ruff-0.12.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42a67d16e5b1ffc6d21c5f67851e0e769517fb57a8ebad1d0781b30888aa704e", size = 11901110 }, + { url = "https://files.pythonhosted.org/packages/9d/3d/f8b1038f4b9822e26ec3d5b49cf2bc313e3c1564cceb4c1a42820bf74853/ruff-0.12.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b216ec0a0674e4b1214dcc998a5088e54eaf39417327b19ffefba1c4a1e4971e", size = 13668352 }, + { url = "https://files.pythonhosted.org/packages/98/0e/91421368ae6c4f3765dd41a150f760c5f725516028a6be30e58255e3c668/ruff-0.12.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:59f909c0fdd8f1dcdbfed0b9569b8bf428cf144bec87d9de298dcd4723f5bee8", size = 14638365 }, + { url = "https://files.pythonhosted.org/packages/74/5d/88f3f06a142f58ecc8ecb0c2fe0b82343e2a2b04dcd098809f717cf74b6c/ruff-0.12.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ac93d87047e765336f0c18eacad51dad0c1c33c9df7484c40f98e1d773876f5", size = 14060812 }, + { url = "https://files.pythonhosted.org/packages/13/fc/8962e7ddd2e81863d5c92400820f650b86f97ff919c59836fbc4c1a6d84c/ruff-0.12.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01543c137fd3650d322922e8b14cc133b8ea734617c4891c5a9fccf4bfc9aa92", size = 13050208 }, + { url = "https://files.pythonhosted.org/packages/53/06/8deb52d48a9a624fd37390555d9589e719eac568c020b27e96eed671f25f/ruff-0.12.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afc2fa864197634e549d87fb1e7b6feb01df0a80fd510d6489e1ce8c0b1cc45", size = 13311444 }, + { url = "https://files.pythonhosted.org/packages/2a/81/de5a29af7eb8f341f8140867ffb93f82e4fde7256dadee79016ac87c2716/ruff-0.12.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0c0945246f5ad776cb8925e36af2438e66188d2b57d9cf2eed2c382c58b371e5", size = 13279474 }, + { url = "https://files.pythonhosted.org/packages/7f/14/d9577fdeaf791737ada1b4f5c6b59c21c3326f3f683229096cccd7674e0c/ruff-0.12.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a0fbafe8c58e37aae28b84a80ba1817f2ea552e9450156018a478bf1fa80f4e4", size = 12070204 }, + { url = "https://files.pythonhosted.org/packages/77/04/a910078284b47fad54506dc0af13839c418ff704e341c176f64e1127e461/ruff-0.12.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b9c456fb2fc8e1282affa932c9e40f5ec31ec9cbb66751a316bd131273b57c23", size = 11880347 }, + { url = "https://files.pythonhosted.org/packages/df/58/30185fcb0e89f05e7ea82e5817b47798f7fa7179863f9d9ba6fd4fe1b098/ruff-0.12.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5f12856123b0ad0147d90b3961f5c90e7427f9acd4b40050705499c98983f489", size = 12891844 }, + { url = "https://files.pythonhosted.org/packages/21/9c/28a8dacce4855e6703dcb8cdf6c1705d0b23dd01d60150786cd55aa93b16/ruff-0.12.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:26a1b5a2bf7dd2c47e3b46d077cd9c0fc3b93e6c6cc9ed750bd312ae9dc302ee", size = 13360687 }, + { url = "https://files.pythonhosted.org/packages/c8/fa/05b6428a008e60f79546c943e54068316f32ec8ab5c4f73e4563934fbdc7/ruff-0.12.12-py3-none-win32.whl", hash = "sha256:173be2bfc142af07a01e3a759aba6f7791aa47acf3604f610b1c36db888df7b1", size = 12052870 }, + { url = "https://files.pythonhosted.org/packages/85/60/d1e335417804df452589271818749d061b22772b87efda88354cf35cdb7a/ruff-0.12.12-py3-none-win_amd64.whl", hash = "sha256:e99620bf01884e5f38611934c09dd194eb665b0109104acae3ba6102b600fd0d", size = 13178016 }, + { url = "https://files.pythonhosted.org/packages/28/7e/61c42657f6e4614a4258f1c3b0c5b93adc4d1f8575f5229d1906b483099b/ruff-0.12.12-py3-none-win_arm64.whl", hash = "sha256:2a8199cab4ce4d72d158319b63370abf60991495fb733db96cd923a34c52d093", size = 12256762 }, ] [[package]] name = "six" version = "1.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, ] [[package]] name = "sniffio" version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, ] [[package]] name = "snowballstemmer" version = "3.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } +sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274 }, ] [[package]] name = "soupsieve" version = "2.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472, upload-time = "2025-08-27T15:39:51.78Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472 } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" }, + { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679 }, ] [[package]] @@ -3365,9 +3694,9 @@ dependencies = [ { name = "sphinxcontrib-serializinghtml", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, { name = "tomli", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611 } wheels = [ - { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125, upload-time = "2024-10-13T20:27:10.448Z" }, + { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125 }, ] [[package]] @@ -3376,11 +3705,14 @@ version = "8.2.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.13' and sys_platform == 'darwin'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", "python_full_version >= '3.13' and sys_platform == 'linux'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and sys_platform == 'linux'", "python_full_version >= '3.13' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", ] dependencies = [ { name = "alabaster", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, @@ -3401,9 +3733,9 @@ dependencies = [ { name = "sphinxcontrib-qthelp", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, { name = "sphinxcontrib-serializinghtml", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876, upload-time = "2025-03-02T22:31:59.658Z" } +sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876 } wheels = [ - { url = "https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741, upload-time = "2025-03-02T22:31:56.836Z" }, + { url = "https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741 }, ] [[package]] @@ -3423,9 +3755,9 @@ dependencies = [ { name = "watchfiles", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, { name = "websockets", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/2c/155e1de2c1ba96a72e5dba152c509a8b41e047ee5c2def9e9f0d812f8be7/sphinx_autobuild-2024.10.3.tar.gz", hash = "sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1", size = 14023, upload-time = "2024-10-02T23:15:30.172Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/2c/155e1de2c1ba96a72e5dba152c509a8b41e047ee5c2def9e9f0d812f8be7/sphinx_autobuild-2024.10.3.tar.gz", hash = "sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1", size = 14023 } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/c0/eba125db38c84d3c74717008fd3cb5000b68cd7e2cbafd1349c6a38c3d3b/sphinx_autobuild-2024.10.3-py3-none-any.whl", hash = "sha256:158e16c36f9d633e613c9aaf81c19b0fc458ca78b112533b20dafcda430d60fa", size = 11908, upload-time = "2024-10-02T23:15:28.739Z" }, + { url = "https://files.pythonhosted.org/packages/18/c0/eba125db38c84d3c74717008fd3cb5000b68cd7e2cbafd1349c6a38c3d3b/sphinx_autobuild-2024.10.3-py3-none-any.whl", hash = "sha256:158e16c36f9d633e613c9aaf81c19b0fc458ca78b112533b20dafcda430d60fa", size = 11908 }, ] [[package]] @@ -3434,11 +3766,14 @@ version = "2025.8.25" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.13' and sys_platform == 'darwin'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and sys_platform == 'darwin'", "python_full_version >= '3.13' and sys_platform == 'linux'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and sys_platform == 'linux'", "python_full_version >= '3.13' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version == '3.12.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", ] dependencies = [ { name = "colorama", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, @@ -3448,9 +3783,9 @@ dependencies = [ { name = "watchfiles", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, { name = "websockets", marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e0/3c/a59a3a453d4133777f7ed2e83c80b7dc817d43c74b74298ca0af869662ad/sphinx_autobuild-2025.8.25.tar.gz", hash = "sha256:9cf5aab32853c8c31af572e4fecdc09c997e2b8be5a07daf2a389e270e85b213", size = 15200, upload-time = "2025-08-25T18:44:55.436Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/3c/a59a3a453d4133777f7ed2e83c80b7dc817d43c74b74298ca0af869662ad/sphinx_autobuild-2025.8.25.tar.gz", hash = "sha256:9cf5aab32853c8c31af572e4fecdc09c997e2b8be5a07daf2a389e270e85b213", size = 15200 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/20/56411b52f917696995f5ad27d2ea7e9492c84a043c5b49a3a3173573cd93/sphinx_autobuild-2025.8.25-py3-none-any.whl", hash = "sha256:b750ac7d5a18603e4665294323fd20f6dcc0a984117026d1986704fa68f0379a", size = 12535, upload-time = "2025-08-25T18:44:54.164Z" }, + { url = "https://files.pythonhosted.org/packages/d7/20/56411b52f917696995f5ad27d2ea7e9492c84a043c5b49a3a3173573cd93/sphinx_autobuild-2025.8.25-py3-none-any.whl", hash = "sha256:b750ac7d5a18603e4665294323fd20f6dcc0a984117026d1986704fa68f0379a", size = 12535 }, ] [[package]] @@ -3461,9 +3796,9 @@ dependencies = [ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039, upload-time = "2023-04-14T08:10:22.998Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343, upload-time = "2023-04-14T08:10:20.844Z" }, + { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343 }, ] [[package]] @@ -3474,63 +3809,63 @@ dependencies = [ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2b/69/b34e0cb5336f09c6866d53b4a19d76c227cdec1bbc7ac4de63ca7d58c9c7/sphinx_design-0.6.1.tar.gz", hash = "sha256:b44eea3719386d04d765c1a8257caca2b3e6f8421d7b3a5e742c0fd45f84e632", size = 2193689, upload-time = "2024-08-02T13:48:44.277Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/69/b34e0cb5336f09c6866d53b4a19d76c227cdec1bbc7ac4de63ca7d58c9c7/sphinx_design-0.6.1.tar.gz", hash = "sha256:b44eea3719386d04d765c1a8257caca2b3e6f8421d7b3a5e742c0fd45f84e632", size = 2193689 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/43/65c0acbd8cc6f50195a3a1fc195c404988b15c67090e73c7a41a9f57d6bd/sphinx_design-0.6.1-py3-none-any.whl", hash = "sha256:b11f37db1a802a183d61b159d9a202314d4d2fe29c163437001324fe2f19549c", size = 2215338, upload-time = "2024-08-02T13:48:42.106Z" }, + { url = "https://files.pythonhosted.org/packages/c6/43/65c0acbd8cc6f50195a3a1fc195c404988b15c67090e73c7a41a9f57d6bd/sphinx_design-0.6.1-py3-none-any.whl", hash = "sha256:b11f37db1a802a183d61b159d9a202314d4d2fe29c163437001324fe2f19549c", size = 2215338 }, ] [[package]] name = "sphinxcontrib-applehelp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, + { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 }, ] [[package]] name = "sphinxcontrib-devhelp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 } wheels = [ - { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, + { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 }, ] [[package]] name = "sphinxcontrib-htmlhelp" version = "2.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, + { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 }, ] [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 }, ] [[package]] name = "sphinxcontrib-qthelp" version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, + { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 }, ] [[package]] name = "sphinxcontrib-serializinghtml" version = "2.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 } wheels = [ - { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, + { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 }, ] [[package]] @@ -3541,9 +3876,9 @@ dependencies = [ { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1f/b4/e5fbb493f796430230189a1ce5f9beff1ac1b98619fc71ed35deca6059a5/sphinxext-rediraffe-0.2.7.tar.gz", hash = "sha256:651dcbfae5ffda9ffd534dfb8025f36120e5efb6ea1a33f5420023862b9f725d", size = 8735, upload-time = "2021-04-16T11:42:28.206Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/b4/e5fbb493f796430230189a1ce5f9beff1ac1b98619fc71ed35deca6059a5/sphinxext-rediraffe-0.2.7.tar.gz", hash = "sha256:651dcbfae5ffda9ffd534dfb8025f36120e5efb6ea1a33f5420023862b9f725d", size = 8735 } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/4f/c8797e796199e55cf6c8979ecdf5f4b09b81e93f87b3193c759faea63263/sphinxext_rediraffe-0.2.7-py3-none-any.whl", hash = "sha256:9e430a52d4403847f4ffb3a8dd6dfc34a9fe43525305131f52ed899743a5fd8c", size = 8267, upload-time = "2021-04-16T11:42:26.95Z" }, + { url = "https://files.pythonhosted.org/packages/76/4f/c8797e796199e55cf6c8979ecdf5f4b09b81e93f87b3193c759faea63263/sphinxext_rediraffe-0.2.7-py3-none-any.whl", hash = "sha256:9e430a52d4403847f4ffb3a8dd6dfc34a9fe43525305131f52ed899743a5fd8c", size = 8267 }, ] [[package]] @@ -3554,41 +3889,41 @@ dependencies = [ { name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'darwin') or (python_full_version < '3.14' and platform_machine == 'WIN32' and sys_platform == 'darwin') or (python_full_version < '3.14' and platform_machine == 'aarch64' and sys_platform == 'darwin') or (python_full_version < '3.14' and platform_machine == 'amd64' and sys_platform == 'darwin') or (python_full_version < '3.14' and platform_machine == 'ppc64le' and sys_platform == 'darwin') or (python_full_version < '3.14' and platform_machine == 'win32' and sys_platform == 'darwin') or (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'WIN32' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'amd64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'ppc64le' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'win32' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32') or (python_full_version < '3.14' and platform_machine == 'WIN32' and sys_platform == 'win32') or (python_full_version < '3.14' and platform_machine == 'aarch64' and sys_platform == 'win32') or (python_full_version < '3.14' and platform_machine == 'amd64' and sys_platform == 'win32') or (python_full_version < '3.14' and platform_machine == 'ppc64le' and sys_platform == 'win32') or (python_full_version < '3.14' and platform_machine == 'win32' and sys_platform == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'win32')" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d7/bc/d59b5d97d27229b0e009bd9098cd81af71c2fa5549c580a0a67b9bed0496/sqlalchemy-2.0.43.tar.gz", hash = "sha256:788bfcef6787a7764169cfe9859fe425bf44559619e1d9f56f5bddf2ebf6f417", size = 9762949, upload-time = "2025-08-11T14:24:58.438Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/bc/d59b5d97d27229b0e009bd9098cd81af71c2fa5549c580a0a67b9bed0496/sqlalchemy-2.0.43.tar.gz", hash = "sha256:788bfcef6787a7764169cfe9859fe425bf44559619e1d9f56f5bddf2ebf6f417", size = 9762949 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/4e/985f7da36f09592c5ade99321c72c15101d23c0bb7eecfd1daaca5714422/sqlalchemy-2.0.43-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70322986c0c699dca241418fcf18e637a4369e0ec50540a2b907b184c8bca069", size = 2133162, upload-time = "2025-08-11T15:52:17.854Z" }, - { url = "https://files.pythonhosted.org/packages/37/34/798af8db3cae069461e3bc0898a1610dc469386a97048471d364dc8aae1c/sqlalchemy-2.0.43-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:87accdbba88f33efa7b592dc2e8b2a9c2cdbca73db2f9d5c510790428c09c154", size = 2123082, upload-time = "2025-08-11T15:52:19.181Z" }, - { url = "https://files.pythonhosted.org/packages/fb/0f/79cf4d9dad42f61ec5af1e022c92f66c2d110b93bb1dc9b033892971abfa/sqlalchemy-2.0.43-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c00e7845d2f692ebfc7d5e4ec1a3fd87698e4337d09e58d6749a16aedfdf8612", size = 3208871, upload-time = "2025-08-11T15:50:30.656Z" }, - { url = "https://files.pythonhosted.org/packages/56/b3/59befa58fb0e1a9802c87df02344548e6d007e77e87e6084e2131c29e033/sqlalchemy-2.0.43-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:022e436a1cb39b13756cf93b48ecce7aa95382b9cfacceb80a7d263129dfd019", size = 3209583, upload-time = "2025-08-11T15:57:47.697Z" }, - { url = "https://files.pythonhosted.org/packages/29/d2/124b50c0eb8146e8f0fe16d01026c1a073844f0b454436d8544fe9b33bd7/sqlalchemy-2.0.43-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c5e73ba0d76eefc82ec0219d2301cb33bfe5205ed7a2602523111e2e56ccbd20", size = 3148177, upload-time = "2025-08-11T15:50:32.078Z" }, - { url = "https://files.pythonhosted.org/packages/83/f5/e369cd46aa84278107624617034a5825fedfc5c958b2836310ced4d2eadf/sqlalchemy-2.0.43-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9c2e02f06c68092b875d5cbe4824238ab93a7fa35d9c38052c033f7ca45daa18", size = 3172276, upload-time = "2025-08-11T15:57:49.477Z" }, - { url = "https://files.pythonhosted.org/packages/de/2b/4602bf4c3477fa4c837c9774e6dd22e0389fc52310c4c4dfb7e7ba05e90d/sqlalchemy-2.0.43-cp310-cp310-win32.whl", hash = "sha256:e7a903b5b45b0d9fa03ac6a331e1c1d6b7e0ab41c63b6217b3d10357b83c8b00", size = 2101491, upload-time = "2025-08-11T15:54:59.191Z" }, - { url = "https://files.pythonhosted.org/packages/38/2d/bfc6b6143adef553a08295490ddc52607ee435b9c751c714620c1b3dd44d/sqlalchemy-2.0.43-cp310-cp310-win_amd64.whl", hash = "sha256:4bf0edb24c128b7be0c61cd17eef432e4bef507013292415f3fb7023f02b7d4b", size = 2125148, upload-time = "2025-08-11T15:55:00.593Z" }, - { url = "https://files.pythonhosted.org/packages/9d/77/fa7189fe44114658002566c6fe443d3ed0ec1fa782feb72af6ef7fbe98e7/sqlalchemy-2.0.43-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:52d9b73b8fb3e9da34c2b31e6d99d60f5f99fd8c1225c9dad24aeb74a91e1d29", size = 2136472, upload-time = "2025-08-11T15:52:21.789Z" }, - { url = "https://files.pythonhosted.org/packages/99/ea/92ac27f2fbc2e6c1766bb807084ca455265707e041ba027c09c17d697867/sqlalchemy-2.0.43-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f42f23e152e4545157fa367b2435a1ace7571cab016ca26038867eb7df2c3631", size = 2126535, upload-time = "2025-08-11T15:52:23.109Z" }, - { url = "https://files.pythonhosted.org/packages/94/12/536ede80163e295dc57fff69724caf68f91bb40578b6ac6583a293534849/sqlalchemy-2.0.43-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fb1a8c5438e0c5ea51afe9c6564f951525795cf432bed0c028c1cb081276685", size = 3297521, upload-time = "2025-08-11T15:50:33.536Z" }, - { url = "https://files.pythonhosted.org/packages/03/b5/cacf432e6f1fc9d156eca0560ac61d4355d2181e751ba8c0cd9cb232c8c1/sqlalchemy-2.0.43-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db691fa174e8f7036afefe3061bc40ac2b770718be2862bfb03aabae09051aca", size = 3297343, upload-time = "2025-08-11T15:57:51.186Z" }, - { url = "https://files.pythonhosted.org/packages/ca/ba/d4c9b526f18457667de4c024ffbc3a0920c34237b9e9dd298e44c7c00ee5/sqlalchemy-2.0.43-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe2b3b4927d0bc03d02ad883f402d5de201dbc8894ac87d2e981e7d87430e60d", size = 3232113, upload-time = "2025-08-11T15:50:34.949Z" }, - { url = "https://files.pythonhosted.org/packages/aa/79/c0121b12b1b114e2c8a10ea297a8a6d5367bc59081b2be896815154b1163/sqlalchemy-2.0.43-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4d3d9b904ad4a6b175a2de0738248822f5ac410f52c2fd389ada0b5262d6a1e3", size = 3258240, upload-time = "2025-08-11T15:57:52.983Z" }, - { url = "https://files.pythonhosted.org/packages/79/99/a2f9be96fb382f3ba027ad42f00dbe30fdb6ba28cda5f11412eee346bec5/sqlalchemy-2.0.43-cp311-cp311-win32.whl", hash = "sha256:5cda6b51faff2639296e276591808c1726c4a77929cfaa0f514f30a5f6156921", size = 2101248, upload-time = "2025-08-11T15:55:01.855Z" }, - { url = "https://files.pythonhosted.org/packages/ee/13/744a32ebe3b4a7a9c7ea4e57babae7aa22070d47acf330d8e5a1359607f1/sqlalchemy-2.0.43-cp311-cp311-win_amd64.whl", hash = "sha256:c5d1730b25d9a07727d20ad74bc1039bbbb0a6ca24e6769861c1aa5bf2c4c4a8", size = 2126109, upload-time = "2025-08-11T15:55:04.092Z" }, - { url = "https://files.pythonhosted.org/packages/61/db/20c78f1081446095450bdc6ee6cc10045fce67a8e003a5876b6eaafc5cc4/sqlalchemy-2.0.43-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:20d81fc2736509d7a2bd33292e489b056cbae543661bb7de7ce9f1c0cd6e7f24", size = 2134891, upload-time = "2025-08-11T15:51:13.019Z" }, - { url = "https://files.pythonhosted.org/packages/45/0a/3d89034ae62b200b4396f0f95319f7d86e9945ee64d2343dcad857150fa2/sqlalchemy-2.0.43-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b9fc27650ff5a2c9d490c13c14906b918b0de1f8fcbb4c992712d8caf40e83", size = 2123061, upload-time = "2025-08-11T15:51:14.319Z" }, - { url = "https://files.pythonhosted.org/packages/cb/10/2711f7ff1805919221ad5bee205971254845c069ee2e7036847103ca1e4c/sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6772e3ca8a43a65a37c88e2f3e2adfd511b0b1da37ef11ed78dea16aeae85bd9", size = 3320384, upload-time = "2025-08-11T15:52:35.088Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0e/3d155e264d2ed2778484006ef04647bc63f55b3e2d12e6a4f787747b5900/sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a113da919c25f7f641ffbd07fbc9077abd4b3b75097c888ab818f962707eb48", size = 3329648, upload-time = "2025-08-11T15:56:34.153Z" }, - { url = "https://files.pythonhosted.org/packages/5b/81/635100fb19725c931622c673900da5efb1595c96ff5b441e07e3dd61f2be/sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4286a1139f14b7d70141c67a8ae1582fc2b69105f1b09d9573494eb4bb4b2687", size = 3258030, upload-time = "2025-08-11T15:52:36.933Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ed/a99302716d62b4965fded12520c1cbb189f99b17a6d8cf77611d21442e47/sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:529064085be2f4d8a6e5fab12d36ad44f1909a18848fcfbdb59cc6d4bbe48efe", size = 3294469, upload-time = "2025-08-11T15:56:35.553Z" }, - { url = "https://files.pythonhosted.org/packages/5d/a2/3a11b06715149bf3310b55a98b5c1e84a42cfb949a7b800bc75cb4e33abc/sqlalchemy-2.0.43-cp312-cp312-win32.whl", hash = "sha256:b535d35dea8bbb8195e7e2b40059e2253acb2b7579b73c1b432a35363694641d", size = 2098906, upload-time = "2025-08-11T15:55:00.645Z" }, - { url = "https://files.pythonhosted.org/packages/bc/09/405c915a974814b90aa591280623adc6ad6b322f61fd5cff80aeaef216c9/sqlalchemy-2.0.43-cp312-cp312-win_amd64.whl", hash = "sha256:1c6d85327ca688dbae7e2b06d7d84cfe4f3fffa5b5f9e21bb6ce9d0e1a0e0e0a", size = 2126260, upload-time = "2025-08-11T15:55:02.965Z" }, - { url = "https://files.pythonhosted.org/packages/41/1c/a7260bd47a6fae7e03768bf66451437b36451143f36b285522b865987ced/sqlalchemy-2.0.43-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e7c08f57f75a2bb62d7ee80a89686a5e5669f199235c6d1dac75cd59374091c3", size = 2130598, upload-time = "2025-08-11T15:51:15.903Z" }, - { url = "https://files.pythonhosted.org/packages/8e/84/8a337454e82388283830b3586ad7847aa9c76fdd4f1df09cdd1f94591873/sqlalchemy-2.0.43-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:14111d22c29efad445cd5021a70a8b42f7d9152d8ba7f73304c4d82460946aaa", size = 2118415, upload-time = "2025-08-11T15:51:17.256Z" }, - { url = "https://files.pythonhosted.org/packages/cf/ff/22ab2328148492c4d71899d62a0e65370ea66c877aea017a244a35733685/sqlalchemy-2.0.43-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21b27b56eb2f82653168cefe6cb8e970cdaf4f3a6cb2c5e3c3c1cf3158968ff9", size = 3248707, upload-time = "2025-08-11T15:52:38.444Z" }, - { url = "https://files.pythonhosted.org/packages/dc/29/11ae2c2b981de60187f7cbc84277d9d21f101093d1b2e945c63774477aba/sqlalchemy-2.0.43-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c5a9da957c56e43d72126a3f5845603da00e0293720b03bde0aacffcf2dc04f", size = 3253602, upload-time = "2025-08-11T15:56:37.348Z" }, - { url = "https://files.pythonhosted.org/packages/b8/61/987b6c23b12c56d2be451bc70900f67dd7d989d52b1ee64f239cf19aec69/sqlalchemy-2.0.43-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d79f9fdc9584ec83d1b3c75e9f4595c49017f5594fee1a2217117647225d738", size = 3183248, upload-time = "2025-08-11T15:52:39.865Z" }, - { url = "https://files.pythonhosted.org/packages/86/85/29d216002d4593c2ce1c0ec2cec46dda77bfbcd221e24caa6e85eff53d89/sqlalchemy-2.0.43-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9df7126fd9db49e3a5a3999442cc67e9ee8971f3cb9644250107d7296cb2a164", size = 3219363, upload-time = "2025-08-11T15:56:39.11Z" }, - { url = "https://files.pythonhosted.org/packages/b6/e4/bd78b01919c524f190b4905d47e7630bf4130b9f48fd971ae1c6225b6f6a/sqlalchemy-2.0.43-cp313-cp313-win32.whl", hash = "sha256:7f1ac7828857fcedb0361b48b9ac4821469f7694089d15550bbcf9ab22564a1d", size = 2096718, upload-time = "2025-08-11T15:55:05.349Z" }, - { url = "https://files.pythonhosted.org/packages/ac/a5/ca2f07a2a201f9497de1928f787926613db6307992fe5cda97624eb07c2f/sqlalchemy-2.0.43-cp313-cp313-win_amd64.whl", hash = "sha256:971ba928fcde01869361f504fcff3b7143b47d30de188b11c6357c0505824197", size = 2123200, upload-time = "2025-08-11T15:55:07.932Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d9/13bdde6521f322861fab67473cec4b1cc8999f3871953531cf61945fad92/sqlalchemy-2.0.43-py3-none-any.whl", hash = "sha256:1681c21dd2ccee222c2fe0bef671d1aef7c504087c9c4e800371cfcc8ac966fc", size = 1924759, upload-time = "2025-08-11T15:39:53.024Z" }, + { url = "https://files.pythonhosted.org/packages/8f/4e/985f7da36f09592c5ade99321c72c15101d23c0bb7eecfd1daaca5714422/sqlalchemy-2.0.43-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:70322986c0c699dca241418fcf18e637a4369e0ec50540a2b907b184c8bca069", size = 2133162 }, + { url = "https://files.pythonhosted.org/packages/37/34/798af8db3cae069461e3bc0898a1610dc469386a97048471d364dc8aae1c/sqlalchemy-2.0.43-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:87accdbba88f33efa7b592dc2e8b2a9c2cdbca73db2f9d5c510790428c09c154", size = 2123082 }, + { url = "https://files.pythonhosted.org/packages/fb/0f/79cf4d9dad42f61ec5af1e022c92f66c2d110b93bb1dc9b033892971abfa/sqlalchemy-2.0.43-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c00e7845d2f692ebfc7d5e4ec1a3fd87698e4337d09e58d6749a16aedfdf8612", size = 3208871 }, + { url = "https://files.pythonhosted.org/packages/56/b3/59befa58fb0e1a9802c87df02344548e6d007e77e87e6084e2131c29e033/sqlalchemy-2.0.43-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:022e436a1cb39b13756cf93b48ecce7aa95382b9cfacceb80a7d263129dfd019", size = 3209583 }, + { url = "https://files.pythonhosted.org/packages/29/d2/124b50c0eb8146e8f0fe16d01026c1a073844f0b454436d8544fe9b33bd7/sqlalchemy-2.0.43-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c5e73ba0d76eefc82ec0219d2301cb33bfe5205ed7a2602523111e2e56ccbd20", size = 3148177 }, + { url = "https://files.pythonhosted.org/packages/83/f5/e369cd46aa84278107624617034a5825fedfc5c958b2836310ced4d2eadf/sqlalchemy-2.0.43-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9c2e02f06c68092b875d5cbe4824238ab93a7fa35d9c38052c033f7ca45daa18", size = 3172276 }, + { url = "https://files.pythonhosted.org/packages/de/2b/4602bf4c3477fa4c837c9774e6dd22e0389fc52310c4c4dfb7e7ba05e90d/sqlalchemy-2.0.43-cp310-cp310-win32.whl", hash = "sha256:e7a903b5b45b0d9fa03ac6a331e1c1d6b7e0ab41c63b6217b3d10357b83c8b00", size = 2101491 }, + { url = "https://files.pythonhosted.org/packages/38/2d/bfc6b6143adef553a08295490ddc52607ee435b9c751c714620c1b3dd44d/sqlalchemy-2.0.43-cp310-cp310-win_amd64.whl", hash = "sha256:4bf0edb24c128b7be0c61cd17eef432e4bef507013292415f3fb7023f02b7d4b", size = 2125148 }, + { url = "https://files.pythonhosted.org/packages/9d/77/fa7189fe44114658002566c6fe443d3ed0ec1fa782feb72af6ef7fbe98e7/sqlalchemy-2.0.43-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:52d9b73b8fb3e9da34c2b31e6d99d60f5f99fd8c1225c9dad24aeb74a91e1d29", size = 2136472 }, + { url = "https://files.pythonhosted.org/packages/99/ea/92ac27f2fbc2e6c1766bb807084ca455265707e041ba027c09c17d697867/sqlalchemy-2.0.43-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f42f23e152e4545157fa367b2435a1ace7571cab016ca26038867eb7df2c3631", size = 2126535 }, + { url = "https://files.pythonhosted.org/packages/94/12/536ede80163e295dc57fff69724caf68f91bb40578b6ac6583a293534849/sqlalchemy-2.0.43-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fb1a8c5438e0c5ea51afe9c6564f951525795cf432bed0c028c1cb081276685", size = 3297521 }, + { url = "https://files.pythonhosted.org/packages/03/b5/cacf432e6f1fc9d156eca0560ac61d4355d2181e751ba8c0cd9cb232c8c1/sqlalchemy-2.0.43-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db691fa174e8f7036afefe3061bc40ac2b770718be2862bfb03aabae09051aca", size = 3297343 }, + { url = "https://files.pythonhosted.org/packages/ca/ba/d4c9b526f18457667de4c024ffbc3a0920c34237b9e9dd298e44c7c00ee5/sqlalchemy-2.0.43-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe2b3b4927d0bc03d02ad883f402d5de201dbc8894ac87d2e981e7d87430e60d", size = 3232113 }, + { url = "https://files.pythonhosted.org/packages/aa/79/c0121b12b1b114e2c8a10ea297a8a6d5367bc59081b2be896815154b1163/sqlalchemy-2.0.43-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4d3d9b904ad4a6b175a2de0738248822f5ac410f52c2fd389ada0b5262d6a1e3", size = 3258240 }, + { url = "https://files.pythonhosted.org/packages/79/99/a2f9be96fb382f3ba027ad42f00dbe30fdb6ba28cda5f11412eee346bec5/sqlalchemy-2.0.43-cp311-cp311-win32.whl", hash = "sha256:5cda6b51faff2639296e276591808c1726c4a77929cfaa0f514f30a5f6156921", size = 2101248 }, + { url = "https://files.pythonhosted.org/packages/ee/13/744a32ebe3b4a7a9c7ea4e57babae7aa22070d47acf330d8e5a1359607f1/sqlalchemy-2.0.43-cp311-cp311-win_amd64.whl", hash = "sha256:c5d1730b25d9a07727d20ad74bc1039bbbb0a6ca24e6769861c1aa5bf2c4c4a8", size = 2126109 }, + { url = "https://files.pythonhosted.org/packages/61/db/20c78f1081446095450bdc6ee6cc10045fce67a8e003a5876b6eaafc5cc4/sqlalchemy-2.0.43-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:20d81fc2736509d7a2bd33292e489b056cbae543661bb7de7ce9f1c0cd6e7f24", size = 2134891 }, + { url = "https://files.pythonhosted.org/packages/45/0a/3d89034ae62b200b4396f0f95319f7d86e9945ee64d2343dcad857150fa2/sqlalchemy-2.0.43-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b9fc27650ff5a2c9d490c13c14906b918b0de1f8fcbb4c992712d8caf40e83", size = 2123061 }, + { url = "https://files.pythonhosted.org/packages/cb/10/2711f7ff1805919221ad5bee205971254845c069ee2e7036847103ca1e4c/sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6772e3ca8a43a65a37c88e2f3e2adfd511b0b1da37ef11ed78dea16aeae85bd9", size = 3320384 }, + { url = "https://files.pythonhosted.org/packages/6e/0e/3d155e264d2ed2778484006ef04647bc63f55b3e2d12e6a4f787747b5900/sqlalchemy-2.0.43-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a113da919c25f7f641ffbd07fbc9077abd4b3b75097c888ab818f962707eb48", size = 3329648 }, + { url = "https://files.pythonhosted.org/packages/5b/81/635100fb19725c931622c673900da5efb1595c96ff5b441e07e3dd61f2be/sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4286a1139f14b7d70141c67a8ae1582fc2b69105f1b09d9573494eb4bb4b2687", size = 3258030 }, + { url = "https://files.pythonhosted.org/packages/0c/ed/a99302716d62b4965fded12520c1cbb189f99b17a6d8cf77611d21442e47/sqlalchemy-2.0.43-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:529064085be2f4d8a6e5fab12d36ad44f1909a18848fcfbdb59cc6d4bbe48efe", size = 3294469 }, + { url = "https://files.pythonhosted.org/packages/5d/a2/3a11b06715149bf3310b55a98b5c1e84a42cfb949a7b800bc75cb4e33abc/sqlalchemy-2.0.43-cp312-cp312-win32.whl", hash = "sha256:b535d35dea8bbb8195e7e2b40059e2253acb2b7579b73c1b432a35363694641d", size = 2098906 }, + { url = "https://files.pythonhosted.org/packages/bc/09/405c915a974814b90aa591280623adc6ad6b322f61fd5cff80aeaef216c9/sqlalchemy-2.0.43-cp312-cp312-win_amd64.whl", hash = "sha256:1c6d85327ca688dbae7e2b06d7d84cfe4f3fffa5b5f9e21bb6ce9d0e1a0e0e0a", size = 2126260 }, + { url = "https://files.pythonhosted.org/packages/41/1c/a7260bd47a6fae7e03768bf66451437b36451143f36b285522b865987ced/sqlalchemy-2.0.43-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e7c08f57f75a2bb62d7ee80a89686a5e5669f199235c6d1dac75cd59374091c3", size = 2130598 }, + { url = "https://files.pythonhosted.org/packages/8e/84/8a337454e82388283830b3586ad7847aa9c76fdd4f1df09cdd1f94591873/sqlalchemy-2.0.43-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:14111d22c29efad445cd5021a70a8b42f7d9152d8ba7f73304c4d82460946aaa", size = 2118415 }, + { url = "https://files.pythonhosted.org/packages/cf/ff/22ab2328148492c4d71899d62a0e65370ea66c877aea017a244a35733685/sqlalchemy-2.0.43-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21b27b56eb2f82653168cefe6cb8e970cdaf4f3a6cb2c5e3c3c1cf3158968ff9", size = 3248707 }, + { url = "https://files.pythonhosted.org/packages/dc/29/11ae2c2b981de60187f7cbc84277d9d21f101093d1b2e945c63774477aba/sqlalchemy-2.0.43-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c5a9da957c56e43d72126a3f5845603da00e0293720b03bde0aacffcf2dc04f", size = 3253602 }, + { url = "https://files.pythonhosted.org/packages/b8/61/987b6c23b12c56d2be451bc70900f67dd7d989d52b1ee64f239cf19aec69/sqlalchemy-2.0.43-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d79f9fdc9584ec83d1b3c75e9f4595c49017f5594fee1a2217117647225d738", size = 3183248 }, + { url = "https://files.pythonhosted.org/packages/86/85/29d216002d4593c2ce1c0ec2cec46dda77bfbcd221e24caa6e85eff53d89/sqlalchemy-2.0.43-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9df7126fd9db49e3a5a3999442cc67e9ee8971f3cb9644250107d7296cb2a164", size = 3219363 }, + { url = "https://files.pythonhosted.org/packages/b6/e4/bd78b01919c524f190b4905d47e7630bf4130b9f48fd971ae1c6225b6f6a/sqlalchemy-2.0.43-cp313-cp313-win32.whl", hash = "sha256:7f1ac7828857fcedb0361b48b9ac4821469f7694089d15550bbcf9ab22564a1d", size = 2096718 }, + { url = "https://files.pythonhosted.org/packages/ac/a5/ca2f07a2a201f9497de1928f787926613db6307992fe5cda97624eb07c2f/sqlalchemy-2.0.43-cp313-cp313-win_amd64.whl", hash = "sha256:971ba928fcde01869361f504fcff3b7143b47d30de188b11c6357c0505824197", size = 2123200 }, + { url = "https://files.pythonhosted.org/packages/b8/d9/13bdde6521f322861fab67473cec4b1cc8999f3871953531cf61945fad92/sqlalchemy-2.0.43-py3-none-any.whl", hash = "sha256:1681c21dd2ccee222c2fe0bef671d1aef7c504087c9c4e800371cfcc8ac966fc", size = 1924759 }, ] [[package]] @@ -3598,9 +3933,9 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/42/6f/22ed6e33f8a9e76ca0a412405f31abb844b779d52c5f96660766edcd737c/sse_starlette-3.0.2.tar.gz", hash = "sha256:ccd60b5765ebb3584d0de2d7a6e4f745672581de4f5005ab31c3a25d10b52b3a", size = 20985, upload-time = "2025-07-27T09:07:44.565Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/6f/22ed6e33f8a9e76ca0a412405f31abb844b779d52c5f96660766edcd737c/sse_starlette-3.0.2.tar.gz", hash = "sha256:ccd60b5765ebb3584d0de2d7a6e4f745672581de4f5005ab31c3a25d10b52b3a", size = 20985 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/10/c78f463b4ef22eef8491f218f692be838282cd65480f6e423d7730dfd1fb/sse_starlette-3.0.2-py3-none-any.whl", hash = "sha256:16b7cbfddbcd4eaca11f7b586f3b8a080f1afe952c15813455b162edea619e5a", size = 11297, upload-time = "2025-07-27T09:07:43.268Z" }, + { url = "https://files.pythonhosted.org/packages/ef/10/c78f463b4ef22eef8491f218f692be838282cd65480f6e423d7730dfd1fb/sse_starlette-3.0.2-py3-none-any.whl", hash = "sha256:16b7cbfddbcd4eaca11f7b586f3b8a080f1afe952c15813455b162edea619e5a", size = 11297 }, ] [[package]] @@ -3612,9 +3947,9 @@ dependencies = [ { name = "executing", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "pure-eval", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, ] [[package]] @@ -3625,85 +3960,85 @@ dependencies = [ { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "(python_full_version < '3.13' and sys_platform == 'darwin') or (python_full_version < '3.13' and sys_platform == 'linux') or (python_full_version < '3.13' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/15/b9/cc3017f9a9c9b6e27c5106cc10cc7904653c3eec0729793aec10479dd669/starlette-0.47.3.tar.gz", hash = "sha256:6bc94f839cc176c4858894f1f8908f0ab79dfec1a6b8402f6da9be26ebea52e9", size = 2584144, upload-time = "2025-08-24T13:36:42.122Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/b9/cc3017f9a9c9b6e27c5106cc10cc7904653c3eec0729793aec10479dd669/starlette-0.47.3.tar.gz", hash = "sha256:6bc94f839cc176c4858894f1f8908f0ab79dfec1a6b8402f6da9be26ebea52e9", size = 2584144 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/fd/901cfa59aaa5b30a99e16876f11abe38b59a1a2c51ffb3d7142bb6089069/starlette-0.47.3-py3-none-any.whl", hash = "sha256:89c0778ca62a76b826101e7c709e70680a1699ca7da6b44d38eb0a7e61fe4b51", size = 72991, upload-time = "2025-08-24T13:36:40.887Z" }, + { url = "https://files.pythonhosted.org/packages/ce/fd/901cfa59aaa5b30a99e16876f11abe38b59a1a2c51ffb3d7142bb6089069/starlette-0.47.3-py3-none-any.whl", hash = "sha256:89c0778ca62a76b826101e7c709e70680a1699ca7da6b44d38eb0a7e61fe4b51", size = 72991 }, ] [[package]] name = "tabulate" version = "0.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" }, + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, ] [[package]] name = "tomli" version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } wheels = [ - { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, - { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, - { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, - { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, - { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, - { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, - { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, - { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, - { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, - { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, - { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, - { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, - { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, - { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, - { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, - { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, - { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, - { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, - { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, - { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, - { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, - { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, - { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, - { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, - { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, ] [[package]] name = "tomli-w" version = "1.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" } +sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" }, + { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675 }, ] [[package]] name = "tornado" version = "6.5.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/ce/1eb500eae19f4648281bb2186927bb062d2438c2e5093d1360391afd2f90/tornado-6.5.2.tar.gz", hash = "sha256:ab53c8f9a0fa351e2c0741284e06c7a45da86afb544133201c5cc8578eb076a0", size = 510821, upload-time = "2025-08-08T18:27:00.78Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/ce/1eb500eae19f4648281bb2186927bb062d2438c2e5093d1360391afd2f90/tornado-6.5.2.tar.gz", hash = "sha256:ab53c8f9a0fa351e2c0741284e06c7a45da86afb544133201c5cc8578eb076a0", size = 510821 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/48/6a7529df2c9cc12efd2e8f5dd219516184d703b34c06786809670df5b3bd/tornado-6.5.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2436822940d37cde62771cff8774f4f00b3c8024fe482e16ca8387b8a2724db6", size = 442563, upload-time = "2025-08-08T18:26:42.945Z" }, - { url = "https://files.pythonhosted.org/packages/f2/b5/9b575a0ed3e50b00c40b08cbce82eb618229091d09f6d14bce80fc01cb0b/tornado-6.5.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:583a52c7aa94ee046854ba81d9ebb6c81ec0fd30386d96f7640c96dad45a03ef", size = 440729, upload-time = "2025-08-08T18:26:44.473Z" }, - { url = "https://files.pythonhosted.org/packages/1b/4e/619174f52b120efcf23633c817fd3fed867c30bff785e2cd5a53a70e483c/tornado-6.5.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0fe179f28d597deab2842b86ed4060deec7388f1fd9c1b4a41adf8af058907e", size = 444295, upload-time = "2025-08-08T18:26:46.021Z" }, - { url = "https://files.pythonhosted.org/packages/95/fa/87b41709552bbd393c85dd18e4e3499dcd8983f66e7972926db8d96aa065/tornado-6.5.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b186e85d1e3536d69583d2298423744740986018e393d0321df7340e71898882", size = 443644, upload-time = "2025-08-08T18:26:47.625Z" }, - { url = "https://files.pythonhosted.org/packages/f9/41/fb15f06e33d7430ca89420283a8762a4e6b8025b800ea51796ab5e6d9559/tornado-6.5.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e792706668c87709709c18b353da1f7662317b563ff69f00bab83595940c7108", size = 443878, upload-time = "2025-08-08T18:26:50.599Z" }, - { url = "https://files.pythonhosted.org/packages/11/92/fe6d57da897776ad2e01e279170ea8ae726755b045fe5ac73b75357a5a3f/tornado-6.5.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06ceb1300fd70cb20e43b1ad8aaee0266e69e7ced38fa910ad2e03285009ce7c", size = 444549, upload-time = "2025-08-08T18:26:51.864Z" }, - { url = "https://files.pythonhosted.org/packages/9b/02/c8f4f6c9204526daf3d760f4aa555a7a33ad0e60843eac025ccfd6ff4a93/tornado-6.5.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:74db443e0f5251be86cbf37929f84d8c20c27a355dd452a5cfa2aada0d001ec4", size = 443973, upload-time = "2025-08-08T18:26:53.625Z" }, - { url = "https://files.pythonhosted.org/packages/ae/2d/f5f5707b655ce2317190183868cd0f6822a1121b4baeae509ceb9590d0bd/tornado-6.5.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5e735ab2889d7ed33b32a459cac490eda71a1ba6857b0118de476ab6c366c04", size = 443954, upload-time = "2025-08-08T18:26:55.072Z" }, - { url = "https://files.pythonhosted.org/packages/e8/59/593bd0f40f7355806bf6573b47b8c22f8e1374c9b6fd03114bd6b7a3dcfd/tornado-6.5.2-cp39-abi3-win32.whl", hash = "sha256:c6f29e94d9b37a95013bb669616352ddb82e3bfe8326fccee50583caebc8a5f0", size = 445023, upload-time = "2025-08-08T18:26:56.677Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2a/f609b420c2f564a748a2d80ebfb2ee02a73ca80223af712fca591386cafb/tornado-6.5.2-cp39-abi3-win_amd64.whl", hash = "sha256:e56a5af51cc30dd2cae649429af65ca2f6571da29504a07995175df14c18f35f", size = 445427, upload-time = "2025-08-08T18:26:57.91Z" }, - { url = "https://files.pythonhosted.org/packages/5e/4f/e1f65e8f8c76d73658b33d33b81eed4322fb5085350e4328d5c956f0c8f9/tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af", size = 444456, upload-time = "2025-08-08T18:26:59.207Z" }, + { url = "https://files.pythonhosted.org/packages/f6/48/6a7529df2c9cc12efd2e8f5dd219516184d703b34c06786809670df5b3bd/tornado-6.5.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2436822940d37cde62771cff8774f4f00b3c8024fe482e16ca8387b8a2724db6", size = 442563 }, + { url = "https://files.pythonhosted.org/packages/f2/b5/9b575a0ed3e50b00c40b08cbce82eb618229091d09f6d14bce80fc01cb0b/tornado-6.5.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:583a52c7aa94ee046854ba81d9ebb6c81ec0fd30386d96f7640c96dad45a03ef", size = 440729 }, + { url = "https://files.pythonhosted.org/packages/1b/4e/619174f52b120efcf23633c817fd3fed867c30bff785e2cd5a53a70e483c/tornado-6.5.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0fe179f28d597deab2842b86ed4060deec7388f1fd9c1b4a41adf8af058907e", size = 444295 }, + { url = "https://files.pythonhosted.org/packages/95/fa/87b41709552bbd393c85dd18e4e3499dcd8983f66e7972926db8d96aa065/tornado-6.5.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b186e85d1e3536d69583d2298423744740986018e393d0321df7340e71898882", size = 443644 }, + { url = "https://files.pythonhosted.org/packages/f9/41/fb15f06e33d7430ca89420283a8762a4e6b8025b800ea51796ab5e6d9559/tornado-6.5.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e792706668c87709709c18b353da1f7662317b563ff69f00bab83595940c7108", size = 443878 }, + { url = "https://files.pythonhosted.org/packages/11/92/fe6d57da897776ad2e01e279170ea8ae726755b045fe5ac73b75357a5a3f/tornado-6.5.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06ceb1300fd70cb20e43b1ad8aaee0266e69e7ced38fa910ad2e03285009ce7c", size = 444549 }, + { url = "https://files.pythonhosted.org/packages/9b/02/c8f4f6c9204526daf3d760f4aa555a7a33ad0e60843eac025ccfd6ff4a93/tornado-6.5.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:74db443e0f5251be86cbf37929f84d8c20c27a355dd452a5cfa2aada0d001ec4", size = 443973 }, + { url = "https://files.pythonhosted.org/packages/ae/2d/f5f5707b655ce2317190183868cd0f6822a1121b4baeae509ceb9590d0bd/tornado-6.5.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5e735ab2889d7ed33b32a459cac490eda71a1ba6857b0118de476ab6c366c04", size = 443954 }, + { url = "https://files.pythonhosted.org/packages/e8/59/593bd0f40f7355806bf6573b47b8c22f8e1374c9b6fd03114bd6b7a3dcfd/tornado-6.5.2-cp39-abi3-win32.whl", hash = "sha256:c6f29e94d9b37a95013bb669616352ddb82e3bfe8326fccee50583caebc8a5f0", size = 445023 }, + { url = "https://files.pythonhosted.org/packages/c7/2a/f609b420c2f564a748a2d80ebfb2ee02a73ca80223af712fca591386cafb/tornado-6.5.2-cp39-abi3-win_amd64.whl", hash = "sha256:e56a5af51cc30dd2cae649429af65ca2f6571da29504a07995175df14c18f35f", size = 445427 }, + { url = "https://files.pythonhosted.org/packages/5e/4f/e1f65e8f8c76d73658b33d33b81eed4322fb5085350e4328d5c956f0c8f9/tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af", size = 444456 }, ] [[package]] @@ -3713,27 +4048,27 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, ] [[package]] name = "traitlets" version = "5.14.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, ] [[package]] name = "typing-extensions" version = "4.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, ] [[package]] @@ -3743,53 +4078,53 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726 } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, + { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552 }, ] [[package]] name = "uc-micro-py" version = "1.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/91/7a/146a99696aee0609e3712f2b44c6274566bc368dfe8375191278045186b8/uc-micro-py-1.0.3.tar.gz", hash = "sha256:d321b92cff673ec58027c04015fcaa8bb1e005478643ff4a500882eaab88c48a", size = 6043, upload-time = "2024-02-09T16:52:01.654Z" } +sdist = { url = "https://files.pythonhosted.org/packages/91/7a/146a99696aee0609e3712f2b44c6274566bc368dfe8375191278045186b8/uc-micro-py-1.0.3.tar.gz", hash = "sha256:d321b92cff673ec58027c04015fcaa8bb1e005478643ff4a500882eaab88c48a", size = 6043 } wheels = [ - { url = "https://files.pythonhosted.org/packages/37/87/1f677586e8ac487e29672e4b17455758fce261de06a0d086167bb760361a/uc_micro_py-1.0.3-py3-none-any.whl", hash = "sha256:db1dffff340817673d7b466ec86114a9dc0e9d4d9b5ba229d9d60e5c12600cd5", size = 6229, upload-time = "2024-02-09T16:52:00.371Z" }, + { url = "https://files.pythonhosted.org/packages/37/87/1f677586e8ac487e29672e4b17455758fce261de06a0d086167bb760361a/uc_micro_py-1.0.3-py3-none-any.whl", hash = "sha256:db1dffff340817673d7b466ec86114a9dc0e9d4d9b5ba229d9d60e5c12600cd5", size = 6229 }, ] [[package]] name = "urllib3" version = "2.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795 }, ] [[package]] name = "uv" -version = "0.8.15" +version = "0.8.16" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/7c/ab905b0425f88842f3d8e5da50491524f45a231b7a3dc9c988608162adb2/uv-0.8.15.tar.gz", hash = "sha256:8ea57b78be9f0911a2a50b6814d15aec7d1f8aa6517059dc8250b1414156f93a", size = 3602914, upload-time = "2025-09-03T14:32:15.552Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/ca/41d49a50387702ce2e1a9d5060dc11177e33eafbb1c34dda5cefca0b3d1e/uv-0.8.16.tar.gz", hash = "sha256:40a02a94642fed9fd60c3b18f47060b20d52e9dd9423327babdd4bf399947930", size = 3613796 } wheels = [ - { url = "https://files.pythonhosted.org/packages/67/1d/794352a01b40f2b0a0abe02f4f020219b3f59ee6ed900561be3b2b47a82b/uv-0.8.15-py3-none-linux_armv6l.whl", hash = "sha256:f02e6b8be08b840f86b8d5997b658b657acdda95bc216ecf62fce6c71414bdc7", size = 20136396, upload-time = "2025-09-03T14:31:30.404Z" }, - { url = "https://files.pythonhosted.org/packages/8f/89/528f01cff01eb8d10dd396f437656266443e399dda2fe4787b2cf6983698/uv-0.8.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b0461bb1ad616c8bcb59c9b39ae9363245ca33815ebb1d11130385236eca21b9", size = 19297422, upload-time = "2025-09-03T14:31:34.412Z" }, - { url = "https://files.pythonhosted.org/packages/94/03/532af32a64d162894a1daebb7bc5028ba00225ea720cf0f287e934dc2bd5/uv-0.8.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:069eed78b79d1e88bced23e3d4303348edb0a0209e7cae0f20024c42430bf50f", size = 17882409, upload-time = "2025-09-03T14:31:36.993Z" }, - { url = "https://files.pythonhosted.org/packages/25/21/57df6d53fbadfa947d9d65a0926e5d8540199f49aa958d23be2707262a80/uv-0.8.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:333a93bb6af64f3b95ee99e82b4ea227e2af6362c45f91c89a24e2bfefb628f9", size = 19557216, upload-time = "2025-09-03T14:31:39.245Z" }, - { url = "https://files.pythonhosted.org/packages/68/22/c3784749e1c78119e5375ec34c6ea29e944192a601f17c746339611db237/uv-0.8.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7d5b19ac2bdda3d1456b5d6013af50b443ffb0e40c66d42874f71190a5364711", size = 19781097, upload-time = "2025-09-03T14:31:42.314Z" }, - { url = "https://files.pythonhosted.org/packages/00/28/0597599fb35408dd73e0a7d25108dca1fa6ce8f8d570c8f24151b0016eef/uv-0.8.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3330bb4f206a6180679a75a8b2e77ff0f933fcb06c028b6f4da877b10a5e4f95", size = 20741549, upload-time = "2025-09-03T14:31:44.574Z" }, - { url = "https://files.pythonhosted.org/packages/4f/61/98fa07981722660f5a3c28b987df99c2486f63d01b1256e6cca05a43bdce/uv-0.8.15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:de9896ad4fa724ab317a8048f4891b9b23df1403b3724e96606f3be2dbbbf009", size = 22193727, upload-time = "2025-09-03T14:31:46.915Z" }, - { url = "https://files.pythonhosted.org/packages/fa/65/523188e11a759144b00f0fe48943f6d00706fcd9b5f561a54a07b9fd4541/uv-0.8.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:226360003e71084e0a73cbec72170e88634b045e95529654d067ea3741bba242", size = 21817550, upload-time = "2025-09-03T14:31:49.548Z" }, - { url = "https://files.pythonhosted.org/packages/99/3c/7898acf3d9ed2d3a2986cccc8209c14d3e9ac72dfaa616e49d329423b1d3/uv-0.8.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9488260536b35b94a79962fea76837f279c0cd0ae5021c761e66b311f47ffa70", size = 21024011, upload-time = "2025-09-03T14:31:51.789Z" }, - { url = "https://files.pythonhosted.org/packages/13/fc/e0da45ee179367dcc1e1040ad00ed8a99b78355d43024b0b5fc2edf5c389/uv-0.8.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07765f99fd5fd3b257d7e210e8d0844c0a8fd111612e31fcca66a85656cc728e", size = 21009338, upload-time = "2025-09-03T14:31:54.104Z" }, - { url = "https://files.pythonhosted.org/packages/ce/5d/180904fa7ed49081b27f00e86f7220ca62cc098d7ef6459f0c69a8ae8f74/uv-0.8.15-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:c4868e6a4e1a8c777a5ba3cff452c405837318fb0b272ff203bfda0e1b8fc54d", size = 19799578, upload-time = "2025-09-03T14:31:56.47Z" }, - { url = "https://files.pythonhosted.org/packages/b6/09/fed823212e695b6765bdb8462850abffbe685cd965c4de905efed5e2e5c9/uv-0.8.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:3ec78a54a8eb0bbb9a9c653982390af84673657c8a48a0be6cdcb81d7d3e95c3", size = 20845428, upload-time = "2025-09-03T14:31:59.475Z" }, - { url = "https://files.pythonhosted.org/packages/b9/f3/9c4211897c00f79b7973a10800166e0580eaad20fe27f7c06adb7b248ac7/uv-0.8.15-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:a022a752d20da80d2a49fc0721522a81e3a32efe539152d756d84ebdba29dbc3", size = 19728113, upload-time = "2025-09-03T14:32:01.686Z" }, - { url = "https://files.pythonhosted.org/packages/3b/43/4ec6047150e2fba494d80d36b881a1a973835afa497ae9ccdf51828cae4f/uv-0.8.15-py3-none-musllinux_1_1_i686.whl", hash = "sha256:3780d2f3951d83e55812fdeb7eee233787b70c774497dbfc55b0fdf6063aa345", size = 20169115, upload-time = "2025-09-03T14:32:03.995Z" }, - { url = "https://files.pythonhosted.org/packages/ea/98/b4220bf462fb225c4a2d74ef4f105020238472b4b0da94ebc17a310d7b4e/uv-0.8.15-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:56f2451c9193ee1754ce1d8390ded68e9cb8dee0aaf7e2f38a9bd04d99be1be7", size = 21129804, upload-time = "2025-09-03T14:32:06.204Z" }, - { url = "https://files.pythonhosted.org/packages/5e/b8/40ce3d385254ac87a664a5d9a4664fac697e2734352f404382b81d03235b/uv-0.8.15-py3-none-win32.whl", hash = "sha256:89c7c10089e07d944c72d388fd88666c650dec2f8c79ca541e365f32843882c6", size = 19077103, upload-time = "2025-09-03T14:32:08.628Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9d/081a0af395c0e307c0c930e80161a2aa551c25064cfb636d060574566fa4/uv-0.8.15-py3-none-win_amd64.whl", hash = "sha256:6aa824ab933dfafe11efe32e6541c6bcd65ecaa927e8e834ea6b14d3821020f6", size = 21179816, upload-time = "2025-09-03T14:32:11.42Z" }, - { url = "https://files.pythonhosted.org/packages/30/47/d8f50264a8c8ebbb9a44a8fed08b6e873d943adf299d944fe3a776ff5fbf/uv-0.8.15-py3-none-win_arm64.whl", hash = "sha256:a395fa1fc8948eacdd18e4592ed489fad13558b13fea6b3544cb16e5006c5b02", size = 19448833, upload-time = "2025-09-03T14:32:13.639Z" }, + { url = "https://files.pythonhosted.org/packages/a2/b5/566116ac887c7eceae9c8221fc4d46433c45c46a1ba2120604cf491196cb/uv-0.8.16-py3-none-linux_armv6l.whl", hash = "sha256:e971f8db3c044ba40cd74446989181c46fad88f91af432f4e7fda3228bc0f653", size = 20239440 }, + { url = "https://files.pythonhosted.org/packages/b1/0c/891ab0a6990c4ea80e8630ff6eaf09c01a004a0f30584b23eba38a651c66/uv-0.8.16-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:44560a1c25c6d3d86194628adad36bfd34decadfd16fe4f0a6736cc5e7c0fa5c", size = 19372085 }, + { url = "https://files.pythonhosted.org/packages/11/f2/8f379585a0d22bfc4342a72e9aacf9072b7846c0f869c909d1466d27e56a/uv-0.8.16-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5fe0dcf41b5114cdb812f475974d36ac606cf90d0a81ef537f027f8bce9610a8", size = 17917456 }, + { url = "https://files.pythonhosted.org/packages/ea/08/eb18459d3bebad865171240a97979cc310508a02055ef8ebcc5505becf99/uv-0.8.16-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:f6b6d4b87283886a3e01d170ece6952fb62e76d4e53a6a98fe07726031757eb9", size = 19503602 }, + { url = "https://files.pythonhosted.org/packages/43/f4/bbd8049ba5c516e6c1ddc550bd17e9872344b65886938d256555ee3e54f3/uv-0.8.16-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a614f4a3453de19cdf1f1e5992f0c360642355cd5d4cfd047837b978736418c", size = 19836939 }, + { url = "https://files.pythonhosted.org/packages/6b/13/5595cefb9ded7db62aaa3a271c81e56cf46dd26b6d8902f6ca11859a23e7/uv-0.8.16-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0a41e518c5012f94e00b384233e90c46ede992f190de4c69bcb9a1bdeebc77e", size = 20827006 }, + { url = "https://files.pythonhosted.org/packages/f8/56/ced3ebb906177c0cdf306e16b6ce13615f04a2db5ff10b928d28e111911b/uv-0.8.16-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:413360923d90f3a3792602d4a17615d92891667bf2eaa1f1b702030e92bccf8c", size = 22239989 }, + { url = "https://files.pythonhosted.org/packages/28/e7/bba9ec72a4cc1e7aa4f4bea11994ca4c6733715a105b8c1094706169e176/uv-0.8.16-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86988e78bc44d7d59d7ad6330aadf6dae95ecc50416ef237f71b84d656b09dad", size = 21858126 }, + { url = "https://files.pythonhosted.org/packages/12/42/4d4354aa33ff538bee9031ee999ea703e3dbc16a1b78d7fef124da6301d9/uv-0.8.16-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2c7776b6d0b89f7ca5630281ea76ea5ec520ae623288f951ded59cd973c86b7", size = 21173353 }, + { url = "https://files.pythonhosted.org/packages/3d/b6/735e2bea9fd49de4feed4317b9e65e12d2da7d54961307e813fc02310b54/uv-0.8.16-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd441a7b703c6e06f5af318c3e9695154409500c64725c522ca9f0a555921e43", size = 21094204 }, + { url = "https://files.pythonhosted.org/packages/36/7f/a21380175e1338b7a4c884e79b5cb82be27ae7a7498abaca1ca6cb390db7/uv-0.8.16-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:0ad851d0968bd7f6e0759bcf9675edce24da0da75985b199008e578a6ab19ab0", size = 19772917 }, + { url = "https://files.pythonhosted.org/packages/70/cb/d25498403075826f3d80af1726f2f619c89f3d9a55de22ab54a0ed36fd82/uv-0.8.16-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:684aed61ca9ba869be8e002bb5b94efd9f24b7c6b901f137648aadffdd87dfab", size = 20840087 }, + { url = "https://files.pythonhosted.org/packages/96/e5/85a63857175d287875b5943709c2bec572d56575044489179b6d00e50013/uv-0.8.16-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:d97b5213d990fc2ed402c5c000d29487c2ccd1386dd7234b3abe0b3e3c835886", size = 19811744 }, + { url = "https://files.pythonhosted.org/packages/ab/2d/25997a67c4e5e07ff715ed86b45654983209781e65259aaa0c4bcb57d8f6/uv-0.8.16-py3-none-musllinux_1_1_i686.whl", hash = "sha256:d9dc8edfff2f2111b3c5c78e9b5ac3f8c54d3718c9daf44f9f4479003193228f", size = 20262204 }, + { url = "https://files.pythonhosted.org/packages/d2/2e/06ff2efde798ce950e89a982bf7fd694793f3f862cb4619d1709a51ffe67/uv-0.8.16-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:1ac75a273e7686da0db4b7fd26e84e066afd66b7d020dd63fb0e96d467c57704", size = 21213633 }, + { url = "https://files.pythonhosted.org/packages/f3/62/c443998b1bf50c0e8768bde97d950dbb5a1cdde7bdaa61873b30349d0474/uv-0.8.16-py3-none-win32.whl", hash = "sha256:be66bfe8337235f3dc34ba84903e2b98e8c14d7e23571b19e47e94316d1588a8", size = 19222031 }, + { url = "https://files.pythonhosted.org/packages/97/00/1bcc3d5e5c728dbb8c525eb29015dade1b7e105ab7b656ac471536568eb6/uv-0.8.16-py3-none-win_amd64.whl", hash = "sha256:989a2cfcf588c903b14f51511abb6b9dd0a9d0f50535b1133fc9bae6f99cf2b7", size = 21114685 }, + { url = "https://files.pythonhosted.org/packages/aa/e2/bd01db75e33ddd6edb7b13ef5958c97ac4a6595e91a5461c1e1767e5fe2b/uv-0.8.16-py3-none-win_arm64.whl", hash = "sha256:5d2c4033e40e011d9db09fe6e66bb9132e63a492eeb15a1d736c1e30096a53ef", size = 19558627 }, ] [[package]] @@ -3801,9 +4136,9 @@ dependencies = [ { name = "h11", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406 }, ] [[package]] @@ -3816,9 +4151,9 @@ dependencies = [ { name = "platformdirs", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/14/37fcdba2808a6c615681cd216fecae00413c9dab44fb2e57805ecf3eaee3/virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a", size = 6003808, upload-time = "2025-08-13T14:24:07.464Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/14/37fcdba2808a6c615681cd216fecae00413c9dab44fb2e57805ecf3eaee3/virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a", size = 6003808 } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/06/04c8e804f813cf972e3262f3f8584c232de64f0cde9f703b46cf53a45090/virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026", size = 5983279, upload-time = "2025-08-13T14:24:05.111Z" }, + { url = "https://files.pythonhosted.org/packages/76/06/04c8e804f813cf972e3262f3f8584c232de64f0cde9f703b46cf53a45090/virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026", size = 5983279 }, ] [[package]] @@ -3828,234 +4163,234 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406, upload-time = "2025-06-15T19:06:59.42Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/dd/579d1dc57f0f895426a1211c4ef3b0cb37eb9e642bb04bdcd962b5df206a/watchfiles-1.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:27f30e14aa1c1e91cb653f03a63445739919aef84c8d2517997a83155e7a2fcc", size = 405757, upload-time = "2025-06-15T19:04:51.058Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a0/7a0318cd874393344d48c34d53b3dd419466adf59a29ba5b51c88dd18b86/watchfiles-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3366f56c272232860ab45c77c3ca7b74ee819c8e1f6f35a7125556b198bbc6df", size = 397511, upload-time = "2025-06-15T19:04:52.79Z" }, - { url = "https://files.pythonhosted.org/packages/06/be/503514656d0555ec2195f60d810eca29b938772e9bfb112d5cd5ad6f6a9e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8412eacef34cae2836d891836a7fff7b754d6bcac61f6c12ba5ca9bc7e427b68", size = 450739, upload-time = "2025-06-15T19:04:54.203Z" }, - { url = "https://files.pythonhosted.org/packages/4e/0d/a05dd9e5f136cdc29751816d0890d084ab99f8c17b86f25697288ca09bc7/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df670918eb7dd719642e05979fc84704af913d563fd17ed636f7c4783003fdcc", size = 458106, upload-time = "2025-06-15T19:04:55.607Z" }, - { url = "https://files.pythonhosted.org/packages/f1/fa/9cd16e4dfdb831072b7ac39e7bea986e52128526251038eb481effe9f48e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7642b9bc4827b5518ebdb3b82698ada8c14c7661ddec5fe719f3e56ccd13c97", size = 484264, upload-time = "2025-06-15T19:04:57.009Z" }, - { url = "https://files.pythonhosted.org/packages/32/04/1da8a637c7e2b70e750a0308e9c8e662ada0cca46211fa9ef24a23937e0b/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:199207b2d3eeaeb80ef4411875a6243d9ad8bc35b07fc42daa6b801cc39cc41c", size = 597612, upload-time = "2025-06-15T19:04:58.409Z" }, - { url = "https://files.pythonhosted.org/packages/30/01/109f2762e968d3e58c95731a206e5d7d2a7abaed4299dd8a94597250153c/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a479466da6db5c1e8754caee6c262cd373e6e6c363172d74394f4bff3d84d7b5", size = 477242, upload-time = "2025-06-15T19:04:59.786Z" }, - { url = "https://files.pythonhosted.org/packages/b5/b8/46f58cf4969d3b7bc3ca35a98e739fa4085b0657a1540ccc29a1a0bc016f/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:935f9edd022ec13e447e5723a7d14456c8af254544cefbc533f6dd276c9aa0d9", size = 453148, upload-time = "2025-06-15T19:05:01.103Z" }, - { url = "https://files.pythonhosted.org/packages/a5/cd/8267594263b1770f1eb76914940d7b2d03ee55eca212302329608208e061/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8076a5769d6bdf5f673a19d51da05fc79e2bbf25e9fe755c47595785c06a8c72", size = 626574, upload-time = "2025-06-15T19:05:02.582Z" }, - { url = "https://files.pythonhosted.org/packages/a1/2f/7f2722e85899bed337cba715723e19185e288ef361360718973f891805be/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86b1e28d4c37e89220e924305cd9f82866bb0ace666943a6e4196c5df4d58dcc", size = 624378, upload-time = "2025-06-15T19:05:03.719Z" }, - { url = "https://files.pythonhosted.org/packages/bf/20/64c88ec43d90a568234d021ab4b2a6f42a5230d772b987c3f9c00cc27b8b/watchfiles-1.1.0-cp310-cp310-win32.whl", hash = "sha256:d1caf40c1c657b27858f9774d5c0e232089bca9cb8ee17ce7478c6e9264d2587", size = 279829, upload-time = "2025-06-15T19:05:04.822Z" }, - { url = "https://files.pythonhosted.org/packages/39/5c/a9c1ed33de7af80935e4eac09570de679c6e21c07070aa99f74b4431f4d6/watchfiles-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a89c75a5b9bc329131115a409d0acc16e8da8dfd5867ba59f1dd66ae7ea8fa82", size = 292192, upload-time = "2025-06-15T19:05:06.348Z" }, - { url = "https://files.pythonhosted.org/packages/8b/78/7401154b78ab484ccaaeef970dc2af0cb88b5ba8a1b415383da444cdd8d3/watchfiles-1.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2", size = 405751, upload-time = "2025-06-15T19:05:07.679Z" }, - { url = "https://files.pythonhosted.org/packages/76/63/e6c3dbc1f78d001589b75e56a288c47723de28c580ad715eb116639152b5/watchfiles-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c", size = 397313, upload-time = "2025-06-15T19:05:08.764Z" }, - { url = "https://files.pythonhosted.org/packages/6c/a2/8afa359ff52e99af1632f90cbf359da46184207e893a5f179301b0c8d6df/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d", size = 450792, upload-time = "2025-06-15T19:05:09.869Z" }, - { url = "https://files.pythonhosted.org/packages/1d/bf/7446b401667f5c64972a57a0233be1104157fc3abf72c4ef2666c1bd09b2/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7", size = 458196, upload-time = "2025-06-15T19:05:11.91Z" }, - { url = "https://files.pythonhosted.org/packages/58/2f/501ddbdfa3fa874ea5597c77eeea3d413579c29af26c1091b08d0c792280/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c", size = 484788, upload-time = "2025-06-15T19:05:13.373Z" }, - { url = "https://files.pythonhosted.org/packages/61/1e/9c18eb2eb5c953c96bc0e5f626f0e53cfef4bd19bd50d71d1a049c63a575/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575", size = 597879, upload-time = "2025-06-15T19:05:14.725Z" }, - { url = "https://files.pythonhosted.org/packages/8b/6c/1467402e5185d89388b4486745af1e0325007af0017c3384cc786fff0542/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8", size = 477447, upload-time = "2025-06-15T19:05:15.775Z" }, - { url = "https://files.pythonhosted.org/packages/2b/a1/ec0a606bde4853d6c4a578f9391eeb3684a9aea736a8eb217e3e00aa89a1/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f", size = 453145, upload-time = "2025-06-15T19:05:17.17Z" }, - { url = "https://files.pythonhosted.org/packages/90/b9/ef6f0c247a6a35d689fc970dc7f6734f9257451aefb30def5d100d6246a5/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4", size = 626539, upload-time = "2025-06-15T19:05:18.557Z" }, - { url = "https://files.pythonhosted.org/packages/34/44/6ffda5537085106ff5aaa762b0d130ac6c75a08015dd1621376f708c94de/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d", size = 624472, upload-time = "2025-06-15T19:05:19.588Z" }, - { url = "https://files.pythonhosted.org/packages/c3/e3/71170985c48028fa3f0a50946916a14055e741db11c2e7bc2f3b61f4d0e3/watchfiles-1.1.0-cp311-cp311-win32.whl", hash = "sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2", size = 279348, upload-time = "2025-06-15T19:05:20.856Z" }, - { url = "https://files.pythonhosted.org/packages/89/1b/3e39c68b68a7a171070f81fc2561d23ce8d6859659406842a0e4bebf3bba/watchfiles-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12", size = 292607, upload-time = "2025-06-15T19:05:21.937Z" }, - { url = "https://files.pythonhosted.org/packages/61/9f/2973b7539f2bdb6ea86d2c87f70f615a71a1fc2dba2911795cea25968aea/watchfiles-1.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a", size = 285056, upload-time = "2025-06-15T19:05:23.12Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b8/858957045a38a4079203a33aaa7d23ea9269ca7761c8a074af3524fbb240/watchfiles-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179", size = 402339, upload-time = "2025-06-15T19:05:24.516Z" }, - { url = "https://files.pythonhosted.org/packages/80/28/98b222cca751ba68e88521fabd79a4fab64005fc5976ea49b53fa205d1fa/watchfiles-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5", size = 394409, upload-time = "2025-06-15T19:05:25.469Z" }, - { url = "https://files.pythonhosted.org/packages/86/50/dee79968566c03190677c26f7f47960aff738d32087087bdf63a5473e7df/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297", size = 450939, upload-time = "2025-06-15T19:05:26.494Z" }, - { url = "https://files.pythonhosted.org/packages/40/45/a7b56fb129700f3cfe2594a01aa38d033b92a33dddce86c8dfdfc1247b72/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0", size = 457270, upload-time = "2025-06-15T19:05:27.466Z" }, - { url = "https://files.pythonhosted.org/packages/b5/c8/fa5ef9476b1d02dc6b5e258f515fcaaecf559037edf8b6feffcbc097c4b8/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e", size = 483370, upload-time = "2025-06-15T19:05:28.548Z" }, - { url = "https://files.pythonhosted.org/packages/98/68/42cfcdd6533ec94f0a7aab83f759ec11280f70b11bfba0b0f885e298f9bd/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee", size = 598654, upload-time = "2025-06-15T19:05:29.997Z" }, - { url = "https://files.pythonhosted.org/packages/d3/74/b2a1544224118cc28df7e59008a929e711f9c68ce7d554e171b2dc531352/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd", size = 478667, upload-time = "2025-06-15T19:05:31.172Z" }, - { url = "https://files.pythonhosted.org/packages/8c/77/e3362fe308358dc9f8588102481e599c83e1b91c2ae843780a7ded939a35/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f", size = 452213, upload-time = "2025-06-15T19:05:32.299Z" }, - { url = "https://files.pythonhosted.org/packages/6e/17/c8f1a36540c9a1558d4faf08e909399e8133599fa359bf52ec8fcee5be6f/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4", size = 626718, upload-time = "2025-06-15T19:05:33.415Z" }, - { url = "https://files.pythonhosted.org/packages/26/45/fb599be38b4bd38032643783d7496a26a6f9ae05dea1a42e58229a20ac13/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f", size = 623098, upload-time = "2025-06-15T19:05:34.534Z" }, - { url = "https://files.pythonhosted.org/packages/a1/e7/fdf40e038475498e160cd167333c946e45d8563ae4dd65caf757e9ffe6b4/watchfiles-1.1.0-cp312-cp312-win32.whl", hash = "sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd", size = 279209, upload-time = "2025-06-15T19:05:35.577Z" }, - { url = "https://files.pythonhosted.org/packages/3f/d3/3ae9d5124ec75143bdf088d436cba39812122edc47709cd2caafeac3266f/watchfiles-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47", size = 292786, upload-time = "2025-06-15T19:05:36.559Z" }, - { url = "https://files.pythonhosted.org/packages/26/2f/7dd4fc8b5f2b34b545e19629b4a018bfb1de23b3a496766a2c1165ca890d/watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6", size = 284343, upload-time = "2025-06-15T19:05:37.5Z" }, - { url = "https://files.pythonhosted.org/packages/d3/42/fae874df96595556a9089ade83be34a2e04f0f11eb53a8dbf8a8a5e562b4/watchfiles-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30", size = 402004, upload-time = "2025-06-15T19:05:38.499Z" }, - { url = "https://files.pythonhosted.org/packages/fa/55/a77e533e59c3003d9803c09c44c3651224067cbe7fb5d574ddbaa31e11ca/watchfiles-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a", size = 393671, upload-time = "2025-06-15T19:05:39.52Z" }, - { url = "https://files.pythonhosted.org/packages/05/68/b0afb3f79c8e832e6571022611adbdc36e35a44e14f129ba09709aa4bb7a/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc", size = 449772, upload-time = "2025-06-15T19:05:40.897Z" }, - { url = "https://files.pythonhosted.org/packages/ff/05/46dd1f6879bc40e1e74c6c39a1b9ab9e790bf1f5a2fe6c08b463d9a807f4/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b", size = 456789, upload-time = "2025-06-15T19:05:42.045Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ca/0eeb2c06227ca7f12e50a47a3679df0cd1ba487ea19cf844a905920f8e95/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895", size = 482551, upload-time = "2025-06-15T19:05:43.781Z" }, - { url = "https://files.pythonhosted.org/packages/31/47/2cecbd8694095647406645f822781008cc524320466ea393f55fe70eed3b/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a", size = 597420, upload-time = "2025-06-15T19:05:45.244Z" }, - { url = "https://files.pythonhosted.org/packages/d9/7e/82abc4240e0806846548559d70f0b1a6dfdca75c1b4f9fa62b504ae9b083/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b", size = 477950, upload-time = "2025-06-15T19:05:46.332Z" }, - { url = "https://files.pythonhosted.org/packages/25/0d/4d564798a49bf5482a4fa9416dea6b6c0733a3b5700cb8a5a503c4b15853/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c", size = 451706, upload-time = "2025-06-15T19:05:47.459Z" }, - { url = "https://files.pythonhosted.org/packages/81/b5/5516cf46b033192d544102ea07c65b6f770f10ed1d0a6d388f5d3874f6e4/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b", size = 625814, upload-time = "2025-06-15T19:05:48.654Z" }, - { url = "https://files.pythonhosted.org/packages/0c/dd/7c1331f902f30669ac3e754680b6edb9a0dd06dea5438e61128111fadd2c/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb", size = 622820, upload-time = "2025-06-15T19:05:50.088Z" }, - { url = "https://files.pythonhosted.org/packages/1b/14/36d7a8e27cd128d7b1009e7715a7c02f6c131be9d4ce1e5c3b73d0e342d8/watchfiles-1.1.0-cp313-cp313-win32.whl", hash = "sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9", size = 279194, upload-time = "2025-06-15T19:05:51.186Z" }, - { url = "https://files.pythonhosted.org/packages/25/41/2dd88054b849aa546dbeef5696019c58f8e0774f4d1c42123273304cdb2e/watchfiles-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7", size = 292349, upload-time = "2025-06-15T19:05:52.201Z" }, - { url = "https://files.pythonhosted.org/packages/c8/cf/421d659de88285eb13941cf11a81f875c176f76a6d99342599be88e08d03/watchfiles-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5", size = 283836, upload-time = "2025-06-15T19:05:53.265Z" }, - { url = "https://files.pythonhosted.org/packages/45/10/6faf6858d527e3599cc50ec9fcae73590fbddc1420bd4fdccfebffeedbc6/watchfiles-1.1.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1", size = 400343, upload-time = "2025-06-15T19:05:54.252Z" }, - { url = "https://files.pythonhosted.org/packages/03/20/5cb7d3966f5e8c718006d0e97dfe379a82f16fecd3caa7810f634412047a/watchfiles-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339", size = 392916, upload-time = "2025-06-15T19:05:55.264Z" }, - { url = "https://files.pythonhosted.org/packages/8c/07/d8f1176328fa9e9581b6f120b017e286d2a2d22ae3f554efd9515c8e1b49/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633", size = 449582, upload-time = "2025-06-15T19:05:56.317Z" }, - { url = "https://files.pythonhosted.org/packages/66/e8/80a14a453cf6038e81d072a86c05276692a1826471fef91df7537dba8b46/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011", size = 456752, upload-time = "2025-06-15T19:05:57.359Z" }, - { url = "https://files.pythonhosted.org/packages/5a/25/0853b3fe0e3c2f5af9ea60eb2e781eade939760239a72c2d38fc4cc335f6/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670", size = 481436, upload-time = "2025-06-15T19:05:58.447Z" }, - { url = "https://files.pythonhosted.org/packages/fe/9e/4af0056c258b861fbb29dcb36258de1e2b857be4a9509e6298abcf31e5c9/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf", size = 596016, upload-time = "2025-06-15T19:05:59.59Z" }, - { url = "https://files.pythonhosted.org/packages/c5/fa/95d604b58aa375e781daf350897aaaa089cff59d84147e9ccff2447c8294/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4", size = 476727, upload-time = "2025-06-15T19:06:01.086Z" }, - { url = "https://files.pythonhosted.org/packages/65/95/fe479b2664f19be4cf5ceeb21be05afd491d95f142e72d26a42f41b7c4f8/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20", size = 451864, upload-time = "2025-06-15T19:06:02.144Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8a/3c4af14b93a15ce55901cd7a92e1a4701910f1768c78fb30f61d2b79785b/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef", size = 625626, upload-time = "2025-06-15T19:06:03.578Z" }, - { url = "https://files.pythonhosted.org/packages/da/f5/cf6aa047d4d9e128f4b7cde615236a915673775ef171ff85971d698f3c2c/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb", size = 622744, upload-time = "2025-06-15T19:06:05.066Z" }, - { url = "https://files.pythonhosted.org/packages/2c/00/70f75c47f05dea6fd30df90f047765f6fc2d6eb8b5a3921379b0b04defa2/watchfiles-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297", size = 402114, upload-time = "2025-06-15T19:06:06.186Z" }, - { url = "https://files.pythonhosted.org/packages/53/03/acd69c48db4a1ed1de26b349d94077cca2238ff98fd64393f3e97484cae6/watchfiles-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018", size = 393879, upload-time = "2025-06-15T19:06:07.369Z" }, - { url = "https://files.pythonhosted.org/packages/2f/c8/a9a2a6f9c8baa4eceae5887fecd421e1b7ce86802bcfc8b6a942e2add834/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0", size = 450026, upload-time = "2025-06-15T19:06:08.476Z" }, - { url = "https://files.pythonhosted.org/packages/fe/51/d572260d98388e6e2b967425c985e07d47ee6f62e6455cefb46a6e06eda5/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12", size = 457917, upload-time = "2025-06-15T19:06:09.988Z" }, - { url = "https://files.pythonhosted.org/packages/c6/2d/4258e52917bf9f12909b6ec314ff9636276f3542f9d3807d143f27309104/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb", size = 483602, upload-time = "2025-06-15T19:06:11.088Z" }, - { url = "https://files.pythonhosted.org/packages/84/99/bee17a5f341a4345fe7b7972a475809af9e528deba056f8963d61ea49f75/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77", size = 596758, upload-time = "2025-06-15T19:06:12.197Z" }, - { url = "https://files.pythonhosted.org/packages/40/76/e4bec1d59b25b89d2b0716b41b461ed655a9a53c60dc78ad5771fda5b3e6/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92", size = 477601, upload-time = "2025-06-15T19:06:13.391Z" }, - { url = "https://files.pythonhosted.org/packages/1f/fa/a514292956f4a9ce3c567ec0c13cce427c158e9f272062685a8a727d08fc/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e", size = 451936, upload-time = "2025-06-15T19:06:14.656Z" }, - { url = "https://files.pythonhosted.org/packages/32/5d/c3bf927ec3bbeb4566984eba8dd7a8eb69569400f5509904545576741f88/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b", size = 626243, upload-time = "2025-06-15T19:06:16.232Z" }, - { url = "https://files.pythonhosted.org/packages/e6/65/6e12c042f1a68c556802a84d54bb06d35577c81e29fba14019562479159c/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259", size = 623073, upload-time = "2025-06-15T19:06:17.457Z" }, - { url = "https://files.pythonhosted.org/packages/89/ab/7f79d9bf57329e7cbb0a6fd4c7bd7d0cee1e4a8ef0041459f5409da3506c/watchfiles-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f", size = 400872, upload-time = "2025-06-15T19:06:18.57Z" }, - { url = "https://files.pythonhosted.org/packages/df/d5/3f7bf9912798e9e6c516094db6b8932df53b223660c781ee37607030b6d3/watchfiles-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e", size = 392877, upload-time = "2025-06-15T19:06:19.55Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c5/54ec7601a2798604e01c75294770dbee8150e81c6e471445d7601610b495/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa", size = 449645, upload-time = "2025-06-15T19:06:20.66Z" }, - { url = "https://files.pythonhosted.org/packages/0a/04/c2f44afc3b2fce21ca0b7802cbd37ed90a29874f96069ed30a36dfe57c2b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8", size = 457424, upload-time = "2025-06-15T19:06:21.712Z" }, - { url = "https://files.pythonhosted.org/packages/9f/b0/eec32cb6c14d248095261a04f290636da3df3119d4040ef91a4a50b29fa5/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f", size = 481584, upload-time = "2025-06-15T19:06:22.777Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e2/ca4bb71c68a937d7145aa25709e4f5d68eb7698a25ce266e84b55d591bbd/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e", size = 596675, upload-time = "2025-06-15T19:06:24.226Z" }, - { url = "https://files.pythonhosted.org/packages/a1/dd/b0e4b7fb5acf783816bc950180a6cd7c6c1d2cf7e9372c0ea634e722712b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb", size = 477363, upload-time = "2025-06-15T19:06:25.42Z" }, - { url = "https://files.pythonhosted.org/packages/69/c4/088825b75489cb5b6a761a4542645718893d395d8c530b38734f19da44d2/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147", size = 452240, upload-time = "2025-06-15T19:06:26.552Z" }, - { url = "https://files.pythonhosted.org/packages/10/8c/22b074814970eeef43b7c44df98c3e9667c1f7bf5b83e0ff0201b0bd43f9/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8", size = 625607, upload-time = "2025-06-15T19:06:27.606Z" }, - { url = "https://files.pythonhosted.org/packages/32/fa/a4f5c2046385492b2273213ef815bf71a0d4c1943b784fb904e184e30201/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db", size = 623315, upload-time = "2025-06-15T19:06:29.076Z" }, - { url = "https://files.pythonhosted.org/packages/be/7c/a3d7c55cfa377c2f62c4ae3c6502b997186bc5e38156bafcb9b653de9a6d/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a6fd40bbb50d24976eb275ccb55cd1951dfb63dbc27cae3066a6ca5f4beabd5", size = 406748, upload-time = "2025-06-15T19:06:44.2Z" }, - { url = "https://files.pythonhosted.org/packages/38/d0/c46f1b2c0ca47f3667b144de6f0515f6d1c670d72f2ca29861cac78abaa1/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f811079d2f9795b5d48b55a37aa7773680a5659afe34b54cc1d86590a51507d", size = 398801, upload-time = "2025-06-15T19:06:45.774Z" }, - { url = "https://files.pythonhosted.org/packages/70/9c/9a6a42e97f92eeed77c3485a43ea96723900aefa3ac739a8c73f4bff2cd7/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2726d7bfd9f76158c84c10a409b77a320426540df8c35be172444394b17f7ea", size = 451528, upload-time = "2025-06-15T19:06:46.791Z" }, - { url = "https://files.pythonhosted.org/packages/51/7b/98c7f4f7ce7ff03023cf971cd84a3ee3b790021ae7584ffffa0eb2554b96/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df32d59cb9780f66d165a9a7a26f19df2c7d24e3bd58713108b41d0ff4f929c6", size = 454095, upload-time = "2025-06-15T19:06:48.211Z" }, - { url = "https://files.pythonhosted.org/packages/8c/6b/686dcf5d3525ad17b384fd94708e95193529b460a1b7bf40851f1328ec6e/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3", size = 406910, upload-time = "2025-06-15T19:06:49.335Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d3/71c2dcf81dc1edcf8af9f4d8d63b1316fb0a2dd90cbfd427e8d9dd584a90/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c", size = 398816, upload-time = "2025-06-15T19:06:50.433Z" }, - { url = "https://files.pythonhosted.org/packages/b8/fa/12269467b2fc006f8fce4cd6c3acfa77491dd0777d2a747415f28ccc8c60/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432", size = 451584, upload-time = "2025-06-15T19:06:51.834Z" }, - { url = "https://files.pythonhosted.org/packages/bd/d3/254cea30f918f489db09d6a8435a7de7047f8cb68584477a515f160541d6/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792", size = 454009, upload-time = "2025-06-15T19:06:52.896Z" }, + { url = "https://files.pythonhosted.org/packages/b9/dd/579d1dc57f0f895426a1211c4ef3b0cb37eb9e642bb04bdcd962b5df206a/watchfiles-1.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:27f30e14aa1c1e91cb653f03a63445739919aef84c8d2517997a83155e7a2fcc", size = 405757 }, + { url = "https://files.pythonhosted.org/packages/1c/a0/7a0318cd874393344d48c34d53b3dd419466adf59a29ba5b51c88dd18b86/watchfiles-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3366f56c272232860ab45c77c3ca7b74ee819c8e1f6f35a7125556b198bbc6df", size = 397511 }, + { url = "https://files.pythonhosted.org/packages/06/be/503514656d0555ec2195f60d810eca29b938772e9bfb112d5cd5ad6f6a9e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8412eacef34cae2836d891836a7fff7b754d6bcac61f6c12ba5ca9bc7e427b68", size = 450739 }, + { url = "https://files.pythonhosted.org/packages/4e/0d/a05dd9e5f136cdc29751816d0890d084ab99f8c17b86f25697288ca09bc7/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df670918eb7dd719642e05979fc84704af913d563fd17ed636f7c4783003fdcc", size = 458106 }, + { url = "https://files.pythonhosted.org/packages/f1/fa/9cd16e4dfdb831072b7ac39e7bea986e52128526251038eb481effe9f48e/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7642b9bc4827b5518ebdb3b82698ada8c14c7661ddec5fe719f3e56ccd13c97", size = 484264 }, + { url = "https://files.pythonhosted.org/packages/32/04/1da8a637c7e2b70e750a0308e9c8e662ada0cca46211fa9ef24a23937e0b/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:199207b2d3eeaeb80ef4411875a6243d9ad8bc35b07fc42daa6b801cc39cc41c", size = 597612 }, + { url = "https://files.pythonhosted.org/packages/30/01/109f2762e968d3e58c95731a206e5d7d2a7abaed4299dd8a94597250153c/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a479466da6db5c1e8754caee6c262cd373e6e6c363172d74394f4bff3d84d7b5", size = 477242 }, + { url = "https://files.pythonhosted.org/packages/b5/b8/46f58cf4969d3b7bc3ca35a98e739fa4085b0657a1540ccc29a1a0bc016f/watchfiles-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:935f9edd022ec13e447e5723a7d14456c8af254544cefbc533f6dd276c9aa0d9", size = 453148 }, + { url = "https://files.pythonhosted.org/packages/a5/cd/8267594263b1770f1eb76914940d7b2d03ee55eca212302329608208e061/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8076a5769d6bdf5f673a19d51da05fc79e2bbf25e9fe755c47595785c06a8c72", size = 626574 }, + { url = "https://files.pythonhosted.org/packages/a1/2f/7f2722e85899bed337cba715723e19185e288ef361360718973f891805be/watchfiles-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86b1e28d4c37e89220e924305cd9f82866bb0ace666943a6e4196c5df4d58dcc", size = 624378 }, + { url = "https://files.pythonhosted.org/packages/bf/20/64c88ec43d90a568234d021ab4b2a6f42a5230d772b987c3f9c00cc27b8b/watchfiles-1.1.0-cp310-cp310-win32.whl", hash = "sha256:d1caf40c1c657b27858f9774d5c0e232089bca9cb8ee17ce7478c6e9264d2587", size = 279829 }, + { url = "https://files.pythonhosted.org/packages/39/5c/a9c1ed33de7af80935e4eac09570de679c6e21c07070aa99f74b4431f4d6/watchfiles-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a89c75a5b9bc329131115a409d0acc16e8da8dfd5867ba59f1dd66ae7ea8fa82", size = 292192 }, + { url = "https://files.pythonhosted.org/packages/8b/78/7401154b78ab484ccaaeef970dc2af0cb88b5ba8a1b415383da444cdd8d3/watchfiles-1.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c9649dfc57cc1f9835551deb17689e8d44666315f2e82d337b9f07bd76ae3aa2", size = 405751 }, + { url = "https://files.pythonhosted.org/packages/76/63/e6c3dbc1f78d001589b75e56a288c47723de28c580ad715eb116639152b5/watchfiles-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:406520216186b99374cdb58bc48e34bb74535adec160c8459894884c983a149c", size = 397313 }, + { url = "https://files.pythonhosted.org/packages/6c/a2/8afa359ff52e99af1632f90cbf359da46184207e893a5f179301b0c8d6df/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45350fd1dc75cd68d3d72c47f5b513cb0578da716df5fba02fff31c69d5f2d", size = 450792 }, + { url = "https://files.pythonhosted.org/packages/1d/bf/7446b401667f5c64972a57a0233be1104157fc3abf72c4ef2666c1bd09b2/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11ee4444250fcbeb47459a877e5e80ed994ce8e8d20283857fc128be1715dac7", size = 458196 }, + { url = "https://files.pythonhosted.org/packages/58/2f/501ddbdfa3fa874ea5597c77eeea3d413579c29af26c1091b08d0c792280/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda8136e6a80bdea23e5e74e09df0362744d24ffb8cd59c4a95a6ce3d142f79c", size = 484788 }, + { url = "https://files.pythonhosted.org/packages/61/1e/9c18eb2eb5c953c96bc0e5f626f0e53cfef4bd19bd50d71d1a049c63a575/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b915daeb2d8c1f5cee4b970f2e2c988ce6514aace3c9296e58dd64dc9aa5d575", size = 597879 }, + { url = "https://files.pythonhosted.org/packages/8b/6c/1467402e5185d89388b4486745af1e0325007af0017c3384cc786fff0542/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed8fc66786de8d0376f9f913c09e963c66e90ced9aa11997f93bdb30f7c872a8", size = 477447 }, + { url = "https://files.pythonhosted.org/packages/2b/a1/ec0a606bde4853d6c4a578f9391eeb3684a9aea736a8eb217e3e00aa89a1/watchfiles-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe4371595edf78c41ef8ac8df20df3943e13defd0efcb732b2e393b5a8a7a71f", size = 453145 }, + { url = "https://files.pythonhosted.org/packages/90/b9/ef6f0c247a6a35d689fc970dc7f6734f9257451aefb30def5d100d6246a5/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b7c5f6fe273291f4d414d55b2c80d33c457b8a42677ad14b4b47ff025d0893e4", size = 626539 }, + { url = "https://files.pythonhosted.org/packages/34/44/6ffda5537085106ff5aaa762b0d130ac6c75a08015dd1621376f708c94de/watchfiles-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7738027989881e70e3723c75921f1efa45225084228788fc59ea8c6d732eb30d", size = 624472 }, + { url = "https://files.pythonhosted.org/packages/c3/e3/71170985c48028fa3f0a50946916a14055e741db11c2e7bc2f3b61f4d0e3/watchfiles-1.1.0-cp311-cp311-win32.whl", hash = "sha256:622d6b2c06be19f6e89b1d951485a232e3b59618def88dbeda575ed8f0d8dbf2", size = 279348 }, + { url = "https://files.pythonhosted.org/packages/89/1b/3e39c68b68a7a171070f81fc2561d23ce8d6859659406842a0e4bebf3bba/watchfiles-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:48aa25e5992b61debc908a61ab4d3f216b64f44fdaa71eb082d8b2de846b7d12", size = 292607 }, + { url = "https://files.pythonhosted.org/packages/61/9f/2973b7539f2bdb6ea86d2c87f70f615a71a1fc2dba2911795cea25968aea/watchfiles-1.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:00645eb79a3faa70d9cb15c8d4187bb72970b2470e938670240c7998dad9f13a", size = 285056 }, + { url = "https://files.pythonhosted.org/packages/f6/b8/858957045a38a4079203a33aaa7d23ea9269ca7761c8a074af3524fbb240/watchfiles-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179", size = 402339 }, + { url = "https://files.pythonhosted.org/packages/80/28/98b222cca751ba68e88521fabd79a4fab64005fc5976ea49b53fa205d1fa/watchfiles-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5", size = 394409 }, + { url = "https://files.pythonhosted.org/packages/86/50/dee79968566c03190677c26f7f47960aff738d32087087bdf63a5473e7df/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297", size = 450939 }, + { url = "https://files.pythonhosted.org/packages/40/45/a7b56fb129700f3cfe2594a01aa38d033b92a33dddce86c8dfdfc1247b72/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0", size = 457270 }, + { url = "https://files.pythonhosted.org/packages/b5/c8/fa5ef9476b1d02dc6b5e258f515fcaaecf559037edf8b6feffcbc097c4b8/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e", size = 483370 }, + { url = "https://files.pythonhosted.org/packages/98/68/42cfcdd6533ec94f0a7aab83f759ec11280f70b11bfba0b0f885e298f9bd/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee", size = 598654 }, + { url = "https://files.pythonhosted.org/packages/d3/74/b2a1544224118cc28df7e59008a929e711f9c68ce7d554e171b2dc531352/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd", size = 478667 }, + { url = "https://files.pythonhosted.org/packages/8c/77/e3362fe308358dc9f8588102481e599c83e1b91c2ae843780a7ded939a35/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f", size = 452213 }, + { url = "https://files.pythonhosted.org/packages/6e/17/c8f1a36540c9a1558d4faf08e909399e8133599fa359bf52ec8fcee5be6f/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4", size = 626718 }, + { url = "https://files.pythonhosted.org/packages/26/45/fb599be38b4bd38032643783d7496a26a6f9ae05dea1a42e58229a20ac13/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f", size = 623098 }, + { url = "https://files.pythonhosted.org/packages/a1/e7/fdf40e038475498e160cd167333c946e45d8563ae4dd65caf757e9ffe6b4/watchfiles-1.1.0-cp312-cp312-win32.whl", hash = "sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd", size = 279209 }, + { url = "https://files.pythonhosted.org/packages/3f/d3/3ae9d5124ec75143bdf088d436cba39812122edc47709cd2caafeac3266f/watchfiles-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47", size = 292786 }, + { url = "https://files.pythonhosted.org/packages/26/2f/7dd4fc8b5f2b34b545e19629b4a018bfb1de23b3a496766a2c1165ca890d/watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6", size = 284343 }, + { url = "https://files.pythonhosted.org/packages/d3/42/fae874df96595556a9089ade83be34a2e04f0f11eb53a8dbf8a8a5e562b4/watchfiles-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30", size = 402004 }, + { url = "https://files.pythonhosted.org/packages/fa/55/a77e533e59c3003d9803c09c44c3651224067cbe7fb5d574ddbaa31e11ca/watchfiles-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a", size = 393671 }, + { url = "https://files.pythonhosted.org/packages/05/68/b0afb3f79c8e832e6571022611adbdc36e35a44e14f129ba09709aa4bb7a/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc", size = 449772 }, + { url = "https://files.pythonhosted.org/packages/ff/05/46dd1f6879bc40e1e74c6c39a1b9ab9e790bf1f5a2fe6c08b463d9a807f4/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b", size = 456789 }, + { url = "https://files.pythonhosted.org/packages/8b/ca/0eeb2c06227ca7f12e50a47a3679df0cd1ba487ea19cf844a905920f8e95/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895", size = 482551 }, + { url = "https://files.pythonhosted.org/packages/31/47/2cecbd8694095647406645f822781008cc524320466ea393f55fe70eed3b/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a", size = 597420 }, + { url = "https://files.pythonhosted.org/packages/d9/7e/82abc4240e0806846548559d70f0b1a6dfdca75c1b4f9fa62b504ae9b083/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b", size = 477950 }, + { url = "https://files.pythonhosted.org/packages/25/0d/4d564798a49bf5482a4fa9416dea6b6c0733a3b5700cb8a5a503c4b15853/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c", size = 451706 }, + { url = "https://files.pythonhosted.org/packages/81/b5/5516cf46b033192d544102ea07c65b6f770f10ed1d0a6d388f5d3874f6e4/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b", size = 625814 }, + { url = "https://files.pythonhosted.org/packages/0c/dd/7c1331f902f30669ac3e754680b6edb9a0dd06dea5438e61128111fadd2c/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb", size = 622820 }, + { url = "https://files.pythonhosted.org/packages/1b/14/36d7a8e27cd128d7b1009e7715a7c02f6c131be9d4ce1e5c3b73d0e342d8/watchfiles-1.1.0-cp313-cp313-win32.whl", hash = "sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9", size = 279194 }, + { url = "https://files.pythonhosted.org/packages/25/41/2dd88054b849aa546dbeef5696019c58f8e0774f4d1c42123273304cdb2e/watchfiles-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7", size = 292349 }, + { url = "https://files.pythonhosted.org/packages/c8/cf/421d659de88285eb13941cf11a81f875c176f76a6d99342599be88e08d03/watchfiles-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5", size = 283836 }, + { url = "https://files.pythonhosted.org/packages/45/10/6faf6858d527e3599cc50ec9fcae73590fbddc1420bd4fdccfebffeedbc6/watchfiles-1.1.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1", size = 400343 }, + { url = "https://files.pythonhosted.org/packages/03/20/5cb7d3966f5e8c718006d0e97dfe379a82f16fecd3caa7810f634412047a/watchfiles-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339", size = 392916 }, + { url = "https://files.pythonhosted.org/packages/8c/07/d8f1176328fa9e9581b6f120b017e286d2a2d22ae3f554efd9515c8e1b49/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633", size = 449582 }, + { url = "https://files.pythonhosted.org/packages/66/e8/80a14a453cf6038e81d072a86c05276692a1826471fef91df7537dba8b46/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011", size = 456752 }, + { url = "https://files.pythonhosted.org/packages/5a/25/0853b3fe0e3c2f5af9ea60eb2e781eade939760239a72c2d38fc4cc335f6/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670", size = 481436 }, + { url = "https://files.pythonhosted.org/packages/fe/9e/4af0056c258b861fbb29dcb36258de1e2b857be4a9509e6298abcf31e5c9/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf", size = 596016 }, + { url = "https://files.pythonhosted.org/packages/c5/fa/95d604b58aa375e781daf350897aaaa089cff59d84147e9ccff2447c8294/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4", size = 476727 }, + { url = "https://files.pythonhosted.org/packages/65/95/fe479b2664f19be4cf5ceeb21be05afd491d95f142e72d26a42f41b7c4f8/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20", size = 451864 }, + { url = "https://files.pythonhosted.org/packages/d3/8a/3c4af14b93a15ce55901cd7a92e1a4701910f1768c78fb30f61d2b79785b/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef", size = 625626 }, + { url = "https://files.pythonhosted.org/packages/da/f5/cf6aa047d4d9e128f4b7cde615236a915673775ef171ff85971d698f3c2c/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb", size = 622744 }, + { url = "https://files.pythonhosted.org/packages/2c/00/70f75c47f05dea6fd30df90f047765f6fc2d6eb8b5a3921379b0b04defa2/watchfiles-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297", size = 402114 }, + { url = "https://files.pythonhosted.org/packages/53/03/acd69c48db4a1ed1de26b349d94077cca2238ff98fd64393f3e97484cae6/watchfiles-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018", size = 393879 }, + { url = "https://files.pythonhosted.org/packages/2f/c8/a9a2a6f9c8baa4eceae5887fecd421e1b7ce86802bcfc8b6a942e2add834/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0", size = 450026 }, + { url = "https://files.pythonhosted.org/packages/fe/51/d572260d98388e6e2b967425c985e07d47ee6f62e6455cefb46a6e06eda5/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12", size = 457917 }, + { url = "https://files.pythonhosted.org/packages/c6/2d/4258e52917bf9f12909b6ec314ff9636276f3542f9d3807d143f27309104/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb", size = 483602 }, + { url = "https://files.pythonhosted.org/packages/84/99/bee17a5f341a4345fe7b7972a475809af9e528deba056f8963d61ea49f75/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77", size = 596758 }, + { url = "https://files.pythonhosted.org/packages/40/76/e4bec1d59b25b89d2b0716b41b461ed655a9a53c60dc78ad5771fda5b3e6/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92", size = 477601 }, + { url = "https://files.pythonhosted.org/packages/1f/fa/a514292956f4a9ce3c567ec0c13cce427c158e9f272062685a8a727d08fc/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e", size = 451936 }, + { url = "https://files.pythonhosted.org/packages/32/5d/c3bf927ec3bbeb4566984eba8dd7a8eb69569400f5509904545576741f88/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b", size = 626243 }, + { url = "https://files.pythonhosted.org/packages/e6/65/6e12c042f1a68c556802a84d54bb06d35577c81e29fba14019562479159c/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259", size = 623073 }, + { url = "https://files.pythonhosted.org/packages/89/ab/7f79d9bf57329e7cbb0a6fd4c7bd7d0cee1e4a8ef0041459f5409da3506c/watchfiles-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f", size = 400872 }, + { url = "https://files.pythonhosted.org/packages/df/d5/3f7bf9912798e9e6c516094db6b8932df53b223660c781ee37607030b6d3/watchfiles-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e", size = 392877 }, + { url = "https://files.pythonhosted.org/packages/0d/c5/54ec7601a2798604e01c75294770dbee8150e81c6e471445d7601610b495/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa", size = 449645 }, + { url = "https://files.pythonhosted.org/packages/0a/04/c2f44afc3b2fce21ca0b7802cbd37ed90a29874f96069ed30a36dfe57c2b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8", size = 457424 }, + { url = "https://files.pythonhosted.org/packages/9f/b0/eec32cb6c14d248095261a04f290636da3df3119d4040ef91a4a50b29fa5/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f", size = 481584 }, + { url = "https://files.pythonhosted.org/packages/d1/e2/ca4bb71c68a937d7145aa25709e4f5d68eb7698a25ce266e84b55d591bbd/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e", size = 596675 }, + { url = "https://files.pythonhosted.org/packages/a1/dd/b0e4b7fb5acf783816bc950180a6cd7c6c1d2cf7e9372c0ea634e722712b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb", size = 477363 }, + { url = "https://files.pythonhosted.org/packages/69/c4/088825b75489cb5b6a761a4542645718893d395d8c530b38734f19da44d2/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147", size = 452240 }, + { url = "https://files.pythonhosted.org/packages/10/8c/22b074814970eeef43b7c44df98c3e9667c1f7bf5b83e0ff0201b0bd43f9/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8", size = 625607 }, + { url = "https://files.pythonhosted.org/packages/32/fa/a4f5c2046385492b2273213ef815bf71a0d4c1943b784fb904e184e30201/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db", size = 623315 }, + { url = "https://files.pythonhosted.org/packages/be/7c/a3d7c55cfa377c2f62c4ae3c6502b997186bc5e38156bafcb9b653de9a6d/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a6fd40bbb50d24976eb275ccb55cd1951dfb63dbc27cae3066a6ca5f4beabd5", size = 406748 }, + { url = "https://files.pythonhosted.org/packages/38/d0/c46f1b2c0ca47f3667b144de6f0515f6d1c670d72f2ca29861cac78abaa1/watchfiles-1.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9f811079d2f9795b5d48b55a37aa7773680a5659afe34b54cc1d86590a51507d", size = 398801 }, + { url = "https://files.pythonhosted.org/packages/70/9c/9a6a42e97f92eeed77c3485a43ea96723900aefa3ac739a8c73f4bff2cd7/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2726d7bfd9f76158c84c10a409b77a320426540df8c35be172444394b17f7ea", size = 451528 }, + { url = "https://files.pythonhosted.org/packages/51/7b/98c7f4f7ce7ff03023cf971cd84a3ee3b790021ae7584ffffa0eb2554b96/watchfiles-1.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df32d59cb9780f66d165a9a7a26f19df2c7d24e3bd58713108b41d0ff4f929c6", size = 454095 }, + { url = "https://files.pythonhosted.org/packages/8c/6b/686dcf5d3525ad17b384fd94708e95193529b460a1b7bf40851f1328ec6e/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0ece16b563b17ab26eaa2d52230c9a7ae46cf01759621f4fbbca280e438267b3", size = 406910 }, + { url = "https://files.pythonhosted.org/packages/f3/d3/71c2dcf81dc1edcf8af9f4d8d63b1316fb0a2dd90cbfd427e8d9dd584a90/watchfiles-1.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:51b81e55d40c4b4aa8658427a3ee7ea847c591ae9e8b81ef94a90b668999353c", size = 398816 }, + { url = "https://files.pythonhosted.org/packages/b8/fa/12269467b2fc006f8fce4cd6c3acfa77491dd0777d2a747415f28ccc8c60/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2bcdc54ea267fe72bfc7d83c041e4eb58d7d8dc6f578dfddb52f037ce62f432", size = 451584 }, + { url = "https://files.pythonhosted.org/packages/bd/d3/254cea30f918f489db09d6a8435a7de7047f8cb68584477a515f160541d6/watchfiles-1.1.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923fec6e5461c42bd7e3fd5ec37492c6f3468be0499bc0707b4bbbc16ac21792", size = 454009 }, ] [[package]] name = "wcwidth" version = "0.2.13" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" }, + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, ] [[package]] name = "websockets" version = "15.0.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423, upload-time = "2025-03-05T20:01:35.363Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080, upload-time = "2025-03-05T20:01:37.304Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329, upload-time = "2025-03-05T20:01:39.668Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312, upload-time = "2025-03-05T20:01:41.815Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319, upload-time = "2025-03-05T20:01:43.967Z" }, - { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631, upload-time = "2025-03-05T20:01:46.104Z" }, - { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016, upload-time = "2025-03-05T20:01:47.603Z" }, - { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426, upload-time = "2025-03-05T20:01:48.949Z" }, - { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360, upload-time = "2025-03-05T20:01:50.938Z" }, - { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388, upload-time = "2025-03-05T20:01:52.213Z" }, - { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830, upload-time = "2025-03-05T20:01:53.922Z" }, - { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" }, - { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" }, - { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" }, - { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" }, - { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" }, - { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" }, - { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" }, - { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" }, - { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" }, - { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, - { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, - { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, - { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, - { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, - { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, - { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, - { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, - { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, - { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, - { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, - { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, - { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, - { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, - { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, - { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, - { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109, upload-time = "2025-03-05T20:03:17.769Z" }, - { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343, upload-time = "2025-03-05T20:03:19.094Z" }, - { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599, upload-time = "2025-03-05T20:03:21.1Z" }, - { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207, upload-time = "2025-03-05T20:03:23.221Z" }, - { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155, upload-time = "2025-03-05T20:03:25.321Z" }, - { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884, upload-time = "2025-03-05T20:03:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, + { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423 }, + { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080 }, + { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329 }, + { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312 }, + { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319 }, + { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631 }, + { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016 }, + { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426 }, + { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360 }, + { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388 }, + { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830 }, + { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423 }, + { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082 }, + { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330 }, + { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878 }, + { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883 }, + { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252 }, + { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521 }, + { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958 }, + { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918 }, + { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388 }, + { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828 }, + { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437 }, + { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096 }, + { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332 }, + { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152 }, + { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096 }, + { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523 }, + { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790 }, + { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165 }, + { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160 }, + { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395 }, + { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841 }, + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440 }, + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098 }, + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329 }, + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111 }, + { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054 }, + { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496 }, + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829 }, + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217 }, + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195 }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393 }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837 }, + { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109 }, + { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343 }, + { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599 }, + { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207 }, + { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155 }, + { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884 }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 }, ] [[package]] name = "wrapt" version = "1.17.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } +sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547 } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/23/bb82321b86411eb51e5a5db3fb8f8032fd30bd7c2d74bfe936136b2fa1d6/wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04", size = 53482, upload-time = "2025-08-12T05:51:44.467Z" }, - { url = "https://files.pythonhosted.org/packages/45/69/f3c47642b79485a30a59c63f6d739ed779fb4cc8323205d047d741d55220/wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2", size = 38676, upload-time = "2025-08-12T05:51:32.636Z" }, - { url = "https://files.pythonhosted.org/packages/d1/71/e7e7f5670c1eafd9e990438e69d8fb46fa91a50785332e06b560c869454f/wrapt-1.17.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c", size = 38957, upload-time = "2025-08-12T05:51:54.655Z" }, - { url = "https://files.pythonhosted.org/packages/de/17/9f8f86755c191d6779d7ddead1a53c7a8aa18bccb7cea8e7e72dfa6a8a09/wrapt-1.17.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775", size = 81975, upload-time = "2025-08-12T05:52:30.109Z" }, - { url = "https://files.pythonhosted.org/packages/f2/15/dd576273491f9f43dd09fce517f6c2ce6eb4fe21681726068db0d0467096/wrapt-1.17.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd", size = 83149, upload-time = "2025-08-12T05:52:09.316Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c4/5eb4ce0d4814521fee7aa806264bf7a114e748ad05110441cd5b8a5c744b/wrapt-1.17.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05", size = 82209, upload-time = "2025-08-12T05:52:10.331Z" }, - { url = "https://files.pythonhosted.org/packages/31/4b/819e9e0eb5c8dc86f60dfc42aa4e2c0d6c3db8732bce93cc752e604bb5f5/wrapt-1.17.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418", size = 81551, upload-time = "2025-08-12T05:52:31.137Z" }, - { url = "https://files.pythonhosted.org/packages/f8/83/ed6baf89ba3a56694700139698cf703aac9f0f9eb03dab92f57551bd5385/wrapt-1.17.3-cp310-cp310-win32.whl", hash = "sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390", size = 36464, upload-time = "2025-08-12T05:53:01.204Z" }, - { url = "https://files.pythonhosted.org/packages/2f/90/ee61d36862340ad7e9d15a02529df6b948676b9a5829fd5e16640156627d/wrapt-1.17.3-cp310-cp310-win_amd64.whl", hash = "sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6", size = 38748, upload-time = "2025-08-12T05:53:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/bd/c3/cefe0bd330d389c9983ced15d326f45373f4073c9f4a8c2f99b50bfea329/wrapt-1.17.3-cp310-cp310-win_arm64.whl", hash = "sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18", size = 36810, upload-time = "2025-08-12T05:52:51.906Z" }, - { url = "https://files.pythonhosted.org/packages/52/db/00e2a219213856074a213503fdac0511203dceefff26e1daa15250cc01a0/wrapt-1.17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7", size = 53482, upload-time = "2025-08-12T05:51:45.79Z" }, - { url = "https://files.pythonhosted.org/packages/5e/30/ca3c4a5eba478408572096fe9ce36e6e915994dd26a4e9e98b4f729c06d9/wrapt-1.17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85", size = 38674, upload-time = "2025-08-12T05:51:34.629Z" }, - { url = "https://files.pythonhosted.org/packages/31/25/3e8cc2c46b5329c5957cec959cb76a10718e1a513309c31399a4dad07eb3/wrapt-1.17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f", size = 38959, upload-time = "2025-08-12T05:51:56.074Z" }, - { url = "https://files.pythonhosted.org/packages/5d/8f/a32a99fc03e4b37e31b57cb9cefc65050ea08147a8ce12f288616b05ef54/wrapt-1.17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311", size = 82376, upload-time = "2025-08-12T05:52:32.134Z" }, - { url = "https://files.pythonhosted.org/packages/31/57/4930cb8d9d70d59c27ee1332a318c20291749b4fba31f113c2f8ac49a72e/wrapt-1.17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1", size = 83604, upload-time = "2025-08-12T05:52:11.663Z" }, - { url = "https://files.pythonhosted.org/packages/a8/f3/1afd48de81d63dd66e01b263a6fbb86e1b5053b419b9b33d13e1f6d0f7d0/wrapt-1.17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5", size = 82782, upload-time = "2025-08-12T05:52:12.626Z" }, - { url = "https://files.pythonhosted.org/packages/1e/d7/4ad5327612173b144998232f98a85bb24b60c352afb73bc48e3e0d2bdc4e/wrapt-1.17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2", size = 82076, upload-time = "2025-08-12T05:52:33.168Z" }, - { url = "https://files.pythonhosted.org/packages/bb/59/e0adfc831674a65694f18ea6dc821f9fcb9ec82c2ce7e3d73a88ba2e8718/wrapt-1.17.3-cp311-cp311-win32.whl", hash = "sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89", size = 36457, upload-time = "2025-08-12T05:53:03.936Z" }, - { url = "https://files.pythonhosted.org/packages/83/88/16b7231ba49861b6f75fc309b11012ede4d6b0a9c90969d9e0db8d991aeb/wrapt-1.17.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77", size = 38745, upload-time = "2025-08-12T05:53:02.885Z" }, - { url = "https://files.pythonhosted.org/packages/9a/1e/c4d4f3398ec073012c51d1c8d87f715f56765444e1a4b11e5180577b7e6e/wrapt-1.17.3-cp311-cp311-win_arm64.whl", hash = "sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a", size = 36806, upload-time = "2025-08-12T05:52:53.368Z" }, - { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998, upload-time = "2025-08-12T05:51:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020, upload-time = "2025-08-12T05:51:35.906Z" }, - { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098, upload-time = "2025-08-12T05:51:57.474Z" }, - { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036, upload-time = "2025-08-12T05:52:34.784Z" }, - { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156, upload-time = "2025-08-12T05:52:13.599Z" }, - { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102, upload-time = "2025-08-12T05:52:14.56Z" }, - { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732, upload-time = "2025-08-12T05:52:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705, upload-time = "2025-08-12T05:53:07.123Z" }, - { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877, upload-time = "2025-08-12T05:53:05.436Z" }, - { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885, upload-time = "2025-08-12T05:52:54.367Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, - { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, - { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, - { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, - { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, - { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, - { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, - { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, - { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, - { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, - { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, - { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, - { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, - { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, - { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, - { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, - { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, - { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, - { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, + { url = "https://files.pythonhosted.org/packages/3f/23/bb82321b86411eb51e5a5db3fb8f8032fd30bd7c2d74bfe936136b2fa1d6/wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04", size = 53482 }, + { url = "https://files.pythonhosted.org/packages/45/69/f3c47642b79485a30a59c63f6d739ed779fb4cc8323205d047d741d55220/wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2", size = 38676 }, + { url = "https://files.pythonhosted.org/packages/d1/71/e7e7f5670c1eafd9e990438e69d8fb46fa91a50785332e06b560c869454f/wrapt-1.17.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c", size = 38957 }, + { url = "https://files.pythonhosted.org/packages/de/17/9f8f86755c191d6779d7ddead1a53c7a8aa18bccb7cea8e7e72dfa6a8a09/wrapt-1.17.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775", size = 81975 }, + { url = "https://files.pythonhosted.org/packages/f2/15/dd576273491f9f43dd09fce517f6c2ce6eb4fe21681726068db0d0467096/wrapt-1.17.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd", size = 83149 }, + { url = "https://files.pythonhosted.org/packages/0c/c4/5eb4ce0d4814521fee7aa806264bf7a114e748ad05110441cd5b8a5c744b/wrapt-1.17.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05", size = 82209 }, + { url = "https://files.pythonhosted.org/packages/31/4b/819e9e0eb5c8dc86f60dfc42aa4e2c0d6c3db8732bce93cc752e604bb5f5/wrapt-1.17.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418", size = 81551 }, + { url = "https://files.pythonhosted.org/packages/f8/83/ed6baf89ba3a56694700139698cf703aac9f0f9eb03dab92f57551bd5385/wrapt-1.17.3-cp310-cp310-win32.whl", hash = "sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390", size = 36464 }, + { url = "https://files.pythonhosted.org/packages/2f/90/ee61d36862340ad7e9d15a02529df6b948676b9a5829fd5e16640156627d/wrapt-1.17.3-cp310-cp310-win_amd64.whl", hash = "sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6", size = 38748 }, + { url = "https://files.pythonhosted.org/packages/bd/c3/cefe0bd330d389c9983ced15d326f45373f4073c9f4a8c2f99b50bfea329/wrapt-1.17.3-cp310-cp310-win_arm64.whl", hash = "sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18", size = 36810 }, + { url = "https://files.pythonhosted.org/packages/52/db/00e2a219213856074a213503fdac0511203dceefff26e1daa15250cc01a0/wrapt-1.17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7", size = 53482 }, + { url = "https://files.pythonhosted.org/packages/5e/30/ca3c4a5eba478408572096fe9ce36e6e915994dd26a4e9e98b4f729c06d9/wrapt-1.17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85", size = 38674 }, + { url = "https://files.pythonhosted.org/packages/31/25/3e8cc2c46b5329c5957cec959cb76a10718e1a513309c31399a4dad07eb3/wrapt-1.17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f", size = 38959 }, + { url = "https://files.pythonhosted.org/packages/5d/8f/a32a99fc03e4b37e31b57cb9cefc65050ea08147a8ce12f288616b05ef54/wrapt-1.17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311", size = 82376 }, + { url = "https://files.pythonhosted.org/packages/31/57/4930cb8d9d70d59c27ee1332a318c20291749b4fba31f113c2f8ac49a72e/wrapt-1.17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1", size = 83604 }, + { url = "https://files.pythonhosted.org/packages/a8/f3/1afd48de81d63dd66e01b263a6fbb86e1b5053b419b9b33d13e1f6d0f7d0/wrapt-1.17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5", size = 82782 }, + { url = "https://files.pythonhosted.org/packages/1e/d7/4ad5327612173b144998232f98a85bb24b60c352afb73bc48e3e0d2bdc4e/wrapt-1.17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2", size = 82076 }, + { url = "https://files.pythonhosted.org/packages/bb/59/e0adfc831674a65694f18ea6dc821f9fcb9ec82c2ce7e3d73a88ba2e8718/wrapt-1.17.3-cp311-cp311-win32.whl", hash = "sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89", size = 36457 }, + { url = "https://files.pythonhosted.org/packages/83/88/16b7231ba49861b6f75fc309b11012ede4d6b0a9c90969d9e0db8d991aeb/wrapt-1.17.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77", size = 38745 }, + { url = "https://files.pythonhosted.org/packages/9a/1e/c4d4f3398ec073012c51d1c8d87f715f56765444e1a4b11e5180577b7e6e/wrapt-1.17.3-cp311-cp311-win_arm64.whl", hash = "sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a", size = 36806 }, + { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998 }, + { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020 }, + { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098 }, + { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036 }, + { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156 }, + { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102 }, + { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732 }, + { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705 }, + { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877 }, + { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885 }, + { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003 }, + { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025 }, + { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108 }, + { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072 }, + { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214 }, + { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105 }, + { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766 }, + { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711 }, + { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885 }, + { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896 }, + { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132 }, + { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091 }, + { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172 }, + { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163 }, + { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963 }, + { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945 }, + { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857 }, + { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178 }, + { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310 }, + { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266 }, + { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544 }, + { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283 }, + { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366 }, + { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571 }, + { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094 }, + { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659 }, + { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946 }, + { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717 }, + { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334 }, + { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471 }, + { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591 }, ] [[package]] @@ -4067,101 +4402,101 @@ dependencies = [ { name = "multidict", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "propcache", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3c/fb/efaa23fa4e45537b827620f04cf8f3cd658b76642205162e072703a5b963/yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac", size = 186428, upload-time = "2025-06-10T00:46:09.923Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3c/fb/efaa23fa4e45537b827620f04cf8f3cd658b76642205162e072703a5b963/yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac", size = 186428 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/65/7fed0d774abf47487c64be14e9223749468922817b5e8792b8a64792a1bb/yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4", size = 132910, upload-time = "2025-06-10T00:42:31.108Z" }, - { url = "https://files.pythonhosted.org/packages/8a/7b/988f55a52da99df9e56dc733b8e4e5a6ae2090081dc2754fc8fd34e60aa0/yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a", size = 90644, upload-time = "2025-06-10T00:42:33.851Z" }, - { url = "https://files.pythonhosted.org/packages/f7/de/30d98f03e95d30c7e3cc093759982d038c8833ec2451001d45ef4854edc1/yarl-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c869f2651cc77465f6cd01d938d91a11d9ea5d798738c1dc077f3de0b5e5fed", size = 89322, upload-time = "2025-06-10T00:42:35.688Z" }, - { url = "https://files.pythonhosted.org/packages/e0/7a/f2f314f5ebfe9200724b0b748de2186b927acb334cf964fd312eb86fc286/yarl-1.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62915e6688eb4d180d93840cda4110995ad50c459bf931b8b3775b37c264af1e", size = 323786, upload-time = "2025-06-10T00:42:37.817Z" }, - { url = "https://files.pythonhosted.org/packages/15/3f/718d26f189db96d993d14b984ce91de52e76309d0fd1d4296f34039856aa/yarl-1.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:41ebd28167bc6af8abb97fec1a399f412eec5fd61a3ccbe2305a18b84fb4ca73", size = 319627, upload-time = "2025-06-10T00:42:39.937Z" }, - { url = "https://files.pythonhosted.org/packages/a5/76/8fcfbf5fa2369157b9898962a4a7d96764b287b085b5b3d9ffae69cdefd1/yarl-1.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21242b4288a6d56f04ea193adde174b7e347ac46ce6bc84989ff7c1b1ecea84e", size = 339149, upload-time = "2025-06-10T00:42:42.627Z" }, - { url = "https://files.pythonhosted.org/packages/3c/95/d7fc301cc4661785967acc04f54a4a42d5124905e27db27bb578aac49b5c/yarl-1.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea21cdae6c7eb02ba02a475f37463abfe0a01f5d7200121b03e605d6a0439f8", size = 333327, upload-time = "2025-06-10T00:42:44.842Z" }, - { url = "https://files.pythonhosted.org/packages/65/94/e21269718349582eee81efc5c1c08ee71c816bfc1585b77d0ec3f58089eb/yarl-1.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f8a891e4a22a89f5dde7862994485e19db246b70bb288d3ce73a34422e55b23", size = 326054, upload-time = "2025-06-10T00:42:47.149Z" }, - { url = "https://files.pythonhosted.org/packages/32/ae/8616d1f07853704523519f6131d21f092e567c5af93de7e3e94b38d7f065/yarl-1.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd803820d44c8853a109a34e3660e5a61beae12970da479cf44aa2954019bf70", size = 315035, upload-time = "2025-06-10T00:42:48.852Z" }, - { url = "https://files.pythonhosted.org/packages/48/aa/0ace06280861ef055855333707db5e49c6e3a08840a7ce62682259d0a6c0/yarl-1.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b982fa7f74c80d5c0c7b5b38f908971e513380a10fecea528091405f519b9ebb", size = 338962, upload-time = "2025-06-10T00:42:51.024Z" }, - { url = "https://files.pythonhosted.org/packages/20/52/1e9d0e6916f45a8fb50e6844f01cb34692455f1acd548606cbda8134cd1e/yarl-1.20.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:33f29ecfe0330c570d997bcf1afd304377f2e48f61447f37e846a6058a4d33b2", size = 335399, upload-time = "2025-06-10T00:42:53.007Z" }, - { url = "https://files.pythonhosted.org/packages/f2/65/60452df742952c630e82f394cd409de10610481d9043aa14c61bf846b7b1/yarl-1.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:835ab2cfc74d5eb4a6a528c57f05688099da41cf4957cf08cad38647e4a83b30", size = 338649, upload-time = "2025-06-10T00:42:54.964Z" }, - { url = "https://files.pythonhosted.org/packages/7b/f5/6cd4ff38dcde57a70f23719a838665ee17079640c77087404c3d34da6727/yarl-1.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46b5e0ccf1943a9a6e766b2c2b8c732c55b34e28be57d8daa2b3c1d1d4009309", size = 358563, upload-time = "2025-06-10T00:42:57.28Z" }, - { url = "https://files.pythonhosted.org/packages/d1/90/c42eefd79d0d8222cb3227bdd51b640c0c1d0aa33fe4cc86c36eccba77d3/yarl-1.20.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:df47c55f7d74127d1b11251fe6397d84afdde0d53b90bedb46a23c0e534f9d24", size = 357609, upload-time = "2025-06-10T00:42:59.055Z" }, - { url = "https://files.pythonhosted.org/packages/03/c8/cea6b232cb4617514232e0f8a718153a95b5d82b5290711b201545825532/yarl-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76d12524d05841276b0e22573f28d5fbcb67589836772ae9244d90dd7d66aa13", size = 350224, upload-time = "2025-06-10T00:43:01.248Z" }, - { url = "https://files.pythonhosted.org/packages/ce/a3/eaa0ab9712f1f3d01faf43cf6f1f7210ce4ea4a7e9b28b489a2261ca8db9/yarl-1.20.1-cp310-cp310-win32.whl", hash = "sha256:6c4fbf6b02d70e512d7ade4b1f998f237137f1417ab07ec06358ea04f69134f8", size = 81753, upload-time = "2025-06-10T00:43:03.486Z" }, - { url = "https://files.pythonhosted.org/packages/8f/34/e4abde70a9256465fe31c88ed02c3f8502b7b5dead693a4f350a06413f28/yarl-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef6c4d69554d44b7f9d923245f8ad9a707d971e6209d51279196d8e8fe1ae16", size = 86817, upload-time = "2025-06-10T00:43:05.231Z" }, - { url = "https://files.pythonhosted.org/packages/b1/18/893b50efc2350e47a874c5c2d67e55a0ea5df91186b2a6f5ac52eff887cd/yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e", size = 133833, upload-time = "2025-06-10T00:43:07.393Z" }, - { url = "https://files.pythonhosted.org/packages/89/ed/b8773448030e6fc47fa797f099ab9eab151a43a25717f9ac043844ad5ea3/yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b", size = 91070, upload-time = "2025-06-10T00:43:09.538Z" }, - { url = "https://files.pythonhosted.org/packages/e3/e3/409bd17b1e42619bf69f60e4f031ce1ccb29bd7380117a55529e76933464/yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b", size = 89818, upload-time = "2025-06-10T00:43:11.575Z" }, - { url = "https://files.pythonhosted.org/packages/f8/77/64d8431a4d77c856eb2d82aa3de2ad6741365245a29b3a9543cd598ed8c5/yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4", size = 347003, upload-time = "2025-06-10T00:43:14.088Z" }, - { url = "https://files.pythonhosted.org/packages/8d/d2/0c7e4def093dcef0bd9fa22d4d24b023788b0a33b8d0088b51aa51e21e99/yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1", size = 336537, upload-time = "2025-06-10T00:43:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/f0/f3/fc514f4b2cf02cb59d10cbfe228691d25929ce8f72a38db07d3febc3f706/yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833", size = 362358, upload-time = "2025-06-10T00:43:18.704Z" }, - { url = "https://files.pythonhosted.org/packages/ea/6d/a313ac8d8391381ff9006ac05f1d4331cee3b1efaa833a53d12253733255/yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d", size = 357362, upload-time = "2025-06-10T00:43:20.888Z" }, - { url = "https://files.pythonhosted.org/packages/00/70/8f78a95d6935a70263d46caa3dd18e1f223cf2f2ff2037baa01a22bc5b22/yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8", size = 348979, upload-time = "2025-06-10T00:43:23.169Z" }, - { url = "https://files.pythonhosted.org/packages/cb/05/42773027968968f4f15143553970ee36ead27038d627f457cc44bbbeecf3/yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf", size = 337274, upload-time = "2025-06-10T00:43:27.111Z" }, - { url = "https://files.pythonhosted.org/packages/05/be/665634aa196954156741ea591d2f946f1b78ceee8bb8f28488bf28c0dd62/yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e", size = 363294, upload-time = "2025-06-10T00:43:28.96Z" }, - { url = "https://files.pythonhosted.org/packages/eb/90/73448401d36fa4e210ece5579895731f190d5119c4b66b43b52182e88cd5/yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389", size = 358169, upload-time = "2025-06-10T00:43:30.701Z" }, - { url = "https://files.pythonhosted.org/packages/c3/b0/fce922d46dc1eb43c811f1889f7daa6001b27a4005587e94878570300881/yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f", size = 362776, upload-time = "2025-06-10T00:43:32.51Z" }, - { url = "https://files.pythonhosted.org/packages/f1/0d/b172628fce039dae8977fd22caeff3eeebffd52e86060413f5673767c427/yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845", size = 381341, upload-time = "2025-06-10T00:43:34.543Z" }, - { url = "https://files.pythonhosted.org/packages/6b/9b/5b886d7671f4580209e855974fe1cecec409aa4a89ea58b8f0560dc529b1/yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1", size = 379988, upload-time = "2025-06-10T00:43:36.489Z" }, - { url = "https://files.pythonhosted.org/packages/73/be/75ef5fd0fcd8f083a5d13f78fd3f009528132a1f2a1d7c925c39fa20aa79/yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e", size = 371113, upload-time = "2025-06-10T00:43:38.592Z" }, - { url = "https://files.pythonhosted.org/packages/50/4f/62faab3b479dfdcb741fe9e3f0323e2a7d5cd1ab2edc73221d57ad4834b2/yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773", size = 81485, upload-time = "2025-06-10T00:43:41.038Z" }, - { url = "https://files.pythonhosted.org/packages/f0/09/d9c7942f8f05c32ec72cd5c8e041c8b29b5807328b68b4801ff2511d4d5e/yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e", size = 86686, upload-time = "2025-06-10T00:43:42.692Z" }, - { url = "https://files.pythonhosted.org/packages/5f/9a/cb7fad7d73c69f296eda6815e4a2c7ed53fc70c2f136479a91c8e5fbdb6d/yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9", size = 133667, upload-time = "2025-06-10T00:43:44.369Z" }, - { url = "https://files.pythonhosted.org/packages/67/38/688577a1cb1e656e3971fb66a3492501c5a5df56d99722e57c98249e5b8a/yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a", size = 91025, upload-time = "2025-06-10T00:43:46.295Z" }, - { url = "https://files.pythonhosted.org/packages/50/ec/72991ae51febeb11a42813fc259f0d4c8e0507f2b74b5514618d8b640365/yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2", size = 89709, upload-time = "2025-06-10T00:43:48.22Z" }, - { url = "https://files.pythonhosted.org/packages/99/da/4d798025490e89426e9f976702e5f9482005c548c579bdae792a4c37769e/yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee", size = 352287, upload-time = "2025-06-10T00:43:49.924Z" }, - { url = "https://files.pythonhosted.org/packages/1a/26/54a15c6a567aac1c61b18aa0f4b8aa2e285a52d547d1be8bf48abe2b3991/yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819", size = 345429, upload-time = "2025-06-10T00:43:51.7Z" }, - { url = "https://files.pythonhosted.org/packages/d6/95/9dcf2386cb875b234353b93ec43e40219e14900e046bf6ac118f94b1e353/yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16", size = 365429, upload-time = "2025-06-10T00:43:53.494Z" }, - { url = "https://files.pythonhosted.org/packages/91/b2/33a8750f6a4bc224242a635f5f2cff6d6ad5ba651f6edcccf721992c21a0/yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6", size = 363862, upload-time = "2025-06-10T00:43:55.766Z" }, - { url = "https://files.pythonhosted.org/packages/98/28/3ab7acc5b51f4434b181b0cee8f1f4b77a65919700a355fb3617f9488874/yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd", size = 355616, upload-time = "2025-06-10T00:43:58.056Z" }, - { url = "https://files.pythonhosted.org/packages/36/a3/f666894aa947a371724ec7cd2e5daa78ee8a777b21509b4252dd7bd15e29/yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a", size = 339954, upload-time = "2025-06-10T00:43:59.773Z" }, - { url = "https://files.pythonhosted.org/packages/f1/81/5f466427e09773c04219d3450d7a1256138a010b6c9f0af2d48565e9ad13/yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38", size = 365575, upload-time = "2025-06-10T00:44:02.051Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e3/e4b0ad8403e97e6c9972dd587388940a032f030ebec196ab81a3b8e94d31/yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef", size = 365061, upload-time = "2025-06-10T00:44:04.196Z" }, - { url = "https://files.pythonhosted.org/packages/ac/99/b8a142e79eb86c926f9f06452eb13ecb1bb5713bd01dc0038faf5452e544/yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f", size = 364142, upload-time = "2025-06-10T00:44:06.527Z" }, - { url = "https://files.pythonhosted.org/packages/34/f2/08ed34a4a506d82a1a3e5bab99ccd930a040f9b6449e9fd050320e45845c/yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8", size = 381894, upload-time = "2025-06-10T00:44:08.379Z" }, - { url = "https://files.pythonhosted.org/packages/92/f8/9a3fbf0968eac704f681726eff595dce9b49c8a25cd92bf83df209668285/yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a", size = 383378, upload-time = "2025-06-10T00:44:10.51Z" }, - { url = "https://files.pythonhosted.org/packages/af/85/9363f77bdfa1e4d690957cd39d192c4cacd1c58965df0470a4905253b54f/yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004", size = 374069, upload-time = "2025-06-10T00:44:12.834Z" }, - { url = "https://files.pythonhosted.org/packages/35/99/9918c8739ba271dcd935400cff8b32e3cd319eaf02fcd023d5dcd487a7c8/yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5", size = 81249, upload-time = "2025-06-10T00:44:14.731Z" }, - { url = "https://files.pythonhosted.org/packages/eb/83/5d9092950565481b413b31a23e75dd3418ff0a277d6e0abf3729d4d1ce25/yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698", size = 86710, upload-time = "2025-06-10T00:44:16.716Z" }, - { url = "https://files.pythonhosted.org/packages/8a/e1/2411b6d7f769a07687acee88a062af5833cf1966b7266f3d8dfb3d3dc7d3/yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a", size = 131811, upload-time = "2025-06-10T00:44:18.933Z" }, - { url = "https://files.pythonhosted.org/packages/b2/27/584394e1cb76fb771371770eccad35de400e7b434ce3142c2dd27392c968/yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3", size = 90078, upload-time = "2025-06-10T00:44:20.635Z" }, - { url = "https://files.pythonhosted.org/packages/bf/9a/3246ae92d4049099f52d9b0fe3486e3b500e29b7ea872d0f152966fc209d/yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7", size = 88748, upload-time = "2025-06-10T00:44:22.34Z" }, - { url = "https://files.pythonhosted.org/packages/a3/25/35afe384e31115a1a801fbcf84012d7a066d89035befae7c5d4284df1e03/yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691", size = 349595, upload-time = "2025-06-10T00:44:24.314Z" }, - { url = "https://files.pythonhosted.org/packages/28/2d/8aca6cb2cabc8f12efcb82749b9cefecbccfc7b0384e56cd71058ccee433/yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31", size = 342616, upload-time = "2025-06-10T00:44:26.167Z" }, - { url = "https://files.pythonhosted.org/packages/0b/e9/1312633d16b31acf0098d30440ca855e3492d66623dafb8e25b03d00c3da/yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28", size = 361324, upload-time = "2025-06-10T00:44:27.915Z" }, - { url = "https://files.pythonhosted.org/packages/bc/a0/688cc99463f12f7669eec7c8acc71ef56a1521b99eab7cd3abb75af887b0/yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653", size = 359676, upload-time = "2025-06-10T00:44:30.041Z" }, - { url = "https://files.pythonhosted.org/packages/af/44/46407d7f7a56e9a85a4c207724c9f2c545c060380718eea9088f222ba697/yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5", size = 352614, upload-time = "2025-06-10T00:44:32.171Z" }, - { url = "https://files.pythonhosted.org/packages/b1/91/31163295e82b8d5485d31d9cf7754d973d41915cadce070491778d9c9825/yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02", size = 336766, upload-time = "2025-06-10T00:44:34.494Z" }, - { url = "https://files.pythonhosted.org/packages/b4/8e/c41a5bc482121f51c083c4c2bcd16b9e01e1cf8729e380273a952513a21f/yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53", size = 364615, upload-time = "2025-06-10T00:44:36.856Z" }, - { url = "https://files.pythonhosted.org/packages/e3/5b/61a3b054238d33d70ea06ebba7e58597891b71c699e247df35cc984ab393/yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc", size = 360982, upload-time = "2025-06-10T00:44:39.141Z" }, - { url = "https://files.pythonhosted.org/packages/df/a3/6a72fb83f8d478cb201d14927bc8040af901811a88e0ff2da7842dd0ed19/yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04", size = 369792, upload-time = "2025-06-10T00:44:40.934Z" }, - { url = "https://files.pythonhosted.org/packages/7c/af/4cc3c36dfc7c077f8dedb561eb21f69e1e9f2456b91b593882b0b18c19dc/yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4", size = 382049, upload-time = "2025-06-10T00:44:42.854Z" }, - { url = "https://files.pythonhosted.org/packages/19/3a/e54e2c4752160115183a66dc9ee75a153f81f3ab2ba4bf79c3c53b33de34/yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b", size = 384774, upload-time = "2025-06-10T00:44:45.275Z" }, - { url = "https://files.pythonhosted.org/packages/9c/20/200ae86dabfca89060ec6447649f219b4cbd94531e425e50d57e5f5ac330/yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1", size = 374252, upload-time = "2025-06-10T00:44:47.31Z" }, - { url = "https://files.pythonhosted.org/packages/83/75/11ee332f2f516b3d094e89448da73d557687f7d137d5a0f48c40ff211487/yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7", size = 81198, upload-time = "2025-06-10T00:44:49.164Z" }, - { url = "https://files.pythonhosted.org/packages/ba/ba/39b1ecbf51620b40ab402b0fc817f0ff750f6d92712b44689c2c215be89d/yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c", size = 86346, upload-time = "2025-06-10T00:44:51.182Z" }, - { url = "https://files.pythonhosted.org/packages/43/c7/669c52519dca4c95153c8ad96dd123c79f354a376346b198f438e56ffeb4/yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d", size = 138826, upload-time = "2025-06-10T00:44:52.883Z" }, - { url = "https://files.pythonhosted.org/packages/6a/42/fc0053719b44f6ad04a75d7f05e0e9674d45ef62f2d9ad2c1163e5c05827/yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf", size = 93217, upload-time = "2025-06-10T00:44:54.658Z" }, - { url = "https://files.pythonhosted.org/packages/4f/7f/fa59c4c27e2a076bba0d959386e26eba77eb52ea4a0aac48e3515c186b4c/yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3", size = 92700, upload-time = "2025-06-10T00:44:56.784Z" }, - { url = "https://files.pythonhosted.org/packages/2f/d4/062b2f48e7c93481e88eff97a6312dca15ea200e959f23e96d8ab898c5b8/yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d", size = 347644, upload-time = "2025-06-10T00:44:59.071Z" }, - { url = "https://files.pythonhosted.org/packages/89/47/78b7f40d13c8f62b499cc702fdf69e090455518ae544c00a3bf4afc9fc77/yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c", size = 323452, upload-time = "2025-06-10T00:45:01.605Z" }, - { url = "https://files.pythonhosted.org/packages/eb/2b/490d3b2dc66f52987d4ee0d3090a147ea67732ce6b4d61e362c1846d0d32/yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1", size = 346378, upload-time = "2025-06-10T00:45:03.946Z" }, - { url = "https://files.pythonhosted.org/packages/66/ad/775da9c8a94ce925d1537f939a4f17d782efef1f973039d821cbe4bcc211/yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce", size = 353261, upload-time = "2025-06-10T00:45:05.992Z" }, - { url = "https://files.pythonhosted.org/packages/4b/23/0ed0922b47a4f5c6eb9065d5ff1e459747226ddce5c6a4c111e728c9f701/yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3", size = 335987, upload-time = "2025-06-10T00:45:08.227Z" }, - { url = "https://files.pythonhosted.org/packages/3e/49/bc728a7fe7d0e9336e2b78f0958a2d6b288ba89f25a1762407a222bf53c3/yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be", size = 329361, upload-time = "2025-06-10T00:45:10.11Z" }, - { url = "https://files.pythonhosted.org/packages/93/8f/b811b9d1f617c83c907e7082a76e2b92b655400e61730cd61a1f67178393/yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16", size = 346460, upload-time = "2025-06-10T00:45:12.055Z" }, - { url = "https://files.pythonhosted.org/packages/70/fd/af94f04f275f95da2c3b8b5e1d49e3e79f1ed8b6ceb0f1664cbd902773ff/yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513", size = 334486, upload-time = "2025-06-10T00:45:13.995Z" }, - { url = "https://files.pythonhosted.org/packages/84/65/04c62e82704e7dd0a9b3f61dbaa8447f8507655fd16c51da0637b39b2910/yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f", size = 342219, upload-time = "2025-06-10T00:45:16.479Z" }, - { url = "https://files.pythonhosted.org/packages/91/95/459ca62eb958381b342d94ab9a4b6aec1ddec1f7057c487e926f03c06d30/yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390", size = 350693, upload-time = "2025-06-10T00:45:18.399Z" }, - { url = "https://files.pythonhosted.org/packages/a6/00/d393e82dd955ad20617abc546a8f1aee40534d599ff555ea053d0ec9bf03/yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458", size = 355803, upload-time = "2025-06-10T00:45:20.677Z" }, - { url = "https://files.pythonhosted.org/packages/9e/ed/c5fb04869b99b717985e244fd93029c7a8e8febdfcffa06093e32d7d44e7/yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e", size = 341709, upload-time = "2025-06-10T00:45:23.221Z" }, - { url = "https://files.pythonhosted.org/packages/24/fd/725b8e73ac2a50e78a4534ac43c6addf5c1c2d65380dd48a9169cc6739a9/yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d", size = 86591, upload-time = "2025-06-10T00:45:25.793Z" }, - { url = "https://files.pythonhosted.org/packages/94/c3/b2e9f38bc3e11191981d57ea08cab2166e74ea770024a646617c9cddd9f6/yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f", size = 93003, upload-time = "2025-06-10T00:45:27.752Z" }, - { url = "https://files.pythonhosted.org/packages/b4/2d/2345fce04cfd4bee161bf1e7d9cdc702e3e16109021035dbb24db654a622/yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77", size = 46542, upload-time = "2025-06-10T00:46:07.521Z" }, + { url = "https://files.pythonhosted.org/packages/cb/65/7fed0d774abf47487c64be14e9223749468922817b5e8792b8a64792a1bb/yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4", size = 132910 }, + { url = "https://files.pythonhosted.org/packages/8a/7b/988f55a52da99df9e56dc733b8e4e5a6ae2090081dc2754fc8fd34e60aa0/yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a", size = 90644 }, + { url = "https://files.pythonhosted.org/packages/f7/de/30d98f03e95d30c7e3cc093759982d038c8833ec2451001d45ef4854edc1/yarl-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c869f2651cc77465f6cd01d938d91a11d9ea5d798738c1dc077f3de0b5e5fed", size = 89322 }, + { url = "https://files.pythonhosted.org/packages/e0/7a/f2f314f5ebfe9200724b0b748de2186b927acb334cf964fd312eb86fc286/yarl-1.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62915e6688eb4d180d93840cda4110995ad50c459bf931b8b3775b37c264af1e", size = 323786 }, + { url = "https://files.pythonhosted.org/packages/15/3f/718d26f189db96d993d14b984ce91de52e76309d0fd1d4296f34039856aa/yarl-1.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:41ebd28167bc6af8abb97fec1a399f412eec5fd61a3ccbe2305a18b84fb4ca73", size = 319627 }, + { url = "https://files.pythonhosted.org/packages/a5/76/8fcfbf5fa2369157b9898962a4a7d96764b287b085b5b3d9ffae69cdefd1/yarl-1.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21242b4288a6d56f04ea193adde174b7e347ac46ce6bc84989ff7c1b1ecea84e", size = 339149 }, + { url = "https://files.pythonhosted.org/packages/3c/95/d7fc301cc4661785967acc04f54a4a42d5124905e27db27bb578aac49b5c/yarl-1.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea21cdae6c7eb02ba02a475f37463abfe0a01f5d7200121b03e605d6a0439f8", size = 333327 }, + { url = "https://files.pythonhosted.org/packages/65/94/e21269718349582eee81efc5c1c08ee71c816bfc1585b77d0ec3f58089eb/yarl-1.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f8a891e4a22a89f5dde7862994485e19db246b70bb288d3ce73a34422e55b23", size = 326054 }, + { url = "https://files.pythonhosted.org/packages/32/ae/8616d1f07853704523519f6131d21f092e567c5af93de7e3e94b38d7f065/yarl-1.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd803820d44c8853a109a34e3660e5a61beae12970da479cf44aa2954019bf70", size = 315035 }, + { url = "https://files.pythonhosted.org/packages/48/aa/0ace06280861ef055855333707db5e49c6e3a08840a7ce62682259d0a6c0/yarl-1.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b982fa7f74c80d5c0c7b5b38f908971e513380a10fecea528091405f519b9ebb", size = 338962 }, + { url = "https://files.pythonhosted.org/packages/20/52/1e9d0e6916f45a8fb50e6844f01cb34692455f1acd548606cbda8134cd1e/yarl-1.20.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:33f29ecfe0330c570d997bcf1afd304377f2e48f61447f37e846a6058a4d33b2", size = 335399 }, + { url = "https://files.pythonhosted.org/packages/f2/65/60452df742952c630e82f394cd409de10610481d9043aa14c61bf846b7b1/yarl-1.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:835ab2cfc74d5eb4a6a528c57f05688099da41cf4957cf08cad38647e4a83b30", size = 338649 }, + { url = "https://files.pythonhosted.org/packages/7b/f5/6cd4ff38dcde57a70f23719a838665ee17079640c77087404c3d34da6727/yarl-1.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46b5e0ccf1943a9a6e766b2c2b8c732c55b34e28be57d8daa2b3c1d1d4009309", size = 358563 }, + { url = "https://files.pythonhosted.org/packages/d1/90/c42eefd79d0d8222cb3227bdd51b640c0c1d0aa33fe4cc86c36eccba77d3/yarl-1.20.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:df47c55f7d74127d1b11251fe6397d84afdde0d53b90bedb46a23c0e534f9d24", size = 357609 }, + { url = "https://files.pythonhosted.org/packages/03/c8/cea6b232cb4617514232e0f8a718153a95b5d82b5290711b201545825532/yarl-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76d12524d05841276b0e22573f28d5fbcb67589836772ae9244d90dd7d66aa13", size = 350224 }, + { url = "https://files.pythonhosted.org/packages/ce/a3/eaa0ab9712f1f3d01faf43cf6f1f7210ce4ea4a7e9b28b489a2261ca8db9/yarl-1.20.1-cp310-cp310-win32.whl", hash = "sha256:6c4fbf6b02d70e512d7ade4b1f998f237137f1417ab07ec06358ea04f69134f8", size = 81753 }, + { url = "https://files.pythonhosted.org/packages/8f/34/e4abde70a9256465fe31c88ed02c3f8502b7b5dead693a4f350a06413f28/yarl-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef6c4d69554d44b7f9d923245f8ad9a707d971e6209d51279196d8e8fe1ae16", size = 86817 }, + { url = "https://files.pythonhosted.org/packages/b1/18/893b50efc2350e47a874c5c2d67e55a0ea5df91186b2a6f5ac52eff887cd/yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e", size = 133833 }, + { url = "https://files.pythonhosted.org/packages/89/ed/b8773448030e6fc47fa797f099ab9eab151a43a25717f9ac043844ad5ea3/yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b", size = 91070 }, + { url = "https://files.pythonhosted.org/packages/e3/e3/409bd17b1e42619bf69f60e4f031ce1ccb29bd7380117a55529e76933464/yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b", size = 89818 }, + { url = "https://files.pythonhosted.org/packages/f8/77/64d8431a4d77c856eb2d82aa3de2ad6741365245a29b3a9543cd598ed8c5/yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4", size = 347003 }, + { url = "https://files.pythonhosted.org/packages/8d/d2/0c7e4def093dcef0bd9fa22d4d24b023788b0a33b8d0088b51aa51e21e99/yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1", size = 336537 }, + { url = "https://files.pythonhosted.org/packages/f0/f3/fc514f4b2cf02cb59d10cbfe228691d25929ce8f72a38db07d3febc3f706/yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833", size = 362358 }, + { url = "https://files.pythonhosted.org/packages/ea/6d/a313ac8d8391381ff9006ac05f1d4331cee3b1efaa833a53d12253733255/yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d", size = 357362 }, + { url = "https://files.pythonhosted.org/packages/00/70/8f78a95d6935a70263d46caa3dd18e1f223cf2f2ff2037baa01a22bc5b22/yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8", size = 348979 }, + { url = "https://files.pythonhosted.org/packages/cb/05/42773027968968f4f15143553970ee36ead27038d627f457cc44bbbeecf3/yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf", size = 337274 }, + { url = "https://files.pythonhosted.org/packages/05/be/665634aa196954156741ea591d2f946f1b78ceee8bb8f28488bf28c0dd62/yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e", size = 363294 }, + { url = "https://files.pythonhosted.org/packages/eb/90/73448401d36fa4e210ece5579895731f190d5119c4b66b43b52182e88cd5/yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389", size = 358169 }, + { url = "https://files.pythonhosted.org/packages/c3/b0/fce922d46dc1eb43c811f1889f7daa6001b27a4005587e94878570300881/yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f", size = 362776 }, + { url = "https://files.pythonhosted.org/packages/f1/0d/b172628fce039dae8977fd22caeff3eeebffd52e86060413f5673767c427/yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845", size = 381341 }, + { url = "https://files.pythonhosted.org/packages/6b/9b/5b886d7671f4580209e855974fe1cecec409aa4a89ea58b8f0560dc529b1/yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1", size = 379988 }, + { url = "https://files.pythonhosted.org/packages/73/be/75ef5fd0fcd8f083a5d13f78fd3f009528132a1f2a1d7c925c39fa20aa79/yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e", size = 371113 }, + { url = "https://files.pythonhosted.org/packages/50/4f/62faab3b479dfdcb741fe9e3f0323e2a7d5cd1ab2edc73221d57ad4834b2/yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773", size = 81485 }, + { url = "https://files.pythonhosted.org/packages/f0/09/d9c7942f8f05c32ec72cd5c8e041c8b29b5807328b68b4801ff2511d4d5e/yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e", size = 86686 }, + { url = "https://files.pythonhosted.org/packages/5f/9a/cb7fad7d73c69f296eda6815e4a2c7ed53fc70c2f136479a91c8e5fbdb6d/yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9", size = 133667 }, + { url = "https://files.pythonhosted.org/packages/67/38/688577a1cb1e656e3971fb66a3492501c5a5df56d99722e57c98249e5b8a/yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a", size = 91025 }, + { url = "https://files.pythonhosted.org/packages/50/ec/72991ae51febeb11a42813fc259f0d4c8e0507f2b74b5514618d8b640365/yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2", size = 89709 }, + { url = "https://files.pythonhosted.org/packages/99/da/4d798025490e89426e9f976702e5f9482005c548c579bdae792a4c37769e/yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee", size = 352287 }, + { url = "https://files.pythonhosted.org/packages/1a/26/54a15c6a567aac1c61b18aa0f4b8aa2e285a52d547d1be8bf48abe2b3991/yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819", size = 345429 }, + { url = "https://files.pythonhosted.org/packages/d6/95/9dcf2386cb875b234353b93ec43e40219e14900e046bf6ac118f94b1e353/yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16", size = 365429 }, + { url = "https://files.pythonhosted.org/packages/91/b2/33a8750f6a4bc224242a635f5f2cff6d6ad5ba651f6edcccf721992c21a0/yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6", size = 363862 }, + { url = "https://files.pythonhosted.org/packages/98/28/3ab7acc5b51f4434b181b0cee8f1f4b77a65919700a355fb3617f9488874/yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd", size = 355616 }, + { url = "https://files.pythonhosted.org/packages/36/a3/f666894aa947a371724ec7cd2e5daa78ee8a777b21509b4252dd7bd15e29/yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a", size = 339954 }, + { url = "https://files.pythonhosted.org/packages/f1/81/5f466427e09773c04219d3450d7a1256138a010b6c9f0af2d48565e9ad13/yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38", size = 365575 }, + { url = "https://files.pythonhosted.org/packages/2e/e3/e4b0ad8403e97e6c9972dd587388940a032f030ebec196ab81a3b8e94d31/yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef", size = 365061 }, + { url = "https://files.pythonhosted.org/packages/ac/99/b8a142e79eb86c926f9f06452eb13ecb1bb5713bd01dc0038faf5452e544/yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f", size = 364142 }, + { url = "https://files.pythonhosted.org/packages/34/f2/08ed34a4a506d82a1a3e5bab99ccd930a040f9b6449e9fd050320e45845c/yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8", size = 381894 }, + { url = "https://files.pythonhosted.org/packages/92/f8/9a3fbf0968eac704f681726eff595dce9b49c8a25cd92bf83df209668285/yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a", size = 383378 }, + { url = "https://files.pythonhosted.org/packages/af/85/9363f77bdfa1e4d690957cd39d192c4cacd1c58965df0470a4905253b54f/yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004", size = 374069 }, + { url = "https://files.pythonhosted.org/packages/35/99/9918c8739ba271dcd935400cff8b32e3cd319eaf02fcd023d5dcd487a7c8/yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5", size = 81249 }, + { url = "https://files.pythonhosted.org/packages/eb/83/5d9092950565481b413b31a23e75dd3418ff0a277d6e0abf3729d4d1ce25/yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698", size = 86710 }, + { url = "https://files.pythonhosted.org/packages/8a/e1/2411b6d7f769a07687acee88a062af5833cf1966b7266f3d8dfb3d3dc7d3/yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a", size = 131811 }, + { url = "https://files.pythonhosted.org/packages/b2/27/584394e1cb76fb771371770eccad35de400e7b434ce3142c2dd27392c968/yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3", size = 90078 }, + { url = "https://files.pythonhosted.org/packages/bf/9a/3246ae92d4049099f52d9b0fe3486e3b500e29b7ea872d0f152966fc209d/yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7", size = 88748 }, + { url = "https://files.pythonhosted.org/packages/a3/25/35afe384e31115a1a801fbcf84012d7a066d89035befae7c5d4284df1e03/yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691", size = 349595 }, + { url = "https://files.pythonhosted.org/packages/28/2d/8aca6cb2cabc8f12efcb82749b9cefecbccfc7b0384e56cd71058ccee433/yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31", size = 342616 }, + { url = "https://files.pythonhosted.org/packages/0b/e9/1312633d16b31acf0098d30440ca855e3492d66623dafb8e25b03d00c3da/yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28", size = 361324 }, + { url = "https://files.pythonhosted.org/packages/bc/a0/688cc99463f12f7669eec7c8acc71ef56a1521b99eab7cd3abb75af887b0/yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653", size = 359676 }, + { url = "https://files.pythonhosted.org/packages/af/44/46407d7f7a56e9a85a4c207724c9f2c545c060380718eea9088f222ba697/yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5", size = 352614 }, + { url = "https://files.pythonhosted.org/packages/b1/91/31163295e82b8d5485d31d9cf7754d973d41915cadce070491778d9c9825/yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02", size = 336766 }, + { url = "https://files.pythonhosted.org/packages/b4/8e/c41a5bc482121f51c083c4c2bcd16b9e01e1cf8729e380273a952513a21f/yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53", size = 364615 }, + { url = "https://files.pythonhosted.org/packages/e3/5b/61a3b054238d33d70ea06ebba7e58597891b71c699e247df35cc984ab393/yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc", size = 360982 }, + { url = "https://files.pythonhosted.org/packages/df/a3/6a72fb83f8d478cb201d14927bc8040af901811a88e0ff2da7842dd0ed19/yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04", size = 369792 }, + { url = "https://files.pythonhosted.org/packages/7c/af/4cc3c36dfc7c077f8dedb561eb21f69e1e9f2456b91b593882b0b18c19dc/yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4", size = 382049 }, + { url = "https://files.pythonhosted.org/packages/19/3a/e54e2c4752160115183a66dc9ee75a153f81f3ab2ba4bf79c3c53b33de34/yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b", size = 384774 }, + { url = "https://files.pythonhosted.org/packages/9c/20/200ae86dabfca89060ec6447649f219b4cbd94531e425e50d57e5f5ac330/yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1", size = 374252 }, + { url = "https://files.pythonhosted.org/packages/83/75/11ee332f2f516b3d094e89448da73d557687f7d137d5a0f48c40ff211487/yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7", size = 81198 }, + { url = "https://files.pythonhosted.org/packages/ba/ba/39b1ecbf51620b40ab402b0fc817f0ff750f6d92712b44689c2c215be89d/yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c", size = 86346 }, + { url = "https://files.pythonhosted.org/packages/43/c7/669c52519dca4c95153c8ad96dd123c79f354a376346b198f438e56ffeb4/yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d", size = 138826 }, + { url = "https://files.pythonhosted.org/packages/6a/42/fc0053719b44f6ad04a75d7f05e0e9674d45ef62f2d9ad2c1163e5c05827/yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf", size = 93217 }, + { url = "https://files.pythonhosted.org/packages/4f/7f/fa59c4c27e2a076bba0d959386e26eba77eb52ea4a0aac48e3515c186b4c/yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3", size = 92700 }, + { url = "https://files.pythonhosted.org/packages/2f/d4/062b2f48e7c93481e88eff97a6312dca15ea200e959f23e96d8ab898c5b8/yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d", size = 347644 }, + { url = "https://files.pythonhosted.org/packages/89/47/78b7f40d13c8f62b499cc702fdf69e090455518ae544c00a3bf4afc9fc77/yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c", size = 323452 }, + { url = "https://files.pythonhosted.org/packages/eb/2b/490d3b2dc66f52987d4ee0d3090a147ea67732ce6b4d61e362c1846d0d32/yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1", size = 346378 }, + { url = "https://files.pythonhosted.org/packages/66/ad/775da9c8a94ce925d1537f939a4f17d782efef1f973039d821cbe4bcc211/yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce", size = 353261 }, + { url = "https://files.pythonhosted.org/packages/4b/23/0ed0922b47a4f5c6eb9065d5ff1e459747226ddce5c6a4c111e728c9f701/yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3", size = 335987 }, + { url = "https://files.pythonhosted.org/packages/3e/49/bc728a7fe7d0e9336e2b78f0958a2d6b288ba89f25a1762407a222bf53c3/yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be", size = 329361 }, + { url = "https://files.pythonhosted.org/packages/93/8f/b811b9d1f617c83c907e7082a76e2b92b655400e61730cd61a1f67178393/yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16", size = 346460 }, + { url = "https://files.pythonhosted.org/packages/70/fd/af94f04f275f95da2c3b8b5e1d49e3e79f1ed8b6ceb0f1664cbd902773ff/yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513", size = 334486 }, + { url = "https://files.pythonhosted.org/packages/84/65/04c62e82704e7dd0a9b3f61dbaa8447f8507655fd16c51da0637b39b2910/yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f", size = 342219 }, + { url = "https://files.pythonhosted.org/packages/91/95/459ca62eb958381b342d94ab9a4b6aec1ddec1f7057c487e926f03c06d30/yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390", size = 350693 }, + { url = "https://files.pythonhosted.org/packages/a6/00/d393e82dd955ad20617abc546a8f1aee40534d599ff555ea053d0ec9bf03/yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458", size = 355803 }, + { url = "https://files.pythonhosted.org/packages/9e/ed/c5fb04869b99b717985e244fd93029c7a8e8febdfcffa06093e32d7d44e7/yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e", size = 341709 }, + { url = "https://files.pythonhosted.org/packages/24/fd/725b8e73ac2a50e78a4534ac43c6addf5c1c2d65380dd48a9169cc6739a9/yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d", size = 86591 }, + { url = "https://files.pythonhosted.org/packages/94/c3/b2e9f38bc3e11191981d57ea08cab2166e74ea770024a646617c9cddd9f6/yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f", size = 93003 }, + { url = "https://files.pythonhosted.org/packages/b4/2d/2345fce04cfd4bee161bf1e7d9cdc702e3e16109021035dbb24db654a622/yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77", size = 46542 }, ] [[package]] name = "zipp" version = "3.23.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276 }, ] From f9e133237d806151cf3c23166d20a6849ee11eed Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 10 Sep 2025 20:40:58 -0400 Subject: [PATCH 29/32] Update M.E.AI and delete a bunch of dead code (#676) --- dotnet/Directory.Packages.props | 88 +- dotnet/samples/Directory.Build.props | 2 +- .../AgentOrchestration/AgentSample.cs | 5 +- .../Agent_OpenAI_Step01_Running/Program.cs | 4 +- .../Handoffs/HandoffOrchestration.cs | 2 +- .../Microsoft.Agents.Orchestration.csproj | 1 + .../MEAI.A/FunctionApprovalRequestContent.cs | 35 - .../MEAI.A/FunctionApprovalResponseContent.cs | 34 - .../MEAI.A/UserInputRequestContent.cs | 25 - .../MEAI.A/UserInputResponseContent.cs | 25 - ...t.Extensions.AI.Agents.Abstractions.csproj | 2 +- .../Extensions/AIAgentWithOpenAIExtensions.cs | 339 +--- .../Extensions/ChatOptionsExtensions.cs | 28 - .../OpenAIAssistantClientExtensions.cs | 143 +- .../OpenAIResponseClientExtensions.cs | 15 +- .../NewOpenAIAssistantChatClient.cs | 722 -------- .../NewOpenAIResponsesChatClient.cs | 918 ---------- .../ChatCompletion/ChatClientExtensions.cs | 19 +- .../MEAI/ApprovalRequiredAIFunction.cs | 40 - .../MEAI/LoggingHelpers.cs | 40 - .../MEAI/NewFunctionInvokingChatClient.cs | 1553 ----------------- .../MEAI/OpenTelemetryConsts.cs | 144 -- .../Microsoft.Extensions.AI.Agents.csproj | 1 + ...ns.AI.Agents.Abstractions.UnitTests.csproj | 1 + .../MEAI/AssertExtensions.cs | 86 - .../NewFunctionInvokingChatClientTests.cs | 931 ---------- .../MEAI/TestChatClient.cs | 51 - .../OpenAIResponseFixture.cs | 2 +- 28 files changed, 67 insertions(+), 5189 deletions(-) delete mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/FunctionApprovalRequestContent.cs delete mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/FunctionApprovalResponseContent.cs delete mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/UserInputRequestContent.cs delete mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/UserInputResponseContent.cs delete mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/ChatOptionsExtensions.cs delete mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIAssistantChatClient.cs delete mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIResponsesChatClient.cs delete mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/ApprovalRequiredAIFunction.cs delete mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/LoggingHelpers.cs delete mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/NewFunctionInvokingChatClient.cs delete mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/OpenTelemetryConsts.cs delete mode 100644 dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/MEAI/AssertExtensions.cs delete mode 100644 dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/MEAI/NewFunctionInvokingChatClientTests.cs delete mode 100644 dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/MEAI/TestChatClient.cs diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index 07fbcf93b4..f0e6d3fe0d 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -11,34 +11,34 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + - + - - - - + + + + @@ -48,31 +48,31 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - + + + + + diff --git a/dotnet/samples/Directory.Build.props b/dotnet/samples/Directory.Build.props index 564527ad9d..065ab34672 100644 --- a/dotnet/samples/Directory.Build.props +++ b/dotnet/samples/Directory.Build.props @@ -7,7 +7,7 @@ false net472;net9.0 5ee045b0-aea3-4f08-8d31-32d1a6f8fed0 - $(NoWarn);CA1707 + $(NoWarn);CA1707;MEAI001 diff --git a/dotnet/samples/GettingStarted/AgentOrchestration/AgentSample.cs b/dotnet/samples/GettingStarted/AgentOrchestration/AgentSample.cs index 004f1bf2e0..0f86869579 100644 --- a/dotnet/samples/GettingStarted/AgentOrchestration/AgentSample.cs +++ b/dotnet/samples/GettingStarted/AgentOrchestration/AgentSample.cs @@ -8,7 +8,6 @@ using Microsoft.Extensions.AI; using Microsoft.Extensions.AI.Agents; using Microsoft.Shared.Diagnostics; using Microsoft.Shared.Samples; -using OpenAI; using OpenAI.Assistants; using OpenAI.Chat; using OpenAI.Responses; @@ -111,13 +110,13 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output) private IChatClient GetOpenAIResponsesClient() => new OpenAIResponseClient(TestConfiguration.OpenAI.ChatModelId, TestConfiguration.OpenAI.ApiKey) - .AsNewIChatClient(); + .AsIChatClient(); private IChatClient GetAzureAIAgentPersistentClient(ChatClientAgentOptions options) => new PersistentAgentsClient(TestConfiguration.AzureAI.Endpoint, new AzureCliCredential()).AsNewIChatClient(options.Id!); private IChatClient GetOpenAIAssistantChatClient(ChatClientAgentOptions options) - => new AssistantClient(TestConfiguration.OpenAI.ApiKey).AsNewIChatClient(options.Id!); + => new AssistantClient(TestConfiguration.OpenAI.ApiKey).AsIChatClient(options.Id!); #endregion diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Program.cs b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Program.cs index 6625bdad2c..13760a2c89 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Program.cs @@ -22,11 +22,11 @@ AIAgent agent = new OpenAIClient(apiKey) UserChatMessage chatMessage = new("Tell me a joke about a pirate."); // Invoke the agent and output the text result. -ChatCompletion chatCompletion = await agent.RunAsync(chatMessage); +ChatCompletion chatCompletion = await agent.RunAsync([chatMessage]); Console.WriteLine(chatCompletion.Content.Last().Text); // Invoke the agent with streaming support. -AsyncCollectionResult completionUpdates = agent.RunStreamingAsync(chatMessage); +AsyncCollectionResult completionUpdates = agent.RunStreamingAsync([chatMessage]); await foreach (StreamingChatCompletionUpdate completionUpdate in completionUpdates) { if (completionUpdate.ContentUpdate.Count > 0) diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs index 321e71b4a2..8ef64910e7 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs @@ -216,7 +216,7 @@ public sealed partial class HandoffOrchestration : OrchestratingAgent static void Terminate() { - if (NewFunctionInvokingChatClient.CurrentContext is not { } ctx) + if (FunctionInvokingChatClient.CurrentContext is not { } ctx) { throw new NotSupportedException($"The agent is not configured with a {nameof(FunctionInvokingChatClient)}. Cease execution."); } diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Microsoft.Agents.Orchestration.csproj b/dotnet/src/Microsoft.Agents.Orchestration/Microsoft.Agents.Orchestration.csproj index de550fc682..9f92f7516a 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/Microsoft.Agents.Orchestration.csproj +++ b/dotnet/src/Microsoft.Agents.Orchestration/Microsoft.Agents.Orchestration.csproj @@ -5,6 +5,7 @@ $(ProjectsDebugTargetFrameworks) Microsoft.Agents.Orchestration alpha + $(NoWarn);MEAI001 diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/FunctionApprovalRequestContent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/FunctionApprovalRequestContent.cs deleted file mode 100644 index b6c561411f..0000000000 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/FunctionApprovalRequestContent.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using Microsoft.Shared.Diagnostics; - -namespace Microsoft.Extensions.AI; - -/// -/// Represents a request for user approval of a function call. -/// -public sealed class FunctionApprovalRequestContent : UserInputRequestContent -{ - /// - /// Initializes a new instance of the class. - /// - /// The ID to uniquely identify the function approval request/response pair. - /// The function call that requires user approval. - public FunctionApprovalRequestContent(string id, FunctionCallContent functionCall) - : base(id) - { - this.FunctionCall = Throw.IfNull(functionCall); - } - - /// - /// Gets the function call that pre-invoke approval is required for. - /// - public FunctionCallContent FunctionCall { get; } - - /// - /// Creates a to indicate whether the function call is approved or rejected based on the value of . - /// - /// if the function call is approved; otherwise, . - /// The representing the approval response. - public FunctionApprovalResponseContent CreateResponse(bool approved) - => new(this.Id, approved, this.FunctionCall); -} diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/FunctionApprovalResponseContent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/FunctionApprovalResponseContent.cs deleted file mode 100644 index 11bb01b5f6..0000000000 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/FunctionApprovalResponseContent.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using Microsoft.Shared.Diagnostics; - -namespace Microsoft.Extensions.AI; - -/// -/// Represents a response to a function approval request. -/// -public sealed class FunctionApprovalResponseContent : UserInputResponseContent -{ - /// - /// Initializes a new instance of the class. - /// - /// The ID to uniquely identify the function approval request/response pair. - /// Indicates whether the request was approved. - /// The function call that requires user approval. - public FunctionApprovalResponseContent(string id, bool approved, FunctionCallContent functionCall) - : base(id) - { - this.Approved = approved; - this.FunctionCall = Throw.IfNull(functionCall); - } - - /// - /// Gets or sets a value indicating whether the user approved the request. - /// - public bool Approved { get; } - - /// - /// Gets the function call that pre-invoke approval is required for. - /// - public FunctionCallContent FunctionCall { get; } -} diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/UserInputRequestContent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/UserInputRequestContent.cs deleted file mode 100644 index ba327b6717..0000000000 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/UserInputRequestContent.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using Microsoft.Shared.Diagnostics; - -namespace Microsoft.Extensions.AI; - -/// -/// Base class for user input request content. -/// -public abstract class UserInputRequestContent : AIContent -{ - /// - /// Initializes a new instance of the class. - /// - /// The ID to uniquely identify the user input request/response pair. - protected UserInputRequestContent(string id) - { - Id = Throw.IfNullOrWhitespace(id); - } - - /// - /// Gets the ID to uniquely identify the user input request/response pair. - /// - public string Id { get; } -} diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/UserInputResponseContent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/UserInputResponseContent.cs deleted file mode 100644 index 78b138779e..0000000000 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/MEAI.A/UserInputResponseContent.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using Microsoft.Shared.Diagnostics; - -namespace Microsoft.Extensions.AI; - -/// -/// Base class for user input response content. -/// -public abstract class UserInputResponseContent : AIContent -{ - /// - /// Initializes a new instance of the class. - /// - /// The ID to uniquely identify the user input request/response pair. - protected UserInputResponseContent(string id) - { - Id = Throw.IfNullOrWhitespace(id); - } - - /// - /// Gets the ID to uniquely identify the user input request/response pair. - /// - public string Id { get; } -} diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/Microsoft.Extensions.AI.Agents.Abstractions.csproj b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/Microsoft.Extensions.AI.Agents.Abstractions.csproj index 37c7bc871b..0bdc6a5b49 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/Microsoft.Extensions.AI.Agents.Abstractions.csproj +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/Microsoft.Extensions.AI.Agents.Abstractions.csproj @@ -4,7 +4,7 @@ $(ProjectsTargetFrameworks) $(ProjectsDebugTargetFrameworks) Microsoft.Extensions.AI.Agents - $(NoWarn);CA1716;IDE0009; + $(NoWarn);CA1716;IDE0009;MEAI001 alpha diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs index c1a8373cfd..37bee66e61 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/AIAgentWithOpenAIExtensions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. using System.ClientModel; -using System.Text; -using Microsoft.Extensions.AI; using Microsoft.Extensions.AI.Agents; using Microsoft.Extensions.AI.Agents.OpenAI.ChatCompletion; using Microsoft.Shared.Diagnostics; @@ -22,33 +20,6 @@ namespace OpenAI; /// public static class AIAgentWithOpenAIExtensions { - /// - /// Runs the AI agent with a single OpenAI chat message and returns the response as a native OpenAI . - /// - /// The AI agent to run. - /// The OpenAI chat message to send to the agent. - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided message and agent response. - /// Optional parameters for agent invocation. - /// The to monitor for cancellation requests. The default is . - /// A representing the asynchronous operation that returns a native OpenAI response. - /// Thrown when or is . - /// Thrown when the agent's response cannot be converted to a , typically when the underlying representation is not an OpenAI response. - /// Thrown when the type is not supported by the message conversion method. - /// - /// This method converts the OpenAI chat message to the Microsoft Extensions AI format using the appropriate conversion method, - /// runs the agent, and then extracts the native OpenAI from the response using . - /// - public static async Task RunAsync(this AIAgent agent, OpenAI.Chat.ChatMessage message, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) - { - Throw.IfNull(agent); - Throw.IfNull(message); - - var response = await agent.RunAsync(message.AsChatMessage(), thread, options, cancellationToken).ConfigureAwait(false); - - var chatCompletion = response.AsChatCompletion(); - return chatCompletion; - } - /// /// Runs the AI agent with a collection of OpenAI chat messages and returns the response as a native OpenAI . /// @@ -62,7 +33,7 @@ public static class AIAgentWithOpenAIExtensions /// Thrown when the agent's response cannot be converted to a , typically when the underlying representation is not an OpenAI response. /// Thrown when any message in has a type that is not supported by the message conversion method. /// - /// This method converts each OpenAI chat message to the Microsoft Extensions AI format using , + /// This method converts the OpenAI chat messages to the Microsoft Extensions AI format using the appropriate conversion method, /// runs the agent with the converted message collection, and then extracts the native OpenAI from the response using . /// public static async Task RunAsync(this AIAgent agent, IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) @@ -76,32 +47,6 @@ public static class AIAgentWithOpenAIExtensions return chatCompletion; } - /// - /// Runs the AI agent with a single OpenAI chat message and returns the response as collection of native OpenAI . - /// - /// The AI agent to run. - /// The OpenAI chat message to send to the agent. - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided message and agent response. - /// Optional parameters for agent invocation. - /// The to monitor for cancellation requests. The default is . - /// A representing the asynchronous operation that returns a native OpenAI response. - /// Thrown when or is . - /// Thrown when the agent's response cannot be converted to a , typically when the underlying representation is not an OpenAI response. - /// Thrown when the type is not supported by the message conversion method. - /// - /// This method converts the OpenAI chat message to the Microsoft Extensions AI format using the appropriate conversion method, - /// runs the agent, and then extracts the native OpenAI from the response using . - /// - public static AsyncCollectionResult RunStreamingAsync(this AIAgent agent, OpenAI.Chat.ChatMessage message, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) - { - Throw.IfNull(agent); - Throw.IfNull(message); - - IAsyncEnumerable response = agent.RunStreamingAsync(message.AsChatMessage(), thread, options, cancellationToken); - - return new AsyncStreamingUpdateCollectionResult(response); - } - /// /// Runs the AI agent with a single OpenAI chat message and returns the response as collection of native OpenAI . /// @@ -115,7 +60,7 @@ public static class AIAgentWithOpenAIExtensions /// Thrown when the agent's response cannot be converted to a , typically when the underlying representation is not an OpenAI response. /// Thrown when the type is not supported by the message conversion method. /// - /// This method converts the OpenAI chat message to the Microsoft Extensions AI format using the appropriate conversion method, + /// This method converts the OpenAI chat messages to the Microsoft Extensions AI format using the appropriate conversion method, /// runs the agent, and then extracts the native OpenAI from the response using . /// public static AsyncCollectionResult RunStreamingAsync(this AIAgent agent, IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) @@ -127,284 +72,4 @@ public static class AIAgentWithOpenAIExtensions return new AsyncStreamingUpdateCollectionResult(response); } - - /// - /// Creates a sequence of instances from the specified OpenAI input messages. - /// - /// The OpenAI input messages to convert. - /// A sequence of Microsoft Extensions AI chat messages converted from the OpenAI messages. - /// Thrown when is . - /// Thrown when a message type is encountered that cannot be converted. - /// - /// This method supports conversion of the following OpenAI message types: - /// - /// - /// - /// (obsolete) - /// - /// - /// - /// - /// - internal static IEnumerable AsChatMessages(this IEnumerable messages) - { - Throw.IfNull(messages); - - foreach (OpenAI.Chat.ChatMessage message in messages) - { - switch (message) - { - case OpenAI.Chat.AssistantChatMessage assistantMessage: - yield return assistantMessage.AsChatMessage(); - break; - case OpenAI.Chat.DeveloperChatMessage developerMessage: - yield return developerMessage.AsChatMessage(); - break; -#pragma warning disable CS0618 // Type or member is obsolete - case OpenAI.Chat.FunctionChatMessage functionMessage: - yield return functionMessage.AsChatMessage(); - break; -#pragma warning restore CS0618 // Type or member is obsolete - case OpenAI.Chat.SystemChatMessage systemMessage: - yield return systemMessage.AsChatMessage(); - break; - case OpenAI.Chat.ToolChatMessage toolMessage: - yield return toolMessage.AsChatMessage(); - break; - case OpenAI.Chat.UserChatMessage userMessage: - yield return userMessage.AsChatMessage(); - break; - } - } - } - - /// - /// Converts an OpenAI chat message to a Microsoft Extensions AI . - /// - /// The OpenAI chat message to convert. - /// A equivalent of the input OpenAI message. - /// Thrown when is . - /// Thrown when the type is not supported for conversion. - /// - /// This method provides a bridge between OpenAI SDK message types and Microsoft Extensions AI message types. - /// It handles the conversion by switching on the concrete type of the OpenAI message and calling the appropriate - /// specialized conversion method. - /// - internal static Microsoft.Extensions.AI.ChatMessage AsChatMessage(this OpenAI.Chat.ChatMessage chatMessage) - { - Throw.IfNull(chatMessage); - - return chatMessage switch - { - AssistantChatMessage assistantMessage => assistantMessage.AsChatMessage(), - DeveloperChatMessage developerMessage => developerMessage.AsChatMessage(), - SystemChatMessage systemMessage => systemMessage.AsChatMessage(), - ToolChatMessage toolMessage => toolMessage.AsChatMessage(), - UserChatMessage userMessage => userMessage.AsChatMessage(), - _ => throw new NotSupportedException($"Message type {chatMessage.GetType().Name} is not supported for conversion.") - }; - } - - /// - /// Converts OpenAI chat message content to Microsoft Extensions AI content items. - /// - /// The OpenAI chat message content to convert. - /// A sequence of items converted from the OpenAI content. - /// - /// This method supports conversion of the following OpenAI content part types: - /// - /// Text content (converted to ) - /// Refusal content (converted to ) - /// Image content (converted to or ) - /// Input audio content (converted to ) - /// File content (converted to ) - /// - /// - private static IEnumerable AsAIContent(this OpenAI.Chat.ChatMessageContent content) - { - Throw.IfNull(content); - - foreach (OpenAI.Chat.ChatMessageContentPart part in content) - { - switch (part.Kind) - { - case OpenAI.Chat.ChatMessageContentPartKind.Text: - yield return new TextContent(part.Text) - { - RawRepresentation = content - }; - break; - case OpenAI.Chat.ChatMessageContentPartKind.Refusal: - yield return new TextContent(part.Refusal) - { - RawRepresentation = content - }; - break; - case OpenAI.Chat.ChatMessageContentPartKind.Image: - if (part.ImageBytes is not null) - { - yield return new DataContent(part.ImageBytes, part.ImageBytesMediaType) - { - RawRepresentation = content - }; - } - else - { - yield return new UriContent(part.ImageUri, "image/*") - { - RawRepresentation = content - }; - } - break; - case OpenAI.Chat.ChatMessageContentPartKind.InputAudio: - yield return new DataContent(part.InputAudioBytes, "audio/*") - { - RawRepresentation = content - }; - break; - case OpenAI.Chat.ChatMessageContentPartKind.File: - yield return new DataContent(part.FileBytes, part.FileBytesMediaType) - { - RawRepresentation = content - }; - break; - default: - throw new NotSupportedException($"Content part kind '{part.Kind}' is not supported for conversion to AIContent."); - } - } - } - - /// - /// Converts OpenAI chat message content to text. - /// - /// The OpenAI chat message content to convert. - /// A string created from the text and refusal parts of the OpenAI content. - /// - /// Using when converting OpenAI For tool messages, the contents can only be of type text. - /// - private static string AsText(this OpenAI.Chat.ChatMessageContent content) - { - Throw.IfNull(content); - - StringBuilder text = new(); - foreach (OpenAI.Chat.ChatMessageContentPart part in content) - { - switch (part.Kind) - { - case OpenAI.Chat.ChatMessageContentPartKind.Text: - text.Append(part.Text); - break; - case OpenAI.Chat.ChatMessageContentPartKind.Refusal: - text.Append(part.Refusal); - break; - default: - throw new NotSupportedException($"Content part kind '{part.Kind}' is not supported for conversion to text."); - } - } - return text.ToString(); - } - - /// - /// Converts an OpenAI to a Microsoft Extensions AI . - /// - /// The OpenAI assistant message to convert. - /// A Microsoft Extensions AI chat message with assistant role. - /// - /// This method converts the assistant message content using and preserves - /// the participant name as the author name in the resulting message. - /// - private static Microsoft.Extensions.AI.ChatMessage AsChatMessage(this AssistantChatMessage assistantMessage) - { - Throw.IfNull(assistantMessage); - - return new Microsoft.Extensions.AI.ChatMessage(Microsoft.Extensions.AI.ChatRole.Assistant, [.. assistantMessage.Content.AsAIContent()]) - { - AuthorName = assistantMessage.ParticipantName, - RawRepresentation = assistantMessage - }; - } - - /// - /// Converts an OpenAI to a Microsoft Extensions AI . - /// - /// The OpenAI developer message to convert. - /// A Microsoft Extensions AI chat message with system role. - /// - /// Developer messages are treated as system messages in the Microsoft Extensions AI framework. - /// The participant name is preserved as the author name. - /// - private static Microsoft.Extensions.AI.ChatMessage AsChatMessage(this DeveloperChatMessage developerMessage) - { - Throw.IfNull(developerMessage); - - return new Microsoft.Extensions.AI.ChatMessage(Microsoft.Extensions.AI.ChatRole.System, [.. developerMessage.Content.AsAIContent()]) - { - AuthorName = developerMessage.ParticipantName, - RawRepresentation = developerMessage - }; - } - - /// - /// Converts an OpenAI to a Microsoft Extensions AI . - /// - /// The OpenAI system message to convert. - /// A Microsoft Extensions AI chat message with system role. - /// - /// This method converts the system message content using and preserves - /// the participant name as the author name in the resulting message. - /// - private static Microsoft.Extensions.AI.ChatMessage AsChatMessage(this SystemChatMessage systemMessage) - { - Throw.IfNull(systemMessage); - - return new Microsoft.Extensions.AI.ChatMessage(Microsoft.Extensions.AI.ChatRole.System, [.. systemMessage.Content.AsAIContent()]) - { - AuthorName = systemMessage.ParticipantName, - RawRepresentation = systemMessage - }; - } - - /// - /// Converts an OpenAI to a Microsoft Extensions AI . - /// - /// The OpenAI tool message to convert. - /// A Microsoft Extensions AI chat message with tool role. - /// - /// This method converts tool message content using and includes the tool call ID - /// in the resulting message's additional properties for traceability. - /// - private static Microsoft.Extensions.AI.ChatMessage AsChatMessage(this ToolChatMessage toolMessage) - { - Throw.IfNull(toolMessage); - - var content = new FunctionResultContent(toolMessage.ToolCallId, toolMessage.Content.AsText()) - { - RawRepresentation = toolMessage - }; - return new Microsoft.Extensions.AI.ChatMessage(Microsoft.Extensions.AI.ChatRole.Tool, [content]) - { - RawRepresentation = toolMessage, - AdditionalProperties = new() { ["tool_call_id"] = toolMessage.ToolCallId } - }; - } - - /// - /// Converts an OpenAI to a Microsoft Extensions AI . - /// - /// The OpenAI user message to convert. - /// A Microsoft Extensions AI chat message with user role. - /// - /// This method converts the user message content using and preserves - /// the participant name as the author name in the resulting message. - /// - private static Microsoft.Extensions.AI.ChatMessage AsChatMessage(this UserChatMessage userMessage) - { - Throw.IfNull(userMessage); - - return new Microsoft.Extensions.AI.ChatMessage(Microsoft.Extensions.AI.ChatRole.User, [.. userMessage.Content.AsAIContent()]) - { - AuthorName = userMessage.ParticipantName, - RawRepresentation = userMessage - }; - } } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/ChatOptionsExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/ChatOptionsExtensions.cs deleted file mode 100644 index f8052f9498..0000000000 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/ChatOptionsExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -using Microsoft.Shared.Diagnostics; -using OpenAI.Responses; - -namespace Microsoft.Extensions.AI.Agents; - -/// -/// Extension methods for -/// -public static class ChatOptionsExtensions -{ - /// - /// Disables the storage of response output in the chat options. - /// - /// Instance of - /// - public static ChatOptions WithResponseStoredOutputDisabled(this ChatOptions options) - { - Throw.IfNull(options); - - // We can use the RawRepresentationFactory to provide Response service specific - // options. Here we can indicate that we do not want the service to store the - // conversation in a service managed thread. - options.RawRepresentationFactory = (_) => new ResponseCreationOptions() { StoredOutputEnabled = false }; - - return options; - } -} diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIAssistantClientExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIAssistantClientExtensions.cs index e048c78fae..cfba779c1e 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIAssistantClientExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIAssistantClientExtensions.cs @@ -1,9 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. -using System.Text; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; using Microsoft.Extensions.AI; using Microsoft.Extensions.AI.Agents; using Microsoft.Extensions.Logging; @@ -24,9 +20,6 @@ namespace OpenAI; /// public static class OpenAIAssistantClientExtensions { - /// Key into AdditionalProperties used to store a strict option. - private const string StrictKey = "strictJsonSchema"; - /// /// Creates an AI agent from an using the OpenAI Assistant API. /// @@ -209,140 +202,6 @@ public static class OpenAIAssistantClientExtensions } }; -#pragma warning disable CA2000 // Dispose objects before losing scope - var chatClient = client.AsNewIChatClient(assistantId); -#pragma warning restore CA2000 // Dispose objects before losing scope - return new ChatClientAgent(chatClient, agentOptions, loggerFactory); - } - - /// Converts an Extensions function to an OpenAI assistants function tool. - private static FunctionToolDefinition ToOpenAIAssistantsFunctionToolDefinition(AIFunction aiFunction, ChatOptions? options = null) - { - bool? strict = - HasStrict(aiFunction.AdditionalProperties) ?? - HasStrict(options?.AdditionalProperties); - - return new FunctionToolDefinition(aiFunction.Name) - { - Description = aiFunction.Description, - Parameters = ToOpenAIFunctionParameters(aiFunction, strict), - StrictParameterSchemaEnabled = strict, - }; - } - - /// Extracts from an the parameters and strictness setting for use with OpenAI's APIs. - private static BinaryData ToOpenAIFunctionParameters(AIFunction aiFunction, bool? strict) - { - // Perform any desirable transformations on the function's JSON schema, if it'll be used in a strict setting. - JsonElement jsonSchema = strict is true ? - StrictSchemaTransformCache.GetOrCreateTransformedSchema(aiFunction) : - aiFunction.JsonSchema; - - // Roundtrip the schema through the ToolJson model type to remove extra properties - // and force missing ones into existence, then return the serialized UTF8 bytes as BinaryData. - var tool = jsonSchema.Deserialize(OpenAIJsonContext.Default.ToolJson)!; - var functionParameters = BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(tool, OpenAIJsonContext.Default.ToolJson)); - - return functionParameters; - } - - /// Gets whether the properties specify that strict schema handling is desired. - private static bool? HasStrict(IReadOnlyDictionary? additionalProperties) => - additionalProperties?.TryGetValue(StrictKey, out object? strictObj) is true && - strictObj is bool strictValue ? - strictValue : null; - - private static AIJsonSchemaTransformCache StrictSchemaTransformCache { get; } = new(new() - { - DisallowAdditionalProperties = true, - ConvertBooleanSchemas = true, - MoveDefaultKeywordToDescription = true, - RequireAllProperties = true, - TransformSchemaNode = (ctx, node) => - { - // Move content from common but unsupported properties to description. In particular, we focus on properties that - // the AIJsonUtilities schema generator might produce and/or that are explicitly mentioned in the OpenAI documentation. - - if (node is JsonObject schemaObj) - { - StringBuilder? additionalDescription = null; - - ReadOnlySpan unsupportedProperties = - [ - // Produced by AIJsonUtilities but not in allow list at https://platform.openai.com/docs/guides/structured-outputs#supported-properties: - "contentEncoding", "contentMediaType", "not", - - // Explicitly mentioned at https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#key-ordering as being unsupported with some models: - "minLength", "maxLength", "pattern", "format", - "minimum", "maximum", "multipleOf", - "patternProperties", - "minItems", "maxItems", - - // Explicitly mentioned at https://learn.microsoft.com/azure/ai-services/openai/how-to/structured-outputs?pivots=programming-language-csharp&tabs=python-secure%2Cdotnet-entra-id#unsupported-type-specific-keywords - // as being unsupported with Azure OpenAI: - "unevaluatedProperties", "propertyNames", "minProperties", "maxProperties", - "unevaluatedItems", "contains", "minContains", "maxContains", "uniqueItems", - ]; - - foreach (string propName in unsupportedProperties) - { - if (schemaObj[propName] is { } propNode) - { - _ = schemaObj.Remove(propName); - AppendLine(ref additionalDescription, propName, propNode); - } - } - - if (additionalDescription is not null) - { - schemaObj["description"] = schemaObj["description"] is { } descriptionNode && descriptionNode.GetValueKind() == JsonValueKind.String ? - $"{descriptionNode.GetValue()}{Environment.NewLine}{additionalDescription}" : - additionalDescription.ToString(); - } - - return node; - - static void AppendLine(ref StringBuilder? sb, string propName, JsonNode propNode) - { - sb ??= new(); - - if (sb.Length > 0) - { - _ = sb.AppendLine(); - } - - _ = sb.Append(propName).Append(": ").Append(propNode); - } - } - - return node; - }, - }); - - /// Used to create the JSON payload for an OpenAI tool description. - internal sealed class ToolJson - { - [JsonPropertyName("type")] - public string Type { get; set; } = "object"; - - [JsonPropertyName("required")] - public HashSet Required { get; set; } = []; - - [JsonPropertyName("properties")] - public Dictionary Properties { get; set; } = []; - - [JsonPropertyName("additionalProperties")] - public bool AdditionalProperties { get; set; } + return new ChatClientAgent(client.AsIChatClient(assistantId), agentOptions, loggerFactory); } } - -/// Source-generated JSON type information for use by all OpenAI implementations. -[JsonSourceGenerationOptions(JsonSerializerDefaults.Web, - UseStringEnumConverter = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - WriteIndented = true)] -[JsonSerializable(typeof(OpenAIAssistantClientExtensions.ToolJson))] -[JsonSerializable(typeof(IDictionary))] -[JsonSerializable(typeof(string[]))] -[JsonSerializable(typeof(JsonElement))] -internal sealed partial class OpenAIJsonContext : JsonSerializerContext; diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIResponseClientExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIResponseClientExtensions.cs index c387be1e5c..fc2a6522fd 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIResponseClientExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/Extensions/OpenAIResponseClientExtensions.cs @@ -33,6 +33,8 @@ public static class OpenAIResponseClientExtensions /// Thrown when is . public static AIAgent CreateAIAgent(this OpenAIResponseClient client, string? instructions = null, string? name = null, string? description = null, IList? tools = null, ILoggerFactory? loggerFactory = null) { + Throw.IfNull(client); + return client.CreateAIAgent( new ChatClientAgentOptions() { @@ -60,17 +62,6 @@ public static class OpenAIResponseClientExtensions Throw.IfNull(client); Throw.IfNull(options); -#pragma warning disable CA2000 // Dispose objects before losing scope - var chatClient = client.AsNewIChatClient(); -#pragma warning restore CA2000 // Dispose objects before losing scope - ChatClientAgent agent = new(chatClient, options, loggerFactory); - return agent; + return new ChatClientAgent(client.AsIChatClient(), options, loggerFactory); } - - /// Gets an for use with this . - /// The client. - /// An that can be used to converse via the . - /// is . - public static IChatClient AsNewIChatClient(this OpenAIResponseClient responseClient) => - new NewOpenAIResponsesChatClient(responseClient); } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIAssistantChatClient.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIAssistantChatClient.cs deleted file mode 100644 index d02646d4d0..0000000000 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIAssistantChatClient.cs +++ /dev/null @@ -1,722 +0,0 @@ -#pragma warning disable IDE0005 // Using directive is unnecessary. -#pragma warning disable IDE0073 // The file header does not match the required text -#pragma warning disable CS0436 // Type conflicts with imported type -#pragma warning disable CA1063 // Implement IDisposable Correctly - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Text; -using System.Text.Json; -using System.Text.Json.Nodes; -using Microsoft.Shared.Diagnostics; -using OpenAI; -using OpenAI.Assistants; - -#pragma warning disable CA1031 // Do not catch general exception types -#pragma warning disable SA1005 // Single line comments should begin with single space -#pragma warning disable SA1204 // Static elements should appear before instance elements -#pragma warning disable S125 // Sections of code should not be commented out -#pragma warning disable S907 // "goto" statement should not be used -#pragma warning disable S1067 // Expressions should not be too complex -#pragma warning disable S1751 // Loops with at most one iteration should be refactored -#pragma warning disable S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields -#pragma warning disable S4456 // Parameter validation in yielding methods should be wrapped -#pragma warning disable S4457 // Parameter validation in "async"/"await" methods should be wrapped - -namespace Microsoft.Extensions.AI; - -/// Represents an for an OpenAI . -internal sealed class NewOpenAIAssistantsChatClient : IChatClient -{ - /// The underlying . - private readonly AssistantClient _client; - - /// Metadata for the client. - private readonly ChatClientMetadata _metadata; - - /// The ID of the agent to use. - private readonly string _assistantId; - - /// The thread ID to use if none is supplied in . - private readonly string? _defaultThreadId; - - /// List of tools associated with the assistant. - private IReadOnlyList? _assistantTools; - - /// Initializes a new instance of the class for the specified . - public NewOpenAIAssistantsChatClient(AssistantClient assistantClient, string assistantId, string? defaultThreadId) - { - _client = Throw.IfNull(assistantClient); - _assistantId = Throw.IfNullOrWhitespace(assistantId); - - _defaultThreadId = defaultThreadId; - - // https://github.com/openai/openai-dotnet/issues/215 - // The endpoint isn't currently exposed, so use reflection to get at it, temporarily. Once packages - // implement the abstractions directly rather than providing adapters on top of the public APIs, - // the package can provide such implementations separate from what's exposed in the public API. - Uri providerUrl = typeof(AssistantClient).GetField("_endpoint", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - ?.GetValue(assistantClient) as Uri ?? OpenAIClientExtensions2.DefaultOpenAIEndpoint; - - _metadata = new("openai", providerUrl); - } - - /// Initializes a new instance of the class for the specified . - public NewOpenAIAssistantsChatClient(AssistantClient assistantClient, Assistant assistant, string? defaultThreadId) - : this(assistantClient, Throw.IfNull(assistant).Id, defaultThreadId) - { - _assistantTools = assistant.Tools; - } - - /// - public object? GetService(Type serviceType, object? serviceKey = null) => - serviceType is null ? throw new ArgumentNullException(nameof(serviceType)) : - serviceKey is not null ? null : - serviceType == typeof(ChatClientMetadata) ? _metadata : - serviceType == typeof(AssistantClient) ? _client : - serviceType.IsInstanceOfType(this) ? this : - null; - - /// - public async Task GetResponseAsync( - IEnumerable messages, ChatOptions? options = null, CancellationToken cancellationToken = default) - { - // Changing the original implementation to provide a RawRepresentation as a list of RawRepresentations of the updates. - // This wouldn't be needed if the API Change Proposal below is accepted: - // https://github.com/dotnet/extensions/issues/6746 - var updates = await GetStreamingResponseAsync(messages, options, cancellationToken).ToListAsync(cancellationToken).ConfigureAwait(false); - var response = updates.ToChatResponse(); - - // Expose all the raw representations of the updates. - response.RawRepresentation = updates.Select(u => u.RawRepresentation).ToArray(); - return response; - } - - /// - public async IAsyncEnumerable GetStreamingResponseAsync( - IEnumerable messages, ChatOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - _ = Throw.IfNull(messages); - - // Extract necessary state from messages and options. - (RunCreationOptions runOptions, ToolResources? toolResources, List? toolResults) = await CreateRunOptionsAsync(messages, options, cancellationToken).ConfigureAwait(false); - - // Get the thread ID. - string? threadId = options?.ConversationId ?? _defaultThreadId; - if (threadId is null && toolResults is not null) - { - Throw.ArgumentException(nameof(messages), "No thread ID was provided, but chat messages includes tool results."); - } - - // Get any active run ID for this thread. This is necessary in case a thread has been left with an - // active run, in which all attempts other than submitting tools will fail. We thus need to cancel - // any active run on the thread. - ThreadRun? threadRun = null; - if (threadId is not null) - { - await foreach (var run in _client.GetRunsAsync( - threadId, - new RunCollectionOptions { Order = RunCollectionOrder.Descending, PageSizeLimit = 1 }, - cancellationToken: cancellationToken).ConfigureAwait(false)) - { - if (run.Status != RunStatus.Completed && run.Status != RunStatus.Cancelled && run.Status != RunStatus.Failed && run.Status != RunStatus.Expired) - { - threadRun = run; - } - - break; - } - } - - // Submit the request. - IAsyncEnumerable updates; - if (threadRun is not null && - ConvertFunctionResultsToToolOutput(toolResults, out List? toolOutputs) is { } toolRunId && - toolRunId == threadRun.Id) - { - // There's an active run and we have tool results to submit, so submit the results and continue streaming. - // This is going to ignore any additional messages in the run options, as we are only submitting tool outputs, - // but there doesn't appear to be a way to submit additional messages, and having such additional messages is rare. - updates = _client.SubmitToolOutputsToRunStreamingAsync(threadRun.ThreadId, threadRun.Id, toolOutputs, cancellationToken); - } - else - { - if (threadId is null) - { - // No thread ID was provided, so create a new thread. - ThreadCreationOptions threadCreationOptions = new() - { - ToolResources = toolResources, - }; - - foreach (var message in runOptions.AdditionalMessages) - { - threadCreationOptions.InitialMessages.Add(message); - } - - runOptions.AdditionalMessages.Clear(); - - var thread = await _client.CreateThreadAsync(threadCreationOptions, cancellationToken).ConfigureAwait(false); - threadId = thread.Value.Id; - } - else if (threadRun is not null) - { - // There was an active run; we need to cancel it before starting a new run. - _ = await _client.CancelRunAsync(threadId, threadRun.Id, cancellationToken).ConfigureAwait(false); - threadRun = null; - } - - // Now create a new run and stream the results. - updates = _client.CreateRunStreamingAsync( - threadId: threadId, - _assistantId, - runOptions, - cancellationToken); - } - - // Process each update. - string? responseId = null; - await foreach (var update in updates.ConfigureAwait(false)) - { - switch (update) - { - case ThreadUpdate tu: - threadId ??= tu.Value.Id; - goto default; - - case RunUpdate ru: - threadId ??= ru.Value.ThreadId; - responseId ??= ru.Value.Id; - - ChatResponseUpdate ruUpdate = new() - { - AuthorName = _assistantId, - ConversationId = threadId, - CreatedAt = ru.Value.CreatedAt, - MessageId = responseId, - ModelId = ru.Value.Model, - RawRepresentation = ru, - ResponseId = responseId, - Role = ChatRole.Assistant, - }; - - if (ru.Value.Usage is { } usage) - { - ruUpdate.Contents.Add(new UsageContent(new() - { - InputTokenCount = usage.InputTokenCount, - OutputTokenCount = usage.OutputTokenCount, - TotalTokenCount = usage.TotalTokenCount, - })); - } - - if (ru is RequiredActionUpdate rau && rau.ToolCallId is string toolCallId && rau.FunctionName is string functionName) - { - var fcc = OpenAIClientExtensions2.ParseCallContent( - rau.FunctionArguments, - JsonSerializer.Serialize([ru.Value.Id, toolCallId], OpenAIJsonContext.Default.StringArray), - functionName); - fcc.RawRepresentation = ru; - ruUpdate.Contents.Add(fcc); - } - - yield return ruUpdate; - break; - - case MessageContentUpdate mcu: - ChatResponseUpdate textUpdate = new(mcu.Role == MessageRole.User ? ChatRole.User : ChatRole.Assistant, mcu.Text) - { - AuthorName = _assistantId, - ConversationId = threadId, - MessageId = responseId, - RawRepresentation = mcu, - ResponseId = responseId, - }; - - // Add any annotations from the text update. The OpenAI Assistants API does not support passing these back - // into the model (MessageContent.FromXx does not support providing annotations), so they end up being one way and are dropped - // on subsequent requests. - if (mcu.TextAnnotation is { } tau) - { - string? fileId = null; - string? toolName = null; - if (!string.IsNullOrWhiteSpace(tau.InputFileId)) - { - fileId = tau.InputFileId; - toolName = "file_search"; - } - else if (!string.IsNullOrWhiteSpace(tau.OutputFileId)) - { - fileId = tau.OutputFileId; - toolName = "code_interpreter"; - } - - if (fileId is not null) - { - if (textUpdate.Contents.Count == 0) - { - // In case a chunk doesn't have text content, create one with empty text to hold the annotation. - textUpdate.Contents.Add(new TextContent(string.Empty)); - } - - (((TextContent)textUpdate.Contents[0]).Annotations ??= []).Add(new CitationAnnotation - { - RawRepresentation = tau, - AnnotatedRegions = [new TextSpanAnnotatedRegion { StartIndex = tau.StartIndex, EndIndex = tau.EndIndex }], - FileId = fileId, - ToolName = toolName, - }); - } - } - - yield return textUpdate; - break; - - default: - yield return new() - { - AuthorName = _assistantId, - ConversationId = threadId, - MessageId = responseId, - RawRepresentation = update, - ResponseId = responseId, - Role = ChatRole.Assistant, - }; - break; - } - } - } - - /// - void IDisposable.Dispose() - { - // nop - } - - /// Converts an Extensions function to an OpenAI assistants function tool. - internal static FunctionToolDefinition ToOpenAIAssistantsFunctionToolDefinition(AIFunction aiFunction, ChatOptions? options = null) - { - bool? strict = - OpenAIClientExtensions2.HasStrict(aiFunction.AdditionalProperties) ?? - OpenAIClientExtensions2.HasStrict(options?.AdditionalProperties); - - return new FunctionToolDefinition(aiFunction.Name) - { - Description = aiFunction.Description, - Parameters = OpenAIClientExtensions2.ToOpenAIFunctionParameters(aiFunction, strict), - StrictParameterSchemaEnabled = strict, - }; - } - - /// - /// Creates the to use for the request and extracts any function result contents - /// that need to be submitted as tool results. - /// - private async ValueTask<(RunCreationOptions RunOptions, ToolResources? Resources, List? ToolResults)> CreateRunOptionsAsync( - IEnumerable messages, ChatOptions? options, CancellationToken cancellationToken) - { - // Create the options instance to populate, either a fresh or using one the caller provides. - RunCreationOptions runOptions = - options?.RawRepresentationFactory?.Invoke(this) as RunCreationOptions ?? - new(); - - ToolResources? resources = null; - - // Populate the run options from the ChatOptions, if provided. - if (options is not null) - { - runOptions.MaxOutputTokenCount ??= options.MaxOutputTokens; - runOptions.ModelOverride ??= options.ModelId; - runOptions.NucleusSamplingFactor ??= options.TopP; - runOptions.Temperature ??= options.Temperature; - runOptions.AllowParallelToolCalls ??= options.AllowMultipleToolCalls; - - if (options.Tools is { Count: > 0 } tools) - { - // If the caller has provided any tool overrides, we'll assume they don't want to use the assistant's tools. - // But if they haven't, the only way we can provide our tools is via an override, whereas we'd really like to - // just add them. To handle that, we'll get all of the assistant's tools and add them to the override list - // along with our tools. - if (runOptions.ToolsOverride.Count == 0) - { - if (_assistantTools is null) - { - var assistant = await _client.GetAssistantAsync(_assistantId, cancellationToken).ConfigureAwait(false); - _assistantTools = assistant.Value.Tools; - } - - foreach (var tool in _assistantTools) - { - runOptions.ToolsOverride.Add(tool); - } - } - - // The caller can provide tools in the supplied ThreadAndRunOptions. Augment it with any supplied via ChatOptions.Tools. - foreach (AITool tool in tools) - { - switch (tool) - { - case AIFunction aiFunction: - runOptions.ToolsOverride.Add(ToOpenAIAssistantsFunctionToolDefinition(aiFunction, options)); - break; - - case HostedCodeInterpreterTool codeInterpreterTool: - var interpreterToolDef = ToolDefinition.CreateCodeInterpreter(); - runOptions.ToolsOverride.Add(interpreterToolDef); - - if (codeInterpreterTool.Inputs?.Count is > 0) - { - ThreadInitializationMessage? threadInitializationMessage = null; - foreach (var input in codeInterpreterTool.Inputs) - { - if (input is HostedFileContent hostedFile) - { - threadInitializationMessage ??= new(MessageRole.User, [MessageContent.FromText("attachments")]); - threadInitializationMessage.Attachments.Add(new(hostedFile.FileId, [interpreterToolDef])); - } - } - - if (threadInitializationMessage is not null) - { - runOptions.AdditionalMessages.Add(threadInitializationMessage); - } - } - - break; - - case HostedFileSearchTool fileSearchTool: - runOptions.ToolsOverride.Add(ToolDefinition.CreateFileSearch(fileSearchTool.MaximumResultCount)); - if (fileSearchTool.Inputs is { Count: > 0 } fileSearchInputs) - { - foreach (var input in fileSearchInputs) - { - if (input is HostedVectorStoreContent file) - { - (resources ??= new()).FileSearch ??= new(); - resources.FileSearch.VectorStoreIds.Add(file.VectorStoreId); - } - } - } - - break; - } - } - } - - // Store the tool mode, if relevant. - if (runOptions.ToolConstraint is null) - { - switch (options.ToolMode) - { - case NoneChatToolMode: - runOptions.ToolConstraint = ToolConstraint.None; - break; - - case AutoChatToolMode: - runOptions.ToolConstraint = ToolConstraint.Auto; - break; - - case RequiredChatToolMode required when required.RequiredFunctionName is { } functionName: - runOptions.ToolConstraint = new ToolConstraint(ToolDefinition.CreateFunction(functionName)); - break; - - case RequiredChatToolMode required: - runOptions.ToolConstraint = ToolConstraint.Required; - break; - } - } - - // Store the response format, if relevant. - if (runOptions.ResponseFormat is null) - { - switch (options.ResponseFormat) - { - case ChatResponseFormatText: - runOptions.ResponseFormat = AssistantResponseFormat.CreateTextFormat(); - break; - - case ChatResponseFormatJson jsonFormat when OpenAIClientExtensions2.StrictSchemaTransformCache.GetOrCreateTransformedSchema(jsonFormat) is { } jsonSchema: - runOptions.ResponseFormat = AssistantResponseFormat.CreateJsonSchemaFormat( - jsonFormat.SchemaName, - BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(jsonSchema, OpenAIJsonContext.Default.JsonElement)), - jsonFormat.SchemaDescription, - OpenAIClientExtensions2.HasStrict(options.AdditionalProperties)); - break; - - case ChatResponseFormatJson jsonFormat: - runOptions.ResponseFormat = AssistantResponseFormat.CreateJsonObjectFormat(); - break; - } - } - } - - // Configure system instructions. - StringBuilder? instructions = null; - void AppendSystemInstructions(string? toAppend) - { - if (!string.IsNullOrEmpty(toAppend)) - { - if (instructions is null) - { - instructions = new(toAppend); - } - else - { - _ = instructions.AppendLine().AppendLine(toAppend); - } - } - } - - AppendSystemInstructions(runOptions.AdditionalInstructions); - AppendSystemInstructions(options?.Instructions); - - // Process ChatMessages. - List? functionResults = null; - foreach (var chatMessage in messages) - { - List messageContents = []; - - // Assistants doesn't support system/developer messages directly. It does support transient per-request instructions, - // so we can use the system/developer messages to build up a set of instructions that will be passed to the assistant - // as part of this request. However, in doing so, on a subsequent request that information will be lost, as there's no - // way to store per-thread instructions in the OpenAI Assistants API. We don't want to convert these to user messages, - // however, as that would then expose the system/developer messages in a way that might make the model more likely - // to include that information in its responses. System messages should ideally be instead done as instructions to - // the assistant when the assistant is created. - if (chatMessage.Role == ChatRole.System || - chatMessage.Role == OpenAIClientExtensions2.ChatRoleDeveloper) - { - foreach (var textContent in chatMessage.Contents.OfType()) - { - AppendSystemInstructions(textContent.Text); - } - - continue; - } - - foreach (AIContent content in chatMessage.Contents) - { - switch (content) - { - case AIContent when content.RawRepresentation is MessageContent rawRep: - messageContents.Add(rawRep); - break; - - case TextContent text: - messageContents.Add(MessageContent.FromText(text.Text)); - break; - - case UriContent image when image.HasTopLevelMediaType("image"): - messageContents.Add(MessageContent.FromImageUri(image.Uri)); - break; - - case FunctionResultContent result: - (functionResults ??= []).Add(result); - break; - } - } - - if (messageContents.Count > 0) - { - runOptions.AdditionalMessages.Add(new ThreadInitializationMessage( - chatMessage.Role == ChatRole.Assistant ? MessageRole.Assistant : MessageRole.User, - messageContents)); - } - } - - runOptions.AdditionalInstructions = instructions?.ToString(); - - return (runOptions, resources, functionResults); - } - - /// Convert instances to instances. - /// The tool results to process. - /// The generated list of tool outputs, if any could be created. - /// The run ID associated with the corresponding function call requests. - private static string? ConvertFunctionResultsToToolOutput(List? toolResults, out List? toolOutputs) - { - string? runId = null; - toolOutputs = null; - if (toolResults?.Count > 0) - { - foreach (var frc in toolResults) - { - // When creating the FunctionCallContext, we created it with a CallId == [runId, callId]. - // We need to extract the run ID and ensure that the ToolOutput we send back to Azure - // is only the call ID. - string[]? runAndCallIDs; - try - { - runAndCallIDs = JsonSerializer.Deserialize(frc.CallId, OpenAIJsonContext.Default.StringArray); - } - catch - { - continue; - } - - if (runAndCallIDs is null || - runAndCallIDs.Length != 2 || - string.IsNullOrWhiteSpace(runAndCallIDs[0]) || // run ID - string.IsNullOrWhiteSpace(runAndCallIDs[1]) || // call ID - (runId is not null && runId != runAndCallIDs[0])) - { - continue; - } - - runId = runAndCallIDs[0]; - (toolOutputs ??= []).Add(new(runAndCallIDs[1], frc.Result?.ToString() ?? string.Empty)); - } - } - - return runId; - } -} - -/// Provides extension methods for working with s. -internal static class OpenAIClientExtensions2 -{ - /// Key into AdditionalProperties used to store a strict option. - private const string StrictKey = "strictJsonSchema"; - - /// Gets the default OpenAI endpoint. - internal static Uri DefaultOpenAIEndpoint { get; } = new("https://api.openai.com/v1"); - - /// Gets a for "developer". - internal static ChatRole ChatRoleDeveloper { get; } = new ChatRole("developer"); - - /// Creates a new instance of parsing arguments using a specified encoding and parser. - /// The input arguments to be parsed. - /// The function call ID. - /// The function name. - /// A new instance of containing the parse result. - /// is . - /// is . - internal static FunctionCallContent ParseCallContent(string json, string callId, string name) => - FunctionCallContent.CreateFromParsedArguments(json, callId, name, - static json => JsonSerializer.Deserialize(json, OpenAIJsonContext.Default.IDictionaryStringObject)!); - - /// Creates a new instance of parsing arguments using a specified encoding and parser. - /// The input arguments to be parsed. - /// The function call ID. - /// The function name. - /// A new instance of containing the parse result. - /// is . - /// is . - internal static FunctionCallContent ParseCallContent(BinaryData utf8json, string callId, string name) => - FunctionCallContent.CreateFromParsedArguments(utf8json, callId, name, - static utf8json => JsonSerializer.Deserialize(utf8json, OpenAIJsonContext.Default.IDictionaryStringObject)!); - - /// - /// Gets the JSON schema transformer cache conforming to OpenAI strict / structured output restrictions per - /// https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#supported-schemas. - /// - internal static AIJsonSchemaTransformCache StrictSchemaTransformCache { get; } = new(new() - { - DisallowAdditionalProperties = true, - ConvertBooleanSchemas = true, - MoveDefaultKeywordToDescription = true, - RequireAllProperties = true, - TransformSchemaNode = (ctx, node) => - { - // Move content from common but unsupported properties to description. In particular, we focus on properties that - // the AIJsonUtilities schema generator might produce and/or that are explicitly mentioned in the OpenAI documentation. - - if (node is JsonObject schemaObj) - { - StringBuilder? additionalDescription = null; - - ReadOnlySpan unsupportedProperties = - [ - // Produced by AIJsonUtilities but not in allow list at https://platform.openai.com/docs/guides/structured-outputs#supported-properties: - "contentEncoding", "contentMediaType", "not", - - // Explicitly mentioned at https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#key-ordering as being unsupported with some models: - "minLength", "maxLength", "pattern", "format", - "minimum", "maximum", "multipleOf", - "patternProperties", - "minItems", "maxItems", - - // Explicitly mentioned at https://learn.microsoft.com/azure/ai-services/openai/how-to/structured-outputs?pivots=programming-language-csharp&tabs=python-secure%2Cdotnet-entra-id#unsupported-type-specific-keywords - // as being unsupported with Azure OpenAI: - "unevaluatedProperties", "propertyNames", "minProperties", "maxProperties", - "unevaluatedItems", "contains", "minContains", "maxContains", "uniqueItems", - ]; - - foreach (string propName in unsupportedProperties) - { - if (schemaObj[propName] is { } propNode) - { - _ = schemaObj.Remove(propName); - AppendLine(ref additionalDescription, propName, propNode); - } - } - - if (additionalDescription is not null) - { - schemaObj["description"] = schemaObj["description"] is { } descriptionNode && descriptionNode.GetValueKind() == JsonValueKind.String ? - $"{descriptionNode.GetValue()}{Environment.NewLine}{additionalDescription}" : - additionalDescription.ToString(); - } - - return node; - - static void AppendLine(ref StringBuilder? sb, string propName, JsonNode propNode) - { - sb ??= new(); - - if (sb.Length > 0) - { - _ = sb.AppendLine(); - } - - _ = sb.Append(propName).Append(": ").Append(propNode); - } - } - - return node; - }, - }); - - // TODO: Once we're ready to rely on C# 14 features, add an extension property ChatOptions.Strict. - - /// Gets whether the properties specify that strict schema handling is desired. - internal static bool? HasStrict(IReadOnlyDictionary? additionalProperties) => - additionalProperties?.TryGetValue(StrictKey, out object? strictObj) is true && - strictObj is bool strictValue ? - strictValue : null; - - /// Extracts from an the parameters and strictness setting for use with OpenAI's APIs. - internal static BinaryData ToOpenAIFunctionParameters(AIFunction aiFunction, bool? strict) - { - // Perform any desirable transformations on the function's JSON schema, if it'll be used in a strict setting. - JsonElement jsonSchema = strict is true ? - StrictSchemaTransformCache.GetOrCreateTransformedSchema(aiFunction) : - aiFunction.JsonSchema; - - // Roundtrip the schema through the ToolJson model type to remove extra properties - // and force missing ones into existence, then return the serialized UTF8 bytes as BinaryData. - var tool = jsonSchema.Deserialize(OpenAIJsonContext.Default.ToolJson)!; - var functionParameters = BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(tool, OpenAIJsonContext.Default.ToolJson)); - - return functionParameters; - } -} - -/// -/// Temporary extension methods to assist with creating proposed changed instances -/// -public static class OpenAIAssistantsExtensions -{ - /// - /// Creates a new instance of an configured for the specified assistant. - /// - /// The instance used to initialize the chat client. Cannot be . - /// The unique identifier of the assistant. Cannot be or whitespace. - /// The optional default thread identifier for the chat client. Can be . - /// A new instance configured with the specified assistant and optional default thread. - public static IChatClient AsNewIChatClient(this AssistantClient client, string assistantId, string? defaultThreadId = null) => - new NewOpenAIAssistantsChatClient(Throw.IfNull(client), Throw.IfNullOrWhitespace(assistantId), defaultThreadId); -} diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIResponsesChatClient.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIResponsesChatClient.cs deleted file mode 100644 index bd849ad320..0000000000 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/NewOpenAIResponsesChatClient.cs +++ /dev/null @@ -1,918 +0,0 @@ -#pragma warning disable IDE0073 // The file header does not match the required text -#pragma warning disable CA1063 // Implement IDisposable Correctly - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.ClientModel.Primitives; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Text; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; -using Microsoft.Shared.Diagnostics; -using OpenAI.Responses; - -#pragma warning disable S907 // "goto" statement should not be used -#pragma warning disable S1067 // Expressions should not be too complex -#pragma warning disable S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields -#pragma warning disable S3604 // Member initializer values should not be redundant -#pragma warning disable SA1202 // Elements should be ordered by access -#pragma warning disable SA1204 // Static elements should appear before instance elements - -namespace Microsoft.Extensions.AI; - -/// Represents an for an . -internal sealed class NewOpenAIResponsesChatClient : IChatClient -{ - /// Type info for serializing and deserializing arbitrary JSON objects. - private static readonly JsonTypeInfo s_jsonTypeInfo = AIJsonUtilities.DefaultOptions.GetTypeInfo(typeof(object)); - - /// Metadata about the client. - private readonly ChatClientMetadata _metadata; - - /// The underlying . - private readonly OpenAIResponseClient _responseClient; - - /// Initializes a new instance of the class for the specified . - /// The underlying client. - /// is . - public NewOpenAIResponsesChatClient(OpenAIResponseClient responseClient) - { - _ = Throw.IfNull(responseClient); - - _responseClient = responseClient; - - // https://github.com/openai/openai-dotnet/issues/215 - // The endpoint and model aren't currently exposed, so use reflection to get at them, temporarily. Once packages - // implement the abstractions directly rather than providing adapters on top of the public APIs, - // the package can provide such implementations separate from what's exposed in the public API. - Uri providerUrl = typeof(OpenAIResponseClient).GetField("_endpoint", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - ?.GetValue(responseClient) as Uri ?? OpenAIClientExtensions3.DefaultOpenAIEndpoint; - string? model = typeof(OpenAIResponseClient).GetField("_model", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - ?.GetValue(responseClient) as string; - - _metadata = new("openai", providerUrl, model); - } - - /// - object? IChatClient.GetService(Type serviceType, object? serviceKey) - { - _ = Throw.IfNull(serviceType); - - return - serviceKey is not null ? null : - serviceType == typeof(ChatClientMetadata) ? _metadata : - serviceType == typeof(OpenAIResponseClient) ? _responseClient : - serviceType.IsInstanceOfType(this) ? this : - null; - } - - /// - public async Task GetResponseAsync( - IEnumerable messages, ChatOptions? options = null, CancellationToken cancellationToken = default) - { - _ = Throw.IfNull(messages); - - // Convert the inputs into what OpenAIResponseClient expects. - var openAIResponseItems = ToOpenAIResponseItems(messages, options); - var openAIOptions = ToOpenAIResponseCreationOptions(options); - - // Make the call to the OpenAIResponseClient. - var openAIResponse = (await _responseClient.CreateResponseAsync(openAIResponseItems, openAIOptions, cancellationToken).ConfigureAwait(false)).Value; - - // Convert the response to a ChatResponse. - return FromOpenAIResponse(openAIResponse, openAIOptions); - } - - internal static ChatResponse FromOpenAIResponse(OpenAIResponse openAIResponse, ResponseCreationOptions? openAIOptions) - { - // Convert and return the results. - ChatResponse response = new() - { - ConversationId = openAIOptions?.StoredOutputEnabled is false ? null : openAIResponse.Id, - CreatedAt = openAIResponse.CreatedAt, - FinishReason = ToFinishReason(openAIResponse.IncompleteStatusDetails?.Reason), - ModelId = openAIResponse.Model, - RawRepresentation = openAIResponse, - ResponseId = openAIResponse.Id, - Usage = ToUsageDetails(openAIResponse), - }; - - if (!string.IsNullOrEmpty(openAIResponse.EndUserId)) - { - (response.AdditionalProperties ??= [])[nameof(openAIResponse.EndUserId)] = openAIResponse.EndUserId; - } - - if (openAIResponse.Error is not null) - { - (response.AdditionalProperties ??= [])[nameof(openAIResponse.Error)] = openAIResponse.Error; - } - - if (openAIResponse.OutputItems is not null) - { - response.Messages = [.. ToChatMessages(openAIResponse.OutputItems)]; - - if (response.Messages.LastOrDefault() is { } lastMessage && openAIResponse.Error is { } error) - { - lastMessage.Contents.Add(new ErrorContent(error.Message) { ErrorCode = error.Code.ToString() }); - } - - foreach (var message in response.Messages) - { - message.CreatedAt ??= openAIResponse.CreatedAt; - } - } - - return response; - } - - internal static IEnumerable ToChatMessages(IEnumerable items) - { - ChatMessage? message = null; - - foreach (ResponseItem outputItem in items) - { - message ??= new(ChatRole.Assistant, (string?)null); - - switch (outputItem) - { - case MessageResponseItem messageItem: - if (message.MessageId is not null && message.MessageId != messageItem.Id) - { - yield return message; - message = new ChatMessage(); - } - - message.MessageId = messageItem.Id; - message.RawRepresentation = messageItem; - message.Role = ToChatRole(messageItem.Role); - ((List)message.Contents).AddRange(ToAIContents(messageItem.Content)); - break; - - case ReasoningResponseItem reasoningItem when reasoningItem.GetSummaryText() is string summary: - message.Contents.Add(new TextReasoningContent(summary) { RawRepresentation = outputItem }); - break; - - case FunctionCallResponseItem functionCall: - var fcc = OpenAIClientExtensions3.ParseCallContent(functionCall.FunctionArguments, functionCall.CallId, functionCall.FunctionName); - fcc.RawRepresentation = outputItem; - message.Contents.Add(fcc); - break; - - case FunctionCallOutputResponseItem functionCallOutputItem: - message.Contents.Add(new FunctionResultContent(functionCallOutputItem.CallId, functionCallOutputItem.FunctionOutput) { RawRepresentation = functionCallOutputItem }); - break; - - default: - message.Contents.Add(new() { RawRepresentation = outputItem }); - break; - } - } - - if (message is not null) - { - yield return message; - } - } - - /// - public IAsyncEnumerable GetStreamingResponseAsync( - IEnumerable messages, ChatOptions? options = null, CancellationToken cancellationToken = default) - { - _ = Throw.IfNull(messages); - - var openAIResponseItems = ToOpenAIResponseItems(messages, options); - var openAIOptions = ToOpenAIResponseCreationOptions(options); - - var streamingUpdates = _responseClient.CreateResponseStreamingAsync(openAIResponseItems, openAIOptions, cancellationToken); - - return FromOpenAIStreamingResponseUpdatesAsync(streamingUpdates, openAIOptions, cancellationToken); - } - - internal static async IAsyncEnumerable FromOpenAIStreamingResponseUpdatesAsync( - IAsyncEnumerable streamingResponseUpdates, ResponseCreationOptions? options, [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - DateTimeOffset? createdAt = null; - string? responseId = null; - string? conversationId = null; - string? modelId = null; - string? lastMessageId = null; - ChatRole? lastRole = null; - Dictionary outputIndexToMessages = []; - Dictionary? functionCallInfos = null; - - await foreach (var streamingUpdate in streamingResponseUpdates.WithCancellation(cancellationToken).ConfigureAwait(false)) - { - // Create an update populated with the current state of the response. - ChatResponseUpdate CreateUpdate(AIContent? content = null) => - new(lastRole, content is not null ? [content] : null) - { - ConversationId = conversationId, - CreatedAt = createdAt, - MessageId = lastMessageId, - ModelId = modelId, - RawRepresentation = streamingUpdate, - ResponseId = responseId, - }; - - switch (streamingUpdate) - { - case StreamingResponseCreatedUpdate createdUpdate: - createdAt = createdUpdate.Response.CreatedAt; - responseId = createdUpdate.Response.Id; - conversationId = options?.StoredOutputEnabled is false ? null : responseId; - modelId = createdUpdate.Response.Model; - goto default; - - case StreamingResponseCompletedUpdate completedUpdate: - { - var update = CreateUpdate(ToUsageDetails(completedUpdate.Response) is { } usage ? new UsageContent(usage) : null); - update.FinishReason = - ToFinishReason(completedUpdate.Response?.IncompleteStatusDetails?.Reason) ?? - (functionCallInfos is not null ? ChatFinishReason.ToolCalls : - ChatFinishReason.Stop); - yield return update; - break; - } - - case StreamingResponseOutputItemAddedUpdate outputItemAddedUpdate: - switch (outputItemAddedUpdate.Item) - { - case MessageResponseItem mri: - outputIndexToMessages[outputItemAddedUpdate.OutputIndex] = mri; - break; - - case FunctionCallResponseItem fcri: - (functionCallInfos ??= [])[outputItemAddedUpdate.OutputIndex] = new(fcri); - break; - } - - goto default; - - case StreamingResponseOutputItemDoneUpdate outputItemDoneUpdate: - _ = outputIndexToMessages.Remove(outputItemDoneUpdate.OutputIndex); - - if (outputItemDoneUpdate.Item is MessageResponseItem item && - item.Content is { Count: > 0 } content && - content.Any(c => c.OutputTextAnnotations is { Count: > 0 })) - { - AIContent annotatedContent = new(); - foreach (var c in content) - { - PopulateAnnotations(c, annotatedContent); - } - - yield return CreateUpdate(annotatedContent); - break; - } - - goto default; - - case StreamingResponseOutputTextDeltaUpdate outputTextDeltaUpdate: - { - _ = outputIndexToMessages.TryGetValue(outputTextDeltaUpdate.OutputIndex, out MessageResponseItem? messageItem); - lastMessageId = messageItem?.Id; - lastRole = ToChatRole(messageItem?.Role); - - yield return CreateUpdate(new TextContent(outputTextDeltaUpdate.Delta)); - break; - } - - case StreamingResponseFunctionCallArgumentsDeltaUpdate functionCallArgumentsDeltaUpdate: - { - if (functionCallInfos?.TryGetValue(functionCallArgumentsDeltaUpdate.OutputIndex, out FunctionCallInfo? callInfo) is true) - { - _ = (callInfo.Arguments ??= new()).Append(functionCallArgumentsDeltaUpdate.Delta); - } - - goto default; - } - - case StreamingResponseFunctionCallArgumentsDoneUpdate functionCallOutputDoneUpdate: - { - if (functionCallInfos?.TryGetValue(functionCallOutputDoneUpdate.OutputIndex, out FunctionCallInfo? callInfo) is true) - { - _ = functionCallInfos.Remove(functionCallOutputDoneUpdate.OutputIndex); - - var fcc = OpenAIClientExtensions3.ParseCallContent( - callInfo.Arguments?.ToString() ?? string.Empty, - callInfo.ResponseItem.CallId, - callInfo.ResponseItem.FunctionName); - - lastMessageId = callInfo.ResponseItem.Id; - lastRole = ChatRole.Assistant; - - yield return CreateUpdate(fcc); - break; - } - - goto default; - } - - case StreamingResponseErrorUpdate errorUpdate: - yield return CreateUpdate(new ErrorContent(errorUpdate.Message) - { - ErrorCode = errorUpdate.Code, - Details = errorUpdate.Param, - }); - break; - - case StreamingResponseRefusalDoneUpdate refusalDone: - yield return CreateUpdate(new ErrorContent(refusalDone.Refusal) - { - ErrorCode = nameof(ResponseContentPart.Refusal), - }); - break; - - default: - { - // Capture streaming thinking contents - if (streamingUpdate.GetType().Name == "InternalResponseReasoningSummaryTextDeltaEvent") - { - var updateJson = JsonSerializer.Deserialize( - JsonSerializer.Serialize(streamingUpdate, s_jsonTypeInfo), - OpenAIJsonContext2.Default.JsonElement); - - if (updateJson.TryGetProperty("delta", out var deltaProperty)) - { - yield return CreateUpdate(new TextReasoningContent(deltaProperty.GetString())); - break; - } - } - - yield return CreateUpdate(); - break; - } - } - } - } - - /// - void IDisposable.Dispose() - { - // Nothing to dispose. Implementation required for the IChatClient interface. - } - - internal static ResponseTool ToResponseTool(AIFunction aiFunction, ChatOptions? options = null) - { - bool? strict = - OpenAIClientExtensions3.HasStrict(aiFunction.AdditionalProperties) ?? - OpenAIClientExtensions3.HasStrict(options?.AdditionalProperties); - - return ResponseTool.CreateFunctionTool( - aiFunction.Name, - aiFunction.Description, - OpenAIClientExtensions3.ToOpenAIFunctionParameters(aiFunction, strict), - strict ?? false); - } - - /// Creates a from a . - private static ChatRole ToChatRole(OpenAI.Responses.MessageRole? role) => - role switch - { - OpenAI.Responses.MessageRole.System => ChatRole.System, - OpenAI.Responses.MessageRole.Developer => OpenAIClientExtensions3.ChatRoleDeveloper, - OpenAI.Responses.MessageRole.User => ChatRole.User, - _ => ChatRole.Assistant, - }; - - /// Creates a from a . - private static ChatFinishReason? ToFinishReason(ResponseIncompleteStatusReason? statusReason) => - statusReason == ResponseIncompleteStatusReason.ContentFilter ? ChatFinishReason.ContentFilter : - statusReason == ResponseIncompleteStatusReason.MaxOutputTokens ? ChatFinishReason.Length : - null; - - /// Converts a to a . - private ResponseCreationOptions ToOpenAIResponseCreationOptions(ChatOptions? options) - { - if (options is null) - { - return new ResponseCreationOptions(); - } - - if (options.RawRepresentationFactory?.Invoke(this) is not ResponseCreationOptions result) - { - result = new ResponseCreationOptions(); - } - - // Handle strongly-typed properties. - result.MaxOutputTokenCount ??= options.MaxOutputTokens; - result.ParallelToolCallsEnabled ??= options.AllowMultipleToolCalls; - result.PreviousResponseId ??= options.ConversationId; - result.Temperature ??= options.Temperature; - result.TopP ??= options.TopP; - - if (options.Instructions is { } instructions) - { - result.Instructions = string.IsNullOrEmpty(result.Instructions) ? - instructions : - $"{result.Instructions}{Environment.NewLine}{instructions}"; - } - - // Populate tools if there are any. - if (options.Tools is { Count: > 0 } tools) - { - foreach (AITool tool in tools) - { - switch (tool) - { - case AIFunction aiFunction: - result.Tools.Add(ToResponseTool(aiFunction, options)); - break; - - case HostedWebSearchTool webSearchTool: - WebSearchUserLocation? location = null; - if (webSearchTool.AdditionalProperties.TryGetValue(nameof(WebSearchUserLocation), out object? objLocation)) - { - location = objLocation as WebSearchUserLocation; - } - - WebSearchContextSize? size = null; - if (webSearchTool.AdditionalProperties.TryGetValue(nameof(WebSearchContextSize), out object? objSize) && - objSize is WebSearchContextSize) - { - size = (WebSearchContextSize)objSize; - } - - result.Tools.Add(ResponseTool.CreateWebSearchTool(location, size)); - break; - - case HostedFileSearchTool fileSearchTool: - result.Tools.Add(ResponseTool.CreateFileSearchTool( - fileSearchTool.Inputs?.OfType().Select(c => c.VectorStoreId) ?? [], - fileSearchTool.MaximumResultCount)); - break; - - case HostedCodeInterpreterTool codeTool: - string json; - if (codeTool.Inputs is { Count: > 0 } inputs) - { - string jsonArray = JsonSerializer.Serialize( - inputs.OfType().Select(c => c.FileId), - OpenAIJsonContext2.Default.IEnumerableString); - json = $$"""{"type":"code_interpreter","container":{"type":"auto",files:{{jsonArray}}} }"""; - } - else - { - json = """{"type":"code_interpreter","container":"auto"}"""; - } - - result.Tools.Add(ModelReaderWriter.Read(BinaryData.FromString(json))); - break; - } - } - - if (result.ToolChoice is null && result.Tools.Count > 0) - { - switch (options.ToolMode) - { - case NoneChatToolMode: - result.ToolChoice = ResponseToolChoice.CreateNoneChoice(); - break; - - case AutoChatToolMode: - case null: - result.ToolChoice = ResponseToolChoice.CreateAutoChoice(); - break; - - case RequiredChatToolMode required: - result.ToolChoice = required.RequiredFunctionName is not null ? - ResponseToolChoice.CreateFunctionChoice(required.RequiredFunctionName) : - ResponseToolChoice.CreateRequiredChoice(); - break; - } - } - } - - if (result.TextOptions is null) - { - if (options.ResponseFormat is ChatResponseFormatText) - { - result.TextOptions = new() - { - TextFormat = ResponseTextFormat.CreateTextFormat() - }; - } - else if (options.ResponseFormat is ChatResponseFormatJson jsonFormat) - { - result.TextOptions = new() - { - TextFormat = OpenAIClientExtensions3.StrictSchemaTransformCache.GetOrCreateTransformedSchema(jsonFormat) is { } jsonSchema ? - ResponseTextFormat.CreateJsonSchemaFormat( - jsonFormat.SchemaName ?? "json_schema", - BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(jsonSchema, OpenAIJsonContext2.Default.JsonElement)), - jsonFormat.SchemaDescription, - OpenAIClientExtensions3.HasStrict(options.AdditionalProperties)) : - ResponseTextFormat.CreateJsonObjectFormat(), - }; - } - } - - return result; - } - - /// Convert a sequence of s to s. - internal static IEnumerable ToOpenAIResponseItems(IEnumerable inputs, ChatOptions? options) - { - _ = options; // currently unused - - foreach (ChatMessage input in inputs) - { - if (input.Role == ChatRole.System || - input.Role == OpenAIClientExtensions3.ChatRoleDeveloper) - { - string text = input.Text; - if (!string.IsNullOrWhiteSpace(text)) - { - yield return input.Role == ChatRole.System ? - ResponseItem.CreateSystemMessageItem(text) : - ResponseItem.CreateDeveloperMessageItem(text); - } - - continue; - } - - if (input.Role == ChatRole.User) - { - yield return ResponseItem.CreateUserMessageItem(ToResponseContentParts(input.Contents)); - continue; - } - - if (input.Role == ChatRole.Tool) - { - foreach (AIContent item in input.Contents) - { - switch (item) - { - case AIContent when item.RawRepresentation is ResponseItem rawRep: - yield return rawRep; - break; - - case FunctionResultContent resultContent: - string? result = resultContent.Result as string; - if (result is null && resultContent.Result is not null) - { - try - { - result = JsonSerializer.Serialize(resultContent.Result, AIJsonUtilities.DefaultOptions.GetTypeInfo(typeof(object))); - } - catch (NotSupportedException) - { - // If the type can't be serialized, skip it. - } - } - - yield return ResponseItem.CreateFunctionCallOutputItem(resultContent.CallId, result ?? string.Empty); - break; - } - } - - continue; - } - - if (input.Role == ChatRole.Assistant) - { - foreach (AIContent item in input.Contents) - { - switch (item) - { - case AIContent when item.RawRepresentation is ResponseItem rawRep: - yield return rawRep; - break; - - case TextContent textContent: - yield return ResponseItem.CreateAssistantMessageItem(textContent.Text); - break; - - case TextReasoningContent reasoningContent: - yield return ResponseItem.CreateReasoningItem(reasoningContent.Text); - break; - - case FunctionCallContent callContent: - yield return ResponseItem.CreateFunctionCallItem( - callContent.CallId, - callContent.Name, - BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes( - callContent.Arguments, - AIJsonUtilities.DefaultOptions.GetTypeInfo(typeof(IDictionary))))); - break; - } - } - - continue; - } - } - } - - /// Extract usage details from an . - private static UsageDetails? ToUsageDetails(OpenAIResponse? openAIResponse) - { - UsageDetails? ud = null; - if (openAIResponse?.Usage is { } usage) - { - ud = new() - { - InputTokenCount = usage.InputTokenCount, - OutputTokenCount = usage.OutputTokenCount, - TotalTokenCount = usage.TotalTokenCount, - }; - - if (usage.InputTokenDetails is { } inputDetails) - { - ud.AdditionalCounts ??= []; - ud.AdditionalCounts.Add($"{nameof(usage.InputTokenDetails)}.{nameof(inputDetails.CachedTokenCount)}", inputDetails.CachedTokenCount); - } - - if (usage.OutputTokenDetails is { } outputDetails) - { - ud.AdditionalCounts ??= []; - ud.AdditionalCounts.Add($"{nameof(usage.OutputTokenDetails)}.{nameof(outputDetails.ReasoningTokenCount)}", outputDetails.ReasoningTokenCount); - } - } - - return ud; - } - - /// Convert a sequence of s to a list of . - private static List ToAIContents(IEnumerable contents) - { - List results = []; - - foreach (ResponseContentPart part in contents) - { - switch (part.Kind) - { - case ResponseContentPartKind.InputText or ResponseContentPartKind.OutputText: - TextContent text = new(part.Text) { RawRepresentation = part }; - PopulateAnnotations(part, text); - results.Add(text); - break; - - case ResponseContentPartKind.InputFile: - if (!string.IsNullOrWhiteSpace(part.InputImageFileId)) - { - results.Add(new HostedFileContent(part.InputImageFileId) { RawRepresentation = part }); - } - else if (!string.IsNullOrWhiteSpace(part.InputFileId)) - { - results.Add(new HostedFileContent(part.InputFileId) { RawRepresentation = part }); - } - else if (part.InputFileBytes is not null) - { - results.Add(new DataContent(part.InputFileBytes, part.InputFileBytesMediaType ?? "application/octet-stream") - { - Name = part.InputFilename, - RawRepresentation = part, - }); - } - - break; - - case ResponseContentPartKind.Refusal: - results.Add(new ErrorContent(part.Refusal) - { - ErrorCode = nameof(ResponseContentPartKind.Refusal), - RawRepresentation = part, - }); - break; - - default: - results.Add(new() { RawRepresentation = part }); - break; - } - } - - return results; - } - - /// Converts any annotations from and stores them in . - private static void PopulateAnnotations(ResponseContentPart source, AIContent destination) - { - if (source.OutputTextAnnotations is { Count: > 0 }) - { - foreach (var ota in source.OutputTextAnnotations) - { - (destination.Annotations ??= []).Add(new CitationAnnotation - { - RawRepresentation = ota, - AnnotatedRegions = [new TextSpanAnnotatedRegion { StartIndex = ota.UriCitationStartIndex, EndIndex = ota.UriCitationEndIndex }], - Title = ota.UriCitationTitle, - Url = ota.UriCitationUri, - FileId = ota.FileCitationFileId ?? ota.FilePathFileId, - }); - } - } - } - - /// Convert a list of s to a list of . - private static List ToResponseContentParts(IList contents) - { - List parts = []; - foreach (var content in contents) - { - switch (content) - { - case AIContent when content.RawRepresentation is ResponseContentPart rawRep: - parts.Add(rawRep); - break; - - case TextContent textContent: - parts.Add(ResponseContentPart.CreateInputTextPart(textContent.Text)); - break; - - case UriContent uriContent when uriContent.HasTopLevelMediaType("image"): - parts.Add(ResponseContentPart.CreateInputImagePart(uriContent.Uri)); - break; - - case DataContent dataContent when dataContent.HasTopLevelMediaType("image"): - parts.Add(ResponseContentPart.CreateInputImagePart(BinaryData.FromBytes(dataContent.Data), dataContent.MediaType)); - break; - - case DataContent dataContent when dataContent.MediaType.StartsWith("application/pdf", StringComparison.OrdinalIgnoreCase): - parts.Add(ResponseContentPart.CreateInputFilePart(BinaryData.FromBytes(dataContent.Data), dataContent.MediaType, dataContent.Name ?? $"{Guid.NewGuid():N}.pdf")); - break; - - case HostedFileContent fileContent: - parts.Add(ResponseContentPart.CreateInputFilePart(fileContent.FileId)); - break; - - case ErrorContent errorContent when errorContent.ErrorCode == nameof(ResponseContentPartKind.Refusal): - parts.Add(ResponseContentPart.CreateRefusalPart(errorContent.Message)); - break; - } - } - - if (parts.Count == 0) - { - parts.Add(ResponseContentPart.CreateInputTextPart(string.Empty)); - } - - return parts; - } - - /// POCO representing function calling info. - /// Used to concatenation information for a single function call from across multiple streaming updates. - private sealed class FunctionCallInfo(FunctionCallResponseItem item) - { - public readonly FunctionCallResponseItem ResponseItem = item; - public StringBuilder? Arguments; - } -} - -internal static class OpenAIClientExtensions3 -{ - /// Key into AdditionalProperties used to store a strict option. - private const string StrictKey = "strictJsonSchema"; - - /// Gets the default OpenAI endpoint. - internal static Uri DefaultOpenAIEndpoint { get; } = new("https://api.openai.com/v1"); - - /// Gets a for "developer". - internal static ChatRole ChatRoleDeveloper { get; } = new ChatRole("developer"); - - /// - /// Gets the JSON schema transformer cache conforming to OpenAI strict / structured output restrictions per - /// https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#supported-schemas. - /// - internal static AIJsonSchemaTransformCache StrictSchemaTransformCache { get; } = new(new() - { - DisallowAdditionalProperties = true, - ConvertBooleanSchemas = true, - MoveDefaultKeywordToDescription = true, - RequireAllProperties = true, - TransformSchemaNode = (ctx, node) => - { - // Move content from common but unsupported properties to description. In particular, we focus on properties that - // the AIJsonUtilities schema generator might produce and/or that are explicitly mentioned in the OpenAI documentation. - - if (node is JsonObject schemaObj) - { - StringBuilder? additionalDescription = null; - - ReadOnlySpan unsupportedProperties = - [ - // Produced by AIJsonUtilities but not in allow list at https://platform.openai.com/docs/guides/structured-outputs#supported-properties: - "contentEncoding", "contentMediaType", "not", - - // Explicitly mentioned at https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#key-ordering as being unsupported with some models: - "minLength", "maxLength", "pattern", "format", - "minimum", "maximum", "multipleOf", - "patternProperties", - "minItems", "maxItems", - - // Explicitly mentioned at https://learn.microsoft.com/azure/ai-services/openai/how-to/structured-outputs?pivots=programming-language-csharp&tabs=python-secure%2Cdotnet-entra-id#unsupported-type-specific-keywords - // as being unsupported with Azure OpenAI: - "unevaluatedProperties", "propertyNames", "minProperties", "maxProperties", - "unevaluatedItems", "contains", "minContains", "maxContains", "uniqueItems", - ]; - - foreach (string propName in unsupportedProperties) - { - if (schemaObj[propName] is { } propNode) - { - _ = schemaObj.Remove(propName); - AppendLine(ref additionalDescription, propName, propNode); - } - } - - if (additionalDescription is not null) - { - schemaObj["description"] = schemaObj["description"] is { } descriptionNode && descriptionNode.GetValueKind() == JsonValueKind.String ? - $"{descriptionNode.GetValue()}{Environment.NewLine}{additionalDescription}" : - additionalDescription.ToString(); - } - - return node; - - static void AppendLine(ref StringBuilder? sb, string propName, JsonNode propNode) - { - sb ??= new(); - - if (sb.Length > 0) - { - _ = sb.AppendLine(); - } - - _ = sb.Append(propName).Append(": ").Append(propNode); - } - } - - return node; - }, - }); - - /// Gets whether the properties specify that strict schema handling is desired. - internal static bool? HasStrict(IReadOnlyDictionary? additionalProperties) => - additionalProperties?.TryGetValue(StrictKey, out object? strictObj) is true && - strictObj is bool strictValue ? - strictValue : null; - - /// Extracts from an the parameters and strictness setting for use with OpenAI's APIs. - internal static BinaryData ToOpenAIFunctionParameters(AIFunction aiFunction, bool? strict) - { - // Perform any desirable transformations on the function's JSON schema, if it'll be used in a strict setting. - JsonElement jsonSchema = strict is true ? - StrictSchemaTransformCache.GetOrCreateTransformedSchema(aiFunction) : - aiFunction.JsonSchema; - - // Roundtrip the schema through the ToolJson model type to remove extra properties - // and force missing ones into existence, then return the serialized UTF8 bytes as BinaryData. - var tool = JsonSerializer.Deserialize(jsonSchema, OpenAIJsonContext2.Default.ToolJson)!; - var functionParameters = BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(tool, OpenAIJsonContext2.Default.ToolJson)); - - return functionParameters; - } - - /// Creates a new instance of parsing arguments using a specified encoding and parser. - /// The input arguments to be parsed. - /// The function call ID. - /// The function name. - /// A new instance of containing the parse result. - /// is . - /// is . - internal static FunctionCallContent ParseCallContent(string json, string callId, string name) => - FunctionCallContent.CreateFromParsedArguments(json, callId, name, - static json => JsonSerializer.Deserialize(json, OpenAIJsonContext2.Default.IDictionaryStringObject)!); - - /// Creates a new instance of parsing arguments using a specified encoding and parser. - /// The input arguments to be parsed. - /// The function call ID. - /// The function name. - /// A new instance of containing the parse result. - /// is . - /// is . - internal static FunctionCallContent ParseCallContent(BinaryData utf8json, string callId, string name) => - FunctionCallContent.CreateFromParsedArguments(utf8json, callId, name, - static utf8json => JsonSerializer.Deserialize(utf8json, OpenAIJsonContext2.Default.IDictionaryStringObject)!); - - /// Used to create the JSON payload for an OpenAI tool description. - internal sealed class ToolJson - { - [JsonPropertyName("type")] - public string Type { get; set; } = "object"; - - [JsonPropertyName("required")] - public HashSet Required { get; set; } = []; - - [JsonPropertyName("properties")] - public Dictionary Properties { get; set; } = []; - - [JsonPropertyName("additionalProperties")] - public bool AdditionalProperties { get; set; } - } -} - -/// Source-generated JSON type information for use by all OpenAI implementations. -[JsonSourceGenerationOptions(JsonSerializerDefaults.Web, - UseStringEnumConverter = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - WriteIndented = true)] -[JsonSerializable(typeof(OpenAIClientExtensions3.ToolJson))] -[JsonSerializable(typeof(IDictionary))] -[JsonSerializable(typeof(string[]))] -[JsonSerializable(typeof(IEnumerable))] -[JsonSerializable(typeof(JsonElement))] -internal sealed partial class OpenAIJsonContext2 : JsonSerializerContext; diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientExtensions.cs index 045dc3ef27..1abe056741 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Diagnostics; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -18,13 +19,13 @@ internal static class ChatClientExtensions chatBuilder.UseAgentInvocation(); } - if (chatClient.GetService() is null && chatClient.GetService() is null) + if (chatClient.GetService() is null) { _ = chatBuilder.Use((IChatClient innerClient, IServiceProvider services) => { var loggerFactory = services.GetService(); - return new NewFunctionInvokingChatClient(innerClient, loggerFactory, services); + return new FunctionInvokingChatClient(innerClient, loggerFactory, services); }); } @@ -33,17 +34,9 @@ internal static class ChatClientExtensions if (options?.ChatOptions?.Tools is { Count: > 0 }) { // When tools are provided in the constructor, set the tools for the whole lifecycle of the chat client - var newFunctionService = agentChatClient.GetService(); - var oldFunctionService = agentChatClient.GetService(); - - if (newFunctionService is not null) - { - newFunctionService.AdditionalTools = options.ChatOptions.Tools; - } - else - { - oldFunctionService!.AdditionalTools = options.ChatOptions.Tools; - } + var functionService = agentChatClient.GetService(); + Debug.Assert(functionService is not null, "FunctionInvokingChatClient should be registered in the chat client."); + functionService!.AdditionalTools = options.ChatOptions.Tools; } return agentChatClient; diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/ApprovalRequiredAIFunction.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/ApprovalRequiredAIFunction.cs deleted file mode 100644 index f1913efb0f..0000000000 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/ApprovalRequiredAIFunction.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using System.Threading.Tasks; -using Microsoft.Shared.Diagnostics; - -namespace Microsoft.Extensions.AI; - -/// -/// Marks an existing with additional metadata to indicate that it requires approval. -/// -/// The that requires approval. -public sealed class ApprovalRequiredAIFunction(AIFunction function) : DelegatingAIFunction(function) -{ - /// - /// An optional callback that can be used to determine if the function call requires approval, instead of the default behavior, which is to always require approval. - /// - public Func> RequiresApprovalCallback { get; set; } = _ => new(true); - - /// - /// Context object that provides information about the function call that requires approval. - /// - public sealed class ApprovalContext - { - /// - /// Initializes a new instance of the class. - /// - /// The containing the details of the invocation. - /// is null. - public ApprovalContext(FunctionCallContent functionCall) - { - this.FunctionCall = Throw.IfNull(functionCall); - } - - /// - /// Gets the containing the details of the invocation that will be made if approval is granted. - /// - public FunctionCallContent FunctionCall { get; } - } -} diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/LoggingHelpers.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/LoggingHelpers.cs deleted file mode 100644 index 6a0e13677f..0000000000 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/LoggingHelpers.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -// WARNING: -// This class has been temporarily copied here from MEAI, to allow prototyping -// functionality that will be moved to MEAI in the future. -// This file is not intended to be modified. - -#pragma warning disable CA1031 // Do not catch general exception types -#pragma warning disable S108 // Nested blocks of code should not be left empty -#pragma warning disable S2486 // Generic exceptions should not be ignored - -using System.Diagnostics.CodeAnalysis; -using System.Text.Json; - -namespace Microsoft.Extensions.AI; - -/// Provides internal helpers for implementing logging. -[ExcludeFromCodeCoverage] -internal static class LoggingHelpers -{ - /// Serializes as JSON for logging purposes. - public static string AsJson(T value, JsonSerializerOptions? options) - { - if (options?.TryGetTypeInfo(typeof(T), out var typeInfo) is true || - AIJsonUtilities.DefaultOptions.TryGetTypeInfo(typeof(T), out typeInfo)) - { - try - { - return JsonSerializer.Serialize(value, typeInfo); - } - catch - { - } - } - - // If we're unable to get a type info for the value, or if we fail to serialize, - // return an empty JSON object. We do not want lack of type info to disrupt application behavior with exceptions. - return "{}"; - } -} diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/NewFunctionInvokingChatClient.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/NewFunctionInvokingChatClient.cs deleted file mode 100644 index 2b41af5671..0000000000 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/NewFunctionInvokingChatClient.cs +++ /dev/null @@ -1,1553 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -// WARNING: -// This class is a copy of FunctionInvokingChatClient from MEAI, and is intended to be modified with -// changes that we want to prototype here, before updating FunctionInvokingChatClient in MEAI. -// The intention is to keep the changes in this file to a minimum, so that we can easily -// merge them back into MEAI when ready. - -// AF repo suppressions for code copied from MEAI. -#pragma warning disable IDE1006 // Naming Styles -#pragma warning disable IDE0009 // Member access should be qualified. -#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task -#pragma warning disable VSTHRD111 // Use ConfigureAwait(bool) - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.ExceptionServices; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Shared.Diagnostics; - -#pragma warning disable CA2213 // Disposable fields should be disposed -#pragma warning disable EA0002 // Use 'System.TimeProvider' to make the code easier to test -#pragma warning disable SA1202 // 'protected' members should come before 'private' members -#pragma warning disable S107 // Methods should not have too many parameters - -namespace Microsoft.Extensions.AI; - -/// -/// A delegating chat client that invokes functions defined on . -/// Include this in a chat pipeline to resolve function calls automatically. -/// -/// -/// -/// When this client receives a in a chat response, it responds -/// by calling the corresponding defined in , -/// producing a that it sends back to the inner client. This loop -/// is repeated until there are no more function calls to make, or until another stop condition is met, -/// such as hitting . -/// -/// -/// The provided implementation of is thread-safe for concurrent use so long as the -/// instances employed as part of the supplied are also safe. -/// The property can be used to control whether multiple function invocation -/// requests as part of the same request are invocable concurrently, but even with that set to -/// (the default), multiple concurrent requests to this same instance and using the same tools could result in those -/// tools being used concurrently (one per request). For example, a function that accesses the HttpContext of a specific -/// ASP.NET web request should only be used as part of a single at a time, and only with -/// set to , in case the inner client decided to issue multiple -/// invocation requests to that same function. -/// -/// -public partial class NewFunctionInvokingChatClient : DelegatingChatClient -{ - /// The for the current function invocation. - private static readonly AsyncLocal _currentContext = new(); - - /// Gets the specified when constructing the , if any. - protected IServiceProvider? FunctionInvocationServices { get; } - - /// The logger to use for logging information about function invocation. - private readonly ILogger _logger; - - /// The to use for telemetry. - /// This component does not own the instance and should not dispose it. - private readonly ActivitySource? _activitySource; - - /// Maximum number of roundtrips allowed to the inner client. - private int _maximumIterationsPerRequest = 40; // arbitrary default to prevent runaway execution - - /// Maximum number of consecutive iterations that are allowed contain at least one exception result. If the limit is exceeded, we rethrow the exception instead of continuing. - private int _maximumConsecutiveErrorsPerRequest = 3; - - /// - /// Initializes a new instance of the class. - /// - /// The underlying , or the next instance in a chain of clients. - /// An to use for logging information about function invocation. - /// An optional to use for resolving services required by the instances being invoked. - public NewFunctionInvokingChatClient(IChatClient innerClient, ILoggerFactory? loggerFactory = null, IServiceProvider? functionInvocationServices = null) - : base(innerClient) - { - _logger = (ILogger?)loggerFactory?.CreateLogger() ?? NullLogger.Instance; - _activitySource = innerClient.GetService(); - FunctionInvocationServices = functionInvocationServices; - } - - /// - /// Gets or sets the for the current function invocation. - /// - /// - /// This value flows across async calls. - /// - public static FunctionInvocationContext? CurrentContext - { - get => _currentContext.Value; - protected set => _currentContext.Value = value; - } - - /// - /// Gets or sets a value indicating whether detailed exception information should be included - /// in the chat history when calling the underlying . - /// - /// - /// if the full exception message is added to the chat history - /// when calling the underlying . - /// if a generic error message is included in the chat history. - /// The default value is . - /// - /// - /// - /// Setting the value to prevents the underlying language model from disclosing - /// raw exception details to the end user, since it doesn't receive that information. Even in this - /// case, the raw object is available to application code by inspecting - /// the property. - /// - /// - /// Setting the value to can help the underlying bypass problems on - /// its own, for example by retrying the function call with different arguments. However it might - /// result in disclosing the raw exception information to external users, which can be a security - /// concern depending on the application scenario. - /// - /// - /// Changing the value of this property while the client is in use might result in inconsistencies - /// as to whether detailed errors are provided during an in-flight request. - /// - /// - public bool IncludeDetailedErrors { get; set; } - - /// - /// Gets or sets a value indicating whether to allow concurrent invocation of functions. - /// - /// - /// if multiple function calls can execute in parallel. - /// if function calls are processed serially. - /// The default value is . - /// - /// - /// An individual response from the inner client might contain multiple function call requests. - /// By default, such function calls are processed serially. Set to - /// to enable concurrent invocation such that multiple function calls can execute in parallel. - /// - public bool AllowConcurrentInvocation { get; set; } - - /// - /// Gets or sets the maximum number of iterations per request. - /// - /// - /// The maximum number of iterations per request. - /// The default value is 40. - /// - /// - /// - /// Each request to this might end up making - /// multiple requests to the inner client. Each time the inner client responds with - /// a function call request, this client might perform that invocation and send the results - /// back to the inner client in a new request. This property limits the number of times - /// such a roundtrip is performed. The value must be at least one, as it includes the initial request. - /// - /// - /// Changing the value of this property while the client is in use might result in inconsistencies - /// as to how many iterations are allowed for an in-flight request. - /// - /// - public int MaximumIterationsPerRequest - { - get => _maximumIterationsPerRequest; - set - { - if (value < 1) - { - Throw.ArgumentOutOfRangeException(nameof(value)); - } - - _maximumIterationsPerRequest = value; - } - } - - /// - /// Gets or sets the maximum number of consecutive iterations that are allowed to fail with an error. - /// - /// - /// The maximum number of consecutive iterations that are allowed to fail with an error. - /// The default value is 3. - /// - /// - /// - /// When function invocations fail with an exception, the - /// continues to make requests to the inner client, optionally supplying exception information (as - /// controlled by ). This allows the to - /// recover from errors by trying other function parameters that may succeed. - /// - /// - /// However, in case function invocations continue to produce exceptions, this property can be used to - /// limit the number of consecutive failing attempts. When the limit is reached, the exception will be - /// rethrown to the caller. - /// - /// - /// If the value is set to zero, all function calling exceptions immediately terminate the function - /// invocation loop and the exception will be rethrown to the caller. - /// - /// - /// Changing the value of this property while the client is in use might result in inconsistencies - /// as to how many iterations are allowed for an in-flight request. - /// - /// - public int MaximumConsecutiveErrorsPerRequest - { - get => _maximumConsecutiveErrorsPerRequest; - set => _maximumConsecutiveErrorsPerRequest = Throw.IfLessThan(value, 0); - } - - /// Gets or sets a collection of additional tools the client is able to invoke. - /// - /// These will not impact the requests sent by the , which will pass through the - /// unmodified. However, if the inner client requests the invocation of a tool - /// that was not in , this collection will also be consulted - /// to look for a corresponding tool to invoke. This is useful when the service may have been pre-configured to be aware - /// of certain tools that aren't also sent on each individual request. - /// - public IList? AdditionalTools { get; set; } - - /// Gets or sets a delegate used to invoke instances. - /// - /// By default, the protected method is called for each to be invoked, - /// invoking the instance and returning its result. If this delegate is set to a non- value, - /// will replace its normal invocation with a call to this delegate, enabling - /// this delegate to assume all invocation handling of the function. - /// - public Func>? FunctionInvoker { get; set; } - - /// - public override async Task GetResponseAsync( - IEnumerable messages, ChatOptions? options = null, CancellationToken cancellationToken = default) - { - _ = Throw.IfNull(messages); - - // A single request into this GetResponseAsync may result in multiple requests to the inner client. - // Create an activity to group them together for better observability. - using Activity? activity = _activitySource?.StartActivity($"{nameof(FunctionInvokingChatClient)}.{nameof(GetResponseAsync)}"); - - // Copy the original messages in order to avoid enumerating the original messages multiple times. - // The IEnumerable can represent an arbitrary amount of work. - List originalMessages = [.. messages]; - messages = originalMessages; - - List? augmentedHistory = null; // the actual history of messages sent on turns other than the first - ChatResponse? response = null; // the response from the inner client, which is possibly modified and then eventually returned - List? responseMessages = null; // tracked list of messages, across multiple turns, to be used for the final response - UsageDetails? totalUsage = null; // tracked usage across all turns, to be used for the final response - List? functionCallContents = null; // function call contents that need responding to in the current turn - bool lastIterationHadConversationId = false; // whether the last iteration's response had a ConversationId set - int consecutiveErrorCount = 0; - - // Process approval requests (remove from originalMessages) and rejected approval responses (re-create FCC and create failed FRC). - var (preDownstreamCallHistory, notInvokedApprovals) = ProcessFunctionApprovalResponses(originalMessages, !string.IsNullOrWhiteSpace(options?.ConversationId), toolResponseId: null, functionCallContentFallbackMessageId: null); - - // Invoke approved approval responses, which generates some additional FRC wrapped in ChatMessage. - (IList? invokedApprovedFunctionApprovalResponses, bool shouldTerminate, consecutiveErrorCount) = - await InvokeApprovedFunctionApprovalResponses(notInvokedApprovals, originalMessages, options, consecutiveErrorCount, isStreaming: false, cancellationToken); - - if (invokedApprovedFunctionApprovalResponses is not null) - { - // We need to add the generated FRC to the list we'll return to callers as part of the next response. - preDownstreamCallHistory ??= []; - preDownstreamCallHistory.AddRange(invokedApprovedFunctionApprovalResponses); - } - - if (shouldTerminate) - { - return new ChatResponse(preDownstreamCallHistory); - } - - for (int iteration = 0; ; iteration++) - { - functionCallContents?.Clear(); - - // Make the call to the inner client. - response = await base.GetResponseAsync(messages, options, cancellationToken); - if (response is null) - { - Throw.InvalidOperationException($"The inner {nameof(IChatClient)} returned a null {nameof(ChatResponse)}."); - } - - // Before we do any function execution, make sure that any functions that require approval, have been turned into approval requests - // so that they don't get executed here. - response.Messages = await ReplaceFunctionCallsWithApprovalRequests(response.Messages, options?.Tools, AdditionalTools); - - // Any function call work to do? If yes, ensure we're tracking that work in functionCallContents. - bool requiresFunctionInvocation = - (options?.Tools is { Count: > 0 } || AdditionalTools is { Count: > 0 }) && - iteration < MaximumIterationsPerRequest && - CopyFunctionCalls(response.Messages, ref functionCallContents); - - // In a common case where we make a request and there's no function calling work required, - // fast path out by just returning the original response. - if (iteration == 0 && !requiresFunctionInvocation) - { - // Insert any pre-invocation FCC and FRC that were converted from approval responses into the response here, - // so they are returned to the caller. - response.Messages = UpdateResponseMessagesWithPreDownstreamCallHistory(response.Messages, preDownstreamCallHistory); - preDownstreamCallHistory = null; - - return response; - } - - // Track aggregate details from the response, including all of the response messages and usage details. - (responseMessages ??= []).AddRange(response.Messages); - if (response.Usage is not null) - { - if (totalUsage is not null) - { - totalUsage.Add(response.Usage); - } - else - { - totalUsage = response.Usage; - } - } - - // If there are no tools to call, or for any other reason we should stop, we're done. - // Break out of the loop and allow the handling at the end to configure the response - // with aggregated data from previous requests. - if (!requiresFunctionInvocation) - { - break; - } - - // Prepare the history for the next iteration. - FixupHistories(originalMessages, ref messages, ref augmentedHistory, response, responseMessages, ref lastIterationHadConversationId); - - // Add the responses from the function calls into the augmented history and also into the tracked - // list of response messages. - var modeAndMessages = await ProcessFunctionCallsAsync(augmentedHistory, options, functionCallContents!, iteration, consecutiveErrorCount, isStreaming: false, cancellationToken); - responseMessages.AddRange(modeAndMessages.MessagesAdded); - consecutiveErrorCount = modeAndMessages.NewConsecutiveErrorCount; - - if (modeAndMessages.ShouldTerminate) - { - break; - } - - UpdateOptionsForNextIteration(ref options, response.ConversationId); - } - - Debug.Assert(responseMessages is not null, "Expected to only be here if we have response messages."); - response.Messages = responseMessages!; - response.Usage = totalUsage; - - AddUsageTags(activity, totalUsage); - - return response; - } - - /// - public override async IAsyncEnumerable GetStreamingResponseAsync( - IEnumerable messages, ChatOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - _ = Throw.IfNull(messages); - - // A single request into this GetStreamingResponseAsync may result in multiple requests to the inner client. - // Create an activity to group them together for better observability. - using Activity? activity = _activitySource?.StartActivity($"{nameof(FunctionInvokingChatClient)}.{nameof(GetStreamingResponseAsync)}"); - UsageDetails? totalUsage = activity is { IsAllDataRequested: true } ? new() : null; // tracked usage across all turns, to be used for activity purposes - - // Copy the original messages in order to avoid enumerating the original messages multiple times. - // The IEnumerable can represent an arbitrary amount of work. - List originalMessages = [.. messages]; - messages = originalMessages; - - List? augmentedHistory = null; // the actual history of messages sent on turns other than the first - List? functionCallContents = null; // function call contents that need responding to in the current turn - List? responseMessages = null; // tracked list of messages, across multiple turns, to be used in fallback cases to reconstitute history - bool lastIterationHadConversationId = false; // whether the last iteration's response had a ConversationId set - List updates = []; // updates from the current response - int consecutiveErrorCount = 0; - - // This is a synthetic ID since we're generating the tool messages instead of getting them from - // the underlying provider. When emitting the streamed chunks, it's perfectly valid for us to - // use the same message ID for all of them within a given iteration, as this is a single logical - // message with multiple content items. We could also use different message IDs per tool content, - // but there's no benefit to doing so. - string toolResponseId = Guid.NewGuid().ToString("N"); - - // We also need a synthetic ID for the function call content for approved function calls - // where we don't know what the original message id of the function call was. - string functionCallContentFallbackMessageId = Guid.NewGuid().ToString("N"); - - ApprovalRequiredAIFunction[]? approvalRequiredFunctions = (options?.Tools ?? []).Concat(AdditionalTools ?? []).OfType().ToArray(); - bool hasApprovalRequiringFunctions = approvalRequiredFunctions.Length > 0; - - // Process approval requests (remove from original messages) and rejected approval responses (re-create FCC and create failed FRC). - var (preDownstreamCallHistory, notInvokedApprovals) = ProcessFunctionApprovalResponses(originalMessages, !string.IsNullOrWhiteSpace(options?.ConversationId), toolResponseId, functionCallContentFallbackMessageId); - if (preDownstreamCallHistory is not null) - { - foreach (var message in preDownstreamCallHistory) - { - yield return ConvertToolResultMessageToUpdate(message, options?.ConversationId, message.MessageId); - Activity.Current = activity; // workaround for https://github.com/dotnet/runtime/issues/47802 - } - } - - // Invoke approved approval responses, which generates some additional FRC wrapped in ChatMessage. - (IList? invokedApprovedFunctionApprovalResponses, bool shouldTerminate, consecutiveErrorCount) = - await InvokeApprovedFunctionApprovalResponses(notInvokedApprovals, originalMessages, options, consecutiveErrorCount, isStreaming: true, cancellationToken); - - if (invokedApprovedFunctionApprovalResponses is not null) - { - foreach (var message in invokedApprovedFunctionApprovalResponses) - { - message.MessageId = toolResponseId; - yield return ConvertToolResultMessageToUpdate(message, options?.ConversationId, message.MessageId); - Activity.Current = activity; // workaround for https://github.com/dotnet/runtime/issues/47802 - } - - if (shouldTerminate) - { - yield break; - } - } - - for (int iteration = 0; ; iteration++) - { - updates.Clear(); - functionCallContents?.Clear(); - - bool hasApprovalRequiringFcc = false; - int lastApprovalCheckedFCCIndex = 0; - int lastYieldedUpdateIndex = 0; - - await foreach (var update in base.GetStreamingResponseAsync(messages, options, cancellationToken)) - { - if (update is null) - { - Throw.InvalidOperationException($"The inner {nameof(IChatClient)} streamed a null {nameof(ChatResponseUpdate)}."); - } - - updates.Add(update); - - _ = CopyFunctionCalls(update.Contents, ref functionCallContents); - - if (totalUsage is not null) - { - IList contents = update.Contents; - int contentsCount = contents.Count; - for (int i = 0; i < contentsCount; i++) - { - if (contents[i] is UsageContent uc) - { - totalUsage.Add(uc.Details); - } - } - } - - if (functionCallContents?.Count is not > 0 || !hasApprovalRequiringFunctions) - { - // If there are no function calls to make yet, or if none of the functions require approval at all, - // we can yield the update as-is. - lastYieldedUpdateIndex++; - yield return update; - Activity.Current = activity; // workaround for https://github.com/dotnet/runtime/issues/47802 - } - else - { - // Check if any of the function call contents in this update requires approval. - // Once we find the first one that requires approval, this method becomes a no-op. - (hasApprovalRequiringFcc, lastApprovalCheckedFCCIndex) = await CheckForApprovalRequiringFCCAsync( - functionCallContents, approvalRequiredFunctions, hasApprovalRequiringFcc, lastApprovalCheckedFCCIndex); - - // We've encountered a function call content that requires approval (either in this update or ealier) - // so we need to ask for approval for all functions, since we cannot mix and match. - if (hasApprovalRequiringFcc) - { - // Convert all function call contents into approval requests from the last yielded update index - // and yield all those updates. - for (; lastYieldedUpdateIndex < updates.Count; lastYieldedUpdateIndex++) - { - var updateToYield = updates[lastYieldedUpdateIndex]; - if (TryReplaceFunctionCallsWithApprovalRequests(updateToYield.Contents, out var updatedContents)) - { - updateToYield.Contents = updatedContents; - } - yield return updateToYield; - Activity.Current = activity; // workaround for https://github.com/dotnet/runtime/issues/47802 - } - } - else - { - // We don't have any appoval requiring function calls yet, but we may receive some in future - // so we cannot yield the updates yet. We'll just keep them in the updates list - // for later. - // We will yield the updates as soon as we receive a function call content that requires approval or - // when we reach the end of the updates stream. - } - } - - Activity.Current = activity; // workaround for https://github.com/dotnet/runtime/issues/47802 - } - - // If there are no tools to call, or for any other reason we should stop, return the response. - if (functionCallContents is not { Count: > 0 } || - hasApprovalRequiringFcc || - (options?.Tools is not { Count: > 0 } && AdditionalTools is not { Count: > 0 }) || - iteration >= _maximumIterationsPerRequest) - { - break; - } - - // Reconstitute a response from the response updates. - var response = updates.ToChatResponse(); - (responseMessages ??= []).AddRange(response.Messages); - - // Prepare the history for the next iteration. - FixupHistories(originalMessages, ref messages, ref augmentedHistory, response, responseMessages, ref lastIterationHadConversationId); - - // Process all of the functions, adding their results into the history. - var modeAndMessages = await ProcessFunctionCallsAsync(augmentedHistory, options, functionCallContents, iteration, consecutiveErrorCount, isStreaming: true, cancellationToken); - responseMessages.AddRange(modeAndMessages.MessagesAdded); - consecutiveErrorCount = modeAndMessages.NewConsecutiveErrorCount; - - // Stream any generated function results. This mirrors what's done for GetResponseAsync, where the returned messages - // includes all activities, including generated function results. - foreach (var message in modeAndMessages.MessagesAdded) - { - yield return ConvertToolResultMessageToUpdate(message, response.ConversationId, toolResponseId); - Activity.Current = activity; // workaround for https://github.com/dotnet/runtime/issues/47802 - } - - if (modeAndMessages.ShouldTerminate) - { - break; - } - - UpdateOptionsForNextIteration(ref options, response.ConversationId); - } - - AddUsageTags(activity, totalUsage); - } - - /// Adds tags to for usage details in . - private static void AddUsageTags(Activity? activity, UsageDetails? usage) - { - if (usage is not null && activity is { IsAllDataRequested: true }) - { - if (usage.InputTokenCount is long inputTokens) - { - _ = activity.AddTag(OpenTelemetryConsts.GenAI.Usage.InputTokens, (int)inputTokens); - } - - if (usage.OutputTokenCount is long outputTokens) - { - _ = activity.AddTag(OpenTelemetryConsts.GenAI.Usage.OutputTokens, (int)outputTokens); - } - } - } - - /// Prepares the various chat message lists after a response from the inner client and before invoking functions. - /// The original messages provided by the caller. - /// The messages reference passed to the inner client. - /// The augmented history containing all the messages to be sent. - /// The most recent response being handled. - /// A list of all response messages received up until this point. - /// Whether the previous iteration's response had a conversation ID. - private static void FixupHistories( - IEnumerable originalMessages, - ref IEnumerable messages, - [NotNull] ref List? augmentedHistory, - ChatResponse response, - List allTurnsResponseMessages, - ref bool lastIterationHadConversationId) - { - // We're now going to need to augment the history with function result contents. - // That means we need a separate list to store the augmented history. - if (response.ConversationId is not null) - { - // The response indicates the inner client is tracking the history, so we don't want to send - // anything we've already sent or received. - if (augmentedHistory is not null) - { - augmentedHistory.Clear(); - } - else - { - augmentedHistory = []; - } - - lastIterationHadConversationId = true; - } - else if (lastIterationHadConversationId) - { - // In the very rare case where the inner client returned a response with a conversation ID but then - // returned a subsequent response without one, we want to reconstitute the full history. To do that, - // we can populate the history with the original chat messages and then all of the response - // messages up until this point, which includes the most recent ones. - augmentedHistory ??= []; - augmentedHistory.Clear(); - augmentedHistory.AddRange(originalMessages); - augmentedHistory.AddRange(allTurnsResponseMessages); - - lastIterationHadConversationId = false; - } - else - { - // If augmentedHistory is already non-null, then we've already populated it with everything up - // until this point (except for the most recent response). If it's null, we need to seed it with - // the chat history provided by the caller. - augmentedHistory ??= originalMessages.ToList(); - - // Now add the most recent response messages. - augmentedHistory.AddMessages(response); - - lastIterationHadConversationId = false; - } - - // Use the augmented history as the new set of messages to send. - messages = augmentedHistory; - } - - /// Copies any from to . - private static bool CopyFunctionCalls( - IList messages, [NotNullWhen(true)] ref List? functionCalls) - { - bool any = false; - int count = messages.Count; - for (int i = 0; i < count; i++) - { - any |= CopyFunctionCalls(messages[i].Contents, ref functionCalls); - } - - return any; - } - - /// Copies any from to . - private static bool CopyFunctionCalls( - IList content, [NotNullWhen(true)] ref List? functionCalls) - { - bool any = false; - int count = content.Count; - for (int i = 0; i < count; i++) - { - if (content[i] is FunctionCallContent functionCall) - { - (functionCalls ??= []).Add(functionCall); - any = true; - } - } - - return any; - } - - private static void UpdateOptionsForNextIteration(ref ChatOptions? options, string? conversationId) - { - if (options is null) - { - if (conversationId is not null) - { - options = new() { ConversationId = conversationId }; - } - } - else if (options.ToolMode is RequiredChatToolMode) - { - // We have to reset the tool mode to be non-required after the first iteration, - // as otherwise we'll be in an infinite loop. - options = options.Clone(); - options.ToolMode = null; - options.ConversationId = conversationId; - } - else if (options.ConversationId != conversationId) - { - // As with the other modes, ensure we've propagated the chat conversation ID to the options. - // We only need to clone the options if we're actually mutating it. - options = options.Clone(); - options.ConversationId = conversationId; - } - } - - /// - /// Processes the function calls in the list. - /// - /// The current chat contents, inclusive of the function call contents being processed. - /// The options used for the response being processed. - /// The function call contents representing the functions to be invoked. - /// The iteration number of how many roundtrips have been made to the inner client. - /// The number of consecutive iterations, prior to this one, that were recorded as having function invocation errors. - /// Whether the function calls are being processed in a streaming context. - /// The to monitor for cancellation requests. - /// A value indicating how the caller should proceed. - private async Task<(bool ShouldTerminate, int NewConsecutiveErrorCount, IList MessagesAdded)> ProcessFunctionCallsAsync( - List messages, ChatOptions? options, List functionCallContents, int iteration, int consecutiveErrorCount, - bool isStreaming, CancellationToken cancellationToken) - { - // We must add a response for every tool call, regardless of whether we successfully executed it or not. - // If we successfully execute it, we'll add the result. If we don't, we'll add an error. - - Debug.Assert(functionCallContents.Count > 0, "Expected at least one function call."); - var shouldTerminate = false; - var captureCurrentIterationExceptions = consecutiveErrorCount < _maximumConsecutiveErrorsPerRequest; - - // Process all functions. If there's more than one and concurrent invocation is enabled, do so in parallel. - if (functionCallContents.Count == 1) - { - FunctionInvocationResult result = await ProcessFunctionCallAsync( - messages, options, functionCallContents, - iteration, 0, captureCurrentIterationExceptions, isStreaming, cancellationToken); - - IList addedMessages = CreateResponseMessages([result]); - ThrowIfNoFunctionResultsAdded(addedMessages); - UpdateConsecutiveErrorCountOrThrow(addedMessages, ref consecutiveErrorCount); - messages.AddRange(addedMessages); - - return (result.Terminate, consecutiveErrorCount, addedMessages); - } - else - { - List results = []; - - if (AllowConcurrentInvocation) - { - // Rather than awaiting each function before invoking the next, invoke all of them - // and then await all of them. We avoid forcibly introducing parallelism via Task.Run, - // but if a function invocation completes asynchronously, its processing can overlap - // with the processing of other the other invocation invocations. - results.AddRange(await Task.WhenAll( - from callIndex in Enumerable.Range(0, functionCallContents.Count) - select ProcessFunctionCallAsync( - messages, options, functionCallContents, - iteration, callIndex, captureExceptions: true, isStreaming, cancellationToken))); - - shouldTerminate = results.Any(r => r.Terminate); - } - else - { - // Invoke each function serially. - for (int callIndex = 0; callIndex < functionCallContents.Count; callIndex++) - { - var functionResult = await ProcessFunctionCallAsync( - messages, options, functionCallContents, - iteration, callIndex, captureCurrentIterationExceptions, isStreaming, cancellationToken); - - results.Add(functionResult); - - // If any function requested termination, we should stop right away. - if (functionResult.Terminate) - { - shouldTerminate = true; - break; - } - } - } - - IList addedMessages = CreateResponseMessages(results.ToArray()); - ThrowIfNoFunctionResultsAdded(addedMessages); - UpdateConsecutiveErrorCountOrThrow(addedMessages, ref consecutiveErrorCount); - messages.AddRange(addedMessages); - - return (shouldTerminate, consecutiveErrorCount, addedMessages); - } - } - -#pragma warning disable CA1851 // Possible multiple enumerations of 'IEnumerable' collection - /// - /// Updates the consecutive error count, and throws an exception if the count exceeds the maximum. - /// - /// Added messages. - /// Consecutive error count. - /// Thrown if the maximum consecutive error count is exceeded. - private void UpdateConsecutiveErrorCountOrThrow(IList added, ref int consecutiveErrorCount) - { - var allExceptions = added.SelectMany(m => m.Contents.OfType()) - .Select(frc => frc.Exception!) - .Where(e => e is not null); - - if (allExceptions.Any()) - { - consecutiveErrorCount++; - if (consecutiveErrorCount > _maximumConsecutiveErrorsPerRequest) - { - var allExceptionsArray = allExceptions.ToArray(); - if (allExceptionsArray.Length == 1) - { - ExceptionDispatchInfo.Capture(allExceptionsArray[0]).Throw(); - } - else - { - throw new AggregateException(allExceptionsArray); - } - } - } - else - { - consecutiveErrorCount = 0; - } - } -#pragma warning restore CA1851 - - /// - /// Throws an exception if doesn't create any messages. - /// - private void ThrowIfNoFunctionResultsAdded(IList? messages) - { - if (messages is null || messages.Count == 0) - { - Throw.InvalidOperationException($"{GetType().Name}.{nameof(CreateResponseMessages)} returned null or an empty collection of messages."); - } - } - - /// Processes the function call described in []. - /// The current chat contents, inclusive of the function call contents being processed. - /// The options used for the response being processed. - /// The function call contents representing all the functions being invoked. - /// The iteration number of how many roundtrips have been made to the inner client. - /// The 0-based index of the function being called out of . - /// If true, handles function-invocation exceptions by returning a value with . Otherwise, rethrows. - /// Whether the function calls are being processed in a streaming context. - /// The to monitor for cancellation requests. - /// A value indicating how the caller should proceed. - private async Task ProcessFunctionCallAsync( - List messages, ChatOptions? options, List callContents, - int iteration, int functionCallIndex, bool captureExceptions, bool isStreaming, CancellationToken cancellationToken) - { - var callContent = callContents[functionCallIndex]; - - // Look up the AIFunction for the function call. If the requested function isn't available, send back an error. - AIFunction? aiFunction = FindAIFunction(options?.Tools, callContent.Name) ?? FindAIFunction(AdditionalTools, callContent.Name); - if (aiFunction is null) - { - return new(terminate: false, FunctionInvocationStatus.NotFound, callContent, result: null, exception: null); - } - - FunctionInvocationContext context = new() - { - Function = aiFunction, - Arguments = new(callContent.Arguments) { Services = FunctionInvocationServices }, - Messages = messages, - Options = options, - CallContent = callContent, - Iteration = iteration, - FunctionCallIndex = functionCallIndex, - FunctionCount = callContents.Count, - IsStreaming = isStreaming - }; - - object? result; - try - { - result = await InstrumentedInvokeFunctionAsync(context, cancellationToken); - } - catch (Exception e) when (!cancellationToken.IsCancellationRequested) - { - if (!captureExceptions) - { - throw; - } - - return new( - terminate: false, - FunctionInvocationStatus.Exception, - callContent, - result: null, - exception: e); - } - - return new( - terminate: context.Terminate, - FunctionInvocationStatus.RanToCompletion, - callContent, - result, - exception: null); - - static AIFunction? FindAIFunction(IList? tools, string functionName) - { - if (tools is not null) - { - int count = tools.Count; - for (int i = 0; i < count; i++) - { - if (tools[i] is AIFunction function && function.Name == functionName) - { - return function; - } - } - } - - return null; - } - } - - /// Creates one or more response messages for function invocation results. - /// Information about the function call invocations and results. - /// A list of all chat messages created from . - protected virtual IList CreateResponseMessages( - ReadOnlySpan results) - { - var contents = new List(results.Length); - for (int i = 0; i < results.Length; i++) - { - contents.Add(CreateFunctionResultContent(results[i])); - } - - return [new(ChatRole.Tool, contents)]; - - FunctionResultContent CreateFunctionResultContent(FunctionInvocationResult result) - { - _ = Throw.IfNull(result); - - object? functionResult; - if (result.Status == FunctionInvocationStatus.RanToCompletion) - { - functionResult = result.Result ?? "Success: Function completed."; - } - else - { - string message = result.Status switch - { - FunctionInvocationStatus.NotFound => $"Error: Requested function \"{result.CallContent.Name}\" not found.", - FunctionInvocationStatus.Exception => "Error: Function failed.", - _ => "Error: Unknown error.", - }; - - if (IncludeDetailedErrors && result.Exception is not null) - { - message = $"{message} Exception: {result.Exception.Message}"; - } - - functionResult = message; - } - - return new FunctionResultContent(result.CallContent.CallId, functionResult) { Exception = result.Exception }; - } - } - - /// Invokes the function asynchronously. - /// - /// The function invocation context detailing the function to be invoked and its arguments along with additional request information. - /// - /// The to monitor for cancellation requests. The default is . - /// The result of the function invocation, or if the function invocation returned . - /// is . - private async Task InstrumentedInvokeFunctionAsync(FunctionInvocationContext context, CancellationToken cancellationToken) - { - _ = Throw.IfNull(context); - - using Activity? activity = _activitySource?.StartActivity( - $"{OpenTelemetryConsts.GenAI.ExecuteTool} {context.Function.Name}", - ActivityKind.Internal, - default(ActivityContext), - [ - new(OpenTelemetryConsts.GenAI.Operation.Name, "execute_tool"), - new(OpenTelemetryConsts.GenAI.Tool.Call.Id, context.CallContent.CallId), - new(OpenTelemetryConsts.GenAI.Tool.Name, context.Function.Name), - new(OpenTelemetryConsts.GenAI.Tool.Description, context.Function.Description), - ]); - - long startingTimestamp = 0; - if (_logger.IsEnabled(LogLevel.Debug)) - { - startingTimestamp = Stopwatch.GetTimestamp(); - if (_logger.IsEnabled(LogLevel.Trace)) - { - LogInvokingSensitive(context.Function.Name, LoggingHelpers.AsJson(context.Arguments, context.Function.JsonSerializerOptions)); - } - else - { - LogInvoking(context.Function.Name); - } - } - - object? result = null; - try - { - CurrentContext = context; // doesn't need to be explicitly reset after, as that's handled automatically at async method exit - result = await InvokeFunctionAsync(context, cancellationToken); - } - catch (Exception e) - { - if (activity is not null) - { - _ = activity.SetTag("error.type", e.GetType().FullName) - .SetStatus(ActivityStatusCode.Error, e.Message); - } - - if (e is OperationCanceledException) - { - LogInvocationCanceled(context.Function.Name); - } - else - { - LogInvocationFailed(context.Function.Name, e); - } - - throw; - } - finally - { - if (_logger.IsEnabled(LogLevel.Debug)) - { - TimeSpan elapsed = GetElapsedTime(startingTimestamp); - - if (result is not null && _logger.IsEnabled(LogLevel.Trace)) - { - LogInvocationCompletedSensitive(context.Function.Name, elapsed, LoggingHelpers.AsJson(result, context.Function.JsonSerializerOptions)); - } - else - { - LogInvocationCompleted(context.Function.Name, elapsed); - } - } - } - - return result; - } - - /// This method will invoke the function within the try block. - /// The function invocation context. - /// Cancellation token. - /// The function result. - protected virtual ValueTask InvokeFunctionAsync(FunctionInvocationContext context, CancellationToken cancellationToken) - { - _ = Throw.IfNull(context); - - return FunctionInvoker is { } invoker ? - invoker(context, cancellationToken) : - context.Function.InvokeAsync(context.Arguments, cancellationToken); - } - - /// - /// 1. Remove all and from the . - /// 2. Recreate for any that haven't been executed yet. - /// 3. Genreate failed for any rejected . - /// 4. add all the new content items to and return them as the pre-invocation history. - /// - private static (List? preDownstreamCallHistory, List? approvals) ProcessFunctionApprovalResponses( - List originalMessages, bool hasConversationId, string? toolResponseId, string? functionCallContentFallbackMessageId) - { - // Extract any approval responses where we need to execute or reject the function calls. - // The original messages are also modified to remove all approval requests and responses. - var notInvokedResponses = ExtractAndRemoveApprovalRequestsAndResponses(originalMessages); - - // Wrap the function call content in message(s). - ICollection? allPreDownstreamCallMessages = ConvertToFunctionCallContentMessages( - [.. notInvokedResponses.rejections ?? [], .. notInvokedResponses.approvals ?? []], functionCallContentFallbackMessageId); - - // Generate failed function result contents for any rejected requests and wrap it in a message. - List? rejectedFunctionCallResults = GenerateRejectedFunctionResults(notInvokedResponses.rejections, toolResponseId); - ChatMessage? rejectedPreDownstreamCallResultsMessage = rejectedFunctionCallResults != null ? - new ChatMessage(ChatRole.Tool, rejectedFunctionCallResults) { MessageId = toolResponseId } : - null; - - // Add all the FCC that we generated to the pre-downstream-call history so that they can be returned to the caller as part of the next response. - // Also, if we are not dealing with a service thread (i.e. we don't have a conversation ID), add them - // into the original messages list so that they are passed to the inner client and can be used to generate a result. - List? preDownstreamCallHistory = null; - if (allPreDownstreamCallMessages is not null) - { - preDownstreamCallHistory ??= []; - foreach (var message in allPreDownstreamCallMessages) - { - preDownstreamCallHistory.Add(message); - if (!hasConversationId) - { - originalMessages.Add(message); - } - } - } - - // Add all the FRC that we generated to the pre-downstream-call history so that they can be returned to the caller as part of the next response. - // Also, add them into the original messages list so that they are passed to the inner client and can be used to generate a result. - if (rejectedPreDownstreamCallResultsMessage is not null) - { - preDownstreamCallHistory ??= []; - originalMessages.Add(rejectedPreDownstreamCallResultsMessage); - preDownstreamCallHistory.Add(rejectedPreDownstreamCallResultsMessage); - } - - return (preDownstreamCallHistory, notInvokedResponses.approvals); - } - - /// - /// Execute the provided and return the resulting . - /// - private async Task<(IList? FunctionResultContent, bool ShouldTerminate, int ConsecutiveErrorCount)> InvokeApprovedFunctionApprovalResponses( - List? notInvokedApprovals, List originalMessages, ChatOptions? options, int consecutiveErrorCount, bool isStreaming, CancellationToken cancellationToken) - { - // Check if there are any function calls to do for any approved functions and execute them. - if (notInvokedApprovals is { Count: > 0 }) - { - // The FRC that is generated here is already added to originalMessages by ProcessFunctionCallsAsync. - var modeAndMessages = await ProcessFunctionCallsAsync(originalMessages, options, notInvokedApprovals.Select(x => x.Response.FunctionCall).ToList(), 0, consecutiveErrorCount, isStreaming, cancellationToken); - consecutiveErrorCount = modeAndMessages.NewConsecutiveErrorCount; - - return (modeAndMessages.MessagesAdded, modeAndMessages.ShouldTerminate, consecutiveErrorCount); - } - - return (null, false, consecutiveErrorCount); - } - - /// - /// This method extracts the approval requests and responses from the provided list of messages, validates them, filters them to ones that require execution and splits them into approved and rejected. - /// - /// - /// 1st iteration: over all messages and content - /// ===== - /// Build a list of all function call ids that are already executed. - /// Build a list of all function approval requests and responses. - /// Build a list of the content we want to keep (everything except approval requests and responses) and create a new list of messages for those. - /// Validate that we have an approval response for each approval request. - /// - /// 2nd iteration: over all approval responses - /// ===== - /// Filter out any approval responses that already have a matching function result (i.e. already executed). - /// Find the matching function approval request for any response (where available). - /// Split the approval responses into two lists: approved and rejected, with their request messages (where available). - /// - /// We return the messages containing the approval requests since these are the same messages that originally contained the FunctionCallContent from the downstream service. - /// We can then use the metadata from these messages when we re-create the FunctionCallContent messages/updates to return to the caller. This way, when we finally do return - /// the FuncionCallContent to users it's part of a message/update that contains the same metadata as originally returned to the downstream service. - /// - private static (List? approvals, List? rejections) ExtractAndRemoveApprovalRequestsAndResponses(List messages) - { - Dictionary? allApprovalRequestsMessages = null; - List? allApprovalResponses = null; - HashSet? approvalRequestCallIds = null; - HashSet? functionResultCallIds = null; - - for (int i = 0; i < messages.Count; i++) - { - var message = messages[i]; - - List? keptContents = null; - - // Find contents we want to keep. - for (int j = 0; j < message.Contents.Count; j++) - { - var content = message.Contents[j]; - - // Maintain a list of function calls that have already been executed, so we can avoid executing them a second time. - if (content is FunctionResultContent functionResultContent) - { - functionResultCallIds ??= []; - functionResultCallIds.Add(functionResultContent.CallId); - } - - // Validation: Capture each call id for each approval request so that we can ensure that we have a matching response later. - if (content is FunctionApprovalRequestContent request_) - { - approvalRequestCallIds ??= []; - approvalRequestCallIds.Add(request_.FunctionCall.CallId); - } - - // Validation: Remove the call id for each approval response, to check it off the list of requests we need responses for. - if (content is FunctionApprovalResponseContent response_ && approvalRequestCallIds is not null) - { - approvalRequestCallIds.Remove(response_.FunctionCall.CallId); - } - - // Build the list of requets and responses and keep them out of the updated message list - // since they will be handled in this class, and don't need to be passed further down the stack. - if (content is FunctionApprovalRequestContent approvalRequest) - { - allApprovalRequestsMessages ??= new Dictionary(); - allApprovalRequestsMessages.Add(approvalRequest.Id, message); - continue; - } - - if (content is FunctionApprovalResponseContent approvalResponse) - { - allApprovalResponses ??= []; - allApprovalResponses.Add(approvalResponse); - continue; - } - - // If we get to here, we should have just the contents that we want to keep. - keptContents ??= []; - keptContents.Add(content); - } - - if (message.Contents.Count > 0 && keptContents?.Count != message.Contents.Count) - { - if (keptContents is null || keptContents.Count == 0) - { - // If we have no contents left after filtering, we can remove the message. - messages.RemoveAt(i); - i--; // Adjust index since we removed an item. - continue; - } - - // If we have any contents left after filtering, we can keep the message with the new remaining content. - var newMessage = message.Clone(); - newMessage.Contents = keptContents; - messages[i] = newMessage; - } - } - - // Validation: If we got an approval for each request, we should have no call ids left. - if (approvalRequestCallIds?.Count is > 0) - { - Throw.InvalidOperationException($"FunctionApprovalRequestContent found with FunctionCall.CallId(s) '{string.Join(", ", approvalRequestCallIds)}' that have no matching FunctionApprovalResponseContent."); - } - - List? approvedFunctionCalls = null; - List? rejectedFunctionCalls = null; - - for (int i = 0; i < (allApprovalResponses?.Count ?? 0); i++) - { - var approvalResponse = allApprovalResponses![i]; - - // Skip any approval responses that have already been executed. - if (functionResultCallIds?.Contains(approvalResponse.FunctionCall.CallId) is not true) - { - ChatMessage? requestMessage = null; - allApprovalRequestsMessages?.TryGetValue(approvalResponse.FunctionCall.CallId, out requestMessage); - - // Split the responses into approved and rejected. - if (approvalResponse.Approved) - { - approvedFunctionCalls ??= []; - approvedFunctionCalls.Add(new ApprovalResultWithRequestMessage { Response = approvalResponse, RequestMessage = requestMessage }); - } - else - { - rejectedFunctionCalls ??= []; - rejectedFunctionCalls.Add(new ApprovalResultWithRequestMessage { Response = approvalResponse, RequestMessage = requestMessage }); - } - } - } - - return (approvedFunctionCalls, rejectedFunctionCalls); - } - - /// - /// If we have any rejected approval responses, we need to generate failed function results for them. - /// - /// Any rejected approval responses. - /// The message id to use for the tool response. - /// The for the rejected function calls. - private static List? GenerateRejectedFunctionResults( - List? rejections, - string? toolResponseId) - { - List? functionResultContent = null; - - if (rejections is { Count: > 0 }) - { - functionResultContent = []; - - foreach (var rejectedCall in rejections) - { - // Create a FunctionResultContent for the rejected function call. - var functionResult = new FunctionResultContent(rejectedCall.Response.FunctionCall.CallId, "Error: Function invocation approval was not granted."); - functionResultContent.Add(functionResult); - } - } - - return functionResultContent; - } - - /// - /// Extracts the from the provided to recreate the original function call messages. - /// The output messages tries to mimic the original messages that contained the , e.g. if the had been split into separate messages, - /// this method will recreate similarly split messages, each with their own . - /// -#pragma warning disable CA1859 // Use concrete types when possible for improved performance - private static ICollection? ConvertToFunctionCallContentMessages(IEnumerable? resultWithRequestMessages, string? fallbackMessageId) -#pragma warning restore CA1859 // Use concrete types when possible for improved performance - { - if (resultWithRequestMessages is not null) - { - ChatMessage? currentMessage = null; - Dictionary? messagesById = null; - - foreach (var resultWithRequestMessage in resultWithRequestMessages) - { - if (currentMessage is not null && messagesById is null // Don't need to create a dictionary on the first iteration or if we alrady have one. - && !(resultWithRequestMessage.RequestMessage is null && currentMessage.MessageId == fallbackMessageId) // Everywhere we have no RequestMessage we use the fallbackMessageId, so in this case there is only one message. - && (resultWithRequestMessage.RequestMessage is not null && currentMessage.MessageId != resultWithRequestMessage.RequestMessage?.MessageId)) // Where we do have a RequestMessage, we can check if its message id differs from the current one. - { - // The majority of the time, all FCC would be part of a single message, so no need to create a dictionary for this case. - // If we are dealing with multiple messages though, we need to keep track of them by their message ID. - messagesById ??= new(); - messagesById[currentMessage.MessageId ?? string.Empty] = currentMessage; - } - - if (messagesById is not null) - { - messagesById.TryGetValue(resultWithRequestMessage.RequestMessage?.MessageId ?? string.Empty, out currentMessage); - } - - if (currentMessage is null) - { - currentMessage = ConvertToFunctionCallContentMessage(resultWithRequestMessage, fallbackMessageId); - } - else - { - currentMessage.Contents.Add(resultWithRequestMessage.Response.FunctionCall); - } - - if (messagesById is not null) - { - messagesById[currentMessage.MessageId ?? string.Empty] = currentMessage; - } - } - - return messagesById?.Values as ICollection ?? (currentMessage != null ? [currentMessage!] : null); - } - - return null; - } - - /// - /// Takes the from the and wraps it in a - /// using the same message id that the was originally returned with from the downstream . - /// - private static ChatMessage ConvertToFunctionCallContentMessage(ApprovalResultWithRequestMessage resultWithRequestMessage, string? fallbackMessageId) - { - if (resultWithRequestMessage.RequestMessage is not null) - { - var functionCallMessage = resultWithRequestMessage.RequestMessage.Clone(); - functionCallMessage.Contents = [resultWithRequestMessage.Response.FunctionCall]; - functionCallMessage.MessageId ??= fallbackMessageId; - return functionCallMessage; - } - - return new ChatMessage(ChatRole.Assistant, [resultWithRequestMessage.Response.FunctionCall]) { MessageId = fallbackMessageId }; - } - - /// - /// Check if any of the provided require approval. - /// Supports checking from a provided index up to the end of the list, to allow efficient incremental checking - /// when streaming. - /// - private static async Task<(bool hasApprovalRequiringFcc, int lastApprovalCheckedFCCIndex)> CheckForApprovalRequiringFCCAsync( - List? functionCallContents, - ApprovalRequiredAIFunction[] approvalRequiredFunctions, - bool hasApprovalRequiringFcc, - int lastApprovalCheckedFCCIndex) - { - // If we already found an approval requiring FCC, we can skip checking the rest. - if (hasApprovalRequiringFcc) - { - return (true, functionCallContents?.Count ?? 0); - } - - for (; lastApprovalCheckedFCCIndex < (functionCallContents?.Count ?? 0); lastApprovalCheckedFCCIndex++) - { - var fcc = functionCallContents![lastApprovalCheckedFCCIndex]; - if (approvalRequiredFunctions.FirstOrDefault(y => y.Name == fcc.Name) is ApprovalRequiredAIFunction approvalFunction && - await approvalFunction.RequiresApprovalCallback(new(fcc))) - { - hasApprovalRequiringFcc |= true; - } - } - - return (hasApprovalRequiringFcc, lastApprovalCheckedFCCIndex); - } - - /// - /// Replaces all with and ouputs a new list if any of them were replaced. - /// - /// true if any was replaced, false otherwise. - private static bool TryReplaceFunctionCallsWithApprovalRequests(IList content, out IList? updatedContent) - { - updatedContent = null; - - if (content is { Count: > 0 }) - { - for (int i = 0; i < content.Count; i++) - { - if (content[i] is FunctionCallContent fcc) - { - updatedContent ??= [.. content]; // Clone the list if we haven't already - var approvalRequest = new FunctionApprovalRequestContent(fcc.CallId, fcc); - updatedContent[i] = approvalRequest; - } - } - } - - return updatedContent is not null; - } - - /// - /// Replaces all from with - /// if any one of them requires approval. - /// - private static async Task> ReplaceFunctionCallsWithApprovalRequests(IList messages, IList? requestOptionsTools, IList? additionalTools) - { - var outputMessages = messages; - ApprovalRequiredAIFunction[]? approvalRequiredFunctions = null; - - bool anyApprovalRequired = false; - List<(int, int)>? allFunctionCallContentIndices = null; - - // Build a list of the indices of all FunctionCallContent items. - // Also check if any of them require approval. - for (int i = 0; i < messages.Count; i++) - { - var content = messages[i].Contents; - for (int j = 0; j < content.Count; j++) - { - if (content[j] is FunctionCallContent functionCall) - { - allFunctionCallContentIndices ??= []; - allFunctionCallContentIndices.Add((i, j)); - - approvalRequiredFunctions ??= (requestOptionsTools ?? []).Concat(additionalTools ?? []) - .OfType() - .ToArray(); - - anyApprovalRequired |= approvalRequiredFunctions.FirstOrDefault(x => x.Name == functionCall.Name) is { } approvalFunction && await approvalFunction.RequiresApprovalCallback(new(functionCall)); - } - } - } - - // If any function calls were found, and any of them required approval, we should replace all of them with approval requests. - // This is because we do not have a way to deal with cases where some function calls require approval and others do not, so we just replace all of them. - if (allFunctionCallContentIndices is not null && anyApprovalRequired) - { - // Clone the list so, we don't mutate the input. - outputMessages = [.. messages]; - int lastMessageIndex = -1; - - foreach (var (messageIndex, contentIndex) in allFunctionCallContentIndices) - { - // Clone the message if we didn't already clone it in a previous iteration. - var message = lastMessageIndex != messageIndex ? outputMessages[messageIndex].Clone() : outputMessages[messageIndex]; - message.Contents = [.. message.Contents]; - - var functionCall = (FunctionCallContent)message.Contents[contentIndex]; - message.Contents[contentIndex] = new FunctionApprovalRequestContent(functionCall.CallId, functionCall); - outputMessages[messageIndex] = message; - - lastMessageIndex = messageIndex; - } - } - - return outputMessages; - } - - /// - /// Insert the given at the start of the . - /// - private static IList UpdateResponseMessagesWithPreDownstreamCallHistory(IList responseMessages, List? preDownstreamCallHistory) - { - if (preDownstreamCallHistory?.Count > 0) - { - // Since these messages are pre-invocation, we want to insert them at the start of the response messages. - return [.. preDownstreamCallHistory, .. responseMessages]; - } - - return responseMessages; - } - - private static ChatResponseUpdate ConvertToolResultMessageToUpdate(ChatMessage message, string? conversationId, string? messageId) - { - return new() - { - AdditionalProperties = message.AdditionalProperties, - AuthorName = message.AuthorName, - ConversationId = conversationId, - CreatedAt = DateTimeOffset.UtcNow, - Contents = message.Contents, - RawRepresentation = message.RawRepresentation, - ResponseId = messageId, - MessageId = messageId, - Role = message.Role, - }; - } - - private static TimeSpan GetElapsedTime(long startingTimestamp) => -#if NET - Stopwatch.GetElapsedTime(startingTimestamp); -#else - new((long)((Stopwatch.GetTimestamp() - startingTimestamp) * ((double)TimeSpan.TicksPerSecond / Stopwatch.Frequency))); -#endif - - [LoggerMessage(LogLevel.Debug, "Invoking {MethodName}.", SkipEnabledCheck = true)] - private partial void LogInvoking(string methodName); - - [LoggerMessage(LogLevel.Trace, "Invoking {MethodName}({Arguments}).", SkipEnabledCheck = true)] - private partial void LogInvokingSensitive(string methodName, string arguments); - - [LoggerMessage(LogLevel.Debug, "{MethodName} invocation completed. Duration: {Duration}", SkipEnabledCheck = true)] - private partial void LogInvocationCompleted(string methodName, TimeSpan duration); - - [LoggerMessage(LogLevel.Trace, "{MethodName} invocation completed. Duration: {Duration}. Result: {Result}", SkipEnabledCheck = true)] - private partial void LogInvocationCompletedSensitive(string methodName, TimeSpan duration, string result); - - [LoggerMessage(LogLevel.Debug, "{MethodName} invocation canceled.")] - private partial void LogInvocationCanceled(string methodName); - - [LoggerMessage(LogLevel.Error, "{MethodName} invocation failed.")] - private partial void LogInvocationFailed(string methodName, Exception error); - - /// Provides information about the invocation of a function call. - public sealed class FunctionInvocationResult - { - /// - /// Initializes a new instance of the class. - /// - /// Indicates whether the caller should terminate the processing loop. - /// Indicates the status of the function invocation. - /// Contains information about the function call. - /// The result of the function call. - /// The exception thrown by the function call, if any. - internal FunctionInvocationResult(bool terminate, FunctionInvocationStatus status, FunctionCallContent callContent, object? result, Exception? exception) - { - Terminate = terminate; - Status = status; - CallContent = callContent; - Result = result; - Exception = exception; - } - - /// Gets status about how the function invocation completed. - public FunctionInvocationStatus Status { get; } - - /// Gets the function call content information associated with this invocation. - public FunctionCallContent CallContent { get; } - - /// Gets the result of the function call. - public object? Result { get; } - - /// Gets any exception the function call threw. - public Exception? Exception { get; } - - /// Gets a value indicating whether the caller should terminate the processing loop. - public bool Terminate { get; } - } - - /// Provides error codes for when errors occur as part of the function calling loop. - public enum FunctionInvocationStatus - { - /// The operation completed successfully. - RanToCompletion, - - /// The requested function could not be found. - NotFound, - - /// The function call failed with an exception. - Exception, - } - - private struct ApprovalResultWithRequestMessage - { - public FunctionApprovalResponseContent Response { get; set; } - public ChatMessage? RequestMessage { get; set; } - } -} diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/OpenTelemetryConsts.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/OpenTelemetryConsts.cs deleted file mode 100644 index 89eebe9f61..0000000000 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/MEAI/OpenTelemetryConsts.cs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -// WARNING: -// This class has been temporarily copied here from MEAI, to allow prototyping -// functionality that will be moved to MEAI in the future. -// This file is not intended to be modified. - -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.Extensions.AI; - -#pragma warning disable CA1716 // Identifiers should not match keywords -#pragma warning disable S4041 // Type names should not match namespaces - -/// Provides constants used by various telemetry services. -[ExcludeFromCodeCoverage] -internal static class OpenTelemetryConsts -{ - public const string DefaultSourceName = "Experimental.Microsoft.Extensions.AI"; - - public const string SecondsUnit = "s"; - public const string TokensUnit = "token"; - - public static class Event - { - public const string Name = "event.name"; - } - - public static class Error - { - public const string Type = "error.type"; - } - - public static class GenAI - { - public const string Choice = "gen_ai.choice"; - public const string SystemName = "gen_ai.system"; - - public const string Chat = "chat"; - public const string Embeddings = "embeddings"; - public const string ExecuteTool = "execute_tool"; - - public static class Assistant - { - public const string Message = "gen_ai.assistant.message"; - } - - public static class Client - { - public static class OperationDuration - { - public const string Description = "Measures the duration of a GenAI operation"; - public const string Name = "gen_ai.client.operation.duration"; - public static readonly double[] ExplicitBucketBoundaries = [0.01, 0.02, 0.04, 0.08, 0.16, 0.32, 0.64, 1.28, 2.56, 5.12, 10.24, 20.48, 40.96, 81.92]; - } - - public static class TokenUsage - { - public const string Description = "Measures number of input and output tokens used"; - public const string Name = "gen_ai.client.token.usage"; - public static readonly int[] ExplicitBucketBoundaries = [1, 4, 16, 64, 256, 1_024, 4_096, 16_384, 65_536, 262_144, 1_048_576, 4_194_304, 16_777_216, 67_108_864]; - } - } - - public static class Conversation - { - public const string Id = "gen_ai.conversation.id"; - } - - public static class Operation - { - public const string Name = "gen_ai.operation.name"; - } - - public static class Output - { - public const string Type = "gen_ai.output.type"; - } - - public static class Request - { - public const string EmbeddingDimensions = "gen_ai.request.embedding.dimensions"; - public const string FrequencyPenalty = "gen_ai.request.frequency_penalty"; - public const string Model = "gen_ai.request.model"; - public const string MaxTokens = "gen_ai.request.max_tokens"; - public const string PresencePenalty = "gen_ai.request.presence_penalty"; - public const string Seed = "gen_ai.request.seed"; - public const string StopSequences = "gen_ai.request.stop_sequences"; - public const string Temperature = "gen_ai.request.temperature"; - public const string TopK = "gen_ai.request.top_k"; - public const string TopP = "gen_ai.request.top_p"; - - public static string PerProvider(string providerName, string parameterName) => $"gen_ai.{providerName}.request.{parameterName}"; - } - - public static class Response - { - public const string FinishReasons = "gen_ai.response.finish_reasons"; - public const string Id = "gen_ai.response.id"; - public const string Model = "gen_ai.response.model"; - - public static string PerProvider(string providerName, string parameterName) => $"gen_ai.{providerName}.response.{parameterName}"; - } - - public static class System - { - public const string Message = "gen_ai.system.message"; - } - - public static class Token - { - public const string Type = "gen_ai.token.type"; - } - - public static class Tool - { - public const string Name = "gen_ai.tool.name"; - public const string Description = "gen_ai.tool.description"; - public const string Message = "gen_ai.tool.message"; - - public static class Call - { - public const string Id = "gen_ai.tool.call.id"; - } - } - - public static class Usage - { - public const string InputTokens = "gen_ai.usage.input_tokens"; - public const string OutputTokens = "gen_ai.usage.output_tokens"; - } - - public static class User - { - public const string Message = "gen_ai.user.message"; - } - } - - public static class Server - { - public const string Address = "server.address"; - public const string Port = "server.port"; - } -} diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/Microsoft.Extensions.AI.Agents.csproj b/dotnet/src/Microsoft.Extensions.AI.Agents/Microsoft.Extensions.AI.Agents.csproj index b7f37ba4b4..0548b595e2 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/Microsoft.Extensions.AI.Agents.csproj +++ b/dotnet/src/Microsoft.Extensions.AI.Agents/Microsoft.Extensions.AI.Agents.csproj @@ -4,6 +4,7 @@ $(ProjectsTargetFrameworks) $(ProjectsDebugTargetFrameworks) alpha + $(NoWarn);MEAI001 diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests.csproj b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests.csproj index 38ae8712cb..ee3bc2d9fb 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests.csproj +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests.csproj @@ -2,6 +2,7 @@ $(ProjectsTargetFrameworks) + $(NoWarn);MEAI001 diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/MEAI/AssertExtensions.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/MEAI/AssertExtensions.cs deleted file mode 100644 index da95df83d0..0000000000 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/MEAI/AssertExtensions.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -// WARNING: -// This class has been temporarily copied here from MEAI, to allow prototyping -// functionality that will be moved to MEAI in the future. -// This file is not intended to be modified. - -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; -using System.Text.Json.Nodes; -using Xunit.Sdk; - -namespace Microsoft.Extensions.AI; - -internal static class AssertExtensions -{ - /// - /// Asserts that the two function call parameters are equal, up to JSON equivalence. - /// - public static void EqualFunctionCallParameters( - IDictionary? expected, - IDictionary? actual, - JsonSerializerOptions? options = null) - { - if (expected is null || actual is null) - { - Assert.Equal(expected, actual); - return; - } - - foreach (var expectedEntry in expected) - { - if (!actual.TryGetValue(expectedEntry.Key, out object? actualValue)) - { - throw new XunitException($"Expected parameter '{expectedEntry.Key}' not found in actual value."); - } - - AreJsonEquivalentValues(expectedEntry.Value, actualValue, options, propertyName: expectedEntry.Key); - } - - if (expected.Count != actual.Count) - { - var extraParameters = actual - .Where(e => !expected.ContainsKey(e.Key)) - .Select(e => $"'{e.Key}'") - .First(); - - throw new XunitException($"Actual value contains additional parameters {string.Join(", ", extraParameters)} not found in expected value."); - } - } - - /// - /// Asserts that the two function call results are equal, up to JSON equivalence. - /// - public static void EqualFunctionCallResults(object? expected, object? actual, JsonSerializerOptions? options = null) - => AreJsonEquivalentValues(expected, actual, options); - - /// - /// Asserts that the two JSON values are equal. - /// - public static void EqualJsonValues(JsonElement expectedJson, JsonElement actualJson, string? propertyName = null) - { - if (!JsonNode.DeepEquals( - JsonSerializer.SerializeToNode(expectedJson, AIJsonUtilities.DefaultOptions), - JsonSerializer.SerializeToNode(actualJson, AIJsonUtilities.DefaultOptions))) - { - string message = propertyName is null - ? $"JSON result does not match expected JSON.\r\nExpected: {expectedJson.GetRawText()}\r\nActual: {actualJson.GetRawText()}" - : $"Parameter '{propertyName}' does not match expected JSON.\r\nExpected: {expectedJson.GetRawText()}\r\nActual: {actualJson.GetRawText()}"; - - throw new XunitException(message); - } - } - - private static void AreJsonEquivalentValues(object? expected, object? actual, JsonSerializerOptions? options, string? propertyName = null) - { - options ??= AIJsonUtilities.DefaultOptions; - JsonElement expectedElement = NormalizeToElement(expected, options); - JsonElement actualElement = NormalizeToElement(actual, options); - EqualJsonValues(expectedElement, actualElement, propertyName); - - static JsonElement NormalizeToElement(object? value, JsonSerializerOptions options) - => value is JsonElement e ? e : JsonSerializer.SerializeToElement(value, options); - } -} diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/MEAI/NewFunctionInvokingChatClientTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/MEAI/NewFunctionInvokingChatClientTests.cs deleted file mode 100644 index b03ac5fdd3..0000000000 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/MEAI/NewFunctionInvokingChatClientTests.cs +++ /dev/null @@ -1,931 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -// AF repo suppressions for code copied from MEAI. -#pragma warning disable CA5394 // Do not use insecure randomness -#pragma warning disable IDE0009 // Member access should be qualified. -#pragma warning disable IDE0039 // Use local function - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Extensions.AI; - -public class NewFunctionInvokingChatClientTests -{ - [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task AllFunctionCallsReplacedWithApprovalsWhenAllRequireApprovalAsync(bool useAdditionalTools) - { - AITool[] tools = - [ - new ApprovalRequiredAIFunction(AIFunctionFactory.Create(() => "Result 1", "Func1")), - new ApprovalRequiredAIFunction(AIFunctionFactory.Create((int i) => $"Result 2: {i}", "Func2")) { RequiresApprovalCallback = async (context) => context.FunctionCall.Name == "Func2" }, - ]; - - var options = new ChatOptions - { - Tools = useAdditionalTools ? null : tools - }; - - List input = - [ - new ChatMessage(ChatRole.User, "hello"), - ]; - - List downstreamClientOutput = - [ - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - ]; - - List expectedOutput = - [ - new ChatMessage(ChatRole.Assistant, - [ - new FunctionApprovalRequestContent("callId1", new FunctionCallContent("callId1", "Func1")), - new FunctionApprovalRequestContent("callId2", new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]) - ]; - - await InvokeAndAssertAsync(options, input, downstreamClientOutput, expectedOutput, additionalTools: useAdditionalTools ? tools : null); - - await InvokeAndAssertStreamingAsync(options, input, downstreamClientOutput, expectedOutput, additionalTools: useAdditionalTools ? tools : null); - } - - [Fact] - public async Task AllFunctionCallsReplacedWithApprovalsWhenAnyRequireApprovalAsync() - { - var options = new ChatOptions - { - Tools = - [ - new ApprovalRequiredAIFunction(AIFunctionFactory.Create(() => "Result 1", "Func1")), - AIFunctionFactory.Create((int i) => $"Result 2: {i}", "Func2"), - ] - }; - - List input = - [ - new ChatMessage(ChatRole.User, "hello"), - ]; - - List downstreamClientOutput = - [ - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - ]; - - List expectedOutput = - [ - new ChatMessage(ChatRole.Assistant, - [ - new FunctionApprovalRequestContent("callId1", new FunctionCallContent("callId1", "Func1")), - new FunctionApprovalRequestContent("callId2", new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]) - ]; - - await InvokeAndAssertAsync(options, input, downstreamClientOutput, expectedOutput); - - await InvokeAndAssertStreamingAsync(options, input, downstreamClientOutput, expectedOutput); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task AllFunctionCallsReplacedWithApprovalsWhenAnyRequestOrAdditionalRequireApprovalAsync(bool additionalToolsRequireApproval) - { - AIFunction func1 = AIFunctionFactory.Create(() => "Result 1", "Func1"); - AIFunction func2 = AIFunctionFactory.Create((int i) => $"Result 2: {i}", "Func2"); - AITool[] additionalTools = - [ - additionalToolsRequireApproval ? new ApprovalRequiredAIFunction(func1) : func1, - ]; - - var options = new ChatOptions - { - Tools = - [ - additionalToolsRequireApproval ? func2 : new ApprovalRequiredAIFunction(func2), - ] - }; - - List input = - [ - new ChatMessage(ChatRole.User, "hello"), - ]; - - List downstreamClientOutput = - [ - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - ]; - - List expectedOutput = - [ - new ChatMessage(ChatRole.Assistant, - [ - new FunctionApprovalRequestContent("callId1", new FunctionCallContent("callId1", "Func1")), - new FunctionApprovalRequestContent("callId2", new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]) - ]; - - await InvokeAndAssertAsync(options, input, downstreamClientOutput, expectedOutput, additionalTools: additionalTools); - - await InvokeAndAssertStreamingAsync(options, input, downstreamClientOutput, expectedOutput, additionalTools: additionalTools); - } - - [Fact] - public async Task ApprovedApprovalResponsesAreExecutedAsync() - { - var options = new ChatOptions - { - Tools = - [ - new ApprovalRequiredAIFunction(AIFunctionFactory.Create(() => "Result 1", "Func1")), - AIFunctionFactory.Create((int i) => $"Result 2: {i}", "Func2"), - ] - }; - - List input = - [ - new ChatMessage(ChatRole.User, "hello"), - new ChatMessage(ChatRole.Assistant, - [ - new FunctionApprovalRequestContent("callId1", new FunctionCallContent("callId1", "Func1")), - new FunctionApprovalRequestContent("callId2", new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]) { MessageId = "resp1" }, - new ChatMessage(ChatRole.User, - [ - new FunctionApprovalResponseContent("callId1", true, new FunctionCallContent("callId1", "Func1")), - new FunctionApprovalResponseContent("callId2", true, new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]), - ]; - - List expectedDownstreamClientInput = - [ - new ChatMessage(ChatRole.User, "hello"), - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Result 1"), new FunctionResultContent("callId2", result: "Result 2: 42")]), - ]; - - List downstreamClientOutput = - [ - new ChatMessage(ChatRole.Assistant, "world"), - ]; - - List output = - [ - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Result 1"), new FunctionResultContent("callId2", result: "Result 2: 42")]), - new ChatMessage(ChatRole.Assistant, "world"), - ]; - - await InvokeAndAssertAsync(options, input, downstreamClientOutput, output, expectedDownstreamClientInput); - - await InvokeAndAssertStreamingAsync(options, input, downstreamClientOutput, output, expectedDownstreamClientInput); - } - - [Fact] - public async Task ApprovedApprovalResponsesFromSeparateFCCMessagesAreExecutedAsync() - { - var options = new ChatOptions - { - Tools = - [ - new ApprovalRequiredAIFunction(AIFunctionFactory.Create(() => "Result 1", "Func1")), - AIFunctionFactory.Create((int i) => $"Result 2: {i}", "Func2"), - ] - }; - - List input = - [ - new ChatMessage(ChatRole.User, "hello"), - new ChatMessage(ChatRole.Assistant, - [ - new FunctionApprovalRequestContent("callId1", new FunctionCallContent("callId1", "Func1")), - ]) { MessageId = "resp1" }, - new ChatMessage(ChatRole.Assistant, - [ - new FunctionApprovalRequestContent("callId2", new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]) { MessageId = "resp2" }, - new ChatMessage(ChatRole.User, - [ - new FunctionApprovalResponseContent("callId1", true, new FunctionCallContent("callId1", "Func1")), - ]), - new ChatMessage(ChatRole.User, - [ - new FunctionApprovalResponseContent("callId2", true, new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]), - ]; - - List expectedDownstreamClientInput = - [ - new ChatMessage(ChatRole.User, "hello"), - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1")]) { MessageId = "resp1" }, - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]) { MessageId = "resp2" }, - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Result 1"), new FunctionResultContent("callId2", result: "Result 2: 42")]), - ]; - - List downstreamClientOutput = - [ - new ChatMessage(ChatRole.Assistant, "world"), - ]; - - List output = - [ - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1")]) { MessageId = "resp1" }, - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]) { MessageId = "resp2" }, - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Result 1"), new FunctionResultContent("callId2", result: "Result 2: 42")]), - new ChatMessage(ChatRole.Assistant, "world"), - ]; - - await InvokeAndAssertAsync(options, input, downstreamClientOutput, output, expectedDownstreamClientInput); - - await InvokeAndAssertStreamingAsync(options, input, downstreamClientOutput, output, expectedDownstreamClientInput); - } - - [Fact] - public async Task RejectedApprovalResponsesAreFailedAsync() - { - var options = new ChatOptions - { - Tools = - [ - new ApprovalRequiredAIFunction(AIFunctionFactory.Create(() => "Result 1", "Func1")), - AIFunctionFactory.Create((int i) => $"Result 2: {i}", "Func2"), - ] - }; - - List input = - [ - new ChatMessage(ChatRole.User, "hello"), - new ChatMessage(ChatRole.Assistant, - [ - new FunctionApprovalRequestContent("callId1", new FunctionCallContent("callId1", "Func1")), - new FunctionApprovalRequestContent("callId2", new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]) { MessageId = "resp1" }, - new ChatMessage(ChatRole.User, - [ - new FunctionApprovalResponseContent("callId1", false, new FunctionCallContent("callId1", "Func1")), - new FunctionApprovalResponseContent("callId2", false, new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]), - ]; - - List expectedDownstreamClientInput = - [ - new ChatMessage(ChatRole.User, "hello"), - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Error: Function invocation approval was not granted."), new FunctionResultContent("callId2", result: "Error: Function invocation approval was not granted.")]), - ]; - - List downstreamClientOutput = - [ - new ChatMessage(ChatRole.Assistant, "world"), - ]; - - List output = - [ - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Error: Function invocation approval was not granted."), new FunctionResultContent("callId2", result: "Error: Function invocation approval was not granted.")]), - new ChatMessage(ChatRole.Assistant, "world"), - ]; - - await InvokeAndAssertAsync(options, input, downstreamClientOutput, output, expectedDownstreamClientInput); - - await InvokeAndAssertStreamingAsync(options, input, downstreamClientOutput, output, expectedDownstreamClientInput); - } - - [Fact] - public async Task MixedApprovedAndRejectedApprovalResponsesAreExecutedAndFailedAsync() - { - var options = new ChatOptions - { - Tools = - [ - new ApprovalRequiredAIFunction(AIFunctionFactory.Create(() => "Result 1", "Func1")), - AIFunctionFactory.Create((int i) => $"Result 2: {i}", "Func2"), - ] - }; - - List input = - [ - new ChatMessage(ChatRole.User, "hello"), - new ChatMessage(ChatRole.Assistant, - [ - new FunctionApprovalRequestContent("callId1", new FunctionCallContent("callId1", "Func1")), - new FunctionApprovalRequestContent("callId2", new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]) { MessageId = "resp1" }, - new ChatMessage(ChatRole.User, - [ - new FunctionApprovalResponseContent("callId1", false, new FunctionCallContent("callId1", "Func1")), - new FunctionApprovalResponseContent("callId2", true, new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]), - ]; - - List expectedDownstreamClientInput = - [ - new ChatMessage(ChatRole.User, "hello"), - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Error: Function invocation approval was not granted.")]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId2", result: "Result 2: 42")]), - ]; - - List downstreamClientOutput = - [ - new ChatMessage(ChatRole.Assistant, "world"), - ]; - - List nonStreamingOutput = - [ - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Error: Function invocation approval was not granted.")]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId2", result: "Result 2: 42")]), - new ChatMessage(ChatRole.Assistant, "world"), - ]; - - List streamingOutput = - [ - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Error: Function invocation approval was not granted."), new FunctionResultContent("callId2", result: "Result 2: 42")]), - new ChatMessage(ChatRole.Assistant, "world"), - ]; - - await InvokeAndAssertAsync(options, input, downstreamClientOutput, nonStreamingOutput, expectedDownstreamClientInput); - - await InvokeAndAssertStreamingAsync(options, input, downstreamClientOutput, streamingOutput, expectedDownstreamClientInput); - } - - [Fact] - public async Task ApprovedInputsAreExecutedAndFunctionResultsAreConvertedAsync() - { - var options = new ChatOptions - { - Tools = - [ - AIFunctionFactory.Create(() => "Result 1", "Func1"), - new ApprovalRequiredAIFunction(AIFunctionFactory.Create((int i) => $"Result 2: {i}", "Func2")), - ] - }; - - List input = - [ - new ChatMessage(ChatRole.User, "hello"), - new ChatMessage(ChatRole.Assistant, - [ - new FunctionApprovalRequestContent("callId1", new FunctionCallContent("callId1", "Func1")), - new FunctionApprovalRequestContent("callId2", new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]) { MessageId = "resp1" }, - new ChatMessage(ChatRole.User, - [ - new FunctionApprovalResponseContent("callId1", true, new FunctionCallContent("callId1", "Func1")), - new FunctionApprovalResponseContent("callId2", true, new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]), - ]; - - List expectedDownstreamClientInput = - [ - new ChatMessage(ChatRole.User, "hello"), - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Result 1"), new FunctionResultContent("callId2", result: "Result 2: 42")]), - ]; - - List downstreamClientOutput = - [ - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 3 } })]), - ]; - - List output = - [ - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Result 1"), new FunctionResultContent("callId2", result: "Result 2: 42")]), - new ChatMessage(ChatRole.Assistant, [new FunctionApprovalRequestContent("callId2", new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 3 } }))]), - ]; - - await InvokeAndAssertAsync(options, input, downstreamClientOutput, output, expectedDownstreamClientInput); - - await InvokeAndAssertStreamingAsync(options, input, downstreamClientOutput, output, expectedDownstreamClientInput); - } - - [Fact] - public async Task AlreadyExecutedApprovalsAreIgnoredAsync() - { - var options = new ChatOptions - { - Tools = - [ - AIFunctionFactory.Create(() => "Result 1", "Func1"), - new ApprovalRequiredAIFunction(AIFunctionFactory.Create((int i) => $"Result 2: {i}", "Func2")), - ] - }; - - List input = - [ - new ChatMessage(ChatRole.User, "hello"), - new ChatMessage(ChatRole.Assistant, - [ - new FunctionApprovalRequestContent("callId1", new FunctionCallContent("callId1", "Func1")), - new FunctionApprovalRequestContent("callId2", new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]) { MessageId = "resp1" }, - new ChatMessage(ChatRole.User, - [ - new FunctionApprovalResponseContent("callId1", true, new FunctionCallContent("callId1", "Func1")), - new FunctionApprovalResponseContent("callId2", true, new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]), - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Result 1"), new FunctionResultContent("callId2", result: "Result 2: 42")]), - new ChatMessage(ChatRole.Assistant, - [ - new FunctionApprovalRequestContent("callId3", new FunctionCallContent("callId3", "Func1")), - ]) { MessageId = "resp2" }, - new ChatMessage(ChatRole.User, - [ - new FunctionApprovalResponseContent("callId3", true, new FunctionCallContent("callId3", "Func1")), - ]), - ]; - - List expectedDownstreamClientInput = - [ - new ChatMessage(ChatRole.User, "hello"), - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Result 1"), new FunctionResultContent("callId2", result: "Result 2: 42")]), - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId3", "Func1")]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId3", result: "Result 1")]), - ]; - - List downstreamClientOutput = - [ - new ChatMessage(ChatRole.Assistant, "World"), - ]; - - List output = - [ - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId3", "Func1")]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId3", result: "Result 1")]), - new ChatMessage(ChatRole.Assistant, "World"), - ]; - - await InvokeAndAssertAsync(options, input, downstreamClientOutput, output, expectedDownstreamClientInput); - - await InvokeAndAssertStreamingAsync(options, input, downstreamClientOutput, output, expectedDownstreamClientInput); - } - - [Fact] - public async Task ApprovalRequestWithoutApprovalResponseThrowsAsync() - { - var options = new ChatOptions - { - Tools = - [ - new ApprovalRequiredAIFunction(AIFunctionFactory.Create(() => "Result 1", "Func1")), - AIFunctionFactory.Create((int i) => $"Result 2: {i}", "Func2"), - ] - }; - - List input = - [ - new ChatMessage(ChatRole.User, "hello"), - new ChatMessage(ChatRole.Assistant, - [ - new FunctionApprovalRequestContent("callId1", new FunctionCallContent("callId1", "Func1")), - ]) { MessageId = "resp1" }, - ]; - - var invokeException = await Assert.ThrowsAsync( - async () => await InvokeAndAssertAsync(options, input, [], [], [])); - Assert.Equal("FunctionApprovalRequestContent found with FunctionCall.CallId(s) 'callId1' that have no matching FunctionApprovalResponseContent.", invokeException.Message); - - var invokeStreamingException = await Assert.ThrowsAsync( - async () => await InvokeAndAssertStreamingAsync(options, input, [], [], [])); - Assert.Equal("FunctionApprovalRequestContent found with FunctionCall.CallId(s) 'callId1' that have no matching FunctionApprovalResponseContent.", invokeStreamingException.Message); - } - - [Fact] - public async Task ApprovedApprovalResponsesWithoutApprovalRequestAreExecutedAsync() - { - var options = new ChatOptions - { - Tools = - [ - new ApprovalRequiredAIFunction(AIFunctionFactory.Create(() => "Result 1", "Func1")), - AIFunctionFactory.Create((int i) => $"Result 2: {i}", "Func2"), - ] - }; - - List input = - [ - new ChatMessage(ChatRole.User, "hello"), - new ChatMessage(ChatRole.User, - [ - new FunctionApprovalResponseContent("callId1", true, new FunctionCallContent("callId1", "Func1")), - new FunctionApprovalResponseContent("callId2", true, new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]), - ]; - - List expectedDownstreamClientInput = - [ - new ChatMessage(ChatRole.User, "hello"), - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Result 1"), new FunctionResultContent("callId2", result: "Result 2: 42")]), - ]; - - List downstreamClientOutput = - [ - new ChatMessage(ChatRole.Assistant, "world"), - ]; - - List output = - [ - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Result 1"), new FunctionResultContent("callId2", result: "Result 2: 42")]), - new ChatMessage(ChatRole.Assistant, "world"), - ]; - - await InvokeAndAssertAsync(options, input, downstreamClientOutput, output, expectedDownstreamClientInput); - - await InvokeAndAssertStreamingAsync(options, input, downstreamClientOutput, output, expectedDownstreamClientInput); - } - - [Fact] - public async Task FunctionCallContentIsNotPassedToDownstreamServiceWithServiceThreadsAsync() - { - var options = new ChatOptions - { - Tools = - [ - new ApprovalRequiredAIFunction(AIFunctionFactory.Create(() => "Result 1", "Func1")), - AIFunctionFactory.Create((int i) => $"Result 2: {i}", "Func2"), - ], - ConversationId = "test-conversation", - }; - - List input = - [ - new ChatMessage(ChatRole.User, - [ - new FunctionApprovalResponseContent("callId1", true, new FunctionCallContent("callId1", "Func1")), - new FunctionApprovalResponseContent("callId2", true, new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })) - ]), - ]; - - List expectedDownstreamClientInput = - [ - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Result 1"), new FunctionResultContent("callId2", result: "Result 2: 42")]), - ]; - - List downstreamClientOutput = - [ - new ChatMessage(ChatRole.Assistant, "world"), - ]; - - List output = - [ - new ChatMessage(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1"), new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]), - new ChatMessage(ChatRole.Tool, [new FunctionResultContent("callId1", result: "Result 1"), new FunctionResultContent("callId2", result: "Result 2: 42")]), - new ChatMessage(ChatRole.Assistant, "world"), - ]; - - await InvokeAndAssertAsync(options, input, downstreamClientOutput, output, expectedDownstreamClientInput); - - await InvokeAndAssertStreamingAsync(options, input, downstreamClientOutput, output, expectedDownstreamClientInput); - } - - /// - /// Since we do not have a way of supporting both functions that require approval and those that do not - /// in one invocation, we always require all function calls to be approved if any require approval. - /// If we are therefore unsure as to whether we will encounter a function call that requires approval, - /// we have to wait until we find one before yielding any function call content. - /// If we don't have any function calls that require approval at all though, we can just yield all content normally - /// since this issue won't apply. - /// - [Fact] - public async Task FunctionCallContentIsYieldedImmediatelyIfNoApprovalRequiredWhenStreamingAsync() - { - var options = new ChatOptions - { - Tools = - [ - AIFunctionFactory.Create(() => "Result 1", "Func1"), - AIFunctionFactory.Create((int i) => $"Result 2: {i}", "Func2"), - ] - }; - - List input = [new ChatMessage(ChatRole.User, "hello")]; - - Func configurePipeline = b => b.Use(s => new NewFunctionInvokingChatClient(s)); - using CancellationTokenSource cts = new(); - - var updateYieldCount = 0; - - async IAsyncEnumerable YieldInnerClientUpdates(IEnumerable contents, ChatOptions? actualOptions, [EnumeratorCancellation] CancellationToken actualCancellationToken) - { - Assert.Equal(cts.Token, actualCancellationToken); - await Task.Yield(); - var messageId = Guid.NewGuid().ToString("N"); - - updateYieldCount++; - yield return new ChatResponseUpdate(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1")]) { MessageId = messageId }; - updateYieldCount++; - yield return new ChatResponseUpdate(ChatRole.Assistant, [new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]) { MessageId = messageId }; - } - - using var innerClient = new TestChatClient { GetStreamingResponseAsyncCallback = YieldInnerClientUpdates }; - IChatClient service = configurePipeline(innerClient.AsBuilder()).Build(); - - var updates = service.GetStreamingResponseAsync(new EnumeratedOnceEnumerable(input), options, cts.Token); - - var updateCount = 0; - await foreach (var update in updates) - { - if (updateCount < 2) - { - var functionCall = update.Contents.OfType().First(); - if (functionCall.CallId == "callId1") - { - Assert.Equal("Func1", functionCall.Name); - Assert.Equal(1, updateYieldCount); - } - else if (functionCall.CallId == "callId2") - { - Assert.Equal("Func2", functionCall.Name); - Assert.Equal(2, updateYieldCount); - } - } - - updateCount++; - } - } - - /// - /// Since we do not have a way of supporting both functions that require approval and those that do not - /// in one invocation, we always require all function calls to be approved if any require approval. - /// If we are therefore unsure as to whether we will encounter a function call that requires approval, - /// we have to wait until we find one before yielding any function call content. - /// We can however, yield any other content until we encounter the first function call. - /// - [Fact] - public async Task FunctionCalsAreBufferedUntilApprovalRequirementEncounteredWhenStreamingAsync() - { - var options = new ChatOptions - { - Tools = - [ - AIFunctionFactory.Create(() => "Result 1", "Func1"), - new ApprovalRequiredAIFunction(AIFunctionFactory.Create((int i) => $"Result 2: {i}", "Func2")), - AIFunctionFactory.Create(() => "Result 3", "Func3"), - ] - }; - - List input = [new ChatMessage(ChatRole.User, "hello")]; - - Func configurePipeline = b => b.Use(s => new NewFunctionInvokingChatClient(s)); - using CancellationTokenSource cts = new(); - - var updateYieldCount = 0; - - async IAsyncEnumerable YieldInnerClientUpdates(IEnumerable contents, ChatOptions? actualOptions, [EnumeratorCancellation] CancellationToken actualCancellationToken) - { - Assert.Equal(cts.Token, actualCancellationToken); - await Task.Yield(); - var messageId = Guid.NewGuid().ToString("N"); - - updateYieldCount++; - yield return new ChatResponseUpdate(ChatRole.Assistant, [new TextContent("Text 1")]) { MessageId = messageId }; - updateYieldCount++; - yield return new ChatResponseUpdate(ChatRole.Assistant, [new TextContent("Text 2")]) { MessageId = messageId }; - updateYieldCount++; - yield return new ChatResponseUpdate(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func1")]) { MessageId = messageId }; - updateYieldCount++; - yield return new ChatResponseUpdate(ChatRole.Assistant, [new FunctionCallContent("callId2", "Func2", arguments: new Dictionary { { "i", 42 } })]) { MessageId = messageId }; - updateYieldCount++; - yield return new ChatResponseUpdate(ChatRole.Assistant, [new FunctionCallContent("callId1", "Func3")]) { MessageId = messageId }; - } - - using var innerClient = new TestChatClient { GetStreamingResponseAsyncCallback = YieldInnerClientUpdates }; - IChatClient service = configurePipeline(innerClient.AsBuilder()).Build(); - - var updates = service.GetStreamingResponseAsync(new EnumeratedOnceEnumerable(input), options, cts.Token); - - var updateCount = 0; - await foreach (var update in updates) - { - switch (updateCount) - { - case 0: - Assert.Equal("Text 1", update.Contents.OfType().First().Text); - // First content should be yielded immedately, since we don't have any function calls yet. - Assert.Equal(1, updateYieldCount); - break; - case 1: - Assert.Equal("Text 2", update.Contents.OfType().First().Text); - // Second content should be yielded immedately, since we don't have any function calls yet. - Assert.Equal(2, updateYieldCount); - break; - case 2: - var approvalRequest1 = update.Contents.OfType().First(); - Assert.Equal("callId1", approvalRequest1.FunctionCall.CallId); - Assert.Equal("Func1", approvalRequest1.FunctionCall.Name); - // Third content should have been buffered, since we have not yet encountered a function call that requires approval. - Assert.Equal(4, updateYieldCount); - break; - case 3: - var approvalRequest2 = update.Contents.OfType().First(); - Assert.Equal("callId2", approvalRequest2.FunctionCall.CallId); - Assert.Equal("Func2", approvalRequest2.FunctionCall.Name); - // Fourth content can be yielded immediately, since it is the first function call that requires approval. - Assert.Equal(4, updateYieldCount); - break; - case 4: - var approvalRequest3 = update.Contents.OfType().First(); - Assert.Equal("callId1", approvalRequest3.FunctionCall.CallId); - Assert.Equal("Func3", approvalRequest3.FunctionCall.Name); - // Fifth content can be yielded immediately, since we previously encountered a function call that requires approval. - Assert.Equal(5, updateYieldCount); - break; - } - - updateCount++; - } - } - - private static async Task> InvokeAndAssertAsync( - ChatOptions? options, - List input, - List downstreamClientOutput, - List expectedOutput, - List? expectedDownstreamClientInput = null, - Func? configurePipeline = null, - AITool[]? additionalTools = null, - IServiceProvider? services = null) - { - Assert.NotEmpty(input); - - configurePipeline ??= b => b.Use(s => new NewFunctionInvokingChatClient(s) { AdditionalTools = additionalTools }); - - using CancellationTokenSource cts = new(); - long expectedTotalTokenCounts = 0; - - using var innerClient = new TestChatClient - { - GetResponseAsyncCallback = async (contents, actualOptions, actualCancellationToken) => - { - Assert.Equal(cts.Token, actualCancellationToken); - if (expectedDownstreamClientInput is not null) - { - CompareMessageLists(expectedDownstreamClientInput, contents.ToList()); - } - - await Task.Yield(); - - var usage = CreateRandomUsage(); - expectedTotalTokenCounts += usage.InputTokenCount!.Value; - - downstreamClientOutput.ForEach(m => m.MessageId = Guid.NewGuid().ToString("N")); - return new ChatResponse(downstreamClientOutput) { Usage = usage }; - } - }; - - IChatClient service = configurePipeline(innerClient.AsBuilder()).Build(services); - - var result = await service.GetResponseAsync(new EnumeratedOnceEnumerable(input), options, cts.Token); - Assert.NotNull(result); - - var actualOutput = result.Messages as List ?? result.Messages.ToList(); - CompareMessageLists(expectedOutput, actualOutput); - - // Usage should be aggregated over all responses, including AdditionalUsage - var actualUsage = result.Usage!; - Assert.Equal(expectedTotalTokenCounts, actualUsage.InputTokenCount); - Assert.Equal(expectedTotalTokenCounts, actualUsage.OutputTokenCount); - Assert.Equal(expectedTotalTokenCounts, actualUsage.TotalTokenCount); - Assert.Equal(2, actualUsage.AdditionalCounts!.Count); - Assert.Equal(expectedTotalTokenCounts, actualUsage.AdditionalCounts["firstValue"]); - Assert.Equal(expectedTotalTokenCounts, actualUsage.AdditionalCounts["secondValue"]); - - return actualOutput; - } - - private static UsageDetails CreateRandomUsage() - { - // We'll set the same random number on all the properties so that, when determining the - // correct sum in tests, we only have to total the values once - var value = new Random().Next(100); - return new UsageDetails - { - InputTokenCount = value, - OutputTokenCount = value, - TotalTokenCount = value, - AdditionalCounts = new() { ["firstValue"] = value, ["secondValue"] = value }, - }; - } - - private static async Task> InvokeAndAssertStreamingAsync( - ChatOptions? options, - List input, - List downstreamClientOutput, - List expectedOutput, - List? expectedDownstreamClientInput = null, - Func? configurePipeline = null, - AITool[]? additionalTools = null, - IServiceProvider? services = null) - { - Assert.NotEmpty(input); - - configurePipeline ??= b => b.Use(s => new NewFunctionInvokingChatClient(s) { AdditionalTools = additionalTools }); - - using CancellationTokenSource cts = new(); - - using var innerClient = new TestChatClient - { - GetStreamingResponseAsyncCallback = (contents, actualOptions, actualCancellationToken) => - { - Assert.Equal(cts.Token, actualCancellationToken); - if (expectedDownstreamClientInput is not null) - { - CompareMessageLists(expectedDownstreamClientInput, contents.ToList()); - } - - downstreamClientOutput.ForEach(m => m.MessageId = Guid.NewGuid().ToString("N")); - return YieldAsync(new ChatResponse(downstreamClientOutput).ToChatResponseUpdates()); - } - }; - - IChatClient service = configurePipeline(innerClient.AsBuilder()).Build(services); - - var result = await service.GetStreamingResponseAsync(new EnumeratedOnceEnumerable(input), options, cts.Token).ToChatResponseAsync(); - Assert.NotNull(result); - - var actualOutput = result.Messages as List ?? result.Messages.ToList(); - - expectedOutput ??= input; - CompareMessageLists(expectedOutput, actualOutput); - - return actualOutput; - } - - private static async IAsyncEnumerable YieldAsync(params T[] items) - { - await Task.Yield(); - foreach (var item in items) - { - yield return item; - } - } - - private static void CompareMessageLists(List expectedMessages, List actualMessages) - { - Assert.Equal(expectedMessages.Count, actualMessages.Count); - for (int i = 0; i < expectedMessages.Count; i++) - { - var expectedMessage = expectedMessages[i]; - var chatMessage = actualMessages[i]; - - Assert.Equal(expectedMessage.Role, chatMessage.Role); - Assert.Equal(expectedMessage.Text, chatMessage.Text); - Assert.Equal(expectedMessage.GetType(), chatMessage.GetType()); - - Assert.Equal(expectedMessage.Contents.Count, chatMessage.Contents.Count); - for (int j = 0; j < expectedMessage.Contents.Count; j++) - { - var expectedItem = expectedMessage.Contents[j]; - var chatItem = chatMessage.Contents[j]; - - Assert.Equal(expectedItem.GetType(), chatItem.GetType()); - Assert.Equal(expectedItem.ToString(), chatItem.ToString()); - if (expectedItem is FunctionCallContent expectedFunctionCall) - { - var chatFunctionCall = (FunctionCallContent)chatItem; - Assert.Equal(expectedFunctionCall.Name, chatFunctionCall.Name); - AssertExtensions.EqualFunctionCallParameters(expectedFunctionCall.Arguments, chatFunctionCall.Arguments); - } - else if (expectedItem is FunctionResultContent expectedFunctionResult) - { - var chatFunctionResult = (FunctionResultContent)chatItem; - AssertExtensions.EqualFunctionCallResults(expectedFunctionResult.Result, chatFunctionResult.Result); - } - } - } - } - - private sealed class EnumeratedOnceEnumerable(IEnumerable items) : IEnumerable - { - private int _iterated; - - public IEnumerator GetEnumerator() - { - if (Interlocked.Exchange(ref _iterated, 1) != 0) - { - throw new InvalidOperationException("This enumerable can only be enumerated once."); - } - - foreach (var item in items) - { - yield return item; - } - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } -} diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/MEAI/TestChatClient.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/MEAI/TestChatClient.cs deleted file mode 100644 index 436d4308ab..0000000000 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/MEAI/TestChatClient.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -// WARNING: -// This class has been temporarily copied here from MEAI, to allow prototyping -// functionality that will be moved to MEAI in the future. -// This file is not intended to be modified. - -// AF repo suppressions for code copied from MEAI. -#pragma warning disable IDE0009 // Member access should be qualified. -#pragma warning disable CA1859 // Use concrete types when possible for improved performance -#pragma warning disable CA1063 // Implement IDisposable Correctly - -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Extensions.AI; - -public sealed class TestChatClient : IChatClient -{ - public TestChatClient() - { - GetServiceCallback = DefaultGetServiceCallback; - } - - public IServiceProvider? Services { get; set; } - - public Func, ChatOptions?, CancellationToken, Task>? GetResponseAsyncCallback { get; set; } - - public Func, ChatOptions?, CancellationToken, IAsyncEnumerable>? GetStreamingResponseAsyncCallback { get; set; } - - public Func GetServiceCallback { get; set; } - - private object? DefaultGetServiceCallback(Type serviceType, object? serviceKey) => - serviceType is not null && serviceKey is null && serviceType.IsInstanceOfType(this) ? this : null; - - public Task GetResponseAsync(IEnumerable messages, ChatOptions? options = null, CancellationToken cancellationToken = default) - => GetResponseAsyncCallback!.Invoke(messages, options, cancellationToken); - - public IAsyncEnumerable GetStreamingResponseAsync(IEnumerable messages, ChatOptions? options = null, CancellationToken cancellationToken = default) - => GetStreamingResponseAsyncCallback!.Invoke(messages, options, cancellationToken); - - public object? GetService(Type serviceType, object? serviceKey = null) - => GetServiceCallback(serviceType, serviceKey); - - void IDisposable.Dispose() - { - // No resources need disposing. - } -} diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs index 1dd8a760f8..de094e46a2 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs @@ -70,7 +70,7 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture IList? aiTools = null) { return Task.FromResult(new ChatClientAgent( - this._openAIResponseClient.AsNewIChatClient(), + this._openAIResponseClient.AsIChatClient(), options: new() { Name = name, From 11df72b801d407a4ae8d37f43083862f024b139f Mon Sep 17 00:00:00 2001 From: Evan Mattson <35585003+moonbox3@users.noreply.github.com> Date: Thu, 11 Sep 2025 16:33:36 +0900 Subject: [PATCH 30/32] Fix sample path (#704) --- .../getting_started/workflow/control-flow/edge_condition.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/python/samples/getting_started/workflow/control-flow/edge_condition.py b/python/samples/getting_started/workflow/control-flow/edge_condition.py index 2dace8b484..63d2977ca4 100644 --- a/python/samples/getting_started/workflow/control-flow/edge_condition.py +++ b/python/samples/getting_started/workflow/control-flow/edge_condition.py @@ -177,9 +177,7 @@ async def main() -> None: # Read Email content from the sample resource file. # This keeps the sample deterministic since the model sees the same email every run. - email_path = os.path.join( - os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "workflow", "resources", "email.txt" - ) + email_path = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "resources", "email.txt") with open(email_path) as email_file: # noqa: ASYNC230 email = email_file.read() From a480366d80549f44fb57cd6e68f115de165918ac Mon Sep 17 00:00:00 2001 From: Alex Lavaee <57336517+lavaman131@users.noreply.github.com> Date: Thu, 11 Sep 2025 02:50:50 -0700 Subject: [PATCH 31/32] Python: added websockets as main dep for agent.run_stream (#672) * added websockets as main dep for agent.run_stream * Update python/packages/main/pyproject.toml Co-authored-by: Eduard van Valkenburg * Update python/packages/main/pyproject.toml Co-authored-by: Eduard van Valkenburg --------- Co-authored-by: Alex Lavaee Co-authored-by: Eduard van Valkenburg --- python/packages/main/pyproject.toml | 2 +- python/uv.lock | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/python/packages/main/pyproject.toml b/python/packages/main/pyproject.toml index 2d06652356..64b34bd0ef 100644 --- a/python/packages/main/pyproject.toml +++ b/python/packages/main/pyproject.toml @@ -29,7 +29,7 @@ dependencies = [ "typing-extensions>=4.14.0", "opentelemetry-api ~= 1.24", "opentelemetry-sdk ~= 1.24", - "mcp>=1.12", + "mcp[ws]>=1.13", "azure-monitor-opentelemetry>=1.7.0", "azure-monitor-opentelemetry-exporter>=1.0.0b41", "opentelemetry-exporter-otlp-proto-grpc>=1.36.0", diff --git a/python/uv.lock b/python/uv.lock index 463f9b0f1c..1eddb3c2c7 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -59,6 +59,7 @@ dependencies = [ { name = "pydantic", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "pydantic-settings", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "websockets", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] [package.optional-dependencies] @@ -91,6 +92,7 @@ requires-dist = [ { name = "pydantic", specifier = ">=2.11.7" }, { name = "pydantic-settings", specifier = ">=2.10.1" }, { name = "typing-extensions", specifier = ">=4.14.0" }, + { name = "websockets", specifier = ">=15.0.1" }, ] provides-extras = ["azure", "foundry", "workflow", "runtime"] From e91b6b08cef230e0971734a78e85cf0223e2ee51 Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Date: Thu, 11 Sep 2025 11:38:29 +0100 Subject: [PATCH 32/32] .NET: Adding Delegating AIAgent (#708) * Delegating AIAgent impl * Warning fix * Fix duplicate --- .../DelegatingAIAgent.cs | 70 ++++ .../OpenTelemetryAgent.cs | 37 +-- .../DelegatingAIAgentTests.cs | 308 ++++++++++++++++++ 3 files changed, 393 insertions(+), 22 deletions(-) create mode 100644 dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/DelegatingAIAgent.cs create mode 100644 dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/DelegatingAIAgentTests.cs diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/DelegatingAIAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/DelegatingAIAgent.cs new file mode 100644 index 0000000000..e2e0fd7f2d --- /dev/null +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/DelegatingAIAgent.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Shared.Diagnostics; + +namespace Microsoft.Extensions.AI.Agents; + +/// +/// Provides an optional base class for an that passes through calls to another instance. +/// +/// +/// This is recommended as a base type when building agents that can be chained around an underlying . +/// The default implementation simply passes each call to the inner agent instance. +/// +public class DelegatingAIAgent : AIAgent +{ + /// + /// Initializes a new instance of the class. + /// + /// The wrapped agent instance. + protected DelegatingAIAgent(AIAgent innerAgent) + { + this.InnerAgent = Throw.IfNull(innerAgent); + } + + /// Gets the inner . + protected AIAgent InnerAgent { get; } + + /// + public override string Id => this.InnerAgent.Id; + + /// + public override string? Name => this.InnerAgent.Name; + + /// + public override string? Description => this.InnerAgent.Description; + + /// + public override object? GetService(Type serviceType, object? serviceKey = null) + { + _ = Throw.IfNull(serviceType); + + // If the key is non-null, we don't know what it means so pass through to the inner service. + return + serviceKey is null && serviceType.IsInstanceOfType(this) ? this : + this.InnerAgent.GetService(serviceType, serviceKey); + } + + /// + public override AgentThread GetNewThread() => this.InnerAgent.GetNewThread(); + + /// + public override Task RunAsync( + IReadOnlyCollection messages, + AgentThread? thread = null, + AgentRunOptions? options = null, + CancellationToken cancellationToken = default) + => this.InnerAgent.RunAsync(messages, thread, options, cancellationToken); + + /// + public override IAsyncEnumerable RunStreamingAsync( + IReadOnlyCollection messages, + AgentThread? thread = null, + AgentRunOptions? options = null, + CancellationToken cancellationToken = default) + => this.InnerAgent.RunStreamingAsync(messages, thread, options, cancellationToken); +} diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/OpenTelemetryAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/OpenTelemetryAgent.cs index fd81f7cd3e..7f8b46c82b 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/OpenTelemetryAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents/OpenTelemetryAgent.cs @@ -24,13 +24,12 @@ namespace Microsoft.Extensions.AI.Agents; /// This class provides telemetry instrumentation for agent operations including activities, metrics, and logging. /// The telemetry output follows OpenTelemetry semantic conventions in and is subject to change as the conventions evolve. /// -public sealed partial class OpenTelemetryAgent : AIAgent, IDisposable +public sealed partial class OpenTelemetryAgent : DelegatingAIAgent, IDisposable { private const LogLevel EventLogLevel = LogLevel.Information; private JsonSerializerOptions _jsonSerializerOptions; private readonly OpenTelemetryChatClient? _openTelemetryChatClient; private readonly string? _system; - private readonly AIAgent _innerAgent; private readonly ActivitySource _activitySource; private readonly Meter _meter; private readonly Histogram _operationDurationHistogram; @@ -44,9 +43,8 @@ public sealed partial class OpenTelemetryAgent : AIAgent, IDisposable /// The to use for emitting events. /// An optional source name that will be used on the telemetry data. public OpenTelemetryAgent(AIAgent innerAgent, ILogger? logger = null, string? sourceName = null) + : base(innerAgent) { - this._innerAgent = Throw.IfNull(innerAgent); - string name = string.IsNullOrEmpty(sourceName) ? OpenTelemetryConsts.DefaultSourceName : sourceName!; this._activitySource = new(name); this._meter = new(name); @@ -54,7 +52,7 @@ public sealed partial class OpenTelemetryAgent : AIAgent, IDisposable this._system = this.GetService()?.ProviderName ?? OpenTelemetryConsts.GenAI.SystemNameValues.MicrosoftExtensionsAIAgents; // Attempt to get the open telemetry chat client if the inner agent is a ChatClientAgent. - this._openTelemetryChatClient = (innerAgent as ChatClientAgent)?.ChatClient.GetService(); + this._openTelemetryChatClient = (this.InnerAgent as ChatClientAgent)?.ChatClient.GetService(); // Inherit by default the EnableSensitiveData setting from the TelemetryChatClient if available. this.EnableSensitiveData = this._openTelemetryChatClient?.EnableSensitiveData ?? false; @@ -112,21 +110,16 @@ public sealed partial class OpenTelemetryAgent : AIAgent, IDisposable /// public override object? GetService(Type serviceType, object? serviceKey = null) - => base.GetService(serviceType, serviceKey) - ?? (serviceType == typeof(ActivitySource) ? this._activitySource - : this._innerAgent.GetService(serviceType, serviceKey)); + { + // Handle ActivitySource requests directly - always return our own ActivitySource + if (serviceType == typeof(ActivitySource)) + { + return this._activitySource; + } - /// - public override string Id => this._innerAgent.Id; - - /// - public override string? Name => this._innerAgent.Name; - - /// - public override string? Description => this._innerAgent.Description; - - /// - public override AgentThread GetNewThread() => this._innerAgent.GetNewThread(); + // For other service types, use the base delegation logic + return base.GetService(serviceType, serviceKey); + } /// public override async Task RunAsync( @@ -146,7 +139,7 @@ public sealed partial class OpenTelemetryAgent : AIAgent, IDisposable Exception? error = null; try { - response = await this._innerAgent.RunAsync(messages, thread, options, cancellationToken).ConfigureAwait(false); + response = await base.RunAsync(messages, thread, options, cancellationToken).ConfigureAwait(false); return response; } catch (Exception ex) @@ -175,7 +168,7 @@ public sealed partial class OpenTelemetryAgent : AIAgent, IDisposable IAsyncEnumerable updates; try { - updates = this._innerAgent.RunStreamingAsync(messages, thread, options, cancellationToken); + updates = base.RunStreamingAsync(messages, thread, options, cancellationToken); } catch (Exception ex) { @@ -224,7 +217,7 @@ public sealed partial class OpenTelemetryAgent : AIAgent, IDisposable private Activity? CreateAndConfigureActivity(string operationName, IReadOnlyCollection messages, AgentThread? thread) { // Get the GenAI system name for telemetry - var chatClientAgent = this._innerAgent as ChatClientAgent; + var chatClientAgent = this.InnerAgent as ChatClientAgent; Activity? activity = null; if (this._activitySource.HasListeners()) { diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/DelegatingAIAgentTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/DelegatingAIAgentTests.cs new file mode 100644 index 0000000000..22abf8e0ba --- /dev/null +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/DelegatingAIAgentTests.cs @@ -0,0 +1,308 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Moq; + +namespace Microsoft.Extensions.AI.Agents.Abstractions.UnitTests; + +/// +/// Unit tests for the class. +/// +public class DelegatingAIAgentTests +{ + private readonly Mock _innerAgentMock; + private readonly TestDelegatingAIAgent _delegatingAgent; + private readonly AgentRunResponse _testResponse; + private readonly List _testStreamingResponses; + private readonly AgentThread _testThread; + + /// + /// Initializes a new instance of the class. + /// + public DelegatingAIAgentTests() + { + this._innerAgentMock = new Mock(); + this._testResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Test response")); + this._testStreamingResponses = [new AgentRunResponseUpdate(ChatRole.Assistant, "Test streaming response")]; + this._testThread = new AgentThread(); + + // Setup inner agent mock + this._innerAgentMock.Setup(x => x.Id).Returns("test-agent-id"); + this._innerAgentMock.Setup(x => x.Name).Returns("Test Agent"); + this._innerAgentMock.Setup(x => x.Description).Returns("Test Description"); + this._innerAgentMock.Setup(x => x.GetNewThread()).Returns(this._testThread); + + this._innerAgentMock + .Setup(x => x.RunAsync( + It.IsAny>(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .ReturnsAsync(this._testResponse); + + this._innerAgentMock + .Setup(x => x.RunStreamingAsync( + It.IsAny>(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns(ToAsyncEnumerableAsync(this._testStreamingResponses)); + + this._delegatingAgent = new TestDelegatingAIAgent(this._innerAgentMock.Object); + } + + #region Constructor Tests + + /// + /// Verify that constructor throws ArgumentNullException when innerAgent is null. + /// + [Fact] + public void RequiresInnerAgent() + { + // Act & Assert + Assert.Throws("innerAgent", () => new TestDelegatingAIAgent(null!)); + } + + /// + /// Verify that constructor sets the inner agent correctly. + /// + [Fact] + public void Constructor_WithValidInnerAgent_SetsInnerAgent() + { + // Act + var delegatingAgent = new TestDelegatingAIAgent(this._innerAgentMock.Object); + + // Assert + Assert.Same(this._innerAgentMock.Object, delegatingAgent.InnerAgent); + } + + #endregion + + #region Property Delegation Tests + + /// + /// Verify that Id property delegates to inner agent. + /// + [Fact] + public void Id_DelegatesToInnerAgent() + { + // Act + var id = this._delegatingAgent.Id; + + // Assert + Assert.Equal("test-agent-id", id); + this._innerAgentMock.Verify(x => x.Id, Times.Once); + } + + /// + /// Verify that Name property delegates to inner agent. + /// + [Fact] + public void Name_DelegatesToInnerAgent() + { + // Act + var name = this._delegatingAgent.Name; + + // Assert + Assert.Equal("Test Agent", name); + this._innerAgentMock.Verify(x => x.Name, Times.Once); + } + + /// + /// Verify that Description property delegates to inner agent. + /// + [Fact] + public void Description_DelegatesToInnerAgent() + { + // Act + var description = this._delegatingAgent.Description; + + // Assert + Assert.Equal("Test Description", description); + this._innerAgentMock.Verify(x => x.Description, Times.Once); + } + + #endregion + + #region Method Delegation Tests + + /// + /// Verify that GetNewThread delegates to inner agent. + /// + [Fact] + public void GetNewThread_DelegatesToInnerAgent() + { + // Act + var thread = this._delegatingAgent.GetNewThread(); + + // Assert + Assert.Same(this._testThread, thread); + this._innerAgentMock.Verify(x => x.GetNewThread(), Times.Once); + } + + /// + /// Verify that RunAsync delegates to inner agent with correct parameters. + /// + [Fact] + public async Task RunAsyncDefaultsToInnerAgentAsync() + { + // Arrange + var expectedMessages = new[] { new ChatMessage(ChatRole.User, "Test message") }; + var expectedThread = new AgentThread(); + var expectedOptions = new AgentRunOptions(); + var expectedCancellationToken = new CancellationToken(); + var expectedResult = new TaskCompletionSource(); + var expectedResponse = new AgentRunResponse(); + + var innerAgentMock = new Mock(); + innerAgentMock + .Setup(x => x.RunAsync(expectedMessages, expectedThread, expectedOptions, expectedCancellationToken)) + .Returns(expectedResult.Task); + + var delegatingAgent = new TestDelegatingAIAgent(innerAgentMock.Object); + + // Act + var resultTask = delegatingAgent.RunAsync(expectedMessages, expectedThread, expectedOptions, expectedCancellationToken); + + // Assert + Assert.False(resultTask.IsCompleted); + expectedResult.SetResult(expectedResponse); + Assert.True(resultTask.IsCompleted); + Assert.Same(expectedResponse, await resultTask); + } + + /// + /// Verify that RunStreamingAsync delegates to inner agent with correct parameters. + /// + [Fact] + public async Task RunStreamingAsyncDefaultsToInnerAgentAsync() + { + // Arrange + var expectedMessages = new[] { new ChatMessage(ChatRole.User, "Test message") }; + var expectedThread = new AgentThread(); + var expectedOptions = new AgentRunOptions(); + var expectedCancellationToken = new CancellationToken(); + AgentRunResponseUpdate[] expectedResults = + [ + new(ChatRole.Assistant, "Message 1"), + new(ChatRole.Assistant, "Message 2") + ]; + + var innerAgentMock = new Mock(); + innerAgentMock + .Setup(x => x.RunStreamingAsync(expectedMessages, expectedThread, expectedOptions, expectedCancellationToken)) + .Returns(ToAsyncEnumerableAsync(expectedResults)); + + var delegatingAgent = new TestDelegatingAIAgent(innerAgentMock.Object); + + // Act + var resultAsyncEnumerable = delegatingAgent.RunStreamingAsync(expectedMessages, expectedThread, expectedOptions, expectedCancellationToken); + + // Assert + var enumerator = resultAsyncEnumerable.GetAsyncEnumerator(); + Assert.True(await enumerator.MoveNextAsync()); + Assert.Same(expectedResults[0], enumerator.Current); + Assert.True(await enumerator.MoveNextAsync()); + Assert.Same(expectedResults[1], enumerator.Current); + Assert.False(await enumerator.MoveNextAsync()); + } + + #endregion + + #region GetService Tests + + /// + /// Verify that GetService throws ArgumentNullException when serviceType is null. + /// + [Fact] + public void GetServiceThrowsForNullType() + { + // Act & Assert + Assert.Throws("serviceType", () => this._delegatingAgent.GetService(null!)); + } + + /// + /// Verify that GetService returns the delegating agent itself when requesting compatible type and key is null. + /// + [Fact] + public void GetServiceReturnsSelfIfCompatibleWithRequestAndKeyIsNull() + { + // Act + var agent = this._delegatingAgent.GetService(); + + // Assert + Assert.Same(this._delegatingAgent, agent); + } + + /// + /// Verify that GetService delegates to inner agent when service key is not null. + /// + [Fact] + public void GetServiceDelegatesToInnerIfKeyIsNotNull() + { + // Arrange + var expectedKey = new object(); + var expectedResult = new Mock().Object; + var innerAgentMock = new Mock(); + innerAgentMock.Setup(x => x.GetService(typeof(AIAgent), expectedKey)).Returns(expectedResult); + var delegatingAgent = new TestDelegatingAIAgent(innerAgentMock.Object); + + // Act + var agent = delegatingAgent.GetService(expectedKey); + + // Assert + Assert.Same(expectedResult, agent); + } + + /// + /// Verify that GetService delegates to inner agent when not compatible with request. + /// + [Fact] + public void GetServiceDelegatesToInnerIfNotCompatibleWithRequest() + { + // Arrange + var expectedResult = TimeZoneInfo.Local; + var expectedKey = new object(); + var innerAgentMock = new Mock(); + innerAgentMock + .Setup(x => x.GetService(typeof(TimeZoneInfo), expectedKey)) + .Returns(expectedResult); + var delegatingAgent = new TestDelegatingAIAgent(innerAgentMock.Object); + + // Act + var tzi = delegatingAgent.GetService(expectedKey); + + // Assert + Assert.Same(expectedResult, tzi); + } + + #endregion + + #region Helper Methods + + private static async IAsyncEnumerable ToAsyncEnumerableAsync(IEnumerable values) + { + await Task.Yield(); + foreach (var value in values) + { + yield return value; + } + } + + #endregion + + #region Test Implementation + + /// + /// Test implementation of DelegatingAIAgent for testing purposes. + /// + private sealed class TestDelegatingAIAgent(AIAgent innerAgent) : DelegatingAIAgent(innerAgent) + { + public new AIAgent InnerAgent => base.InnerAgent; + } + + #endregion +}