From 30ebaf9d0f1c4de5fb5b994a87bee670558d6e0e Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Mon, 25 Nov 2024 20:33:17 -0800 Subject: [PATCH 01/16] stash --- dotnet/AutoGen.sln | 7 ++ .../HelloPythonAgent.AppHost.csproj | 17 +++ .../HelloPythonAgent.AppHost/Program.cs | 6 + .../Properties/launchSettings.json | 29 +++++ .../HelloPythonAgent.AppHost/appsettings.json | 9 ++ .../Extensions.cs | 111 ++++++++++++++++++ .../HelloPythonAgent.ServiceDefaults.csproj | 22 ++++ .../HelloPythonAgent/HelloPythonAgent.sln | 30 +++++ .../HelloPythonAgent/hello_python_agent.py | 50 ++++++++ 9 files changed, 281 insertions(+) create mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/HelloPythonAgent.AppHost.csproj create mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Program.cs create mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Properties/launchSettings.json create mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/appsettings.json create mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/Extensions.cs create mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/HelloPythonAgent.ServiceDefaults.csproj create mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.sln create mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent/hello_python_agent.py diff --git a/dotnet/AutoGen.sln b/dotnet/AutoGen.sln index 5b26e27165b..2637d38294f 100644 --- a/dotnet/AutoGen.sln +++ b/dotnet/AutoGen.sln @@ -132,6 +132,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AutoGen.Extension EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AutoGen.Agents.Tests", "test\Microsoft.AutoGen.Agents.Tests\Microsoft.AutoGen.Agents.Tests.csproj", "{394FDAF8-74F9-4977-94A5-3371737EB774}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloPythonAgent.AppHost", "samples\Hello\HelloX-Lang\HelloPythonAgent\HelloPythonAgent.AppHost\HelloPythonAgent.AppHost.csproj", "{B461636A-B901-47C1-A378-7CC8025E72DD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -342,6 +344,10 @@ Global {394FDAF8-74F9-4977-94A5-3371737EB774}.Debug|Any CPU.Build.0 = Debug|Any CPU {394FDAF8-74F9-4977-94A5-3371737EB774}.Release|Any CPU.ActiveCfg = Release|Any CPU {394FDAF8-74F9-4977-94A5-3371737EB774}.Release|Any CPU.Build.0 = Release|Any CPU + {B461636A-B901-47C1-A378-7CC8025E72DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B461636A-B901-47C1-A378-7CC8025E72DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B461636A-B901-47C1-A378-7CC8025E72DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B461636A-B901-47C1-A378-7CC8025E72DD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -402,6 +408,7 @@ Global {64EF61E7-00A6-4E5E-9808-62E10993A0E5} = {7EB336C2-7C0A-4BC8-80C6-A3173AB8DC45} {65059914-5527-4A00-9308-9FAF23D5E85A} = {18BF8DD7-0585-48BF-8F97-AD333080CE06} {394FDAF8-74F9-4977-94A5-3371737EB774} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64} + {B461636A-B901-47C1-A378-7CC8025E72DD} = {7EB336C2-7C0A-4BC8-80C6-A3173AB8DC45} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {93384647-528D-46C8-922C-8DB36A382F0B} diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/HelloPythonAgent.AppHost.csproj b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/HelloPythonAgent.AppHost.csproj new file mode 100644 index 00000000000..f1ffb8a4cfa --- /dev/null +++ b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/HelloPythonAgent.AppHost.csproj @@ -0,0 +1,17 @@ + + + + + Exe + net8.0 + enable + enable + true + 0635cf78-0570-44ec-b8d3-bf09c05817dc + + + + + + + diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Program.cs b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Program.cs new file mode 100644 index 00000000000..73e3400bb46 --- /dev/null +++ b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Program.cs @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Program.cs + +var builder = DistributedApplication.CreateBuilder(args); + +builder.Build().Run(); diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Properties/launchSettings.json b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Properties/launchSettings.json new file mode 100644 index 00000000000..d0ec31a9d26 --- /dev/null +++ b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17048;http://localhost:15182", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21259", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22226" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15182", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19298", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20119" + } + } + } +} diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/appsettings.json b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/appsettings.json new file mode 100644 index 00000000000..31c092aa450 --- /dev/null +++ b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/Extensions.cs b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/Extensions.cs new file mode 100644 index 00000000000..ce94dc2c43d --- /dev/null +++ b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/Extensions.cs @@ -0,0 +1,111 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. +// This project should be referenced by each service project in your solution. +// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults +public static class Extensions +{ + public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder) + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.AddServiceDiscovery(); + }); + + return builder; + } + + public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder) + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddAspNetCoreInstrumentation() + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder) + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder) + { + builder.Services.AddHealthChecks() + // Add a default liveness check to ensure app is responsive + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks("/health"); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks("/alive", new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/HelloPythonAgent.ServiceDefaults.csproj b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/HelloPythonAgent.ServiceDefaults.csproj new file mode 100644 index 00000000000..9adc293690f --- /dev/null +++ b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/HelloPythonAgent.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.sln b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.sln new file mode 100644 index 00000000000..84dedc55f8c --- /dev/null +++ b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.0.0 +MinimumVisualStudioVersion = 17.8.0.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloPythonAgent.AppHost", "HelloPythonAgent.AppHost\HelloPythonAgent.AppHost.csproj", "{109149B5-573D-49D4-A41E-72AB7070BE9D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloPythonAgent.ServiceDefaults", "HelloPythonAgent.ServiceDefaults\HelloPythonAgent.ServiceDefaults.csproj", "{1233A964-E549-41B3-85D8-D46C76A28B06}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {109149B5-573D-49D4-A41E-72AB7070BE9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {109149B5-573D-49D4-A41E-72AB7070BE9D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {109149B5-573D-49D4-A41E-72AB7070BE9D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {109149B5-573D-49D4-A41E-72AB7070BE9D}.Release|Any CPU.Build.0 = Release|Any CPU + {1233A964-E549-41B3-85D8-D46C76A28B06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1233A964-E549-41B3-85D8-D46C76A28B06}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1233A964-E549-41B3-85D8-D46C76A28B06}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1233A964-E549-41B3-85D8-D46C76A28B06}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C18A43A3-5D53-430F-9B77-65458734F0AB} + EndGlobalSection +EndGlobal diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent/hello_python_agent.py b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent/hello_python_agent.py new file mode 100644 index 00000000000..c0c547af4d3 --- /dev/null +++ b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent/hello_python_agent.py @@ -0,0 +1,50 @@ +import asyncio +import logging +import os + +from autogen_core.application import WorkerAgentRuntime +from autogen_core.application.protos.agent_events_pb2 import NewMessageReceived +from autogen_core.base import AgentId, try_get_known_serializers_for_type +from autogen_core.components import DefaultSubscription, DefaultTopicId + +# Add the local package directory to sys.path +# sys.path.append(os.path.abspath('../../../../python/packages/autogen-core')) +from dotenv import load_dotenv +from user_input import UserProxy + +agnext_logger = logging.getLogger("autogen_core") + + +async def main() -> None: + load_dotenv() + agentHost = os.getenv("AGENT_HOST") or "localhost:53072" + agnext_logger.info("0") + agnext_logger.info(agentHost) + runtime = WorkerAgentRuntime(host_address=agentHost) + + agnext_logger.info("1") + runtime.start() + runtime.add_message_serializer(try_get_known_serializers_for_type(NewMessageReceived)) + + agnext_logger.info("2") + + await UserProxy.register(runtime, "HelloAgents", lambda: UserProxy()) + await runtime.add_subscription(DefaultSubscription(agent_type="HelloAgents")) + agnext_logger.info("3") + + message = NewMessageReceived(message="Hello from Python!") + + await runtime.publish_message( + message=message, + topic_id=DefaultTopicId("agents.NewMessageReceived"), + sender=AgentId("HelloAgents", "python"), + ) + await runtime.stop_when_signal() + # await runtime.stop_when_idle() + + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + agnext_logger.setLevel(logging.DEBUG) + agnext_logger.log(logging.DEBUG, "Starting worker") + asyncio.run(main()) \ No newline at end of file From 46ea90c488de9be44fb055bca36171503f6bbca5 Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Mon, 25 Nov 2024 20:40:12 -0800 Subject: [PATCH 02/16] merge from python-xlang-branch --- python/README.md | 8 + .../autogen-core/samples/xlang/user_input.py | 39 ++ .../autogen-core/samples/xlang/worker.py | 50 +++ .../application/_worker_runtime.py | 57 ++- .../_worker_runtime_host_servicer.py | 32 +- .../application/protos/agent_events_pb2.py | 47 +++ .../application/protos/agent_events_pb2.pyi | 179 +++++++++ .../protos/agent_events_pb2_grpc.py | 4 + .../protos/agent_events_pb2_grpc.pyi | 17 + python/packages/autogen-studio/README.md | 6 +- .../autogenstudio/teammanager.py | 4 +- .../autogen-studio/autogenstudio/version.py | 2 +- .../autogenstudio/web/managers/connection.py | 4 +- .../views/playground/chat/agentflow/edge.tsx | 1 - python/uv.lock | 350 +++++++++++++++++- 15 files changed, 765 insertions(+), 35 deletions(-) create mode 100644 python/packages/autogen-core/samples/xlang/user_input.py create mode 100644 python/packages/autogen-core/samples/xlang/worker.py create mode 100644 python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.py create mode 100644 python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.pyi create mode 100644 python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2_grpc.py create mode 100644 python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2_grpc.pyi diff --git a/python/README.md b/python/README.md index 5b012ff60bf..cf18c4927d9 100644 --- a/python/README.md +++ b/python/README.md @@ -7,12 +7,20 @@ See [`autogen-core`](./packages/autogen-core/) package for main functionality. **TL;DR**, run all checks with: +macOS & Linux: ```sh uv sync --all-extras source .venv/bin/activate poe check ``` +Windows (pwsh): +```sh +uv sync --all-extras +./.venv/Scripts/activate.ps1 +poe check +``` + ### Setup `uv` is a package manager that assists in creating the necessary environment and installing packages to run AutoGen. - [Install `uv`](https://docs.astral.sh/uv/getting-started/installation/). diff --git a/python/packages/autogen-core/samples/xlang/user_input.py b/python/packages/autogen-core/samples/xlang/user_input.py new file mode 100644 index 00000000000..62d766ca05c --- /dev/null +++ b/python/packages/autogen-core/samples/xlang/user_input.py @@ -0,0 +1,39 @@ +import asyncio +import logging +from typing import Union + +from autogen_core.application.protos.agent_events_pb2 import ConversationClosed, Input, NewMessageReceived, Output +from autogen_core.base import MessageContext +from autogen_core.components import DefaultTopicId, RoutedAgent, message_handler + +input_types = Union[ConversationClosed, Input, Output] + + +class UserProxy(RoutedAgent): + """An agent that allows the user to play the role of an agent in the conversation via input.""" + + DEFAULT_DESCRIPTION = "A human user." + + def __init__( + self, + description: str = DEFAULT_DESCRIPTION, + ) -> None: + super().__init__(description) + + @message_handler + async def handle_user_chat_input(self, message: input_types, ctx: MessageContext) -> None: + agnext_logger = logging.getLogger("autogen_core") + + if isinstance(message, Input): + response = await self.ainput("User input ('exit' to quit): ") + response = response.strip() + agnext_logger.info(response) + + await self.publish_message(NewMessageReceived(message=response), topic_id=DefaultTopicId()) + elif isinstance(message, Output): + agnext_logger.info(message.message) + else: + pass + + async def ainput(self, prompt: str) -> str: + return await asyncio.to_thread(input, f"{prompt} ") diff --git a/python/packages/autogen-core/samples/xlang/worker.py b/python/packages/autogen-core/samples/xlang/worker.py new file mode 100644 index 00000000000..08b9ab635bb --- /dev/null +++ b/python/packages/autogen-core/samples/xlang/worker.py @@ -0,0 +1,50 @@ +import asyncio +import logging +import os + +from autogen_core.application import WorkerAgentRuntime +from autogen_core.application.protos.agent_events_pb2 import NewMessageReceived +from autogen_core.base import AgentId, try_get_known_serializers_for_type +from autogen_core.components import DefaultSubscription, DefaultTopicId + +# Add the local package directory to sys.path +# sys.path.append(os.path.abspath('../../../../python/packages/autogen-core')) +from dotenv import load_dotenv +from user_input import UserProxy + +agnext_logger = logging.getLogger("autogen_core") + + +async def main() -> None: + load_dotenv() + agentHost = os.getenv("AGENT_HOST") or "localhost:53072" + agnext_logger.info("0") + agnext_logger.info(agentHost) + runtime = WorkerAgentRuntime(host_address=agentHost) + + agnext_logger.info("1") + runtime.start() + runtime.add_message_serializer(try_get_known_serializers_for_type(NewMessageReceived)) + + agnext_logger.info("2") + + await UserProxy.register(runtime, "HelloAgents", lambda: UserProxy()) + await runtime.add_subscription(DefaultSubscription(agent_type="HelloAgents")) + agnext_logger.info("3") + + message = NewMessageReceived(message="Hello from Python!") + + await runtime.publish_message( + message=message, + topic_id=DefaultTopicId("agents.NewMessageReceived"), + sender=AgentId("HelloAgents", "python"), + ) + await runtime.stop_when_signal() + # await runtime.stop_when_idle() + + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + agnext_logger.setLevel(logging.DEBUG) + agnext_logger.log(logging.DEBUG, "Starting worker") + asyncio.run(main()) diff --git a/python/packages/autogen-core/src/autogen_core/application/_worker_runtime.py b/python/packages/autogen-core/src/autogen_core/application/_worker_runtime.py index 2c405710876..a66e4063b08 100644 --- a/python/packages/autogen-core/src/autogen_core/application/_worker_runtime.py +++ b/python/packages/autogen-core/src/autogen_core/application/_worker_runtime.py @@ -27,6 +27,9 @@ cast, ) +import grpc +from google.protobuf import any_pb2 +from grpc.aio import StreamStreamCall from opentelemetry.trace import TracerProvider from typing_extensions import Self, deprecated @@ -49,8 +52,12 @@ from ..base._type_helpers import ChannelArgumentType from ..components import TypeSubscription from ._helpers import SubscriptionManager, get_impl +from .protos import ( + agent_worker_pb2, + agent_worker_pb2_grpc, + cloudevent_pb2, +) from ._utils import GRPC_IMPORT_ERROR_STR -from .protos import agent_worker_pb2, agent_worker_pb2_grpc from .telemetry import MessageRuntimeTracingConfig, TraceHelper, get_telemetry_grpc_metadata try: @@ -254,6 +261,11 @@ async def _run_read_loop(self) -> None: self._background_tasks.add(task) task.add_done_callback(self._raise_on_exception) task.add_done_callback(self._background_tasks.discard) + case "cloudEvent": + task = asyncio.create_task(self._process_cloud_event(message.cloudEvent)) + self._background_tasks.add(task) + task.add_done_callback(self._raise_on_exception) + task.add_done_callback(self._background_tasks.discard) case None: logger.warning("No message") case other: @@ -380,24 +392,22 @@ async def publish_message( with self._trace_helper.trace_block( "create", topic_id, parent=None, extraAttributes={"message_type": message_type} ): - serialized_message = self._serialization_registry.serialize( - message, type_name=message_type, data_content_type=JSON_DATA_CONTENT_TYPE - ) telemetry_metadata = get_telemetry_grpc_metadata() + proto_data = any_pb2.Any() + proto_data.Pack(msg=message) + if sender is not None: + source = str(AgentId(type=sender.type, key=sender.key)) + else: + source = "" runtime_message = agent_worker_pb2.Message( - event=agent_worker_pb2.Event( - topic_type=topic_id.type, - topic_source=topic_id.source, - source=agent_worker_pb2.AgentId(type=sender.type, key=sender.key) if sender is not None else None, + cloudEvent=cloudevent_pb2.CloudEvent( + spec_version="1.0", + type=topic_id.type, + source=source, metadata=telemetry_metadata, - payload=agent_worker_pb2.Payload( - data_type=message_type, - data=serialized_message, - data_content_type=JSON_DATA_CONTENT_TYPE, - ), + proto_data=proto_data, ) ) - task = asyncio.create_task(self._send_message(runtime_message, "publish", topic_id, telemetry_metadata)) self._background_tasks.add(task) task.add_done_callback(self._raise_on_exception) @@ -648,7 +658,7 @@ async def factory_wrapper() -> T: async def _process_register_agent_type_response(self, response: agent_worker_pb2.RegisterAgentTypeResponse) -> None: future = self._pending_requests.pop(response.request_id) - if response.HasField("error"): + if response.HasField("error") and response.error: future.set_exception(RuntimeError(response.error)) else: future.set_result(None) @@ -733,7 +743,7 @@ async def add_subscription(self, subscription: Subscription) -> None: async def _process_add_subscription_response(self, response: agent_worker_pb2.AddSubscriptionResponse) -> None: future = self._pending_requests.pop(response.request_id) - if response.HasField("error"): + if response.HasField("error") and response.error: future.set_exception(RuntimeError(response.error)) else: future.set_result(None) @@ -753,3 +763,18 @@ async def get( def add_message_serializer(self, serializer: MessageSerializer[Any] | Sequence[MessageSerializer[Any]]) -> None: self._serialization_registry.add_serializer(serializer) + + async def _process_cloud_event(self, cloud_event: cloudevent_pb2.CloudEvent) -> None: + logger.info(f"Processing CloudEvent: {cloud_event}") + + event = agent_worker_pb2.Event( + topic_type=cloud_event.type, + topic_source=cloud_event.source, + payload=agent_worker_pb2.Payload( + data_type=cloud_event.type, + data=cloud_event.proto_data.value, + data_content_type=JSON_DATA_CONTENT_TYPE, + ), + metadata=get_telemetry_grpc_metadata(), + ) + await self._process_event(event) diff --git a/python/packages/autogen-core/src/autogen_core/application/_worker_runtime_host_servicer.py b/python/packages/autogen-core/src/autogen_core/application/_worker_runtime_host_servicer.py index 3da50c56f04..2dba017df9f 100644 --- a/python/packages/autogen-core/src/autogen_core/application/_worker_runtime_host_servicer.py +++ b/python/packages/autogen-core/src/autogen_core/application/_worker_runtime_host_servicer.py @@ -4,18 +4,24 @@ from asyncio import Future, Task from typing import Any, Dict, Set -from ..base import TopicId +import grpc + +from ..base import JSON_DATA_CONTENT_TYPE, TopicId from ..components import TypeSubscription from ._helpers import SubscriptionManager from ._utils import GRPC_IMPORT_ERROR_STR +from .protos import ( + agent_worker_pb2, + agent_worker_pb2_grpc, + cloudevent_pb2, +) +from .telemetry import get_telemetry_grpc_metadata try: import grpc except ImportError as e: raise ImportError(GRPC_IMPORT_ERROR_STR) from e -from .protos import agent_worker_pb2, agent_worker_pb2_grpc - logger = logging.getLogger("autogen_core") event_logger = logging.getLogger("autogen_core.events") @@ -132,6 +138,11 @@ async def _receive_messages( self._background_tasks.add(task) task.add_done_callback(self._raise_on_exception) task.add_done_callback(self._background_tasks.discard) + case "cloudEvent": + task = asyncio.create_task(self._process_cloud_event(message.cloudEvent)) + self._background_tasks.add(task) + task.add_done_callback(self._raise_on_exception) + task.add_done_callback(self._background_tasks.discard) case "registerAgentTypeResponse" | "addSubscriptionResponse": logger.warning(f"Received unexpected message type: {oneofcase}") case None: @@ -262,3 +273,18 @@ async def SaveState( # type: ignore context: grpc.aio.ServicerContext[agent_worker_pb2.AgentId, agent_worker_pb2.SaveStateResponse], ) -> agent_worker_pb2.SaveStateResponse: # type: ignore raise NotImplementedError("Method not implemented!") + + async def _process_cloud_event(self, cloud_event: cloudevent_pb2.CloudEvent) -> None: + logger.info(f"Processing CloudEvent: {cloud_event}") + + event = agent_worker_pb2.Event( + topic_type=cloud_event.type, + topic_source=cloud_event.source, + payload=agent_worker_pb2.Payload( + data_type=cloud_event.type, + data=cloud_event.proto_data.value, + data_content_type=JSON_DATA_CONTENT_TYPE, + ), + metadata=get_telemetry_grpc_metadata(), + ) + await self._process_event(event) diff --git a/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.py b/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.py new file mode 100644 index 00000000000..b5aac871cb3 --- /dev/null +++ b/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: agent_events.proto +# Protobuf Python Version: 4.25.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x61gent_events.proto\x12\x06\x61gents\"\x18\n\x05Input\x12\x0f\n\x07message\x18\x01 \x01(\t\"\x1f\n\x0eInputProcessed\x12\r\n\x05route\x18\x01 \x01(\t\"\x19\n\x06Output\x12\x0f\n\x07message\x18\x01 \x01(\t\"\x1e\n\rOutputWritten\x12\r\n\x05route\x18\x01 \x01(\t\"\x1a\n\x07IOError\x12\x0f\n\x07message\x18\x01 \x01(\t\"%\n\x12NewMessageReceived\x12\x0f\n\x07message\x18\x01 \x01(\t\"%\n\x11ResponseGenerated\x12\x10\n\x08response\x18\x01 \x01(\t\"\x1a\n\x07GoodBye\x12\x0f\n\x07message\x18\x01 \x01(\t\" \n\rMessageStored\x12\x0f\n\x07message\x18\x01 \x01(\t\";\n\x12\x43onversationClosed\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x14\n\x0cuser_message\x18\x02 \x01(\t\"\x1b\n\x08Shutdown\x12\x0f\n\x07message\x18\x01 \x01(\tB!\xaa\x02\x1eMicrosoft.AutoGen.Abstractionsb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agent_events_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + _globals['DESCRIPTOR']._options = None + _globals['DESCRIPTOR']._serialized_options = b'\252\002\036Microsoft.AutoGen.Abstractions' + _globals['_INPUT']._serialized_start=30 + _globals['_INPUT']._serialized_end=54 + _globals['_INPUTPROCESSED']._serialized_start=56 + _globals['_INPUTPROCESSED']._serialized_end=87 + _globals['_OUTPUT']._serialized_start=89 + _globals['_OUTPUT']._serialized_end=114 + _globals['_OUTPUTWRITTEN']._serialized_start=116 + _globals['_OUTPUTWRITTEN']._serialized_end=146 + _globals['_IOERROR']._serialized_start=148 + _globals['_IOERROR']._serialized_end=174 + _globals['_NEWMESSAGERECEIVED']._serialized_start=176 + _globals['_NEWMESSAGERECEIVED']._serialized_end=213 + _globals['_RESPONSEGENERATED']._serialized_start=215 + _globals['_RESPONSEGENERATED']._serialized_end=252 + _globals['_GOODBYE']._serialized_start=254 + _globals['_GOODBYE']._serialized_end=280 + _globals['_MESSAGESTORED']._serialized_start=282 + _globals['_MESSAGESTORED']._serialized_end=314 + _globals['_CONVERSATIONCLOSED']._serialized_start=316 + _globals['_CONVERSATIONCLOSED']._serialized_end=375 + _globals['_SHUTDOWN']._serialized_start=377 + _globals['_SHUTDOWN']._serialized_end=404 +# @@protoc_insertion_point(module_scope) diff --git a/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.pyi b/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.pyi new file mode 100644 index 00000000000..a19a251d392 --- /dev/null +++ b/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.pyi @@ -0,0 +1,179 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class Input(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + def __init__( + self, + *, + message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... + +global___Input = Input + +@typing.final +class InputProcessed(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ROUTE_FIELD_NUMBER: builtins.int + route: builtins.str + def __init__( + self, + *, + route: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["route", b"route"]) -> None: ... + +global___InputProcessed = InputProcessed + +@typing.final +class Output(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + def __init__( + self, + *, + message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... + +global___Output = Output + +@typing.final +class OutputWritten(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ROUTE_FIELD_NUMBER: builtins.int + route: builtins.str + def __init__( + self, + *, + route: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["route", b"route"]) -> None: ... + +global___OutputWritten = OutputWritten + +@typing.final +class IOError(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + def __init__( + self, + *, + message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... + +global___IOError = IOError + +@typing.final +class NewMessageReceived(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + def __init__( + self, + *, + message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... + +global___NewMessageReceived = NewMessageReceived + +@typing.final +class ResponseGenerated(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESPONSE_FIELD_NUMBER: builtins.int + response: builtins.str + def __init__( + self, + *, + response: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["response", b"response"]) -> None: ... + +global___ResponseGenerated = ResponseGenerated + +@typing.final +class GoodBye(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + def __init__( + self, + *, + message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... + +global___GoodBye = GoodBye + +@typing.final +class MessageStored(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + def __init__( + self, + *, + message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... + +global___MessageStored = MessageStored + +@typing.final +class ConversationClosed(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + USER_ID_FIELD_NUMBER: builtins.int + USER_MESSAGE_FIELD_NUMBER: builtins.int + user_id: builtins.str + user_message: builtins.str + def __init__( + self, + *, + user_id: builtins.str = ..., + user_message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["user_id", b"user_id", "user_message", b"user_message"]) -> None: ... + +global___ConversationClosed = ConversationClosed + +@typing.final +class Shutdown(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + def __init__( + self, + *, + message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... + +global___Shutdown = Shutdown diff --git a/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2_grpc.py b/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2_grpc.py new file mode 100644 index 00000000000..2daafffebfc --- /dev/null +++ b/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2_grpc.py @@ -0,0 +1,4 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + diff --git a/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2_grpc.pyi b/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2_grpc.pyi new file mode 100644 index 00000000000..a6a9cff9dfd --- /dev/null +++ b/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2_grpc.pyi @@ -0,0 +1,17 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import abc +import collections.abc +import grpc +import grpc.aio +import typing + +_T = typing.TypeVar("_T") + +class _MaybeAsyncIterator(collections.abc.AsyncIterator[_T], collections.abc.Iterator[_T], metaclass=abc.ABCMeta): ... + +class _ServicerContext(grpc.ServicerContext, grpc.aio.ServicerContext): # type: ignore[misc, type-arg] + ... diff --git a/python/packages/autogen-studio/README.md b/python/packages/autogen-studio/README.md index 00721035082..c456b4837b9 100644 --- a/python/packages/autogen-studio/README.md +++ b/python/packages/autogen-studio/README.md @@ -85,7 +85,6 @@ AutoGen Studio also takes several parameters to customize the application: - `--port ` argument to specify the port number. By default, it is set to `8080`. - `--reload` argument to enable auto-reloading of the server when changes are made to the code. By default, it is set to `False`. - `--database-uri` argument to specify the database URI. Example values include `sqlite:///database.sqlite` for SQLite and `postgresql+psycopg://user:password@localhost/dbname` for PostgreSQL. If this is not specified, the database URIL defaults to a `database.sqlite` file in the `--appdir` directory. -- `--upgrade-database` argument to upgrade the database schema to the latest version. By default, it is set to `False`. Now that you have AutoGen Studio installed and running, you are ready to explore its capabilities, including defining and modifying agent workflows, interacting with agents and sessions, and expanding agent skills. @@ -116,11 +115,12 @@ npm run start We welcome contributions to AutoGen Studio. We recommend the following general steps to contribute to the project: - Review the overall AutoGen project [contribution guide](https://github.com/microsoft/autogen?tab=readme-ov-file#contributing) -- Please review the AutoGen Studio [roadmap](https://github.com/microsoft/autogen/issues/4006) to get a sense of the current priorities for the project. Help is appreciated especially with Studio issues tagged with `help-wanted` +- Please review the AutoGen Studio [roadmap](https://github.com/microsoft/autogen/issues/737) to get a sense of the current priorities for the project. Help is appreciated especially with Studio issues tagged with `help-wanted` - Please initiate a discussion on the roadmap issue or a new issue to discuss your proposed contribution. +- Please review the autogenstudio dev branch here [dev branch](https://github.com/microsoft/autogen/tree/autogenstudio) and use as a base for your contribution. This way, your contribution will be aligned with the latest changes in the AutoGen Studio project. - Submit a pull request with your contribution! - If you are modifying AutoGen Studio, it has its own devcontainer. See instructions in `.devcontainer/README.md` to use it -- Please use the tag `proj-studio` for any issues, questions, and PRs related to Studio +- Please use the tag `studio` for any issues, questions, and PRs related to Studio ## FAQ diff --git a/python/packages/autogen-studio/autogenstudio/teammanager.py b/python/packages/autogen-studio/autogenstudio/teammanager.py index ebe4302c5f5..6e9ef170441 100644 --- a/python/packages/autogen-studio/autogenstudio/teammanager.py +++ b/python/packages/autogen-studio/autogenstudio/teammanager.py @@ -2,7 +2,7 @@ import time from .database import ComponentFactory, Component from .datamodel import TeamResult, TaskResult, ComponentConfigInput -from autogen_agentchat.messages import ChatMessage, AgentMessage +from autogen_agentchat.messages import InnerMessage, ChatMessage from autogen_core.base import CancellationToken @@ -35,7 +35,7 @@ async def run_stream( team_config: ComponentConfigInput, input_func: Optional[Callable] = None, cancellation_token: Optional[CancellationToken] = None - ) -> AsyncGenerator[Union[AgentMessage, ChatMessage, TaskResult], None]: + ) -> AsyncGenerator[Union[InnerMessage, ChatMessage, TaskResult], None]: """Stream the team's execution results""" start_time = time.time() diff --git a/python/packages/autogen-studio/autogenstudio/version.py b/python/packages/autogen-studio/autogenstudio/version.py index a6f047da1cb..1c67823dfd1 100644 --- a/python/packages/autogen-studio/autogenstudio/version.py +++ b/python/packages/autogen-studio/autogenstudio/version.py @@ -1,3 +1,3 @@ -VERSION = "0.4.0.dev37" +VERSION = "0.4.0.dev35" __version__ = VERSION APP_NAME = "autogenstudio" diff --git a/python/packages/autogen-studio/autogenstudio/web/managers/connection.py b/python/packages/autogen-studio/autogenstudio/web/managers/connection.py index 5ed613cbb83..6db34f06c8d 100644 --- a/python/packages/autogen-studio/autogenstudio/web/managers/connection.py +++ b/python/packages/autogen-studio/autogenstudio/web/managers/connection.py @@ -9,7 +9,7 @@ from ...datamodel import Run, RunStatus, TeamResult from ...database import DatabaseManager from ...teammanager import TeamManager -from autogen_agentchat.messages import AgentMessage, ChatMessage, TextMessage +from autogen_agentchat.messages import InnerMessage, ChatMessage, TextMessage from autogen_core.base import CancellationToken logger = logging.getLogger(__name__) @@ -235,7 +235,7 @@ def _format_message(self, message: Any) -> Optional[dict]: Optional[dict]: Formatted message or None if formatting fails """ try: - if isinstance(message, (AgentMessage, ChatMessage)): + if isinstance(message, (InnerMessage, ChatMessage)): return { "type": "message", "data": message.model_dump() diff --git a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/edge.tsx b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/edge.tsx index 67d82b1ca8e..b569110e9be 100644 --- a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/edge.tsx +++ b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/edge.tsx @@ -126,7 +126,6 @@ export const CustomEdge: React.FC = ({ style={{ ...style, strokeWidth: finalStrokeWidth, - stroke: data.routingType === "secondary" ? "#0891b2" : style.stroke, }} markerEnd={markerEnd} /> diff --git a/python/uv.lock b/python/uv.lock index 4602559d0e0..04d981b1385 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -165,6 +165,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/05/be/6a403b464dcab3631fe8e27b0f1d906d9e45c5e92aca97ee007e5a895560/aiohttp-3.10.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c1277cd707c465cd09572a774559a3cc7c7a28802eb3a2a9472588f062097205", size = 1306186 }, { url = "https://files.pythonhosted.org/packages/8e/fd/bb50fe781068a736a02bf5c7ad5f3ab53e39f1d1e63110da6d30f7605edc/aiohttp-3.10.10-cp312-cp312-win32.whl", hash = "sha256:59bb3c54aa420521dc4ce3cc2c3fe2ad82adf7b09403fa1f48ae45c0cbde6628", size = 359289 }, { url = "https://files.pythonhosted.org/packages/70/9e/5add7e240f77ef67c275c82cc1d08afbca57b77593118c1f6e920ae8ad3f/aiohttp-3.10.10-cp312-cp312-win_amd64.whl", hash = "sha256:0e1b370d8007c4ae31ee6db7f9a2fe801a42b146cec80a86766e7ad5c4a259cf", size = 379313 }, + { url = "https://files.pythonhosted.org/packages/b1/eb/618b1b76c7fe8082a71c9d62e3fe84c5b9af6703078caa9ec57850a12080/aiohttp-3.10.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ad7593bb24b2ab09e65e8a1d385606f0f47c65b5a2ae6c551db67d6653e78c28", size = 576114 }, + { url = "https://files.pythonhosted.org/packages/aa/37/3126995d7869f8b30d05381b81a2d4fb4ec6ad313db788e009bc6d39c211/aiohttp-3.10.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1eb89d3d29adaf533588f209768a9c02e44e4baf832b08118749c5fad191781d", size = 391901 }, + { url = "https://files.pythonhosted.org/packages/3e/f2/8fdfc845be1f811c31ceb797968523813f8e1263ee3e9120d61253f6848f/aiohttp-3.10.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3fe407bf93533a6fa82dece0e74dbcaaf5d684e5a51862887f9eaebe6372cd79", size = 387418 }, + { url = "https://files.pythonhosted.org/packages/60/d5/33d2061d36bf07e80286e04b7e0a4de37ce04b5ebfed72dba67659a05250/aiohttp-3.10.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50aed5155f819873d23520919e16703fc8925e509abbb1a1491b0087d1cd969e", size = 1287073 }, + { url = "https://files.pythonhosted.org/packages/00/52/affb55be16a4747740bd630b4c002dac6c5eac42f9bb64202fc3cf3f1930/aiohttp-3.10.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f05e9727ce409358baa615dbeb9b969db94324a79b5a5cea45d39bdb01d82e6", size = 1323612 }, + { url = "https://files.pythonhosted.org/packages/94/f2/cddb69b975387daa2182a8442566971d6410b8a0179bb4540d81c97b1611/aiohttp-3.10.10-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dffb610a30d643983aeb185ce134f97f290f8935f0abccdd32c77bed9388b42", size = 1368406 }, + { url = "https://files.pythonhosted.org/packages/c1/e4/afba7327da4d932da8c6e29aecaf855f9d52dace53ac15bfc8030a246f1b/aiohttp-3.10.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa6658732517ddabe22c9036479eabce6036655ba87a0224c612e1ae6af2087e", size = 1282761 }, + { url = "https://files.pythonhosted.org/packages/9f/6b/364856faa0c9031ea76e24ef0f7fef79cddd9fa8e7dba9a1771c6acc56b5/aiohttp-3.10.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:741a46d58677d8c733175d7e5aa618d277cd9d880301a380fd296975a9cdd7bc", size = 1236518 }, + { url = "https://files.pythonhosted.org/packages/46/af/c382846f8356fe64a7b5908bb9b477457aa23b71be7ed551013b7b7d4d87/aiohttp-3.10.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e00e3505cd80440f6c98c6d69269dcc2a119f86ad0a9fd70bccc59504bebd68a", size = 1250344 }, + { url = "https://files.pythonhosted.org/packages/87/53/294f87fc086fd0772d0ab82497beb9df67f0f27a8b3dd5742a2656db2bc6/aiohttp-3.10.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ffe595f10566f8276b76dc3a11ae4bb7eba1aac8ddd75811736a15b0d5311414", size = 1248956 }, + { url = "https://files.pythonhosted.org/packages/86/30/7d746717fe11bdfefb88bb6c09c5fc985d85c4632da8bb6018e273899254/aiohttp-3.10.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdfcf6443637c148c4e1a20c48c566aa694fa5e288d34b20fcdc58507882fed3", size = 1293379 }, + { url = "https://files.pythonhosted.org/packages/48/b9/45d670a834458db67a24258e9139ba61fa3bd7d69b98ecf3650c22806f8f/aiohttp-3.10.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d183cf9c797a5291e8301790ed6d053480ed94070637bfaad914dd38b0981f67", size = 1320108 }, + { url = "https://files.pythonhosted.org/packages/72/8c/804bb2e837a175635d2000a0659eafc15b2e9d92d3d81c8f69e141ecd0b0/aiohttp-3.10.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:77abf6665ae54000b98b3c742bc6ea1d1fb31c394bcabf8b5d2c1ac3ebfe7f3b", size = 1281546 }, + { url = "https://files.pythonhosted.org/packages/89/c0/862e6a9de3d6eeb126cd9d9ea388243b70df9b871ce1a42b193b7a4a77fc/aiohttp-3.10.10-cp313-cp313-win32.whl", hash = "sha256:4470c73c12cd9109db8277287d11f9dd98f77fc54155fc71a7738a83ffcc8ea8", size = 357516 }, + { url = "https://files.pythonhosted.org/packages/ae/63/3e1aee3e554263f3f1011cca50d78a4894ae16ce99bf78101ac3a2f0ef74/aiohttp-3.10.10-cp313-cp313-win_amd64.whl", hash = "sha256:486f7aabfa292719a2753c016cc3a8f8172965cabb3ea2e7f7436c7f5a22a151", size = 376785 }, ] [[package]] @@ -324,9 +339,6 @@ dependencies = [ [package.metadata] requires-dist = [{ name = "autogen-core", editable = "packages/autogen-core" }] -[package.metadata.requires-dev] -dev = [] - [[package]] name = "autogen-core" version = "0.4.0.dev7" @@ -499,9 +511,6 @@ requires-dist = [ { name = "playwright", marker = "extra == 'web-surfer'", specifier = ">=1.48.0" }, ] -[package.metadata.requires-dev] -dev = [] - [[package]] name = "autogen-magentic-one" version = "0.0.1" @@ -573,7 +582,7 @@ dev = [ [[package]] name = "autogenstudio" -version = "0.4.0.dev37" +version = "0.4.0.dev35" source = { editable = "packages/autogen-studio" } dependencies = [ { name = "alembic" }, @@ -755,6 +764,17 @@ wheels = [ { 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 }, { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 }, { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, ] [[package]] @@ -817,6 +837,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/fa/d3fc622de05a86f30beea5fc4e9ac46aead4731e73fd9055496732bcc0a4/charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", size = 144800 }, { url = "https://files.pythonhosted.org/packages/9a/65/bdb9bc496d7d190d725e96816e20e2ae3a6fa42a5cac99c3c3d6ff884118/charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", size = 94836 }, { url = "https://files.pythonhosted.org/packages/3e/67/7b72b69d25b89c0b3cea583ee372c43aa24df15f0e0f8d3982c57804984b/charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", size = 102187 }, + { url = "https://files.pythonhosted.org/packages/f3/89/68a4c86f1a0002810a27f12e9a7b22feb198c59b2f05231349fbce5c06f4/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", size = 194617 }, + { url = "https://files.pythonhosted.org/packages/4f/cd/8947fe425e2ab0aa57aceb7807af13a0e4162cd21eee42ef5b053447edf5/charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", size = 125310 }, + { url = "https://files.pythonhosted.org/packages/5b/f0/b5263e8668a4ee9becc2b451ed909e9c27058337fda5b8c49588183c267a/charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", size = 119126 }, + { url = "https://files.pythonhosted.org/packages/ff/6e/e445afe4f7fda27a533f3234b627b3e515a1b9429bc981c9a5e2aa5d97b6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", size = 139342 }, + { url = "https://files.pythonhosted.org/packages/a1/b2/4af9993b532d93270538ad4926c8e37dc29f2111c36f9c629840c57cd9b3/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", size = 149383 }, + { url = "https://files.pythonhosted.org/packages/fb/6f/4e78c3b97686b871db9be6f31d64e9264e889f8c9d7ab33c771f847f79b7/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", size = 142214 }, + { url = "https://files.pythonhosted.org/packages/2b/c9/1c8fe3ce05d30c87eff498592c89015b19fade13df42850aafae09e94f35/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", size = 144104 }, + { url = "https://files.pythonhosted.org/packages/ee/68/efad5dcb306bf37db7db338338e7bb8ebd8cf38ee5bbd5ceaaaa46f257e6/charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", size = 146255 }, + { url = "https://files.pythonhosted.org/packages/0c/75/1ed813c3ffd200b1f3e71121c95da3f79e6d2a96120163443b3ad1057505/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", size = 140251 }, + { url = "https://files.pythonhosted.org/packages/7d/0d/6f32255c1979653b448d3c709583557a4d24ff97ac4f3a5be156b2e6a210/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", size = 148474 }, + { url = "https://files.pythonhosted.org/packages/ac/a0/c1b5298de4670d997101fef95b97ac440e8c8d8b4efa5a4d1ef44af82f0d/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", size = 151849 }, + { url = "https://files.pythonhosted.org/packages/04/4f/b3961ba0c664989ba63e30595a3ed0875d6790ff26671e2aae2fdc28a399/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", size = 149781 }, + { url = "https://files.pythonhosted.org/packages/d8/90/6af4cd042066a4adad58ae25648a12c09c879efa4849c705719ba1b23d8c/charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482", size = 144970 }, + { url = "https://files.pythonhosted.org/packages/cc/67/e5e7e0cbfefc4ca79025238b43cdf8a2037854195b37d6417f3d0895c4c2/charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", size = 94973 }, + { url = "https://files.pythonhosted.org/packages/65/97/fc9bbc54ee13d33dc54a7fcf17b26368b18505500fc01e228c27b5222d80/charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", size = 102308 }, { url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 }, ] @@ -972,6 +1007,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/80/79/8bba39190d2ea17840925d287f1c6c3a7c60b58f5090444e9ecf176c540f/debugpy-1.8.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c1fd62ae0356e194f3e7b7a92acd931f71fe81c4b3be2c17a7b8a4b546ec2", size = 4170911 }, { url = "https://files.pythonhosted.org/packages/3b/19/5b3d312936db8eb281310fa27903459328ed722d845d594ba5feaeb2f0b3/debugpy-1.8.7-cp312-cp312-win32.whl", hash = "sha256:2f729228430ef191c1e4df72a75ac94e9bf77413ce5f3f900018712c9da0aaca", size = 5195476 }, { url = "https://files.pythonhosted.org/packages/9f/49/ad20b29f8c921fd5124530d3d39b8f2077efd51b71339a2eff02bba693e9/debugpy-1.8.7-cp312-cp312-win_amd64.whl", hash = "sha256:45c30aaefb3e1975e8a0258f5bbd26cd40cde9bfe71e9e5a7ac82e79bad64e39", size = 5235031 }, + { url = "https://files.pythonhosted.org/packages/41/95/29b247518d0a6afdb5249f5d05743c9c5bfaf4bd13a85b81cb5e1dc65837/debugpy-1.8.7-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:d050a1ec7e925f514f0f6594a1e522580317da31fbda1af71d1530d6ea1f2b40", size = 2517557 }, + { url = "https://files.pythonhosted.org/packages/4d/93/026e2000a0740e2f54b198f8dc317accf3a70b6524b2b15fa8e6eca74414/debugpy-1.8.7-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f4349a28e3228a42958f8ddaa6333d6f8282d5edaea456070e48609c5983b7", size = 4162703 }, + { url = "https://files.pythonhosted.org/packages/c3/92/a48e653b19a171434290ecdc5935b7a292a65488139c5271d6d0eceeb0f1/debugpy-1.8.7-cp313-cp313-win32.whl", hash = "sha256:11ad72eb9ddb436afb8337891a986302e14944f0f755fd94e90d0d71e9100bba", size = 5195220 }, + { url = "https://files.pythonhosted.org/packages/4e/b3/dc3c5527edafcd1a6d0f8c4ecc6c5c9bc431f77340cf4193328e98f0ac38/debugpy-1.8.7-cp313-cp313-win_amd64.whl", hash = "sha256:2efb84d6789352d7950b03d7f866e6d180284bc02c7e12cb37b489b7083d81aa", size = 5235333 }, { url = "https://files.pythonhosted.org/packages/51/b1/a0866521c71a6ae3d3ca320e74835163a4671b1367ba360a55a0a51e5a91/debugpy-1.8.7-py2.py3-none-any.whl", hash = "sha256:57b00de1c8d2c84a61b90880f7e5b6deaf4c312ecbde3a0e8912f2a56c4ac9ae", size = 5210683 }, ] @@ -1179,6 +1218,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/37/e0/47f87544055b3349b633a03c4d94b405956cf2437f4ab46d0928b74b7526/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f", size = 280569 }, { url = "https://files.pythonhosted.org/packages/f9/7c/490133c160fb6b84ed374c266f42800e33b50c3bbab1652764e6e1fc498a/frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8", size = 44721 }, { url = "https://files.pythonhosted.org/packages/b1/56/4e45136ffc6bdbfa68c29ca56ef53783ef4c2fd395f7cbf99a2624aa9aaa/frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f", size = 51329 }, + { url = "https://files.pythonhosted.org/packages/da/3b/915f0bca8a7ea04483622e84a9bd90033bab54bdf485479556c74fd5eaf5/frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953", size = 91538 }, + { url = "https://files.pythonhosted.org/packages/c7/d1/a7c98aad7e44afe5306a2b068434a5830f1470675f0e715abb86eb15f15b/frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0", size = 52849 }, + { url = "https://files.pythonhosted.org/packages/3a/c8/76f23bf9ab15d5f760eb48701909645f686f9c64fbb8982674c241fbef14/frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2", size = 50583 }, + { url = "https://files.pythonhosted.org/packages/1f/22/462a3dd093d11df623179d7754a3b3269de3b42de2808cddef50ee0f4f48/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f", size = 265636 }, + { url = "https://files.pythonhosted.org/packages/80/cf/e075e407fc2ae7328155a1cd7e22f932773c8073c1fc78016607d19cc3e5/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608", size = 270214 }, + { url = "https://files.pythonhosted.org/packages/a1/58/0642d061d5de779f39c50cbb00df49682832923f3d2ebfb0fedf02d05f7f/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b", size = 273905 }, + { url = "https://files.pythonhosted.org/packages/ab/66/3fe0f5f8f2add5b4ab7aa4e199f767fd3b55da26e3ca4ce2cc36698e50c4/frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840", size = 250542 }, + { url = "https://files.pythonhosted.org/packages/f6/b8/260791bde9198c87a465224e0e2bb62c4e716f5d198fc3a1dacc4895dbd1/frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439", size = 267026 }, + { url = "https://files.pythonhosted.org/packages/2e/a4/3d24f88c527f08f8d44ade24eaee83b2627793fa62fa07cbb7ff7a2f7d42/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de", size = 257690 }, + { url = "https://files.pythonhosted.org/packages/de/9a/d311d660420b2beeff3459b6626f2ab4fb236d07afbdac034a4371fe696e/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641", size = 253893 }, + { url = "https://files.pythonhosted.org/packages/c6/23/e491aadc25b56eabd0f18c53bb19f3cdc6de30b2129ee0bc39cd387cd560/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e", size = 267006 }, + { url = "https://files.pythonhosted.org/packages/08/c4/ab918ce636a35fb974d13d666dcbe03969592aeca6c3ab3835acff01f79c/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9", size = 276157 }, + { url = "https://files.pythonhosted.org/packages/c0/29/3b7a0bbbbe5a34833ba26f686aabfe982924adbdcafdc294a7a129c31688/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03", size = 264642 }, + { url = "https://files.pythonhosted.org/packages/ab/42/0595b3dbffc2e82d7fe658c12d5a5bafcd7516c6bf2d1d1feb5387caa9c1/frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c", size = 44914 }, + { url = "https://files.pythonhosted.org/packages/17/c4/b7db1206a3fea44bf3b838ca61deb6f74424a8a5db1dd53ecb21da669be6/frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28", size = 51167 }, { url = "https://files.pythonhosted.org/packages/c6/c8/a5be5b7550c10858fcf9b0ea054baccab474da77d37f1e828ce043a3a5d4/frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", size = 11901 }, ] @@ -1224,6 +1278,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975 }, { url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955 }, { url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655 }, + { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990 }, + { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175 }, + { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425 }, + { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736 }, + { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347 }, + { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583 }, + { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039 }, + { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716 }, + { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490 }, + { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731 }, + { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304 }, + { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537 }, + { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506 }, + { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 }, + { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 }, + { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 }, ] [[package]] @@ -1526,6 +1596,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/e8/e47734280e19cd465832e610e1c69367ee72947de738785c4b6fc4031e25/jiter-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e7b75436d4fa2032b2530ad989e4cb0ca74c655975e3ff49f91a1a3d7f4e1df2", size = 496023 }, { url = "https://files.pythonhosted.org/packages/52/01/5f65dd1387d39aa3fd4a98a5be1d8470e929a0cb0dd6cbfebaccd9a20ac5/jiter-0.6.1-cp312-none-win32.whl", hash = "sha256:883d2ced7c21bf06874fdeecab15014c1c6d82216765ca6deef08e335fa719e0", size = 197425 }, { url = "https://files.pythonhosted.org/packages/43/b2/bd6665030f7d7cd5d9182c62a869c3d5ceadd7bff9f1b305de9192e7dbf8/jiter-0.6.1-cp312-none-win_amd64.whl", hash = "sha256:91e63273563401aadc6c52cca64a7921c50b29372441adc104127b910e98a5b6", size = 198966 }, + { url = "https://files.pythonhosted.org/packages/23/38/7b48e0149778ff4b893567c9fd997ecfcc013e290375aa7823e1f681b3d3/jiter-0.6.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:852508a54fe3228432e56019da8b69208ea622a3069458252f725d634e955b31", size = 288674 }, + { url = "https://files.pythonhosted.org/packages/85/3b/96d15b483d82a637279da53a1d299dd5da6e029b9905bcd1a4e1f89b8e4f/jiter-0.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f491cc69ff44e5a1e8bc6bf2b94c1f98d179e1aaf4a554493c171a5b2316b701", size = 301531 }, + { url = "https://files.pythonhosted.org/packages/cf/54/9681f112cbec4e197259e9db679bd4bc314f4bd24f74b9aa5e93073990b5/jiter-0.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc56c8f0b2a28ad4d8047f3ae62d25d0e9ae01b99940ec0283263a04724de1f3", size = 335954 }, + { url = "https://files.pythonhosted.org/packages/4a/4d/f9c0ba82b154c66278e28348086086264ccf50622ae468ec215e4bbc2873/jiter-0.6.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:51b58f7a0d9e084a43b28b23da2b09fc5e8df6aa2b6a27de43f991293cab85fd", size = 353996 }, + { url = "https://files.pythonhosted.org/packages/ee/be/7f26b258ef190f6d582e21c76c7dd1097753a2203bad3e1643f45392720a/jiter-0.6.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f79ce15099154c90ef900d69c6b4c686b64dfe23b0114e0971f2fecd306ec6c", size = 369733 }, + { url = "https://files.pythonhosted.org/packages/5f/85/037ed5261fa622312471ef5520b2135c26b29256c83adc16c8cc55dc4108/jiter-0.6.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:03a025b52009f47e53ea619175d17e4ded7c035c6fbd44935cb3ada11e1fd592", size = 389920 }, + { url = "https://files.pythonhosted.org/packages/a8/f3/2e01294712faa476be9e6ceb49e424c3919e03415ded76d103378a06bb80/jiter-0.6.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c74a8d93718137c021d9295248a87c2f9fdc0dcafead12d2930bc459ad40f885", size = 324138 }, + { url = "https://files.pythonhosted.org/packages/00/45/50377814f21b6412c7785be27f2dace225af52e0af20be7af899a7e3f264/jiter-0.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40b03b75f903975f68199fc4ec73d546150919cb7e534f3b51e727c4d6ccca5a", size = 367610 }, + { url = "https://files.pythonhosted.org/packages/af/fc/51ba30875125381bfe21a1572c176de1a7dd64a386a7498355fc100decc4/jiter-0.6.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:825651a3f04cf92a661d22cad61fc913400e33aa89b3e3ad9a6aa9dc8a1f5a71", size = 512945 }, + { url = "https://files.pythonhosted.org/packages/69/60/af26168bd4916f9199ed433161e9f8a4eeda581a4e5982560d0f22dd146c/jiter-0.6.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:928bf25eb69ddb292ab8177fe69d3fbf76c7feab5fce1c09265a7dccf25d3991", size = 494963 }, + { url = "https://files.pythonhosted.org/packages/f3/2f/4f3cc5c9067a6fd1020d3c4365546535a69ed77da7fba2bec24368f3662c/jiter-0.6.1-cp313-none-win32.whl", hash = "sha256:352cd24121e80d3d053fab1cc9806258cad27c53cad99b7a3cac57cf934b12e4", size = 196869 }, + { url = "https://files.pythonhosted.org/packages/7a/fc/8709ee90837e94790d8b50db51c7b8a70e86e41b2c81e824c20b0ecfeba7/jiter-0.6.1-cp313-none-win_amd64.whl", hash = "sha256:be7503dd6f4bf02c2a9bacb5cc9335bc59132e7eee9d3e931b13d76fd80d7fda", size = 198919 }, ] [[package]] @@ -2138,6 +2220,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7d/ed/e6276c8d9668028213df01f598f385b05b55a4e1b4662ee12ef05dab35aa/lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d", size = 5012542 }, { url = "https://files.pythonhosted.org/packages/36/88/684d4e800f5aa28df2a991a6a622783fb73cf0e46235cfa690f9776f032e/lxml-5.3.0-cp312-cp312-win32.whl", hash = "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30", size = 3486454 }, { url = "https://files.pythonhosted.org/packages/fc/82/ace5a5676051e60355bd8fb945df7b1ba4f4fb8447f2010fb816bfd57724/lxml-5.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f", size = 3816857 }, + { url = "https://files.pythonhosted.org/packages/94/6a/42141e4d373903bfea6f8e94b2f554d05506dfda522ada5343c651410dc8/lxml-5.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a", size = 8156284 }, + { url = "https://files.pythonhosted.org/packages/91/5e/fa097f0f7d8b3d113fb7312c6308af702f2667f22644441715be961f2c7e/lxml-5.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd", size = 4432407 }, + { url = "https://files.pythonhosted.org/packages/2d/a1/b901988aa6d4ff937f2e5cfc114e4ec561901ff00660c3e56713642728da/lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51", size = 5048331 }, + { url = "https://files.pythonhosted.org/packages/30/0f/b2a54f48e52de578b71bbe2a2f8160672a8a5e103df3a78da53907e8c7ed/lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b", size = 4744835 }, + { url = "https://files.pythonhosted.org/packages/82/9d/b000c15538b60934589e83826ecbc437a1586488d7c13f8ee5ff1f79a9b8/lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002", size = 5316649 }, + { url = "https://files.pythonhosted.org/packages/e3/ee/ffbb9eaff5e541922611d2c56b175c45893d1c0b8b11e5a497708a6a3b3b/lxml-5.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4", size = 4812046 }, + { url = "https://files.pythonhosted.org/packages/15/ff/7ff89d567485c7b943cdac316087f16b2399a8b997007ed352a1248397e5/lxml-5.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492", size = 4918597 }, + { url = "https://files.pythonhosted.org/packages/c6/a3/535b6ed8c048412ff51268bdf4bf1cf052a37aa7e31d2e6518038a883b29/lxml-5.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3", size = 4738071 }, + { url = "https://files.pythonhosted.org/packages/7a/8f/cbbfa59cb4d4fd677fe183725a76d8c956495d7a3c7f111ab8f5e13d2e83/lxml-5.3.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4", size = 5342213 }, + { url = "https://files.pythonhosted.org/packages/5c/fb/db4c10dd9958d4b52e34d1d1f7c1f434422aeaf6ae2bbaaff2264351d944/lxml-5.3.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367", size = 4893749 }, + { url = "https://files.pythonhosted.org/packages/f2/38/bb4581c143957c47740de18a3281a0cab7722390a77cc6e610e8ebf2d736/lxml-5.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832", size = 4945901 }, + { url = "https://files.pythonhosted.org/packages/fc/d5/18b7de4960c731e98037bd48fa9f8e6e8f2558e6fbca4303d9b14d21ef3b/lxml-5.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff", size = 4815447 }, + { url = "https://files.pythonhosted.org/packages/97/a8/cd51ceaad6eb849246559a8ef60ae55065a3df550fc5fcd27014361c1bab/lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd", size = 5411186 }, + { url = "https://files.pythonhosted.org/packages/89/c3/1e3dabab519481ed7b1fdcba21dcfb8832f57000733ef0e71cf6d09a5e03/lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb", size = 5324481 }, + { url = "https://files.pythonhosted.org/packages/b6/17/71e9984cf0570cd202ac0a1c9ed5c1b8889b0fc8dc736f5ef0ffb181c284/lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b", size = 5011053 }, + { url = "https://files.pythonhosted.org/packages/69/68/9f7e6d3312a91e30829368c2b3217e750adef12a6f8eb10498249f4e8d72/lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957", size = 3485634 }, + { url = "https://files.pythonhosted.org/packages/7d/db/214290d58ad68c587bd5d6af3d34e56830438733d0d0856c0275fde43652/lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d", size = 3814417 }, { url = "https://files.pythonhosted.org/packages/99/f7/b73a431c8500565aa500e99e60b448d305eaf7c0b4c893c7c5a8a69cc595/lxml-5.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7b1cd427cb0d5f7393c31b7496419da594fe600e6fdc4b105a54f82405e6626c", size = 3925431 }, { url = "https://files.pythonhosted.org/packages/db/48/4a206623c0d093d0e3b15f415ffb4345b0bdf661a3d0b15a112948c033c7/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51806cfe0279e06ed8500ce19479d757db42a30fd509940b1701be9c86a5ff9a", size = 4216683 }, { url = "https://files.pythonhosted.org/packages/54/47/577820c45dd954523ae8453b632d91e76da94ca6d9ee40d8c98dd86f916b/lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee70d08fd60c9565ba8190f41a46a54096afa0eeb8f76bd66f2c25d3b1b83005", size = 4326732 }, @@ -2239,6 +2338,26 @@ wheels = [ { 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]] @@ -2352,6 +2471,17 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/23/f0/d4101d4da054f04274995ddc4086c2715d9b93111eb9ed49686c0f7ccc8a/msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", size = 394254 }, { url = "https://files.pythonhosted.org/packages/1c/12/cf07458f35d0d775ff3a2dc5559fa2e1fcd06c46f1ef510e594ebefdca01/msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b", size = 69085 }, { url = "https://files.pythonhosted.org/packages/73/80/2708a4641f7d553a63bc934a3eb7214806b5b39d200133ca7f7afb0a53e8/msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", size = 75347 }, + { url = "https://files.pythonhosted.org/packages/c8/b0/380f5f639543a4ac413e969109978feb1f3c66e931068f91ab6ab0f8be00/msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf", size = 151142 }, + { url = "https://files.pythonhosted.org/packages/c8/ee/be57e9702400a6cb2606883d55b05784fada898dfc7fd12608ab1fdb054e/msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330", size = 84523 }, + { url = "https://files.pythonhosted.org/packages/7e/3a/2919f63acca3c119565449681ad08a2f84b2171ddfcff1dba6959db2cceb/msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734", size = 81556 }, + { url = "https://files.pythonhosted.org/packages/7c/43/a11113d9e5c1498c145a8925768ea2d5fce7cbab15c99cda655aa09947ed/msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e", size = 392105 }, + { url = "https://files.pythonhosted.org/packages/2d/7b/2c1d74ca6c94f70a1add74a8393a0138172207dc5de6fc6269483519d048/msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca", size = 399979 }, + { url = "https://files.pythonhosted.org/packages/82/8c/cf64ae518c7b8efc763ca1f1348a96f0e37150061e777a8ea5430b413a74/msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915", size = 383816 }, + { url = "https://files.pythonhosted.org/packages/69/86/a847ef7a0f5ef3fa94ae20f52a4cacf596a4e4a010197fbcc27744eb9a83/msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d", size = 380973 }, + { url = "https://files.pythonhosted.org/packages/aa/90/c74cf6e1126faa93185d3b830ee97246ecc4fe12cf9d2d31318ee4246994/msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434", size = 387435 }, + { url = "https://files.pythonhosted.org/packages/7a/40/631c238f1f338eb09f4acb0f34ab5862c4e9d7eda11c1b685471a4c5ea37/msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c", size = 399082 }, + { url = "https://files.pythonhosted.org/packages/e9/1b/fa8a952be252a1555ed39f97c06778e3aeb9123aa4cccc0fd2acd0b4e315/msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc", size = 69037 }, + { url = "https://files.pythonhosted.org/packages/b6/bc/8bd826dd03e022153bfa1766dcdec4976d6c818865ed54223d71f07862b3/msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f", size = 75140 }, ] [[package]] @@ -2408,6 +2538,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/77/00/8538f11e3356b5d95fa4b024aa566cde7a38aa7a5f08f4912b32a037c5dc/multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3", size = 125360 }, { url = "https://files.pythonhosted.org/packages/be/05/5d334c1f2462d43fec2363cd00b1c44c93a78c3925d952e9a71caf662e96/multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133", size = 26382 }, { url = "https://files.pythonhosted.org/packages/a3/bf/f332a13486b1ed0496d624bcc7e8357bb8053823e8cd4b9a18edc1d97e73/multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1", size = 28529 }, + { url = "https://files.pythonhosted.org/packages/22/67/1c7c0f39fe069aa4e5d794f323be24bf4d33d62d2a348acdb7991f8f30db/multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008", size = 48771 }, + { url = "https://files.pythonhosted.org/packages/3c/25/c186ee7b212bdf0df2519eacfb1981a017bda34392c67542c274651daf23/multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f", size = 29533 }, + { url = "https://files.pythonhosted.org/packages/67/5e/04575fd837e0958e324ca035b339cea174554f6f641d3fb2b4f2e7ff44a2/multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28", size = 29595 }, + { url = "https://files.pythonhosted.org/packages/d3/b2/e56388f86663810c07cfe4a3c3d87227f3811eeb2d08450b9e5d19d78876/multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b", size = 130094 }, + { url = "https://files.pythonhosted.org/packages/6c/ee/30ae9b4186a644d284543d55d491fbd4239b015d36b23fea43b4c94f7052/multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c", size = 134876 }, + { url = "https://files.pythonhosted.org/packages/84/c7/70461c13ba8ce3c779503c70ec9d0345ae84de04521c1f45a04d5f48943d/multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3", size = 133500 }, + { url = "https://files.pythonhosted.org/packages/4a/9f/002af221253f10f99959561123fae676148dd730e2daa2cd053846a58507/multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44", size = 131099 }, + { url = "https://files.pythonhosted.org/packages/82/42/d1c7a7301d52af79d88548a97e297f9d99c961ad76bbe6f67442bb77f097/multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2", size = 120403 }, + { url = "https://files.pythonhosted.org/packages/68/f3/471985c2c7ac707547553e8f37cff5158030d36bdec4414cb825fbaa5327/multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3", size = 125348 }, + { url = "https://files.pythonhosted.org/packages/67/2c/e6df05c77e0e433c214ec1d21ddd203d9a4770a1f2866a8ca40a545869a0/multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa", size = 119673 }, + { url = "https://files.pythonhosted.org/packages/c5/cd/bc8608fff06239c9fb333f9db7743a1b2eafe98c2666c9a196e867a3a0a4/multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa", size = 129927 }, + { url = "https://files.pythonhosted.org/packages/44/8e/281b69b7bc84fc963a44dc6e0bbcc7150e517b91df368a27834299a526ac/multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4", size = 128711 }, + { url = "https://files.pythonhosted.org/packages/12/a4/63e7cd38ed29dd9f1881d5119f272c898ca92536cdb53ffe0843197f6c85/multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6", size = 125519 }, + { url = "https://files.pythonhosted.org/packages/38/e0/4f5855037a72cd8a7a2f60a3952d9aa45feedb37ae7831642102604e8a37/multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81", size = 26426 }, + { url = "https://files.pythonhosted.org/packages/7e/a5/17ee3a4db1e310b7405f5d25834460073a8ccd86198ce044dfaf69eac073/multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774", size = 28531 }, { url = "https://files.pythonhosted.org/packages/99/b7/b9e70fde2c0f0c9af4cc5277782a89b66d35948ea3369ec9f598358c3ac5/multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", size = 10051 }, ] @@ -2750,6 +2895,13 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ad/9b/be8b3d3aec42aa47f6058482ace0d2ca3023477a46643d766e96281d5d31/orjson-3.10.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:730ed5350147db7beb23ddaf072f490329e90a1d059711d364b49fe352ec987b", size = 170424 }, { url = "https://files.pythonhosted.org/packages/1b/15/a4cc61e23c39b9dec4620cb95817c83c84078be1771d602f6d03f0e5c696/orjson-3.10.10-cp312-none-win32.whl", hash = "sha256:a8f4bf5f1c85bea2170800020d53a8877812892697f9c2de73d576c9307a8a5f", size = 145132 }, { url = "https://files.pythonhosted.org/packages/9f/8a/ce7c28e4ea337f6d95261345d7c61322f8561c52f57b263a3ad7025984f4/orjson-3.10.10-cp312-none-win_amd64.whl", hash = "sha256:384cd13579a1b4cd689d218e329f459eb9ddc504fa48c5a83ef4889db7fd7a4f", size = 139389 }, + { url = "https://files.pythonhosted.org/packages/0c/69/f1c4382cd44bdaf10006c4e82cb85d2bcae735369f84031e203c4e5d87de/orjson-3.10.10-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44bffae68c291f94ff5a9b4149fe9d1bdd4cd0ff0fb575bcea8351d48db629a1", size = 270695 }, + { url = "https://files.pythonhosted.org/packages/61/29/aeb5153271d4953872b06ed239eb54993a5f344353727c42d3aabb2046f6/orjson-3.10.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e27b4c6437315df3024f0835887127dac2a0a3ff643500ec27088d2588fa5ae1", size = 141632 }, + { url = "https://files.pythonhosted.org/packages/bc/a2/c8ac38d8fb461a9b717c766fbe1f7d3acf9bde2f12488eb13194960782e4/orjson-3.10.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca84df16d6b49325a4084fd8b2fe2229cb415e15c46c529f868c3387bb1339d", size = 144854 }, + { url = "https://files.pythonhosted.org/packages/79/51/e7698fdb28bdec633888cc667edc29fd5376fce9ade0a5b3e22f5ebe0343/orjson-3.10.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c14ce70e8f39bd71f9f80423801b5d10bf93d1dceffdecd04df0f64d2c69bc01", size = 172023 }, + { url = "https://files.pythonhosted.org/packages/02/2d/0d99c20878658c7e33b90e6a4bb75cf2924d6ff29c2365262cff3c26589a/orjson-3.10.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:24ac62336da9bda1bd93c0491eff0613003b48d3cb5d01470842e7b52a40d5b4", size = 170429 }, + { url = "https://files.pythonhosted.org/packages/cd/45/6a4a446f4fb29bb4703c3537d5c6a2bf7fed768cb4d7b7dce9d71b72fc93/orjson-3.10.10-cp313-none-win32.whl", hash = "sha256:eb0a42831372ec2b05acc9ee45af77bcaccbd91257345f93780a8e654efc75db", size = 145099 }, + { url = "https://files.pythonhosted.org/packages/72/6e/4631fe219a4203aa111e9bb763ad2e2e0cdd1a03805029e4da124d96863f/orjson-3.10.10-cp313-none-win_amd64.whl", hash = "sha256:f0c4f37f8bf3f1075c6cc8dd8a9f843689a4b618628f8812d0a71e6968b95ffd", size = 139176 }, ] [[package]] @@ -2806,6 +2958,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", size = 16359235 }, { url = "https://files.pythonhosted.org/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", size = 14056756 }, { url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248 }, + { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643 }, + { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573 }, + { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085 }, + { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809 }, + { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316 }, + { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055 }, + { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175 }, + { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650 }, + { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177 }, + { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526 }, + { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013 }, + { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620 }, + { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 }, ] [[package]] @@ -2908,6 +3073,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/af/3a/da80224a6eb15bba7a0dcb2346e2b686bb9bf98378c0b4353cd88e62b171/pillow-11.0.0-cp312-cp312-win32.whl", hash = "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6", size = 2249631 }, { url = "https://files.pythonhosted.org/packages/57/97/73f756c338c1d86bb802ee88c3cab015ad7ce4b838f8a24f16b676b1ac7c/pillow-11.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47", size = 2567533 }, { url = "https://files.pythonhosted.org/packages/0b/30/2b61876e2722374558b871dfbfcbe4e406626d63f4f6ed92e9c8e24cac37/pillow-11.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25", size = 2254890 }, + { url = "https://files.pythonhosted.org/packages/63/24/e2e15e392d00fcf4215907465d8ec2a2f23bcec1481a8ebe4ae760459995/pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699", size = 3147300 }, + { url = "https://files.pythonhosted.org/packages/43/72/92ad4afaa2afc233dc44184adff289c2e77e8cd916b3ddb72ac69495bda3/pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38", size = 2978742 }, + { url = "https://files.pythonhosted.org/packages/9e/da/c8d69c5bc85d72a8523fe862f05ababdc52c0a755cfe3d362656bb86552b/pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2", size = 4194349 }, + { url = "https://files.pythonhosted.org/packages/cd/e8/686d0caeed6b998351d57796496a70185376ed9c8ec7d99e1d19ad591fc6/pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2", size = 4298714 }, + { url = "https://files.pythonhosted.org/packages/ec/da/430015cec620d622f06854be67fd2f6721f52fc17fca8ac34b32e2d60739/pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527", size = 4208514 }, + { url = "https://files.pythonhosted.org/packages/44/ae/7e4f6662a9b1cb5f92b9cc9cab8321c381ffbee309210940e57432a4063a/pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa", size = 4380055 }, + { url = "https://files.pythonhosted.org/packages/74/d5/1a807779ac8a0eeed57f2b92a3c32ea1b696e6140c15bd42eaf908a261cd/pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f", size = 4296751 }, + { url = "https://files.pythonhosted.org/packages/38/8c/5fa3385163ee7080bc13026d59656267daaaaf3c728c233d530e2c2757c8/pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb", size = 4430378 }, + { url = "https://files.pythonhosted.org/packages/ca/1d/ad9c14811133977ff87035bf426875b93097fb50af747793f013979facdb/pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798", size = 2249588 }, + { url = "https://files.pythonhosted.org/packages/fb/01/3755ba287dac715e6afdb333cb1f6d69740a7475220b4637b5ce3d78cec2/pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de", size = 2567509 }, + { url = "https://files.pythonhosted.org/packages/c0/98/2c7d727079b6be1aba82d195767d35fcc2d32204c7a5820f822df5330152/pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84", size = 2254791 }, + { url = "https://files.pythonhosted.org/packages/eb/38/998b04cc6f474e78b563716b20eecf42a2fa16a84589d23c8898e64b0ffd/pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b", size = 3150854 }, + { url = "https://files.pythonhosted.org/packages/13/8e/be23a96292113c6cb26b2aa3c8b3681ec62b44ed5c2bd0b258bd59503d3c/pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003", size = 2982369 }, + { url = "https://files.pythonhosted.org/packages/97/8a/3db4eaabb7a2ae8203cd3a332a005e4aba00067fc514aaaf3e9721be31f1/pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2", size = 4333703 }, + { url = "https://files.pythonhosted.org/packages/28/ac/629ffc84ff67b9228fe87a97272ab125bbd4dc462745f35f192d37b822f1/pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a", size = 4412550 }, + { url = "https://files.pythonhosted.org/packages/d6/07/a505921d36bb2df6868806eaf56ef58699c16c388e378b0dcdb6e5b2fb36/pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8", size = 4461038 }, + { url = "https://files.pythonhosted.org/packages/d6/b9/fb620dd47fc7cc9678af8f8bd8c772034ca4977237049287e99dda360b66/pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8", size = 2253197 }, + { url = "https://files.pythonhosted.org/packages/df/86/25dde85c06c89d7fc5db17940f07aae0a56ac69aa9ccb5eb0f09798862a8/pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904", size = 2572169 }, + { url = "https://files.pythonhosted.org/packages/51/85/9c33f2517add612e17f3381aee7c4072779130c634921a756c97bc29fb49/pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3", size = 2256828 }, { url = "https://files.pythonhosted.org/packages/36/57/42a4dd825eab762ba9e690d696d894ba366e06791936056e26e099398cda/pillow-11.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1187739620f2b365de756ce086fdb3604573337cc28a0d3ac4a01ab6b2d2a6d2", size = 3119239 }, { url = "https://files.pythonhosted.org/packages/98/f7/25f9f9e368226a1d6cf3507081a1a7944eddd3ca7821023377043f5a83c8/pillow-11.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fbbcb7b57dc9c794843e3d1258c0fbf0f48656d46ffe9e09b63bbd6e8cd5d0a2", size = 2950803 }, { url = "https://files.pythonhosted.org/packages/59/01/98ead48a6c2e31e6185d4c16c978a67fe3ccb5da5c2ff2ba8475379bb693/pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d203af30149ae339ad1b4f710d9844ed8796e97fda23ffbc4cc472968a47d0b", size = 3281098 }, @@ -3067,6 +3251,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fd/bd/8657918a35d50b18a9e4d78a5df7b6c82a637a311ab20851eef4326305c1/propcache-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348", size = 235922 }, { url = "https://files.pythonhosted.org/packages/a8/6f/ec0095e1647b4727db945213a9f395b1103c442ef65e54c62e92a72a3f75/propcache-0.2.0-cp312-cp312-win32.whl", hash = "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5", size = 40177 }, { url = "https://files.pythonhosted.org/packages/20/a2/bd0896fdc4f4c1db46d9bc361c8c79a9bf08ccc08ba054a98e38e7ba1557/propcache-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3", size = 44446 }, + { url = "https://files.pythonhosted.org/packages/a8/a7/5f37b69197d4f558bfef5b4bceaff7c43cc9b51adf5bd75e9081d7ea80e4/propcache-0.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7", size = 78120 }, + { url = "https://files.pythonhosted.org/packages/c8/cd/48ab2b30a6b353ecb95a244915f85756d74f815862eb2ecc7a518d565b48/propcache-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763", size = 45127 }, + { url = "https://files.pythonhosted.org/packages/a5/ba/0a1ef94a3412aab057bd996ed5f0ac7458be5bf469e85c70fa9ceb43290b/propcache-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d", size = 44419 }, + { url = "https://files.pythonhosted.org/packages/b4/6c/ca70bee4f22fa99eacd04f4d2f1699be9d13538ccf22b3169a61c60a27fa/propcache-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a", size = 229611 }, + { url = "https://files.pythonhosted.org/packages/19/70/47b872a263e8511ca33718d96a10c17d3c853aefadeb86dc26e8421184b9/propcache-0.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b", size = 234005 }, + { url = "https://files.pythonhosted.org/packages/4f/be/3b0ab8c84a22e4a3224719099c1229ddfdd8a6a1558cf75cb55ee1e35c25/propcache-0.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb", size = 237270 }, + { url = "https://files.pythonhosted.org/packages/04/d8/f071bb000d4b8f851d312c3c75701e586b3f643fe14a2e3409b1b9ab3936/propcache-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf", size = 231877 }, + { url = "https://files.pythonhosted.org/packages/93/e7/57a035a1359e542bbb0a7df95aad6b9871ebee6dce2840cb157a415bd1f3/propcache-0.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2", size = 217848 }, + { url = "https://files.pythonhosted.org/packages/f0/93/d1dea40f112ec183398fb6c42fde340edd7bab202411c4aa1a8289f461b6/propcache-0.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f", size = 216987 }, + { url = "https://files.pythonhosted.org/packages/62/4c/877340871251145d3522c2b5d25c16a1690ad655fbab7bb9ece6b117e39f/propcache-0.2.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136", size = 212451 }, + { url = "https://files.pythonhosted.org/packages/7c/bb/a91b72efeeb42906ef58ccf0cdb87947b54d7475fee3c93425d732f16a61/propcache-0.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325", size = 212879 }, + { url = "https://files.pythonhosted.org/packages/9b/7f/ee7fea8faac57b3ec5d91ff47470c6c5d40d7f15d0b1fccac806348fa59e/propcache-0.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44", size = 222288 }, + { url = "https://files.pythonhosted.org/packages/ff/d7/acd67901c43d2e6b20a7a973d9d5fd543c6e277af29b1eb0e1f7bd7ca7d2/propcache-0.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83", size = 228257 }, + { url = "https://files.pythonhosted.org/packages/8d/6f/6272ecc7a8daad1d0754cfc6c8846076a8cb13f810005c79b15ce0ef0cf2/propcache-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544", size = 221075 }, + { url = "https://files.pythonhosted.org/packages/7c/bd/c7a6a719a6b3dd8b3aeadb3675b5783983529e4a3185946aa444d3e078f6/propcache-0.2.0-cp313-cp313-win32.whl", hash = "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032", size = 39654 }, + { url = "https://files.pythonhosted.org/packages/88/e7/0eef39eff84fa3e001b44de0bd41c7c0e3432e7648ffd3d64955910f002d/propcache-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e", size = 43705 }, { url = "https://files.pythonhosted.org/packages/3d/b6/e6d98278f2d49b22b4d033c9f792eda783b9ab2094b041f013fc69bcde87/propcache-0.2.0-py3-none-any.whl", hash = "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036", size = 11603 }, ] @@ -3090,6 +3290,8 @@ version = "6.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/26/10/2a30b13c61e7cf937f4adf90710776b7918ed0a9c434e2c38224732af310/psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a", size = 508565 } wheels = [ + { url = "https://files.pythonhosted.org/packages/da/2b/f4dea5d993d9cd22ad958eea828a41d5d225556123d372f02547c29c4f97/psutil-6.1.0-cp27-none-win32.whl", hash = "sha256:9118f27452b70bb1d9ab3198c1f626c2499384935aaf55388211ad982611407e", size = 246648 }, + { url = "https://files.pythonhosted.org/packages/9f/14/4aa97a7f2e0ac33a050d990ab31686d651ae4ef8c86661fef067f00437b9/psutil-6.1.0-cp27-none-win_amd64.whl", hash = "sha256:a8506f6119cff7015678e2bce904a4da21025cc70ad283a53b099e7620061d85", size = 249905 }, { url = "https://files.pythonhosted.org/packages/01/9e/8be43078a171381953cfee33c07c0d628594b5dbfc5157847b85022c2c1b/psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688", size = 247762 }, { url = "https://files.pythonhosted.org/packages/1d/cb/313e80644ea407f04f6602a9e23096540d9dc1878755f3952ea8d3d104be/psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e", size = 248777 }, { url = "https://files.pythonhosted.org/packages/65/8e/bcbe2025c587b5d703369b6a75b65d41d1367553da6e3f788aff91eaf5bd/psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38", size = 284259 }, @@ -3216,6 +3418,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e3/b9/41f7efe80f6ce2ed3ee3c2dcfe10ab7adc1172f778cc9659509a79518c43/pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24", size = 2116872 }, { url = "https://files.pythonhosted.org/packages/63/08/b59b7a92e03dd25554b0436554bf23e7c29abae7cce4b1c459cd92746811/pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84", size = 1738535 }, { url = "https://files.pythonhosted.org/packages/88/8d/479293e4d39ab409747926eec4329de5b7129beaedc3786eca070605d07f/pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9", size = 1917992 }, + { url = "https://files.pythonhosted.org/packages/ad/ef/16ee2df472bf0e419b6bc68c05bf0145c49247a1095e85cee1463c6a44a1/pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc", size = 1856143 }, + { url = "https://files.pythonhosted.org/packages/da/fa/bc3dbb83605669a34a93308e297ab22be82dfb9dcf88c6cf4b4f264e0a42/pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd", size = 1770063 }, + { url = "https://files.pythonhosted.org/packages/4e/48/e813f3bbd257a712303ebdf55c8dc46f9589ec74b384c9f652597df3288d/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05", size = 1790013 }, + { url = "https://files.pythonhosted.org/packages/b4/e0/56eda3a37929a1d297fcab1966db8c339023bcca0b64c5a84896db3fcc5c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d", size = 1801077 }, + { url = "https://files.pythonhosted.org/packages/04/be/5e49376769bfbf82486da6c5c1683b891809365c20d7c7e52792ce4c71f3/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510", size = 1996782 }, + { url = "https://files.pythonhosted.org/packages/bc/24/e3ee6c04f1d58cc15f37bcc62f32c7478ff55142b7b3e6d42ea374ea427c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6", size = 2661375 }, + { url = "https://files.pythonhosted.org/packages/c1/f8/11a9006de4e89d016b8de74ebb1db727dc100608bb1e6bbe9d56a3cbbcce/pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b", size = 2071635 }, + { url = "https://files.pythonhosted.org/packages/7c/45/bdce5779b59f468bdf262a5bc9eecbae87f271c51aef628d8c073b4b4b4c/pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327", size = 1916994 }, + { url = "https://files.pythonhosted.org/packages/d8/fa/c648308fe711ee1f88192cad6026ab4f925396d1293e8356de7e55be89b5/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6", size = 1968877 }, + { url = "https://files.pythonhosted.org/packages/16/16/b805c74b35607d24d37103007f899abc4880923b04929547ae68d478b7f4/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f", size = 2116814 }, + { url = "https://files.pythonhosted.org/packages/d1/58/5305e723d9fcdf1c5a655e6a4cc2a07128bf644ff4b1d98daf7a9dbf57da/pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769", size = 1738360 }, + { url = "https://files.pythonhosted.org/packages/a5/ae/e14b0ff8b3f48e02394d8acd911376b7b66e164535687ef7dc24ea03072f/pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5", size = 1919411 }, { url = "https://files.pythonhosted.org/packages/13/a9/5d582eb3204464284611f636b55c0a7410d748ff338756323cb1ce721b96/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5", size = 1857135 }, { url = "https://files.pythonhosted.org/packages/2c/57/faf36290933fe16717f97829eabfb1868182ac495f99cf0eda9f59687c9d/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec", size = 1740583 }, { url = "https://files.pythonhosted.org/packages/91/7c/d99e3513dc191c4fec363aef1bf4c8af9125d8fa53af7cb97e8babef4e40/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480", size = 1793637 }, @@ -3461,6 +3675,9 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/00/7c/d00d6bdd96de4344e06c4afbf218bc86b54436a94c01c71a8701f613aa56/pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897", size = 5939729 }, { url = "https://files.pythonhosted.org/packages/21/27/0c8811fbc3ca188f93b5354e7c286eb91f80a53afa4e11007ef661afa746/pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47", size = 6543015 }, { url = "https://files.pythonhosted.org/packages/9d/0f/d40f8373608caed2255781a3ad9a51d03a594a1248cd632d6a298daca693/pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091", size = 7976033 }, + { url = "https://files.pythonhosted.org/packages/a9/a4/aa562d8935e3df5e49c161b427a3a2efad2ed4e9cf81c3de636f1fdddfd0/pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed", size = 5938579 }, + { url = "https://files.pythonhosted.org/packages/c7/50/b0efb8bb66210da67a53ab95fd7a98826a97ee21f1d22949863e6d588b22/pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4", size = 6542056 }, + { url = "https://files.pythonhosted.org/packages/26/df/2b63e3e4f2df0224f8aaf6d131f54fe4e8c96400eb9df563e2aae2e1a1f9/pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd", size = 7974986 }, ] [[package]] @@ -3496,6 +3713,15 @@ wheels = [ { 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]] @@ -3543,6 +3769,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/18/907134c85c7152f679ed744e73e645b365f3ad571f38bdb62e36f347699a/pyzmq-26.2.0-cp312-cp312-win32.whl", hash = "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7", size = 575533 }, { url = "https://files.pythonhosted.org/packages/ce/2c/a6f4a20202a4d3c582ad93f95ee78d79bbdc26803495aec2912b17dbbb6c/pyzmq-26.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a", size = 637768 }, { url = "https://files.pythonhosted.org/packages/5f/0e/eb16ff731632d30554bf5af4dbba3ffcd04518219d82028aea4ae1b02ca5/pyzmq-26.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b", size = 540675 }, + { url = "https://files.pythonhosted.org/packages/04/a7/0f7e2f6c126fe6e62dbae0bc93b1bd3f1099cf7fea47a5468defebe3f39d/pyzmq-26.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726", size = 1006564 }, + { url = "https://files.pythonhosted.org/packages/31/b6/a187165c852c5d49f826a690857684333a6a4a065af0a6015572d2284f6a/pyzmq-26.2.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3", size = 1340447 }, + { url = "https://files.pythonhosted.org/packages/68/ba/f4280c58ff71f321602a6e24fd19879b7e79793fb8ab14027027c0fb58ef/pyzmq-26.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50", size = 665485 }, + { url = "https://files.pythonhosted.org/packages/77/b5/c987a5c53c7d8704216f29fc3d810b32f156bcea488a940e330e1bcbb88d/pyzmq-26.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb", size = 903484 }, + { url = "https://files.pythonhosted.org/packages/29/c9/07da157d2db18c72a7eccef8e684cefc155b712a88e3d479d930aa9eceba/pyzmq-26.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187", size = 859981 }, + { url = "https://files.pythonhosted.org/packages/43/09/e12501bd0b8394b7d02c41efd35c537a1988da67fc9c745cae9c6c776d31/pyzmq-26.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b", size = 860334 }, + { url = "https://files.pythonhosted.org/packages/eb/ff/f5ec1d455f8f7385cc0a8b2acd8c807d7fade875c14c44b85c1bddabae21/pyzmq-26.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18", size = 1196179 }, + { url = "https://files.pythonhosted.org/packages/ec/8a/bb2ac43295b1950fe436a81fc5b298be0b96ac76fb029b514d3ed58f7b27/pyzmq-26.2.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115", size = 1507668 }, + { url = "https://files.pythonhosted.org/packages/a9/49/dbc284ebcfd2dca23f6349227ff1616a7ee2c4a35fe0a5d6c3deff2b4fed/pyzmq-26.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e", size = 1406539 }, + { url = "https://files.pythonhosted.org/packages/00/68/093cdce3fe31e30a341d8e52a1ad86392e13c57970d722c1f62a1d1a54b6/pyzmq-26.2.0-cp313-cp313-win32.whl", hash = "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5", size = 575567 }, + { url = "https://files.pythonhosted.org/packages/92/ae/6cc4657148143412b5819b05e362ae7dd09fb9fe76e2a539dcff3d0386bc/pyzmq-26.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad", size = 637551 }, + { url = "https://files.pythonhosted.org/packages/6c/67/fbff102e201688f97c8092e4c3445d1c1068c2f27bbd45a578df97ed5f94/pyzmq-26.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797", size = 540378 }, + { url = "https://files.pythonhosted.org/packages/3f/fe/2d998380b6e0122c6c4bdf9b6caf490831e5f5e2d08a203b5adff060c226/pyzmq-26.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a", size = 1007378 }, + { url = "https://files.pythonhosted.org/packages/4a/f4/30d6e7157f12b3a0390bde94d6a8567cdb88846ed068a6e17238a4ccf600/pyzmq-26.2.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc", size = 1329532 }, + { url = "https://files.pythonhosted.org/packages/82/86/3fe917870e15ee1c3ad48229a2a64458e36036e64b4afa9659045d82bfa8/pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5", size = 653242 }, + { url = "https://files.pythonhosted.org/packages/50/2d/242e7e6ef6c8c19e6cb52d095834508cd581ffb925699fd3c640cdc758f1/pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672", size = 888404 }, + { url = "https://files.pythonhosted.org/packages/ac/11/7270566e1f31e4ea73c81ec821a4b1688fd551009a3d2bab11ec66cb1e8f/pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797", size = 845858 }, + { url = "https://files.pythonhosted.org/packages/91/d5/72b38fbc69867795c8711bdd735312f9fef1e3d9204e2f63ab57085434b9/pyzmq-26.2.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386", size = 847375 }, + { url = "https://files.pythonhosted.org/packages/dd/9a/10ed3c7f72b4c24e719c59359fbadd1a27556a28b36cdf1cd9e4fb7845d5/pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306", size = 1183489 }, + { url = "https://files.pythonhosted.org/packages/72/2d/8660892543fabf1fe41861efa222455811adac9f3c0818d6c3170a1153e3/pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6", size = 1492932 }, + { url = "https://files.pythonhosted.org/packages/7b/d6/32fd69744afb53995619bc5effa2a405ae0d343cd3e747d0fbc43fe894ee/pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0", size = 1392485 }, { url = "https://files.pythonhosted.org/packages/53/fb/36b2b2548286e9444e52fcd198760af99fd89102b5be50f0660fcfe902df/pyzmq-26.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072", size = 906955 }, { url = "https://files.pythonhosted.org/packages/77/8f/6ce54f8979a01656e894946db6299e2273fcee21c8e5fa57c6295ef11f57/pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1", size = 565701 }, { url = "https://files.pythonhosted.org/packages/ee/1c/bf8cd66730a866b16db8483286078892b7f6536f8c389fb46e4beba0a970/pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d", size = 794312 }, @@ -3615,6 +3862,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ea/75/9753e9dcebfa7c3645563ef5c8a58f3a47e799c872165f37c55737dadd3e/regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a", size = 787333 }, { url = "https://files.pythonhosted.org/packages/bc/4e/ba1cbca93141f7416624b3ae63573e785d4bc1834c8be44a8f0747919eca/regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776", size = 262058 }, { url = "https://files.pythonhosted.org/packages/6e/16/efc5f194778bf43e5888209e5cec4b258005d37c613b67ae137df3b89c53/regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009", size = 273526 }, + { url = "https://files.pythonhosted.org/packages/93/0a/d1c6b9af1ff1e36832fe38d74d5c5bab913f2bdcbbd6bc0e7f3ce8b2f577/regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784", size = 483376 }, + { url = "https://files.pythonhosted.org/packages/a4/42/5910a050c105d7f750a72dcb49c30220c3ae4e2654e54aaaa0e9bc0584cb/regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36", size = 288112 }, + { url = "https://files.pythonhosted.org/packages/8d/56/0c262aff0e9224fa7ffce47b5458d373f4d3e3ff84e99b5ff0cb15e0b5b2/regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92", size = 284608 }, + { url = "https://files.pythonhosted.org/packages/b9/54/9fe8f9aec5007bbbbce28ba3d2e3eaca425f95387b7d1e84f0d137d25237/regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86", size = 795337 }, + { url = "https://files.pythonhosted.org/packages/b2/e7/6b2f642c3cded271c4f16cc4daa7231be544d30fe2b168e0223724b49a61/regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85", size = 835848 }, + { url = "https://files.pythonhosted.org/packages/cd/9e/187363bdf5d8c0e4662117b92aa32bf52f8f09620ae93abc7537d96d3311/regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963", size = 823503 }, + { url = "https://files.pythonhosted.org/packages/f8/10/601303b8ee93589f879664b0cfd3127949ff32b17f9b6c490fb201106c4d/regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6", size = 797049 }, + { url = "https://files.pythonhosted.org/packages/ef/1c/ea200f61ce9f341763f2717ab4daebe4422d83e9fd4ac5e33435fd3a148d/regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802", size = 784144 }, + { url = "https://files.pythonhosted.org/packages/d8/5c/d2429be49ef3292def7688401d3deb11702c13dcaecdc71d2b407421275b/regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29", size = 782483 }, + { url = "https://files.pythonhosted.org/packages/12/d9/cbc30f2ff7164f3b26a7760f87c54bf8b2faed286f60efd80350a51c5b99/regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8", size = 790320 }, + { url = "https://files.pythonhosted.org/packages/19/1d/43ed03a236313639da5a45e61bc553c8d41e925bcf29b0f8ecff0c2c3f25/regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84", size = 860435 }, + { url = "https://files.pythonhosted.org/packages/34/4f/5d04da61c7c56e785058a46349f7285ae3ebc0726c6ea7c5c70600a52233/regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554", size = 859571 }, + { url = "https://files.pythonhosted.org/packages/12/7f/8398c8155a3c70703a8e91c29532558186558e1aea44144b382faa2a6f7a/regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8", size = 787398 }, + { url = "https://files.pythonhosted.org/packages/58/3a/f5903977647a9a7e46d5535e9e96c194304aeeca7501240509bde2f9e17f/regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8", size = 262035 }, + { url = "https://files.pythonhosted.org/packages/ff/80/51ba3a4b7482f6011095b3a036e07374f64de180b7d870b704ed22509002/regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f", size = 273510 }, ] [[package]] @@ -3715,6 +3977,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/70/2d/5536d28c507a4679179ab15aa0049440e4d3dd6752050fa0843ed11e9354/rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174", size = 528807 }, { url = "https://files.pythonhosted.org/packages/e3/62/7ebe6ec0d3dd6130921f8cffb7e34afb7f71b3819aa0446a24c5e81245ec/rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139", size = 200993 }, { url = "https://files.pythonhosted.org/packages/ec/2f/b938864d66b86a6e4acadefdc56de75ef56f7cafdfd568a6464605457bd5/rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585", size = 214458 }, + { url = "https://files.pythonhosted.org/packages/99/32/43b919a0a423c270a838ac2726b1c7168b946f2563fd99a51aaa9692d00f/rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29", size = 321465 }, + { url = "https://files.pythonhosted.org/packages/58/a9/c4d899cb28e9e47b0ff12462e8f827381f243176036f17bef9c1604667f2/rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91", size = 312900 }, + { url = "https://files.pythonhosted.org/packages/8f/90/9e51670575b5dfaa8c823369ef7d943087bfb73d4f124a99ad6ef19a2b26/rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24", size = 370973 }, + { url = "https://files.pythonhosted.org/packages/fc/c1/523f2a03f853fc0d4c1acbef161747e9ab7df0a8abf6236106e333540921/rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7", size = 370890 }, + { url = "https://files.pythonhosted.org/packages/51/ca/2458a771f16b0931de4d384decbe43016710bc948036c8f4562d6e063437/rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9", size = 397174 }, + { url = "https://files.pythonhosted.org/packages/00/7d/6e06807f6305ea2408b364efb0eef83a6e21b5e7b5267ad6b473b9a7e416/rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8", size = 426449 }, + { url = "https://files.pythonhosted.org/packages/8c/d1/6c9e65260a819a1714510a7d69ac1d68aa23ee9ce8a2d9da12187263c8fc/rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879", size = 357698 }, + { url = "https://files.pythonhosted.org/packages/5d/fb/ecea8b5286d2f03eec922be7173a03ed17278944f7c124348f535116db15/rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f", size = 378530 }, + { url = "https://files.pythonhosted.org/packages/e3/e3/ac72f858957f52a109c588589b73bd2fad4a0fc82387fb55fb34aeb0f9cd/rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c", size = 545753 }, + { url = "https://files.pythonhosted.org/packages/b2/a4/a27683b519d5fc98e4390a3b130117d80fd475c67aeda8aac83c0e8e326a/rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2", size = 552443 }, + { url = "https://files.pythonhosted.org/packages/a1/ed/c074d248409b4432b1ccb2056974175fa0af2d1bc1f9c21121f80a358fa3/rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57", size = 528380 }, + { url = "https://files.pythonhosted.org/packages/d5/bd/04caf938895d2d78201e89c0c8a94dfd9990c34a19ff52fb01d0912343e3/rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a", size = 200540 }, + { url = "https://files.pythonhosted.org/packages/95/cc/109eb8b9863680411ae703664abacaa035820c7755acc9686d5dd02cdd2e/rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2", size = 214111 }, { url = "https://files.pythonhosted.org/packages/06/39/bf1f664c347c946ef56cecaa896e3693d91acc741afa78ebb3fdb7aba08b/rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045", size = 319444 }, { url = "https://files.pythonhosted.org/packages/c1/71/876135d3cb90d62468540b84e8e83ff4dc92052ab309bfdea7ea0b9221ad/rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc", size = 311699 }, { url = "https://files.pythonhosted.org/packages/f7/da/8ccaeba6a3dda7467aebaf893de9eafd56275e2c90773c83bf15fb0b8374/rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02", size = 367825 }, @@ -3786,6 +4061,14 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8e/ee/8a26858ca517e9c64f84b4c7734b89bda8e63bec85c3d2f432d225bb1886/scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066", size = 40849331 }, { url = "https://files.pythonhosted.org/packages/a5/cd/06f72bc9187840f1c99e1a8750aad4216fc7dfdd7df46e6280add14b4822/scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1", size = 42544049 }, { url = "https://files.pythonhosted.org/packages/aa/7d/43ab67228ef98c6b5dd42ab386eae2d7877036970a0d7e3dd3eb47a0d530/scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f", size = 44521212 }, + { url = "https://files.pythonhosted.org/packages/50/ef/ac98346db016ff18a6ad7626a35808f37074d25796fd0234c2bb0ed1e054/scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79", size = 39091068 }, + { url = "https://files.pythonhosted.org/packages/b9/cc/70948fe9f393b911b4251e96b55bbdeaa8cca41f37c26fd1df0232933b9e/scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e", size = 29875417 }, + { url = "https://files.pythonhosted.org/packages/3b/2e/35f549b7d231c1c9f9639f9ef49b815d816bf54dd050da5da1c11517a218/scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73", size = 23084508 }, + { url = "https://files.pythonhosted.org/packages/3f/d6/b028e3f3e59fae61fb8c0f450db732c43dd1d836223a589a8be9f6377203/scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e", size = 25503364 }, + { url = "https://files.pythonhosted.org/packages/a7/2f/6c142b352ac15967744d62b165537a965e95d557085db4beab2a11f7943b/scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d", size = 35292639 }, + { url = "https://files.pythonhosted.org/packages/56/46/2449e6e51e0d7c3575f289f6acb7f828938eaab8874dbccfeb0cd2b71a27/scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e", size = 40798288 }, + { url = "https://files.pythonhosted.org/packages/32/cd/9d86f7ed7f4497c9fd3e39f8918dd93d9f647ba80d7e34e4946c0c2d1a7c/scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06", size = 42524647 }, + { url = "https://files.pythonhosted.org/packages/f5/1b/6ee032251bf4cdb0cc50059374e86a9f076308c1512b61c4e003e241efb7/scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84", size = 44469524 }, ] [[package]] @@ -4066,6 +4349,14 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ea/09/badfc9293bc3ccba6ede05e5f2b44a760aa47d84da1fc5a326e963e3d4d9/SQLAlchemy-2.0.36-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1bc330d9d29c7f06f003ab10e1eaced295e87940405afe1b110f2eb93a233588", size = 3205147 }, { url = "https://files.pythonhosted.org/packages/c8/60/70e681de02a13c4b27979b7b78da3058c49bacc9858c89ba672e030f03f2/SQLAlchemy-2.0.36-cp312-cp312-win32.whl", hash = "sha256:79d2e78abc26d871875b419e1fd3c0bca31a1cb0043277d0d850014599626c2e", size = 2062709 }, { url = "https://files.pythonhosted.org/packages/b7/ed/f6cd9395e41bfe47dd253d74d2dfc3cab34980d4e20c8878cb1117306085/SQLAlchemy-2.0.36-cp312-cp312-win_amd64.whl", hash = "sha256:b544ad1935a8541d177cb402948b94e871067656b3a0b9e91dbec136b06a2ff5", size = 2088433 }, + { url = "https://files.pythonhosted.org/packages/78/5c/236398ae3678b3237726819b484f15f5c038a9549da01703a771f05a00d6/SQLAlchemy-2.0.36-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5cc79df7f4bc3d11e4b542596c03826063092611e481fcf1c9dfee3c94355ef", size = 2087651 }, + { url = "https://files.pythonhosted.org/packages/a8/14/55c47420c0d23fb67a35af8be4719199b81c59f3084c28d131a7767b0b0b/SQLAlchemy-2.0.36-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3c01117dd36800f2ecaa238c65365b7b16497adc1522bf84906e5710ee9ba0e8", size = 2078132 }, + { url = "https://files.pythonhosted.org/packages/3d/97/1e843b36abff8c4a7aa2e37f9bea364f90d021754c2de94d792c2d91405b/SQLAlchemy-2.0.36-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bc633f4ee4b4c46e7adcb3a9b5ec083bf1d9a97c1d3854b92749d935de40b9b", size = 3164559 }, + { url = "https://files.pythonhosted.org/packages/7b/c5/07f18a897b997f6d6b234fab2bf31dccf66d5d16a79fe329aefc95cd7461/SQLAlchemy-2.0.36-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e46ed38affdfc95d2c958de328d037d87801cfcbea6d421000859e9789e61c2", size = 3177897 }, + { url = "https://files.pythonhosted.org/packages/b3/cd/e16f3cbefd82b5c40b33732da634ec67a5f33b587744c7ab41699789d492/SQLAlchemy-2.0.36-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b2985c0b06e989c043f1dc09d4fe89e1616aadd35392aea2844f0458a989eacf", size = 3111289 }, + { url = "https://files.pythonhosted.org/packages/15/85/5b8a3b0bc29c9928aa62b5c91fcc8335f57c1de0a6343873b5f372e3672b/SQLAlchemy-2.0.36-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a121d62ebe7d26fec9155f83f8be5189ef1405f5973ea4874a26fab9f1e262c", size = 3139491 }, + { url = "https://files.pythonhosted.org/packages/a1/95/81babb6089938680dfe2cd3f88cd3fd39cccd1543b7cb603b21ad881bff1/SQLAlchemy-2.0.36-cp313-cp313-win32.whl", hash = "sha256:0572f4bd6f94752167adfd7c1bed84f4b240ee6203a95e05d1e208d488d0d436", size = 2060439 }, + { url = "https://files.pythonhosted.org/packages/c1/ce/5f7428df55660d6879d0522adc73a3364970b5ef33ec17fa125c5dbcac1d/SQLAlchemy-2.0.36-cp313-cp313-win_amd64.whl", hash = "sha256:8c78ac40bde930c60e0f78b3cd184c580f89456dd87fc08f9e3ee3ce8765ce88", size = 2084574 }, { url = "https://files.pythonhosted.org/packages/b8/49/21633706dd6feb14cd3f7935fc00b60870ea057686035e1a99ae6d9d9d53/SQLAlchemy-2.0.36-py3-none-any.whl", hash = "sha256:fddbe92b4760c6f5d48162aef14824add991aeda8ddadb3c31d56eb15ca69f8e", size = 1883787 }, ] @@ -4253,6 +4544,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/26/32/e0e3a859136e95c85a572e4806dc58bf1ddf651108ae8b97d5f3ebe1a244/tiktoken-0.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2908c0d043a7d03ebd80347266b0e58440bdef5564f84f4d29fb235b5df3b04", size = 1175432 }, { url = "https://files.pythonhosted.org/packages/c7/89/926b66e9025b97e9fbabeaa59048a736fe3c3e4530a204109571104f921c/tiktoken-0.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:294440d21a2a51e12d4238e68a5972095534fe9878be57d905c476017bff99fc", size = 1236576 }, { url = "https://files.pythonhosted.org/packages/45/e2/39d4aa02a52bba73b2cd21ba4533c84425ff8786cc63c511d68c8897376e/tiktoken-0.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:d8f3192733ac4d77977432947d563d7e1b310b96497acd3c196c9bddb36ed9db", size = 883824 }, + { url = "https://files.pythonhosted.org/packages/e3/38/802e79ba0ee5fcbf240cd624143f57744e5d411d2e9d9ad2db70d8395986/tiktoken-0.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:02be1666096aff7da6cbd7cdaa8e7917bfed3467cd64b38b1f112e96d3b06a24", size = 1039648 }, + { url = "https://files.pythonhosted.org/packages/b1/da/24cdbfc302c98663fbea66f5866f7fa1048405c7564ab88483aea97c3b1a/tiktoken-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94ff53c5c74b535b2cbf431d907fc13c678bbd009ee633a2aca269a04389f9a", size = 982763 }, + { url = "https://files.pythonhosted.org/packages/e4/f0/0ecf79a279dfa41fc97d00adccf976ecc2556d3c08ef3e25e45eb31f665b/tiktoken-0.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b231f5e8982c245ee3065cd84a4712d64692348bc609d84467c57b4b72dcbc5", size = 1144417 }, + { url = "https://files.pythonhosted.org/packages/ab/d3/155d2d4514f3471a25dc1d6d20549ef254e2aa9bb5b1060809b1d3b03d3a/tiktoken-0.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4177faa809bd55f699e88c96d9bb4635d22e3f59d635ba6fd9ffedf7150b9953", size = 1175108 }, + { url = "https://files.pythonhosted.org/packages/19/eb/5989e16821ee8300ef8ee13c16effc20dfc26c777d05fbb6825e3c037b81/tiktoken-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5376b6f8dc4753cd81ead935c5f518fa0fbe7e133d9e25f648d8c4dabdd4bad7", size = 1236520 }, + { url = "https://files.pythonhosted.org/packages/40/59/14b20465f1d1cb89cfbc96ec27e5617b2d41c79da12b5e04e96d689be2a7/tiktoken-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:18228d624807d66c87acd8f25fc135665617cab220671eb65b50f5d70fa51f69", size = 883849 }, ] [[package]] @@ -4565,6 +4862,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/44/81/1f701323a9f70805bc81c74c990137123344a80ea23ab9504a99492907f8/watchfiles-0.24.0-cp312-none-win32.whl", hash = "sha256:d9018153cf57fc302a2a34cb7564870b859ed9a732d16b41a9b5cb2ebed2d444", size = 264109 }, { url = "https://files.pythonhosted.org/packages/b4/0b/32cde5bc2ebd9f351be326837c61bdeb05ad652b793f25c91cac0b48a60b/watchfiles-0.24.0-cp312-none-win_amd64.whl", hash = "sha256:551ec3ee2a3ac9cbcf48a4ec76e42c2ef938a7e905a35b42a1267fa4b1645896", size = 277055 }, { url = "https://files.pythonhosted.org/packages/4b/81/daade76ce33d21dbec7a15afd7479de8db786e5f7b7d249263b4ea174e08/watchfiles-0.24.0-cp312-none-win_arm64.whl", hash = "sha256:b52a65e4ea43c6d149c5f8ddb0bef8d4a1e779b77591a458a893eb416624a418", size = 266169 }, + { url = "https://files.pythonhosted.org/packages/30/dc/6e9f5447ae14f645532468a84323a942996d74d5e817837a5c8ce9d16c69/watchfiles-0.24.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3d2e3ab79a1771c530233cadfd277fcc762656d50836c77abb2e5e72b88e3a48", size = 373764 }, + { url = "https://files.pythonhosted.org/packages/79/c0/c3a9929c372816c7fc87d8149bd722608ea58dc0986d3ef7564c79ad7112/watchfiles-0.24.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327763da824817b38ad125dcd97595f942d720d32d879f6c4ddf843e3da3fe90", size = 367873 }, + { url = "https://files.pythonhosted.org/packages/2e/11/ff9a4445a7cfc1c98caf99042df38964af12eed47d496dd5d0d90417349f/watchfiles-0.24.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd82010f8ab451dabe36054a1622870166a67cf3fce894f68895db6f74bbdc94", size = 438381 }, + { url = "https://files.pythonhosted.org/packages/48/a3/763ba18c98211d7bb6c0f417b2d7946d346cdc359d585cc28a17b48e964b/watchfiles-0.24.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d64ba08db72e5dfd5c33be1e1e687d5e4fcce09219e8aee893a4862034081d4e", size = 432809 }, + { url = "https://files.pythonhosted.org/packages/30/4c/616c111b9d40eea2547489abaf4ffc84511e86888a166d3a4522c2ba44b5/watchfiles-0.24.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1cf1f6dd7825053f3d98f6d33f6464ebdd9ee95acd74ba2c34e183086900a827", size = 451801 }, + { url = "https://files.pythonhosted.org/packages/b6/be/d7da83307863a422abbfeb12903a76e43200c90ebe5d6afd6a59d158edea/watchfiles-0.24.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43e3e37c15a8b6fe00c1bce2473cfa8eb3484bbeecf3aefbf259227e487a03df", size = 468886 }, + { url = "https://files.pythonhosted.org/packages/1d/d3/3dfe131ee59d5e90b932cf56aba5c996309d94dafe3d02d204364c23461c/watchfiles-0.24.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88bcd4d0fe1d8ff43675360a72def210ebad3f3f72cabfeac08d825d2639b4ab", size = 472973 }, + { url = "https://files.pythonhosted.org/packages/42/6c/279288cc5653a289290d183b60a6d80e05f439d5bfdfaf2d113738d0f932/watchfiles-0.24.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:999928c6434372fde16c8f27143d3e97201160b48a614071261701615a2a156f", size = 425282 }, + { url = "https://files.pythonhosted.org/packages/d6/d7/58afe5e85217e845edf26d8780c2d2d2ae77675eeb8d1b8b8121d799ce52/watchfiles-0.24.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:30bbd525c3262fd9f4b1865cb8d88e21161366561cd7c9e1194819e0a33ea86b", size = 612540 }, + { url = "https://files.pythonhosted.org/packages/6d/d5/b96eeb9fe3fda137200dd2f31553670cbc731b1e13164fd69b49870b76ec/watchfiles-0.24.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:edf71b01dec9f766fb285b73930f95f730bb0943500ba0566ae234b5c1618c18", size = 593625 }, + { url = "https://files.pythonhosted.org/packages/c1/e5/c326fe52ee0054107267608d8cea275e80be4455b6079491dfd9da29f46f/watchfiles-0.24.0-cp313-none-win32.whl", hash = "sha256:f4c96283fca3ee09fb044f02156d9570d156698bc3734252175a38f0e8975f07", size = 263899 }, + { url = "https://files.pythonhosted.org/packages/a6/8b/8a7755c5e7221bb35fe4af2dc44db9174f90ebf0344fd5e9b1e8b42d381e/watchfiles-0.24.0-cp313-none-win_amd64.whl", hash = "sha256:a974231b4fdd1bb7f62064a0565a6b107d27d21d9acb50c484d2cdba515b9366", size = 276622 }, { url = "https://files.pythonhosted.org/packages/df/94/1ad200e937ec91b2a9d6b39ae1cf9c2b1a9cc88d5ceb43aa5c6962eb3c11/watchfiles-0.24.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:632676574429bee8c26be8af52af20e0c718cc7f5f67f3fb658c71928ccd4f7f", size = 376986 }, { url = "https://files.pythonhosted.org/packages/ee/fd/d9e020d687ccf90fe95efc513fbb39a8049cf5a3ff51f53c59fcf4c47a5d/watchfiles-0.24.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a2a9891723a735d3e2540651184be6fd5b96880c08ffe1a98bae5017e65b544b", size = 369445 }, { url = "https://files.pythonhosted.org/packages/43/cb/c0279b35053555d10ef03559c5aebfcb0c703d9c70a7b4e532df74b9b0e8/watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7fa2bc0efef3e209a8199fd111b8969fe9db9c711acc46636686331eda7dd4", size = 439383 }, @@ -4628,6 +4937,17 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5c/f1/a29dd6046d3a722d26f182b783a7997d25298873a14028c4760347974ea3/websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf", size = 164686 }, { url = "https://files.pythonhosted.org/packages/0f/99/ab1cdb282f7e595391226f03f9b498f52109d25a2ba03832e21614967dfa/websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c", size = 158712 }, { url = "https://files.pythonhosted.org/packages/46/93/e19160db48b5581feac8468330aa11b7292880a94a37d7030478596cc14e/websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3", size = 159145 }, + { url = "https://files.pythonhosted.org/packages/51/20/2b99ca918e1cbd33c53db2cace5f0c0cd8296fc77558e1908799c712e1cd/websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6", size = 157828 }, + { url = "https://files.pythonhosted.org/packages/b8/47/0932a71d3d9c0e9483174f60713c84cee58d62839a143f21a2bcdbd2d205/websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708", size = 155487 }, + { url = "https://files.pythonhosted.org/packages/a9/60/f1711eb59ac7a6c5e98e5637fef5302f45b6f76a2c9d64fd83bbb341377a/websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418", size = 155721 }, + { url = "https://files.pythonhosted.org/packages/6a/e6/ba9a8db7f9d9b0e5f829cf626ff32677f39824968317223605a6b419d445/websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a", size = 165609 }, + { url = "https://files.pythonhosted.org/packages/c1/22/4ec80f1b9c27a0aebd84ccd857252eda8418ab9681eb571b37ca4c5e1305/websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f", size = 164556 }, + { url = "https://files.pythonhosted.org/packages/27/ac/35f423cb6bb15600438db80755609d27eda36d4c0b3c9d745ea12766c45e/websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5", size = 164993 }, + { url = "https://files.pythonhosted.org/packages/31/4e/98db4fd267f8be9e52e86b6ee4e9aa7c42b83452ea0ea0672f176224b977/websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135", size = 165360 }, + { url = "https://files.pythonhosted.org/packages/3f/15/3f0de7cda70ffc94b7e7024544072bc5b26e2c1eb36545291abb755d8cdb/websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2", size = 164745 }, + { url = "https://files.pythonhosted.org/packages/a1/6e/66b6b756aebbd680b934c8bdbb6dcb9ce45aad72cde5f8a7208dbb00dd36/websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6", size = 164732 }, + { url = "https://files.pythonhosted.org/packages/35/c6/12e3aab52c11aeb289e3dbbc05929e7a9d90d7a9173958477d3ef4f8ce2d/websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d", size = 158709 }, + { url = "https://files.pythonhosted.org/packages/41/d8/63d6194aae711d7263df4498200c690a9c39fb437ede10f3e157a6343e0d/websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2", size = 159144 }, { url = "https://files.pythonhosted.org/packages/2d/75/6da22cb3ad5b8c606963f9a5f9f88656256fecc29d420b4b2bf9e0c7d56f/websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238", size = 155499 }, { url = "https://files.pythonhosted.org/packages/c0/ba/22833d58629088fcb2ccccedfae725ac0bbcd713319629e97125b52ac681/websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5", size = 155737 }, { url = "https://files.pythonhosted.org/packages/95/54/61684fe22bdb831e9e1843d972adadf359cf04ab8613285282baea6a24bb/websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9", size = 157095 }, @@ -4775,6 +5095,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ad/8d/b7b5d43cf22a020b564ddf7502d83df150d797e34f18f6bf5fe0f12cbd91/yarl-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1a5cf32539373ff39d97723e39a9283a7277cbf1224f7aef0c56c9598b6486c3", size = 355746 }, { url = "https://files.pythonhosted.org/packages/d9/a6/a2098bf3f09d38eb540b2b192e180d9d41c2ff64b692783db2188f0a55e3/yarl-1.16.0-cp312-cp312-win32.whl", hash = "sha256:a5b6c09b9b4253d6a208b0f4a2f9206e511ec68dce9198e0fbec4f160137aa67", size = 82675 }, { url = "https://files.pythonhosted.org/packages/ed/a6/0a54b382cfc336e772b72681d6816a99222dc2d21876e649474973b8d244/yarl-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:1208ca14eed2fda324042adf8d6c0adf4a31522fa95e0929027cd487875f0240", size = 88986 }, + { url = "https://files.pythonhosted.org/packages/57/56/eef0a7050fcd11d70c536453f014d4b2dfd83fb934c9857fa1a912832405/yarl-1.16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5ace0177520bd4caa99295a9b6fb831d0e9a57d8e0501a22ffaa61b4c024283", size = 139373 }, + { url = "https://files.pythonhosted.org/packages/3f/b2/88eb9e98c5a4549606ebf673cba0d701f13ec855021b781f8e3fd7c04190/yarl-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7118bdb5e3ed81acaa2095cba7ec02a0fe74b52a16ab9f9ac8e28e53ee299732", size = 92759 }, + { url = "https://files.pythonhosted.org/packages/95/1d/c3b794ef82a3b1894a9f8fc1012b073a85464b95c646ac217e8013137ea3/yarl-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38fec8a2a94c58bd47c9a50a45d321ab2285ad133adefbbadf3012c054b7e656", size = 90573 }, + { url = "https://files.pythonhosted.org/packages/7f/35/39a5dcbf7ef320607bcfd1c0498ce348181b97627c3901139b429d806cf1/yarl-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8791d66d81ee45866a7bb15a517b01a2bcf583a18ebf5d72a84e6064c417e64b", size = 332461 }, + { url = "https://files.pythonhosted.org/packages/36/29/2a468c8b44aa750d0f3416bc24d58464237b402388a8f03091a58537274a/yarl-1.16.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cf936ba67bc6c734f3aa1c01391da74ab7fc046a9f8bbfa230b8393b90cf472", size = 343045 }, + { url = "https://files.pythonhosted.org/packages/91/6a/002300c86ed7ef3cd5ac890a0e17101aee06c64abe2e43f9dad85bc32c70/yarl-1.16.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1aab176dd55b59f77a63b27cffaca67d29987d91a5b615cbead41331e6b7428", size = 344592 }, + { url = "https://files.pythonhosted.org/packages/ea/69/ca4228e0f560f0c5817e0ebd789690c78ab17e6a876b38a5d000889b2f63/yarl-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:995d0759004c08abd5d1b81300a91d18c8577c6389300bed1c7c11675105a44d", size = 338127 }, + { url = "https://files.pythonhosted.org/packages/81/df/32eea6e5199f7298ec15cf708895f35a7d2899177ed556e6bdf6819462aa/yarl-1.16.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1bc22e00edeb068f71967ab99081e9406cd56dbed864fc3a8259442999d71552", size = 326127 }, + { url = "https://files.pythonhosted.org/packages/9a/11/1a888df53acd3d1d4b8dc803e0c8ed4a4b6cabc2abe19e4de31aa6b86857/yarl-1.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:35b4f7842154176523e0a63c9b871168c69b98065d05a4f637fce342a6a2693a", size = 345219 }, + { url = "https://files.pythonhosted.org/packages/34/88/44fd8b372c4c50c010e66c62bfb34e67d6bd217c973599e0ee03f74e74ec/yarl-1.16.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:7ace71c4b7a0c41f317ae24be62bb61e9d80838d38acb20e70697c625e71f120", size = 339742 }, + { url = "https://files.pythonhosted.org/packages/ee/c8/eaa53bd40db61265cec09d3c432d8bcd8ab9fd3a9fc5b0afdd13ab27b4a8/yarl-1.16.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8f639e3f5795a6568aa4f7d2ac6057c757dcd187593679f035adbf12b892bb00", size = 344695 }, + { url = "https://files.pythonhosted.org/packages/1b/8f/b00aa91bd3bc8ef41781b13ac967c9c5c2e3ca0c516cffdd15ac035a1839/yarl-1.16.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e8be3aff14f0120ad049121322b107f8a759be76a6a62138322d4c8a337a9e2c", size = 353617 }, + { url = "https://files.pythonhosted.org/packages/f1/88/8e86a28a840b8dc30c880fdde127f9610c56e55796a2cc969949b4a60fe7/yarl-1.16.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:122d8e7986043d0549e9eb23c7fd23be078be4b70c9eb42a20052b3d3149c6f2", size = 359911 }, + { url = "https://files.pythonhosted.org/packages/ee/61/9d59f7096fd72d5f68168ed8134773982ee48a8cb4009ecb34344e064999/yarl-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0fd9c227990f609c165f56b46107d0bc34553fe0387818c42c02f77974402c36", size = 358847 }, + { url = "https://files.pythonhosted.org/packages/f7/25/c323097b066a2b5a554f77e27a35bc067aebfcd3a001a0a3a6bc14190460/yarl-1.16.0-cp313-cp313-win32.whl", hash = "sha256:595ca5e943baed31d56b33b34736461a371c6ea0038d3baec399949dd628560b", size = 308302 }, + { url = "https://files.pythonhosted.org/packages/52/76/ca2c3de3511a127fc4124723e7ccc641aef5e0ec56c66d25dbd11f19ab84/yarl-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:921b81b8d78f0e60242fb3db615ea3f368827a76af095d5a69f1c3366db3f596", size = 314035 }, { url = "https://files.pythonhosted.org/packages/fb/f7/87a32867ddc1a9817018bfd6109ee57646a543acf0d272843d8393e575f9/yarl-1.16.0-py3-none-any.whl", hash = "sha256:e6980a558d8461230c457218bd6c92dfc1d10205548215c2c21d79dc8d0a96f3", size = 43746 }, ] From afa52c8fa234725f3166c247b3e87de571bcd5e6 Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Mon, 25 Nov 2024 20:47:06 -0800 Subject: [PATCH 03/16] add agent_events.proto --- .../application/protos/agent_events_pb2.py | 48 ++++++++++--------- .../application/protos/agent_events_pb2.pyi | 18 +++++++ python/pyproject.toml | 2 +- 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.py b/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.py index b5aac871cb3..e5e7f4d59a1 100644 --- a/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.py +++ b/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.py @@ -14,7 +14,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x61gent_events.proto\x12\x06\x61gents\"\x18\n\x05Input\x12\x0f\n\x07message\x18\x01 \x01(\t\"\x1f\n\x0eInputProcessed\x12\r\n\x05route\x18\x01 \x01(\t\"\x19\n\x06Output\x12\x0f\n\x07message\x18\x01 \x01(\t\"\x1e\n\rOutputWritten\x12\r\n\x05route\x18\x01 \x01(\t\"\x1a\n\x07IOError\x12\x0f\n\x07message\x18\x01 \x01(\t\"%\n\x12NewMessageReceived\x12\x0f\n\x07message\x18\x01 \x01(\t\"%\n\x11ResponseGenerated\x12\x10\n\x08response\x18\x01 \x01(\t\"\x1a\n\x07GoodBye\x12\x0f\n\x07message\x18\x01 \x01(\t\" \n\rMessageStored\x12\x0f\n\x07message\x18\x01 \x01(\t\";\n\x12\x43onversationClosed\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x14\n\x0cuser_message\x18\x02 \x01(\t\"\x1b\n\x08Shutdown\x12\x0f\n\x07message\x18\x01 \x01(\tB!\xaa\x02\x1eMicrosoft.AutoGen.Abstractionsb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x61gent_events.proto\x12\x06\x61gents\"2\n\x0bTextMessage\x12\x13\n\x0btextMessage\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"\x18\n\x05Input\x12\x0f\n\x07message\x18\x01 \x01(\t\"\x1f\n\x0eInputProcessed\x12\r\n\x05route\x18\x01 \x01(\t\"\x19\n\x06Output\x12\x0f\n\x07message\x18\x01 \x01(\t\"\x1e\n\rOutputWritten\x12\r\n\x05route\x18\x01 \x01(\t\"\x1a\n\x07IOError\x12\x0f\n\x07message\x18\x01 \x01(\t\"%\n\x12NewMessageReceived\x12\x0f\n\x07message\x18\x01 \x01(\t\"%\n\x11ResponseGenerated\x12\x10\n\x08response\x18\x01 \x01(\t\"\x1a\n\x07GoodBye\x12\x0f\n\x07message\x18\x01 \x01(\t\" \n\rMessageStored\x12\x0f\n\x07message\x18\x01 \x01(\t\";\n\x12\x43onversationClosed\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x14\n\x0cuser_message\x18\x02 \x01(\t\"\x1b\n\x08Shutdown\x12\x0f\n\x07message\x18\x01 \x01(\tB!\xaa\x02\x1eMicrosoft.AutoGen.Abstractionsb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -22,26 +22,28 @@ if _descriptor._USE_C_DESCRIPTORS == False: _globals['DESCRIPTOR']._options = None _globals['DESCRIPTOR']._serialized_options = b'\252\002\036Microsoft.AutoGen.Abstractions' - _globals['_INPUT']._serialized_start=30 - _globals['_INPUT']._serialized_end=54 - _globals['_INPUTPROCESSED']._serialized_start=56 - _globals['_INPUTPROCESSED']._serialized_end=87 - _globals['_OUTPUT']._serialized_start=89 - _globals['_OUTPUT']._serialized_end=114 - _globals['_OUTPUTWRITTEN']._serialized_start=116 - _globals['_OUTPUTWRITTEN']._serialized_end=146 - _globals['_IOERROR']._serialized_start=148 - _globals['_IOERROR']._serialized_end=174 - _globals['_NEWMESSAGERECEIVED']._serialized_start=176 - _globals['_NEWMESSAGERECEIVED']._serialized_end=213 - _globals['_RESPONSEGENERATED']._serialized_start=215 - _globals['_RESPONSEGENERATED']._serialized_end=252 - _globals['_GOODBYE']._serialized_start=254 - _globals['_GOODBYE']._serialized_end=280 - _globals['_MESSAGESTORED']._serialized_start=282 - _globals['_MESSAGESTORED']._serialized_end=314 - _globals['_CONVERSATIONCLOSED']._serialized_start=316 - _globals['_CONVERSATIONCLOSED']._serialized_end=375 - _globals['_SHUTDOWN']._serialized_start=377 - _globals['_SHUTDOWN']._serialized_end=404 + _globals['_TEXTMESSAGE']._serialized_start=30 + _globals['_TEXTMESSAGE']._serialized_end=80 + _globals['_INPUT']._serialized_start=82 + _globals['_INPUT']._serialized_end=106 + _globals['_INPUTPROCESSED']._serialized_start=108 + _globals['_INPUTPROCESSED']._serialized_end=139 + _globals['_OUTPUT']._serialized_start=141 + _globals['_OUTPUT']._serialized_end=166 + _globals['_OUTPUTWRITTEN']._serialized_start=168 + _globals['_OUTPUTWRITTEN']._serialized_end=198 + _globals['_IOERROR']._serialized_start=200 + _globals['_IOERROR']._serialized_end=226 + _globals['_NEWMESSAGERECEIVED']._serialized_start=228 + _globals['_NEWMESSAGERECEIVED']._serialized_end=265 + _globals['_RESPONSEGENERATED']._serialized_start=267 + _globals['_RESPONSEGENERATED']._serialized_end=304 + _globals['_GOODBYE']._serialized_start=306 + _globals['_GOODBYE']._serialized_end=332 + _globals['_MESSAGESTORED']._serialized_start=334 + _globals['_MESSAGESTORED']._serialized_end=366 + _globals['_CONVERSATIONCLOSED']._serialized_start=368 + _globals['_CONVERSATIONCLOSED']._serialized_end=427 + _globals['_SHUTDOWN']._serialized_start=429 + _globals['_SHUTDOWN']._serialized_end=456 # @@protoc_insertion_point(module_scope) diff --git a/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.pyi b/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.pyi index a19a251d392..01cfbafee51 100644 --- a/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.pyi +++ b/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.pyi @@ -10,6 +10,24 @@ import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final +class TextMessage(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TEXTMESSAGE_FIELD_NUMBER: builtins.int + SOURCE_FIELD_NUMBER: builtins.int + textMessage: builtins.str + source: builtins.str + def __init__( + self, + *, + textMessage: builtins.str = ..., + source: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["source", b"source", "textMessage", b"textMessage"]) -> None: ... + +global___TextMessage = TextMessage + @typing.final class Input(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor diff --git a/python/pyproject.toml b/python/pyproject.toml index 3b099db535e..a7fb3f89f42 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -78,4 +78,4 @@ test = "python run_task_in_pkgs_if_exist.py test" check = ["fmt", "lint", "pyright", "mypy", "test"] -gen-proto = "python -m grpc_tools.protoc --python_out=./packages/autogen-core/src/autogen_core/application/protos --grpc_python_out=./packages/autogen-core/src/autogen_core/application/protos --mypy_out=./packages/autogen-core/src/autogen_core/application/protos --mypy_grpc_out=./packages/autogen-core/src/autogen_core/application/protos --proto_path ../protos/ agent_worker.proto --proto_path ../protos/ cloudevent.proto" +gen-proto = "python -m grpc_tools.protoc --python_out=./packages/autogen-core/src/autogen_core/application/protos --grpc_python_out=./packages/autogen-core/src/autogen_core/application/protos --mypy_out=./packages/autogen-core/src/autogen_core/application/protos --mypy_grpc_out=./packages/autogen-core/src/autogen_core/application/protos --proto_path ../protos/ agent_worker.proto --proto_path ../protos/ cloudevent.proto --proto_path ../protos/ agent_events.proto" From ec10a669bdfb19d4ba84fb955058d6cba2311e3e Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Tue, 26 Nov 2024 18:57:14 -0800 Subject: [PATCH 04/16] when running from aspire, keep HelloAgents alive --- dotnet/samples/Hello/Hello.AppHost/Program.cs | 1 + dotnet/samples/Hello/HelloAgent/Program.cs | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/dotnet/samples/Hello/Hello.AppHost/Program.cs b/dotnet/samples/Hello/Hello.AppHost/Program.cs index 326eddbcc9e..303855de538 100644 --- a/dotnet/samples/Hello/Hello.AppHost/Program.cs +++ b/dotnet/samples/Hello/Hello.AppHost/Program.cs @@ -8,6 +8,7 @@ builder.AddProject("client") .WithReference(backend) .WithEnvironment("AGENT_HOST", $"{backend.GetEndpoint("https").Property(EndpointProperty.Url)}") + .WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true") .WaitFor(backend); using var app = builder.Build(); diff --git a/dotnet/samples/Hello/HelloAgent/Program.cs b/dotnet/samples/Hello/HelloAgent/Program.cs index 4f74520a71e..3c1601076ab 100644 --- a/dotnet/samples/Hello/HelloAgent/Program.cs +++ b/dotnet/samples/Hello/HelloAgent/Program.cs @@ -53,7 +53,10 @@ public async Task Handle(ConversationClosed item) var goodbye = $"********************* {item.UserId} said {item.UserMessage} ************************"; var evt = new Output { Message = goodbye }; await PublishMessageAsync(evt).ConfigureAwait(true); - await PublishMessageAsync(new Shutdown()).ConfigureAwait(false); + if (Environment.GetEnvironmentVariable("STAY_ALIVE_ON_GOODBYE") != "true") + { + await PublishMessageAsync(new Shutdown()).ConfigureAwait(false); + } } public async Task Handle(Shutdown item) From 759844850ac18c82703df677b2ded518b0d6ada5 Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Tue, 26 Nov 2024 21:31:10 -0800 Subject: [PATCH 05/16] interim --- dotnet/Directory.Packages.props | 10 +---- dotnet/samples/Hello/HelloAgent/Program.cs | 14 ++----- .../HelloPythonAgent.AppHost.csproj | 3 +- .../HelloPythonAgent/user_input.py | 39 +++++++++++++++++++ 4 files changed, 45 insertions(+), 21 deletions(-) create mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent/user_input.py diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index 9bde32e6d01..5222105ca68 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -8,6 +8,7 @@ + @@ -24,15 +25,6 @@ - - - - - - - - - diff --git a/dotnet/samples/Hello/HelloAgent/Program.cs b/dotnet/samples/Hello/HelloAgent/Program.cs index 3c1601076ab..2558a0f1f19 100644 --- a/dotnet/samples/Hello/HelloAgent/Program.cs +++ b/dotnet/samples/Hello/HelloAgent/Program.cs @@ -6,20 +6,12 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -// step 1: create in-memory agent runtime - -// step 2: register HelloAgent to that agent runtime - -// step 3: start the agent runtime - -// step 4: send a message to the agent - -// step 5: wait for the agent runtime to shutdown +var local = true; +if (Environment.GetEnvironmentVariable("AGENT_HOST") != null) { local = false; } var app = await AgentsApp.PublishMessageAsync("HelloAgents", new NewMessageReceived { Message = "World" -}, local: true); -//var app = await AgentsApp.StartAsync(); +}, local: local).ConfigureAwait(false); await app.WaitForShutdownAsync(); namespace Hello diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/HelloPythonAgent.AppHost.csproj b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/HelloPythonAgent.AppHost.csproj index f1ffb8a4cfa..deac43d63ad 100644 --- a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/HelloPythonAgent.AppHost.csproj +++ b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/HelloPythonAgent.AppHost.csproj @@ -11,7 +11,8 @@ - + + diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent/user_input.py b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent/user_input.py new file mode 100644 index 00000000000..62d766ca05c --- /dev/null +++ b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent/user_input.py @@ -0,0 +1,39 @@ +import asyncio +import logging +from typing import Union + +from autogen_core.application.protos.agent_events_pb2 import ConversationClosed, Input, NewMessageReceived, Output +from autogen_core.base import MessageContext +from autogen_core.components import DefaultTopicId, RoutedAgent, message_handler + +input_types = Union[ConversationClosed, Input, Output] + + +class UserProxy(RoutedAgent): + """An agent that allows the user to play the role of an agent in the conversation via input.""" + + DEFAULT_DESCRIPTION = "A human user." + + def __init__( + self, + description: str = DEFAULT_DESCRIPTION, + ) -> None: + super().__init__(description) + + @message_handler + async def handle_user_chat_input(self, message: input_types, ctx: MessageContext) -> None: + agnext_logger = logging.getLogger("autogen_core") + + if isinstance(message, Input): + response = await self.ainput("User input ('exit' to quit): ") + response = response.strip() + agnext_logger.info(response) + + await self.publish_message(NewMessageReceived(message=response), topic_id=DefaultTopicId()) + elif isinstance(message, Output): + agnext_logger.info(message.message) + else: + pass + + async def ainput(self, prompt: str) -> str: + return await asyncio.to_thread(input, f"{prompt} ") From cdee4471681c68529d5736be3ab591f00989d835 Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Tue, 26 Nov 2024 22:55:38 -0800 Subject: [PATCH 06/16] rearrange --- .../Hello/Hello.AppHost/Hello.AppHost.csproj | 1 + dotnet/samples/Hello/Hello.AppHost/Program.cs | 12 +- .../HelloPythonAgent/hello_python_agent.py | 2 +- .../HelloPythonAgent/user_input.py | 0 .../HelloPythonAgent.AppHost.csproj | 18 --- .../HelloPythonAgent.AppHost/Program.cs | 6 - .../Properties/launchSettings.json | 29 ----- .../HelloPythonAgent.AppHost/appsettings.json | 9 -- .../Extensions.cs | 111 ------------------ .../HelloPythonAgent.ServiceDefaults.csproj | 22 ---- .../HelloPythonAgent/HelloPythonAgent.sln | 30 ----- 11 files changed, 11 insertions(+), 229 deletions(-) rename dotnet/samples/Hello/{HelloX-Lang/HelloPythonAgent => }/HelloPythonAgent/hello_python_agent.py (96%) rename dotnet/samples/Hello/{HelloX-Lang/HelloPythonAgent => }/HelloPythonAgent/user_input.py (100%) delete mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/HelloPythonAgent.AppHost.csproj delete mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Program.cs delete mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Properties/launchSettings.json delete mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/appsettings.json delete mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/Extensions.cs delete mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/HelloPythonAgent.ServiceDefaults.csproj delete mode 100644 dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.sln diff --git a/dotnet/samples/Hello/Hello.AppHost/Hello.AppHost.csproj b/dotnet/samples/Hello/Hello.AppHost/Hello.AppHost.csproj index 5ce0d0531fa..370d13fb32e 100644 --- a/dotnet/samples/Hello/Hello.AppHost/Hello.AppHost.csproj +++ b/dotnet/samples/Hello/Hello.AppHost/Hello.AppHost.csproj @@ -14,6 +14,7 @@ + diff --git a/dotnet/samples/Hello/Hello.AppHost/Program.cs b/dotnet/samples/Hello/Hello.AppHost/Program.cs index 303855de538..01314cfe9cf 100644 --- a/dotnet/samples/Hello/Hello.AppHost/Program.cs +++ b/dotnet/samples/Hello/Hello.AppHost/Program.cs @@ -10,11 +10,17 @@ .WithEnvironment("AGENT_HOST", $"{backend.GetEndpoint("https").Property(EndpointProperty.Url)}") .WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true") .WaitFor(backend); - +#pragma warning disable ASPIREHOSTINGPYTHON001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +builder.AddPythonApp("hello-python", "../HelloPythonAgent", "hello_python_agent.py", "../../../../python/.venv") + .WithReference(backend) + .WithEnvironment("AGENT_HOST", $"{backend.GetEndpoint("http").Property(EndpointProperty.Url)}") + .WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true") + .WithEnvironment("GRPC_DNS_RESOLVER", "native") + .WithOtlpExporter() + .WaitFor(backend); +#pragma warning restore ASPIREHOSTINGPYTHON001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. using var app = builder.Build(); - await app.StartAsync(); var url = backend.GetEndpoint("http").Url; Console.WriteLine("Backend URL: " + url); - await app.WaitForShutdownAsync(); diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent/hello_python_agent.py b/dotnet/samples/Hello/HelloPythonAgent/hello_python_agent.py similarity index 96% rename from dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent/hello_python_agent.py rename to dotnet/samples/Hello/HelloPythonAgent/hello_python_agent.py index c0c547af4d3..a1b3c0f4059 100644 --- a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent/hello_python_agent.py +++ b/dotnet/samples/Hello/HelloPythonAgent/hello_python_agent.py @@ -17,7 +17,7 @@ async def main() -> None: load_dotenv() - agentHost = os.getenv("AGENT_HOST") or "localhost:53072" + agentHost = os.getenv("AGENT_HOST") or "burger:53072" agnext_logger.info("0") agnext_logger.info(agentHost) runtime = WorkerAgentRuntime(host_address=agentHost) diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent/user_input.py b/dotnet/samples/Hello/HelloPythonAgent/user_input.py similarity index 100% rename from dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent/user_input.py rename to dotnet/samples/Hello/HelloPythonAgent/user_input.py diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/HelloPythonAgent.AppHost.csproj b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/HelloPythonAgent.AppHost.csproj deleted file mode 100644 index deac43d63ad..00000000000 --- a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/HelloPythonAgent.AppHost.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Exe - net8.0 - enable - enable - true - 0635cf78-0570-44ec-b8d3-bf09c05817dc - - - - - - - - diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Program.cs b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Program.cs deleted file mode 100644 index 73e3400bb46..00000000000 --- a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Program.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Program.cs - -var builder = DistributedApplication.CreateBuilder(args); - -builder.Build().Run(); diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Properties/launchSettings.json b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Properties/launchSettings.json deleted file mode 100644 index d0ec31a9d26..00000000000 --- a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/Properties/launchSettings.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/launchsettings.json", - "profiles": { - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "applicationUrl": "https://localhost:17048;http://localhost:15182", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "DOTNET_ENVIRONMENT": "Development", - "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21259", - "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22226" - } - }, - "http": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "applicationUrl": "http://localhost:15182", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "DOTNET_ENVIRONMENT": "Development", - "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19298", - "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20119" - } - } - } -} diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/appsettings.json b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/appsettings.json deleted file mode 100644 index 31c092aa450..00000000000 --- a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.AppHost/appsettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning", - "Aspire.Hosting.Dcp": "Warning" - } - } -} diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/Extensions.cs b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/Extensions.cs deleted file mode 100644 index ce94dc2c43d..00000000000 --- a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/Extensions.cs +++ /dev/null @@ -1,111 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Diagnostics.HealthChecks; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Diagnostics.HealthChecks; -using Microsoft.Extensions.Logging; -using OpenTelemetry; -using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; - -namespace Microsoft.Extensions.Hosting; - -// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. -// This project should be referenced by each service project in your solution. -// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults -public static class Extensions -{ - public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder) - { - builder.ConfigureOpenTelemetry(); - - builder.AddDefaultHealthChecks(); - - builder.Services.AddServiceDiscovery(); - - builder.Services.ConfigureHttpClientDefaults(http => - { - // Turn on resilience by default - http.AddStandardResilienceHandler(); - - // Turn on service discovery by default - http.AddServiceDiscovery(); - }); - - return builder; - } - - public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder) - { - builder.Logging.AddOpenTelemetry(logging => - { - logging.IncludeFormattedMessage = true; - logging.IncludeScopes = true; - }); - - builder.Services.AddOpenTelemetry() - .WithMetrics(metrics => - { - metrics.AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation() - .AddRuntimeInstrumentation(); - }) - .WithTracing(tracing => - { - tracing.AddAspNetCoreInstrumentation() - // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) - //.AddGrpcClientInstrumentation() - .AddHttpClientInstrumentation(); - }); - - builder.AddOpenTelemetryExporters(); - - return builder; - } - - private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder) - { - var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); - - if (useOtlpExporter) - { - builder.Services.AddOpenTelemetry().UseOtlpExporter(); - } - - // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) - //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) - //{ - // builder.Services.AddOpenTelemetry() - // .UseAzureMonitor(); - //} - - return builder; - } - - public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder) - { - builder.Services.AddHealthChecks() - // Add a default liveness check to ensure app is responsive - .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); - - return builder; - } - - public static WebApplication MapDefaultEndpoints(this WebApplication app) - { - // Adding health checks endpoints to applications in non-development environments has security implications. - // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. - if (app.Environment.IsDevelopment()) - { - // All health checks must pass for app to be considered ready to accept traffic after starting - app.MapHealthChecks("/health"); - - // Only health checks tagged with the "live" tag must pass for app to be considered alive - app.MapHealthChecks("/alive", new HealthCheckOptions - { - Predicate = r => r.Tags.Contains("live") - }); - } - - return app; - } -} diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/HelloPythonAgent.ServiceDefaults.csproj b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/HelloPythonAgent.ServiceDefaults.csproj deleted file mode 100644 index 9adc293690f..00000000000 --- a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.ServiceDefaults/HelloPythonAgent.ServiceDefaults.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - net8.0 - enable - enable - true - - - - - - - - - - - - - - - diff --git a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.sln b/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.sln deleted file mode 100644 index 84dedc55f8c..00000000000 --- a/dotnet/samples/Hello/HelloX-Lang/HelloPythonAgent/HelloPythonAgent.sln +++ /dev/null @@ -1,30 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.8.0.0 -MinimumVisualStudioVersion = 17.8.0.0 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloPythonAgent.AppHost", "HelloPythonAgent.AppHost\HelloPythonAgent.AppHost.csproj", "{109149B5-573D-49D4-A41E-72AB7070BE9D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloPythonAgent.ServiceDefaults", "HelloPythonAgent.ServiceDefaults\HelloPythonAgent.ServiceDefaults.csproj", "{1233A964-E549-41B3-85D8-D46C76A28B06}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {109149B5-573D-49D4-A41E-72AB7070BE9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {109149B5-573D-49D4-A41E-72AB7070BE9D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {109149B5-573D-49D4-A41E-72AB7070BE9D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {109149B5-573D-49D4-A41E-72AB7070BE9D}.Release|Any CPU.Build.0 = Release|Any CPU - {1233A964-E549-41B3-85D8-D46C76A28B06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1233A964-E549-41B3-85D8-D46C76A28B06}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1233A964-E549-41B3-85D8-D46C76A28B06}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1233A964-E549-41B3-85D8-D46C76A28B06}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {C18A43A3-5D53-430F-9B77-65458734F0AB} - EndGlobalSection -EndGlobal From 5e1580ea30ecb2213c9cc6510a24a75495e8fd28 Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Tue, 26 Nov 2024 23:14:08 -0800 Subject: [PATCH 07/16] stupid python grpc bug --- dotnet/samples/Hello/Hello.AppHost/Program.cs | 2 +- .../samples/Hello/HelloPythonAgent/hello_python_agent.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/dotnet/samples/Hello/Hello.AppHost/Program.cs b/dotnet/samples/Hello/Hello.AppHost/Program.cs index 01314cfe9cf..84b71290095 100644 --- a/dotnet/samples/Hello/Hello.AppHost/Program.cs +++ b/dotnet/samples/Hello/Hello.AppHost/Program.cs @@ -13,7 +13,7 @@ #pragma warning disable ASPIREHOSTINGPYTHON001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. builder.AddPythonApp("hello-python", "../HelloPythonAgent", "hello_python_agent.py", "../../../../python/.venv") .WithReference(backend) - .WithEnvironment("AGENT_HOST", $"{backend.GetEndpoint("http").Property(EndpointProperty.Url)}") + .WithEnvironment("AGENT_HOST", "backend-http") .WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true") .WithEnvironment("GRPC_DNS_RESOLVER", "native") .WithOtlpExporter() diff --git a/dotnet/samples/Hello/HelloPythonAgent/hello_python_agent.py b/dotnet/samples/Hello/HelloPythonAgent/hello_python_agent.py index a1b3c0f4059..dc4522c8f5e 100644 --- a/dotnet/samples/Hello/HelloPythonAgent/hello_python_agent.py +++ b/dotnet/samples/Hello/HelloPythonAgent/hello_python_agent.py @@ -17,7 +17,12 @@ async def main() -> None: load_dotenv() - agentHost = os.getenv("AGENT_HOST") or "burger:53072" + agentHost = os.getenv("AGENT_HOST") or "localhost:53072" + # stupid grpc python bug - can only use the hostname, not prefix - if hostname has a prefix we have to remove it: + if agentHost.startswith("http://"): + agentHost = agentHost[7:] + if agentHost.startswith("https://"): + agentHost = agentHost[8:] agnext_logger.info("0") agnext_logger.info(agentHost) runtime = WorkerAgentRuntime(host_address=agentHost) From f1e2e9aecec6ef40b62ca943501ad50da962a832 Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Tue, 26 Nov 2024 23:18:28 -0800 Subject: [PATCH 08/16] http --- dotnet/samples/Hello/Hello.AppHost/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/samples/Hello/Hello.AppHost/Program.cs b/dotnet/samples/Hello/Hello.AppHost/Program.cs index 84b71290095..01314cfe9cf 100644 --- a/dotnet/samples/Hello/Hello.AppHost/Program.cs +++ b/dotnet/samples/Hello/Hello.AppHost/Program.cs @@ -13,7 +13,7 @@ #pragma warning disable ASPIREHOSTINGPYTHON001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. builder.AddPythonApp("hello-python", "../HelloPythonAgent", "hello_python_agent.py", "../../../../python/.venv") .WithReference(backend) - .WithEnvironment("AGENT_HOST", "backend-http") + .WithEnvironment("AGENT_HOST", $"{backend.GetEndpoint("http").Property(EndpointProperty.Url)}") .WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true") .WithEnvironment("GRPC_DNS_RESOLVER", "native") .WithOtlpExporter() From 16b7d31d2adc4124eb01d112242fa4cf9ca38158 Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Tue, 26 Nov 2024 23:28:58 -0800 Subject: [PATCH 09/16] get python to waitfor client --- dotnet/samples/Hello/Hello.AppHost/Program.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dotnet/samples/Hello/Hello.AppHost/Program.cs b/dotnet/samples/Hello/Hello.AppHost/Program.cs index 01314cfe9cf..ffb557f121b 100644 --- a/dotnet/samples/Hello/Hello.AppHost/Program.cs +++ b/dotnet/samples/Hello/Hello.AppHost/Program.cs @@ -5,7 +5,7 @@ var builder = DistributedApplication.CreateBuilder(args); var backend = builder.AddProject("backend").WithExternalHttpEndpoints(); -builder.AddProject("client") +var client = builder.AddProject("client") .WithReference(backend) .WithEnvironment("AGENT_HOST", $"{backend.GetEndpoint("https").Property(EndpointProperty.Url)}") .WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true") @@ -17,7 +17,8 @@ .WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true") .WithEnvironment("GRPC_DNS_RESOLVER", "native") .WithOtlpExporter() - .WaitFor(backend); + .WaitFor(backend) + .WaitFor(client); #pragma warning restore ASPIREHOSTINGPYTHON001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. using var app = builder.Build(); await app.StartAsync(); From bb547a649624c7602665c5d1af834d28ed6b6809 Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Tue, 26 Nov 2024 23:32:18 -0800 Subject: [PATCH 10/16] resource names --- dotnet/samples/Hello/Hello.AppHost/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotnet/samples/Hello/Hello.AppHost/Program.cs b/dotnet/samples/Hello/Hello.AppHost/Program.cs index ffb557f121b..f8964a22b03 100644 --- a/dotnet/samples/Hello/Hello.AppHost/Program.cs +++ b/dotnet/samples/Hello/Hello.AppHost/Program.cs @@ -5,13 +5,13 @@ var builder = DistributedApplication.CreateBuilder(args); var backend = builder.AddProject("backend").WithExternalHttpEndpoints(); -var client = builder.AddProject("client") +var client = builder.AddProject("HelloAgentsDotNET") .WithReference(backend) .WithEnvironment("AGENT_HOST", $"{backend.GetEndpoint("https").Property(EndpointProperty.Url)}") .WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true") .WaitFor(backend); #pragma warning disable ASPIREHOSTINGPYTHON001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. -builder.AddPythonApp("hello-python", "../HelloPythonAgent", "hello_python_agent.py", "../../../../python/.venv") +builder.AddPythonApp("HelloAgentsPython", "../HelloPythonAgent", "hello_python_agent.py", "../../../../python/.venv") .WithReference(backend) .WithEnvironment("AGENT_HOST", $"{backend.GetEndpoint("http").Property(EndpointProperty.Url)}") .WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true") From 7144d4f04ca114c0ff8a488d5efcbe3a8f129c4a Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Wed, 27 Nov 2024 08:49:15 -0800 Subject: [PATCH 11/16] rebase python dir to main --- python/README.md | 13 +- .../packages/autogen-agentchat/pyproject.toml | 1 + .../teams/_group_chat/_base_group_chat.py | 6 +- .../_magentic_one/_magentic_one_group_chat.py | 1 + .../_magentic_one_orchestrator.py | 15 +- .../extracting-results-with-an-agent.ipynb | 16 +- python/packages/autogen-core/pyproject.toml | 9 +- .../semantic_router/run_semantic_router.py | 12 +- .../samples/slow_human_in_loop.py | 20 +- .../autogen-core/samples/xlang/user_input.py | 39 - .../autogen-core/samples/xlang/worker.py | 50 - .../autogen_core/application/_constants.py | 13 + .../_single_threaded_agent_runtime.py | 10 + .../src/autogen_core/application/_utils.py | 3 - .../application/_worker_runtime.py | 221 +- .../application/_worker_runtime_host.py | 2 +- .../_worker_runtime_host_servicer.py | 97 +- .../application/protos/agent_events_pb2.py | 49 - .../application/protos/agent_events_pb2.pyi | 197 -- .../application/protos/agent_worker_pb2.py | 36 +- .../application/protos/agent_worker_pb2.pyi | 44 +- .../src/autogen_core/base/__init__.py | 2 + .../src/autogen_core/base/_agent_runtime.py | 4 +- .../src/autogen_core/base/_base_agent.py | 12 + .../src/autogen_core/base/_message_context.py | 1 + .../src/autogen_core/base/_serialization.py | 25 +- .../base/_type_prefix_subscription.py | 65 + .../src/autogen_core/components/__init__.py | 5 +- .../autogen_core/components/_closure_agent.py | 71 +- .../components/_type_subscription.py | 6 +- .../tests/protos/serialization_test.proto | 11 + .../tests/protos/serialization_test_pb2.py | 28 + .../tests/protos/serialization_test_pb2.pyi | 46 + .../protos/serialization_test_pb2_grpc.py} | 0 .../protos/serialization_test_pb2_grpc.pyi} | 0 .../autogen-core/tests/test_cancellation.py | 12 +- .../autogen-core/tests/test_closure_agent.py | 11 +- .../autogen-core/tests/test_intervention.py | 10 +- .../autogen-core/tests/test_routed_agent.py | 14 +- .../autogen-core/tests/test_runtime.py | 86 +- .../autogen-core/tests/test_serialization.py | 31 +- .../packages/autogen-core/tests/test_state.py | 6 +- .../autogen-core/tests/test_subscription.py | 2 +- .../autogen-core/tests/test_tool_agent.py | 6 +- .../autogen-core/tests/test_utils/__init__.py | 2 + .../autogen-core/tests/test_worker_runtime.py | 70 +- python/packages/autogen-studio/README.md | 6 +- .../autogen-studio/autogenstudio/__init__.py | 19 +- .../autogen-studio/autogenstudio/cli.py | 8 +- .../autogenstudio/components/__init__.py | 1 - .../components/agents/__init__.py | 0 .../components/agents/userproxy.py | 47 - .../autogenstudio/database/__init__.py | 4 +- .../database/component_factory.py | 237 +- .../autogenstudio/database/config_manager.py | 156 +- .../autogenstudio/database/db_manager.py | 198 +- .../autogenstudio/database/schema_manager.py | 331 +-- .../autogenstudio/datamodel/__init__.py | 13 +- .../autogenstudio/datamodel/db.py | 159 +- .../autogenstudio/datamodel/types.py | 42 +- .../autogen-studio/autogenstudio/profiler.py | 108 - .../autogenstudio/teammanager.py | 44 +- .../autogenstudio/utils/__init__.py | 1 - .../autogenstudio/utils/utils.py | 62 +- .../autogen-studio/autogenstudio/version.py | 2 +- .../autogen-studio/autogenstudio/web/app.py | 16 +- .../autogen-studio/autogenstudio/web/deps.py | 63 +- .../autogenstudio/web/initialization.py | 15 +- .../autogenstudio/web/managers/connection.py | 276 ++- .../autogenstudio/web/routes/agents.py | 168 +- .../autogenstudio/web/routes/gallery.py | 62 + .../autogenstudio/web/routes/models.py | 81 +- .../autogenstudio/web/routes/runs.py | 75 +- .../autogenstudio/web/routes/sessions.py | 155 +- .../autogenstudio/web/routes/teams.py | 133 +- .../autogenstudio/web/routes/tools.py | 90 +- .../autogenstudio/web/routes/ws.py | 55 +- .../autogen-studio/frontend/package.json | 4 +- .../src/components/types/datamodel.ts | 53 +- .../playground/chat/agentflow/agentflow.tsx | 221 +- .../playground/chat/agentflow/agentnode.tsx | 10 +- .../views/playground/chat/agentflow/edge.tsx | 95 +- .../chat/agentflow/edgemessagemodal.tsx | 103 + .../components/views/playground/chat/chat.tsx | 877 ++++--- .../chat/{threadview.tsx => inputrequest.tsx} | 112 +- .../views/playground/chat/messagelist.tsx | 289 --- .../views/playground/chat/rendermessage.tsx | 127 +- .../views/playground/chat/runview.tsx | 271 +++ .../src/components/views/shared/atoms.tsx | 129 +- .../src/components/views/shared/markdown.tsx | 12 +- .../components/views/shared/session/api.ts | 19 +- .../frontend/src/pages/index.tsx | 2 +- .../autogen-studio/frontend/yarn.lock | 2048 ++++------------- .../autogen-studio/notebooks/tutorial.ipynb | 122 +- python/packages/autogen-studio/pyproject.toml | 21 +- .../tests/test_component_factory.py | 129 +- .../autogen-studio/tests/test_db_manager.py | 89 +- python/pyproject.toml | 4 +- python/uv.lock | 22 +- 99 files changed, 3687 insertions(+), 5079 deletions(-) delete mode 100644 python/packages/autogen-core/samples/xlang/user_input.py delete mode 100644 python/packages/autogen-core/samples/xlang/worker.py create mode 100644 python/packages/autogen-core/src/autogen_core/application/_constants.py delete mode 100644 python/packages/autogen-core/src/autogen_core/application/_utils.py delete mode 100644 python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.py delete mode 100644 python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.pyi create mode 100644 python/packages/autogen-core/src/autogen_core/base/_type_prefix_subscription.py create mode 100644 python/packages/autogen-core/tests/protos/serialization_test.proto create mode 100644 python/packages/autogen-core/tests/protos/serialization_test_pb2.py create mode 100644 python/packages/autogen-core/tests/protos/serialization_test_pb2.pyi rename python/packages/autogen-core/{src/autogen_core/application/protos/agent_events_pb2_grpc.py => tests/protos/serialization_test_pb2_grpc.py} (100%) rename python/packages/autogen-core/{src/autogen_core/application/protos/agent_events_pb2_grpc.pyi => tests/protos/serialization_test_pb2_grpc.pyi} (100%) delete mode 100644 python/packages/autogen-studio/autogenstudio/components/__init__.py delete mode 100644 python/packages/autogen-studio/autogenstudio/components/agents/__init__.py delete mode 100644 python/packages/autogen-studio/autogenstudio/components/agents/userproxy.py delete mode 100644 python/packages/autogen-studio/autogenstudio/profiler.py create mode 100644 python/packages/autogen-studio/autogenstudio/web/routes/gallery.py create mode 100644 python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/edgemessagemodal.tsx rename python/packages/autogen-studio/frontend/src/components/views/playground/chat/{threadview.tsx => inputrequest.tsx} (59%) delete mode 100644 python/packages/autogen-studio/frontend/src/components/views/playground/chat/messagelist.tsx create mode 100644 python/packages/autogen-studio/frontend/src/components/views/playground/chat/runview.tsx diff --git a/python/README.md b/python/README.md index cf18c4927d9..69085b4c8dd 100644 --- a/python/README.md +++ b/python/README.md @@ -1,26 +1,21 @@ # AutoGen Python packages -See [`autogen-core`](./packages/autogen-core/) package for main functionality. +[![0.4 Docs](https://img.shields.io/badge/Docs-0.4-blue)](https://microsoft.github.io/autogen/dev/) +[![PyPi autogen-core](https://img.shields.io/badge/PyPi-autogen--core-blue?logo=pypi)](https://pypi.org/project/autogen-core/0.4.0.dev7/) [![PyPi autogen-agentchat](https://img.shields.io/badge/PyPi-autogen--agentchat-blue?logo=pypi)](https://pypi.org/project/autogen-agentchat/0.4.0.dev7/) [![PyPi autogen-ext](https://img.shields.io/badge/PyPi-autogen--ext-blue?logo=pypi)](https://pypi.org/project/autogen-ext/0.4.0.dev7/) +This directory works as a single `uv` workspace containing all project packages. See [`packages`](./packages/) to discover all project packages. + ## Development **TL;DR**, run all checks with: -macOS & Linux: ```sh uv sync --all-extras source .venv/bin/activate poe check ``` -Windows (pwsh): -```sh -uv sync --all-extras -./.venv/Scripts/activate.ps1 -poe check -``` - ### Setup `uv` is a package manager that assists in creating the necessary environment and installing packages to run AutoGen. - [Install `uv`](https://docs.astral.sh/uv/getting-started/installation/). diff --git a/python/packages/autogen-agentchat/pyproject.toml b/python/packages/autogen-agentchat/pyproject.toml index c2336a6eeba..705a125528a 100644 --- a/python/packages/autogen-agentchat/pyproject.toml +++ b/python/packages/autogen-agentchat/pyproject.toml @@ -29,6 +29,7 @@ include = ["src/**", "tests/*.py"] [tool.pyright] extends = "../../pyproject.toml" include = ["src", "tests"] +reportDeprecated = true [tool.pytest.ini_options] minversion = "6.0" diff --git a/python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_base_group_chat.py b/python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_base_group_chat.py index fbca2644920..b038d857305 100644 --- a/python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_base_group_chat.py +++ b/python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_base_group_chat.py @@ -14,6 +14,7 @@ MessageContext, ) from autogen_core.components import ClosureAgent, TypeSubscription +from autogen_core.components._closure_agent import ClosureContext from ... import EVENT_LOGGER_NAME from ...base import ChatAgent, TaskResult, Team, TerminationCondition @@ -139,8 +140,7 @@ async def _init(self, runtime: AgentRuntime) -> None: ) async def collect_output_messages( - _runtime: AgentRuntime, - id: AgentId, + _runtime: ClosureContext, message: GroupChatStart | GroupChatMessage | GroupChatTermination, ctx: MessageContext, ) -> None: @@ -150,7 +150,7 @@ async def collect_output_messages( return await self._output_message_queue.put(message.message) - await ClosureAgent.register( + await ClosureAgent.register_closure( runtime, type=self._collector_agent_type, closure=collect_output_messages, diff --git a/python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_magentic_one/_magentic_one_group_chat.py b/python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_magentic_one/_magentic_one_group_chat.py index d199cbfd712..cd67ced11e5 100644 --- a/python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_magentic_one/_magentic_one_group_chat.py +++ b/python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_magentic_one/_magentic_one_group_chat.py @@ -47,6 +47,7 @@ def _create_group_chat_manager_factory( return lambda: MagenticOneOrchestrator( group_topic_type, output_topic_type, + self._team_id, participant_topic_types, participant_descriptions, max_turns, diff --git a/python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_magentic_one/_magentic_one_orchestrator.py b/python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_magentic_one/_magentic_one_orchestrator.py index ae1567a7dae..f6963016234 100644 --- a/python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_magentic_one/_magentic_one_orchestrator.py +++ b/python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_magentic_one/_magentic_one_orchestrator.py @@ -1,7 +1,7 @@ import json from typing import Any, List -from autogen_core.base import MessageContext +from autogen_core.base import MessageContext, AgentId from autogen_core.components import DefaultTopicId, Image, event, rpc from autogen_core.components.models import ( AssistantMessage, @@ -42,6 +42,7 @@ def __init__( self, group_topic_type: str, output_topic_type: str, + team_id: str, participant_topic_types: List[str], participant_descriptions: List[str], max_turns: int | None, @@ -51,6 +52,7 @@ def __init__( super().__init__(description="Group chat manager") self._group_topic_type = group_topic_type self._output_topic_type = output_topic_type + self._team_id = team_id if len(participant_topic_types) != len(participant_descriptions): raise ValueError("The number of participant topic types, agent types, and descriptions must be the same.") if len(set(participant_topic_types)) != len(participant_topic_types): @@ -164,10 +166,13 @@ async def on_unhandled_message(self, message: Any, ctx: MessageContext) -> None: async def _reenter_inner_loop(self) -> None: # Reset the agents - await self.publish_message( - GroupChatReset(), - topic_id=DefaultTopicId(type=self._group_topic_type), - ) + for participant_topic_type in self._participant_topic_types: + await self._runtime.send_message( + GroupChatReset(), + recipient=AgentId(type=participant_topic_type, key=self._team_id), + ) + # Reset the group chat manager + await self.reset() self._message_thread.clear() # Prepare the ledger diff --git a/python/packages/autogen-core/docs/src/user-guide/core-user-guide/cookbook/extracting-results-with-an-agent.ipynb b/python/packages/autogen-core/docs/src/user-guide/core-user-guide/cookbook/extracting-results-with-an-agent.ipynb index c386699f0d7..e86b807d783 100644 --- a/python/packages/autogen-core/docs/src/user-guide/core-user-guide/cookbook/extracting-results-with-an-agent.ipynb +++ b/python/packages/autogen-core/docs/src/user-guide/core-user-guide/cookbook/extracting-results-with-an-agent.ipynb @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -26,8 +26,8 @@ "from dataclasses import dataclass\n", "\n", "from autogen_core.application import SingleThreadedAgentRuntime\n", - "from autogen_core.base import AgentId, AgentRuntime, MessageContext\n", - "from autogen_core.components import ClosureAgent, DefaultSubscription, DefaultTopicId" + "from autogen_core.base import MessageContext\n", + "from autogen_core.components import ClosureAgent, ClosureContext, DefaultSubscription, DefaultTopicId" ] }, { @@ -77,11 +77,11 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "async def output_result(_runtime: AgentRuntime, id: AgentId, message: FinalResult, ctx: MessageContext) -> None:\n", + "async def output_result(_agent: ClosureContext, message: FinalResult, ctx: MessageContext) -> None:\n", " await queue.put(message)" ] }, @@ -94,7 +94,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -110,7 +110,9 @@ ], "source": [ "runtime = SingleThreadedAgentRuntime()\n", - "await ClosureAgent.register(runtime, \"output_result\", output_result, subscriptions=lambda: [DefaultSubscription()])" + "await ClosureAgent.register_closure(\n", + " runtime, \"output_result\", output_result, subscriptions=lambda: [DefaultSubscription()]\n", + ")" ] }, { diff --git a/python/packages/autogen-core/pyproject.toml b/python/packages/autogen-core/pyproject.toml index 064928590b0..759137ddf2f 100644 --- a/python/packages/autogen-core/pyproject.toml +++ b/python/packages/autogen-core/pyproject.toml @@ -25,6 +25,7 @@ dependencies = [ "opentelemetry-api~=1.27.0", "asyncio_atexit", "jsonref~=1.1.0", + "grpcio~=1.62.0", # TODO: update this once we have a stable version. ] [project.optional-dependencies] @@ -81,7 +82,7 @@ dev-dependencies = [ [tool.ruff] extend = "../../pyproject.toml" -exclude = ["build", "dist", "src/autogen_core/application/protos"] +exclude = ["build", "dist", "src/autogen_core/application/protos", "tests/protos"] include = ["src/**", "samples/*.py", "docs/**/*.ipynb", "tests/**"] [tool.ruff.lint.per-file-ignores] @@ -91,8 +92,8 @@ include = ["src/**", "samples/*.py", "docs/**/*.ipynb", "tests/**"] [tool.pyright] extends = "../../pyproject.toml" include = ["src", "tests", "samples"] -exclude = ["src/autogen_core/application/protos"] -reportDeprecated = false +exclude = ["src/autogen_core/application/protos", "tests/protos"] +reportDeprecated = true [tool.pytest.ini_options] minversion = "6.0" @@ -111,7 +112,7 @@ include = "../../shared_tasks.toml" test = "pytest -n auto" mypy.default_item_type = "cmd" mypy.sequence = [ - "mypy --config-file ../../pyproject.toml --exclude src/autogen_core/application/protos src tests", + "mypy --config-file ../../pyproject.toml --exclude src/autogen_core/application/protos --exclude tests/protos src tests", "nbqa mypy docs/src --config-file ../../pyproject.toml", ] diff --git a/python/packages/autogen-core/samples/semantic_router/run_semantic_router.py b/python/packages/autogen-core/samples/semantic_router/run_semantic_router.py index 48f57eece71..35d057fb994 100644 --- a/python/packages/autogen-core/samples/semantic_router/run_semantic_router.py +++ b/python/packages/autogen-core/samples/semantic_router/run_semantic_router.py @@ -32,8 +32,8 @@ WorkerAgentMessage, ) from autogen_core.application import WorkerAgentRuntime -from autogen_core.base import AgentId, AgentRuntime, MessageContext -from autogen_core.components import ClosureAgent, DefaultSubscription, DefaultTopicId +from autogen_core.base import MessageContext +from autogen_core.components import ClosureAgent, ClosureContext, DefaultSubscription, DefaultTopicId class MockIntentClassifier(IntentClassifierBase): @@ -60,12 +60,12 @@ async def get_agent(self, intent: str) -> str: async def output_result( - _runtime: AgentRuntime, id: AgentId, message: WorkerAgentMessage | FinalResult, ctx: MessageContext + closure_ctx: ClosureContext, message: WorkerAgentMessage | FinalResult, ctx: MessageContext ) -> None: if isinstance(message, WorkerAgentMessage): print(f"{message.source} Agent: {message.content}") new_message = input("User response: ") - await _runtime.publish_message( + await closure_ctx.publish_message( UserProxyMessage(content=new_message, source="user"), topic_id=DefaultTopicId(type=message.source, source="user"), ) @@ -73,7 +73,7 @@ async def output_result( print(f"{message.source} Agent: {message.content}") print("Conversation ended") new_message = input("Enter a new conversation start: ") - await _runtime.publish_message( + await closure_ctx.publish_message( UserProxyMessage(content=new_message, source="user"), topic_id=DefaultTopicId(type="default", source="user") ) @@ -95,7 +95,7 @@ async def run_workers(): await agent_runtime.add_subscription(DefaultSubscription(topic_type="user_proxy", agent_type="user_proxy")) # A closure agent surfaces the final result to external systems (e.g. an API) so that the system can interact with the user - await ClosureAgent.register( + await ClosureAgent.register_closure( agent_runtime, "closure_agent", output_result, diff --git a/python/packages/autogen-core/samples/slow_human_in_loop.py b/python/packages/autogen-core/samples/slow_human_in_loop.py index cc8012f601c..348b156493b 100644 --- a/python/packages/autogen-core/samples/slow_human_in_loop.py +++ b/python/packages/autogen-core/samples/slow_human_in_loop.py @@ -33,7 +33,13 @@ from autogen_core.application import SingleThreadedAgentRuntime from autogen_core.base import AgentId, CancellationToken, MessageContext from autogen_core.base.intervention import DefaultInterventionHandler -from autogen_core.components import DefaultSubscription, DefaultTopicId, FunctionCall, RoutedAgent, message_handler +from autogen_core.components import ( + DefaultTopicId, + FunctionCall, + RoutedAgent, + message_handler, + type_subscription, +) from autogen_core.components.model_context import BufferedChatCompletionContext from autogen_core.components.models import ( AssistantMessage, @@ -81,6 +87,7 @@ def save_content(self, content: Mapping[str, Any]) -> None: state_persister = MockPersistence() +@type_subscription("scheduling_assistant_conversation") class SlowUserProxyAgent(RoutedAgent): def __init__( self, @@ -132,6 +139,7 @@ async def run(self, args: ScheduleMeetingInput, cancellation_token: Cancellation return ScheduleMeetingOutput() +@type_subscription("scheduling_assistant_conversation") class SchedulingAssistantAgent(RoutedAgent): def __init__( self, @@ -256,16 +264,13 @@ async def main(latest_user_input: Optional[str] = None) -> None | str: needs_user_input_handler = NeedsUserInputHandler() runtime = SingleThreadedAgentRuntime(intervention_handlers=[needs_user_input_handler, termination_handler]) - await runtime.register( - "User", - lambda: SlowUserProxyAgent("User", "I am a user"), - subscriptions=lambda: [DefaultSubscription("scheduling_assistant_conversation")], - ) + await SlowUserProxyAgent.register(runtime, "User", lambda: SlowUserProxyAgent("User", "I am a user")) initial_schedule_assistant_message = AssistantTextMessage( content="Hi! How can I help you? I can help schedule meetings", source="User" ) - await runtime.register( + await SchedulingAssistantAgent.register( + runtime, "SchedulingAssistant", lambda: SchedulingAssistantAgent( "SchedulingAssistant", @@ -273,7 +278,6 @@ async def main(latest_user_input: Optional[str] = None) -> None | str: model_client=get_chat_completion_client_from_envs(model="gpt-4o-mini"), initial_message=initial_schedule_assistant_message, ), - subscriptions=lambda: [DefaultSubscription("scheduling_assistant_conversation")], ) if latest_user_input is not None: diff --git a/python/packages/autogen-core/samples/xlang/user_input.py b/python/packages/autogen-core/samples/xlang/user_input.py deleted file mode 100644 index 62d766ca05c..00000000000 --- a/python/packages/autogen-core/samples/xlang/user_input.py +++ /dev/null @@ -1,39 +0,0 @@ -import asyncio -import logging -from typing import Union - -from autogen_core.application.protos.agent_events_pb2 import ConversationClosed, Input, NewMessageReceived, Output -from autogen_core.base import MessageContext -from autogen_core.components import DefaultTopicId, RoutedAgent, message_handler - -input_types = Union[ConversationClosed, Input, Output] - - -class UserProxy(RoutedAgent): - """An agent that allows the user to play the role of an agent in the conversation via input.""" - - DEFAULT_DESCRIPTION = "A human user." - - def __init__( - self, - description: str = DEFAULT_DESCRIPTION, - ) -> None: - super().__init__(description) - - @message_handler - async def handle_user_chat_input(self, message: input_types, ctx: MessageContext) -> None: - agnext_logger = logging.getLogger("autogen_core") - - if isinstance(message, Input): - response = await self.ainput("User input ('exit' to quit): ") - response = response.strip() - agnext_logger.info(response) - - await self.publish_message(NewMessageReceived(message=response), topic_id=DefaultTopicId()) - elif isinstance(message, Output): - agnext_logger.info(message.message) - else: - pass - - async def ainput(self, prompt: str) -> str: - return await asyncio.to_thread(input, f"{prompt} ") diff --git a/python/packages/autogen-core/samples/xlang/worker.py b/python/packages/autogen-core/samples/xlang/worker.py deleted file mode 100644 index 08b9ab635bb..00000000000 --- a/python/packages/autogen-core/samples/xlang/worker.py +++ /dev/null @@ -1,50 +0,0 @@ -import asyncio -import logging -import os - -from autogen_core.application import WorkerAgentRuntime -from autogen_core.application.protos.agent_events_pb2 import NewMessageReceived -from autogen_core.base import AgentId, try_get_known_serializers_for_type -from autogen_core.components import DefaultSubscription, DefaultTopicId - -# Add the local package directory to sys.path -# sys.path.append(os.path.abspath('../../../../python/packages/autogen-core')) -from dotenv import load_dotenv -from user_input import UserProxy - -agnext_logger = logging.getLogger("autogen_core") - - -async def main() -> None: - load_dotenv() - agentHost = os.getenv("AGENT_HOST") or "localhost:53072" - agnext_logger.info("0") - agnext_logger.info(agentHost) - runtime = WorkerAgentRuntime(host_address=agentHost) - - agnext_logger.info("1") - runtime.start() - runtime.add_message_serializer(try_get_known_serializers_for_type(NewMessageReceived)) - - agnext_logger.info("2") - - await UserProxy.register(runtime, "HelloAgents", lambda: UserProxy()) - await runtime.add_subscription(DefaultSubscription(agent_type="HelloAgents")) - agnext_logger.info("3") - - message = NewMessageReceived(message="Hello from Python!") - - await runtime.publish_message( - message=message, - topic_id=DefaultTopicId("agents.NewMessageReceived"), - sender=AgentId("HelloAgents", "python"), - ) - await runtime.stop_when_signal() - # await runtime.stop_when_idle() - - -if __name__ == "__main__": - logging.basicConfig(level=logging.DEBUG) - agnext_logger.setLevel(logging.DEBUG) - agnext_logger.log(logging.DEBUG, "Starting worker") - asyncio.run(main()) diff --git a/python/packages/autogen-core/src/autogen_core/application/_constants.py b/python/packages/autogen-core/src/autogen_core/application/_constants.py new file mode 100644 index 00000000000..6dab3fffdb4 --- /dev/null +++ b/python/packages/autogen-core/src/autogen_core/application/_constants.py @@ -0,0 +1,13 @@ +GRPC_IMPORT_ERROR_STR = ( + "Distributed runtime features require additional dependencies. Install them with: pip install autogen-core[grpc]" +) + +DATA_CONTENT_TYPE_ATTR = "datacontenttype" +DATA_SCHEMA_ATTR = "dataschema" +AGENT_SENDER_TYPE_ATTR = "agagentsendertype" +AGENT_SENDER_KEY_ATTR = "agagentsenderkey" +MESSAGE_KIND_ATTR = "agmsgkind" +MESSAGE_KIND_VALUE_PUBLISH = "publish" +MESSAGE_KIND_VALUE_RPC_REQUEST = "rpc_request" +MESSAGE_KIND_VALUE_RPC_RESPONSE = "rpc_response" +MESSAGE_KIND_VALUE_RPC_ERROR = "error" diff --git a/python/packages/autogen-core/src/autogen_core/application/_single_threaded_agent_runtime.py b/python/packages/autogen-core/src/autogen_core/application/_single_threaded_agent_runtime.py index 52d24c64d0c..3d81f15eb33 100644 --- a/python/packages/autogen-core/src/autogen_core/application/_single_threaded_agent_runtime.py +++ b/python/packages/autogen-core/src/autogen_core/application/_single_threaded_agent_runtime.py @@ -4,6 +4,7 @@ import inspect import logging import threading +import uuid import warnings from asyncio import CancelledError, Future, Task from collections.abc import Sequence @@ -53,6 +54,7 @@ class PublishMessageEnvelope: sender: AgentId | None topic_id: TopicId metadata: EnvelopeMetadata | None = None + message_id: str @dataclass(kw_only=True) @@ -256,6 +258,7 @@ async def publish_message( *, sender: AgentId | None = None, cancellation_token: CancellationToken | None = None, + message_id: str | None = None, ) -> None: with self._tracer_helper.trace_block( "create", @@ -268,6 +271,9 @@ async def publish_message( content = message.__dict__ if hasattr(message, "__dict__") else message logger.info(f"Publishing message of type {type(message).__name__} to all subscribers: {content}") + if message_id is None: + message_id = str(uuid.uuid4()) + # event_logger.info( # MessageEvent( # payload=message, @@ -285,6 +291,7 @@ async def publish_message( sender=sender, topic_id=topic_id, metadata=get_telemetry_envelope_metadata(), + message_id=message_id, ) ) @@ -327,6 +334,8 @@ async def _process_send(self, message_envelope: SendMessageEnvelope) -> None: topic_id=None, is_rpc=True, cancellation_token=message_envelope.cancellation_token, + # Will be fixed when send API removed + message_id="NOT_DEFINED_TODO_FIX", ) with MessageHandlerContext.populate_context(recipient_agent.id): response = await recipient_agent.on_message( @@ -385,6 +394,7 @@ async def _process_publish(self, message_envelope: PublishMessageEnvelope) -> No topic_id=message_envelope.topic_id, is_rpc=False, cancellation_token=message_envelope.cancellation_token, + message_id=message_envelope.message_id, ) agent = await self._get_agent(agent_id) diff --git a/python/packages/autogen-core/src/autogen_core/application/_utils.py b/python/packages/autogen-core/src/autogen_core/application/_utils.py deleted file mode 100644 index 10fbfd1b8c8..00000000000 --- a/python/packages/autogen-core/src/autogen_core/application/_utils.py +++ /dev/null @@ -1,3 +0,0 @@ -GRPC_IMPORT_ERROR_STR = ( - "Distributed runtime features require additional dependencies. Install them with: pip install autogen-core[grpc]" -) diff --git a/python/packages/autogen-core/src/autogen_core/application/_worker_runtime.py b/python/packages/autogen-core/src/autogen_core/application/_worker_runtime.py index a66e4063b08..24007fadfc7 100644 --- a/python/packages/autogen-core/src/autogen_core/application/_worker_runtime.py +++ b/python/packages/autogen-core/src/autogen_core/application/_worker_runtime.py @@ -3,6 +3,7 @@ import json import logging import signal +import uuid import warnings from asyncio import Future, Task from collections import defaultdict @@ -27,14 +28,15 @@ cast, ) -import grpc from google.protobuf import any_pb2 -from grpc.aio import StreamStreamCall from opentelemetry.trace import TracerProvider from typing_extensions import Self, deprecated +from autogen_core.application.protos import cloudevent_pb2 + from ..base import ( JSON_DATA_CONTENT_TYPE, + PROTOBUF_DATA_CONTENT_TYPE, Agent, AgentId, AgentInstantiationContext, @@ -50,14 +52,11 @@ ) from ..base._serialization import MessageSerializer, SerializationRegistry from ..base._type_helpers import ChannelArgumentType -from ..components import TypeSubscription +from ..components import TypePrefixSubscription, TypeSubscription +from . import _constants +from ._constants import GRPC_IMPORT_ERROR_STR from ._helpers import SubscriptionManager, get_impl -from .protos import ( - agent_worker_pb2, - agent_worker_pb2_grpc, - cloudevent_pb2, -) -from ._utils import GRPC_IMPORT_ERROR_STR +from .protos import agent_worker_pb2, agent_worker_pb2_grpc from .telemetry import MessageRuntimeTracingConfig, TraceHelper, get_telemetry_grpc_metadata try: @@ -184,6 +183,7 @@ def __init__( host_address: str, tracer_provider: TracerProvider | None = None, extra_grpc_config: ChannelArgumentType | None = None, + payload_serialization_format: str = JSON_DATA_CONTENT_TYPE, ) -> None: self._host_address = host_address self._trace_helper = TraceHelper(tracer_provider, MessageRuntimeTracingConfig("Worker Runtime")) @@ -204,6 +204,11 @@ def __init__( self._serialization_registry = SerializationRegistry() self._extra_grpc_config = extra_grpc_config or [] + if payload_serialization_format not in {JSON_DATA_CONTENT_TYPE, PROTOBUF_DATA_CONTENT_TYPE}: + raise ValueError(f"Unsupported payload serialization format: {payload_serialization_format}") + + self._payload_serialization_format = payload_serialization_format + def start(self) -> None: """Start the runtime in a background task.""" if self._running: @@ -242,8 +247,10 @@ async def _run_read_loop(self) -> None: self._background_tasks.add(task) task.add_done_callback(self._raise_on_exception) task.add_done_callback(self._background_tasks.discard) - case "event": - task = asyncio.create_task(self._process_event(message.event)) + case "cloudEvent": + # The proto typing doesnt resolve this one + cloud_event = cast(cloudevent_pb2.CloudEvent, message.cloudEvent) # type: ignore + task = asyncio.create_task(self._process_event(cloud_event)) self._background_tasks.add(task) task.add_done_callback(self._raise_on_exception) task.add_done_callback(self._background_tasks.discard) @@ -261,15 +268,8 @@ async def _run_read_loop(self) -> None: self._background_tasks.add(task) task.add_done_callback(self._raise_on_exception) task.add_done_callback(self._background_tasks.discard) - case "cloudEvent": - task = asyncio.create_task(self._process_cloud_event(message.cloudEvent)) - self._background_tasks.add(task) - task.add_done_callback(self._raise_on_exception) - task.add_done_callback(self._background_tasks.discard) case None: logger.warning("No message") - case other: - logger.error(f"Unknown message type: {other}") except Exception as e: logger.error("Error in read loop", exc_info=e) @@ -383,31 +383,73 @@ async def publish_message( *, sender: AgentId | None = None, cancellation_token: CancellationToken | None = None, + message_id: str | None = None, ) -> None: if not self._running: raise ValueError("Runtime must be running when publishing message.") if self._host_connection is None: raise RuntimeError("Host connection is not set.") + if message_id is None: + message_id = str(uuid.uuid4()) + message_type = self._serialization_registry.type_name(message) with self._trace_helper.trace_block( "create", topic_id, parent=None, extraAttributes={"message_type": message_type} ): - telemetry_metadata = get_telemetry_grpc_metadata() - proto_data = any_pb2.Any() - proto_data.Pack(msg=message) - if sender is not None: - source = str(AgentId(type=sender.type, key=sender.key)) + serialized_message = self._serialization_registry.serialize( + message, type_name=message_type, data_content_type=self._payload_serialization_format + ) + + sender_id = sender or AgentId("unknown", "unknown") + attributes = { + _constants.DATA_CONTENT_TYPE_ATTR: cloudevent_pb2.CloudEvent.CloudEventAttributeValue( + ce_string=self._payload_serialization_format + ), + _constants.DATA_SCHEMA_ATTR: cloudevent_pb2.CloudEvent.CloudEventAttributeValue(ce_string=message_type), + _constants.AGENT_SENDER_TYPE_ATTR: cloudevent_pb2.CloudEvent.CloudEventAttributeValue( + ce_string=sender_id.type + ), + _constants.AGENT_SENDER_KEY_ATTR: cloudevent_pb2.CloudEvent.CloudEventAttributeValue( + ce_string=sender_id.key + ), + _constants.MESSAGE_KIND_ATTR: cloudevent_pb2.CloudEvent.CloudEventAttributeValue( + ce_string=_constants.MESSAGE_KIND_VALUE_PUBLISH + ), + } + + # If sending JSON we fill text_data with the serialized message + # If sending Protobuf we fill proto_data with the serialized message + # TODO: add an encoding field for serializer + + if self._payload_serialization_format == JSON_DATA_CONTENT_TYPE: + runtime_message = agent_worker_pb2.Message( + cloudEvent=cloudevent_pb2.CloudEvent( + id=message_id, + spec_version="1.0", + type=topic_id.type, + source=topic_id.source, + attributes=attributes, + # TODO: use text, or proto fields appropriately + binary_data=serialized_message, + ) + ) else: - source = "" - runtime_message = agent_worker_pb2.Message( - cloudEvent=cloudevent_pb2.CloudEvent( - spec_version="1.0", - type=topic_id.type, - source=source, - metadata=telemetry_metadata, - proto_data=proto_data, + # We need to unpack the serialized proto back into an Any + # TODO: find a way to prevent the roundtrip serialization + any_proto = any_pb2.Any() + any_proto.ParseFromString(serialized_message) + runtime_message = agent_worker_pb2.Message( + cloudEvent=cloudevent_pb2.CloudEvent( + id=message_id, + spec_version="1.0", + type=topic_id.type, + source=topic_id.source, + attributes=attributes, + proto_data=any_proto, + ) ) - ) + + telemetry_metadata = get_telemetry_grpc_metadata() task = asyncio.create_task(self._send_message(runtime_message, "publish", topic_id, telemetry_metadata)) self._background_tasks.add(task) task.add_done_callback(self._raise_on_exception) @@ -457,6 +499,7 @@ async def _process_request(self, request: agent_worker_pb2.RpcRequest) -> None: topic_id=None, is_rpc=True, cancellation_token=CancellationToken(), + message_id=request.request_id, ) # Call the receiving agent. @@ -525,16 +568,47 @@ async def _process_response(self, response: agent_worker_pb2.RpcResponse) -> Non else: future.set_result(result) - async def _process_event(self, event: agent_worker_pb2.Event) -> None: - message = self._serialization_registry.deserialize( - event.payload.data, type_name=event.payload.data_type, data_content_type=event.payload.data_content_type - ) + async def _process_event(self, event: cloudevent_pb2.CloudEvent) -> None: + event_attributes = event.attributes sender: AgentId | None = None - if event.HasField("source"): - sender = AgentId(event.source.type, event.source.key) - topic_id = TopicId(event.topic_type, event.topic_source) + if ( + _constants.AGENT_SENDER_TYPE_ATTR in event_attributes + and _constants.AGENT_SENDER_KEY_ATTR in event_attributes + ): + sender = AgentId( + event_attributes[_constants.AGENT_SENDER_TYPE_ATTR].ce_string, + event_attributes[_constants.AGENT_SENDER_KEY_ATTR].ce_string, + ) + topic_id = TopicId(event.type, event.source) # Get the recipients for the topic. recipients = await self._subscription_manager.get_subscribed_recipients(topic_id) + + message_content_type = event_attributes[_constants.DATA_CONTENT_TYPE_ATTR].ce_string + message_type = event_attributes[_constants.DATA_SCHEMA_ATTR].ce_string + + if message_content_type == JSON_DATA_CONTENT_TYPE: + message = self._serialization_registry.deserialize( + event.binary_data, type_name=message_type, data_content_type=message_content_type + ) + elif message_content_type == PROTOBUF_DATA_CONTENT_TYPE: + # TODO: find a way to prevent the roundtrip serialization + proto_binary_data = event.proto_data.SerializeToString() + message = self._serialization_registry.deserialize( + proto_binary_data, type_name=message_type, data_content_type=message_content_type + ) + else: + raise ValueError(f"Unsupported message content type: {message_content_type}") + + # TODO: dont read these values in the runtime + topic_type_suffix = topic_id.type.split(":", maxsplit=1)[1] if ":" in topic_id.type else "" + is_rpc = topic_type_suffix == _constants.MESSAGE_KIND_VALUE_RPC_REQUEST + is_marked_rpc_type = ( + _constants.MESSAGE_KIND_ATTR in event_attributes + and event_attributes[_constants.MESSAGE_KIND_ATTR].ce_string == _constants.MESSAGE_KIND_VALUE_RPC_REQUEST + ) + if is_rpc and not is_marked_rpc_type: + warnings.warn("Received RPC request with topic type suffix but not marked as RPC request.", stacklevel=2) + # Send the message to each recipient. responses: List[Awaitable[Any]] = [] for agent_id in recipients: @@ -543,8 +617,9 @@ async def _process_event(self, event: agent_worker_pb2.Event) -> None: message_context = MessageContext( sender=sender, topic_id=topic_id, - is_rpc=False, + is_rpc=is_rpc, cancellation_token=CancellationToken(), + message_id=event.id, ) agent = await self._get_agent(agent_id) with MessageHandlerContext.populate_context(agent.id): @@ -554,7 +629,7 @@ async def send_message(agent: Agent, message_context: MessageContext) -> Any: "process", agent.id, parent=event.metadata, - extraAttributes={"message_type": event.payload.data_type}, + extraAttributes={"message_type": message_type}, ): await agent.on_message(message, ctx=message_context) @@ -658,7 +733,7 @@ async def factory_wrapper() -> T: async def _process_register_agent_type_response(self, response: agent_worker_pb2.RegisterAgentTypeResponse) -> None: future = self._pending_requests.pop(response.request_id) - if response.HasField("error") and response.error: + if response.HasField("error"): future.set_exception(RuntimeError(response.error)) else: future.set_result(None) @@ -715,27 +790,44 @@ async def try_get_underlying_agent_instance(self, id: AgentId, type: Type[T] = A async def add_subscription(self, subscription: Subscription) -> None: if self._host_connection is None: raise RuntimeError("Host connection is not set.") - if not isinstance(subscription, TypeSubscription): - raise ValueError("Only TypeSubscription is supported.") - # Add to local subscription manager. - await self._subscription_manager.add_subscription(subscription) # Create a future for the subscription response. future = asyncio.get_event_loop().create_future() request_id = await self._get_new_request_id() + + match subscription: + case TypeSubscription(topic_type=topic_type, agent_type=agent_type): + message = agent_worker_pb2.Message( + addSubscriptionRequest=agent_worker_pb2.AddSubscriptionRequest( + request_id=request_id, + subscription=agent_worker_pb2.Subscription( + typeSubscription=agent_worker_pb2.TypeSubscription( + topic_type=topic_type, agent_type=agent_type + ) + ), + ) + ) + case TypePrefixSubscription(topic_type_prefix=topic_type_prefix, agent_type=agent_type): + message = agent_worker_pb2.Message( + addSubscriptionRequest=agent_worker_pb2.AddSubscriptionRequest( + request_id=request_id, + subscription=agent_worker_pb2.Subscription( + typePrefixSubscription=agent_worker_pb2.TypePrefixSubscription( + topic_type_prefix=topic_type_prefix, agent_type=agent_type + ) + ), + ) + ) + case _: + raise ValueError("Unsupported subscription type.") + + # Add the future to the pending requests. self._pending_requests[request_id] = future + # Add to local subscription manager. + await self._subscription_manager.add_subscription(subscription) + # Send the subscription to the host. - message = agent_worker_pb2.Message( - addSubscriptionRequest=agent_worker_pb2.AddSubscriptionRequest( - request_id=request_id, - subscription=agent_worker_pb2.Subscription( - typeSubscription=agent_worker_pb2.TypeSubscription( - topic_type=subscription.topic_type, agent_type=subscription.agent_type - ) - ), - ) - ) await self._host_connection.send(message) # Wait for the subscription response. @@ -743,7 +835,7 @@ async def add_subscription(self, subscription: Subscription) -> None: async def _process_add_subscription_response(self, response: agent_worker_pb2.AddSubscriptionResponse) -> None: future = self._pending_requests.pop(response.request_id) - if response.HasField("error") and response.error: + if response.HasField("error"): future.set_exception(RuntimeError(response.error)) else: future.set_result(None) @@ -763,18 +855,3 @@ async def get( def add_message_serializer(self, serializer: MessageSerializer[Any] | Sequence[MessageSerializer[Any]]) -> None: self._serialization_registry.add_serializer(serializer) - - async def _process_cloud_event(self, cloud_event: cloudevent_pb2.CloudEvent) -> None: - logger.info(f"Processing CloudEvent: {cloud_event}") - - event = agent_worker_pb2.Event( - topic_type=cloud_event.type, - topic_source=cloud_event.source, - payload=agent_worker_pb2.Payload( - data_type=cloud_event.type, - data=cloud_event.proto_data.value, - data_content_type=JSON_DATA_CONTENT_TYPE, - ), - metadata=get_telemetry_grpc_metadata(), - ) - await self._process_event(event) diff --git a/python/packages/autogen-core/src/autogen_core/application/_worker_runtime_host.py b/python/packages/autogen-core/src/autogen_core/application/_worker_runtime_host.py index d7fee07ff1f..b9befce585d 100644 --- a/python/packages/autogen-core/src/autogen_core/application/_worker_runtime_host.py +++ b/python/packages/autogen-core/src/autogen_core/application/_worker_runtime_host.py @@ -4,7 +4,7 @@ from typing import Optional, Sequence from ..base._type_helpers import ChannelArgumentType -from ._utils import GRPC_IMPORT_ERROR_STR +from ._constants import GRPC_IMPORT_ERROR_STR from ._worker_runtime_host_servicer import WorkerAgentRuntimeHostServicer try: diff --git a/python/packages/autogen-core/src/autogen_core/application/_worker_runtime_host_servicer.py b/python/packages/autogen-core/src/autogen_core/application/_worker_runtime_host_servicer.py index 2dba017df9f..e24a7db3f30 100644 --- a/python/packages/autogen-core/src/autogen_core/application/_worker_runtime_host_servicer.py +++ b/python/packages/autogen-core/src/autogen_core/application/_worker_runtime_host_servicer.py @@ -2,26 +2,22 @@ import logging from _collections_abc import AsyncIterator, Iterator from asyncio import Future, Task -from typing import Any, Dict, Set +from typing import Any, Dict, Set, cast -import grpc +from autogen_core.base._type_prefix_subscription import TypePrefixSubscription -from ..base import JSON_DATA_CONTENT_TYPE, TopicId +from ..base import Subscription, TopicId from ..components import TypeSubscription +from ._constants import GRPC_IMPORT_ERROR_STR from ._helpers import SubscriptionManager -from ._utils import GRPC_IMPORT_ERROR_STR -from .protos import ( - agent_worker_pb2, - agent_worker_pb2_grpc, - cloudevent_pb2, -) -from .telemetry import get_telemetry_grpc_metadata try: import grpc except ImportError as e: raise ImportError(GRPC_IMPORT_ERROR_STR) from e +from .protos import agent_worker_pb2, agent_worker_pb2_grpc, cloudevent_pb2 + logger = logging.getLogger("autogen_core") event_logger = logging.getLogger("autogen_core.events") @@ -88,7 +84,7 @@ async def _on_client_disconnect(self, client_id: int) -> None: for agent_type in agent_types: logger.info(f"Removing agent type {agent_type} from agent type to client id mapping") del self._agent_type_to_client_id[agent_type] - for sub_id in self._client_id_to_subscription_id_mapping.get(client_id, []): + for sub_id in self._client_id_to_subscription_id_mapping.get(client_id, set()): logger.info(f"Client id {client_id} disconnected. Removing corresponding subscription with id {id}") await self._subscription_manager.remove_subscription(sub_id) logger.info(f"Client {client_id} disconnected successfully") @@ -118,8 +114,9 @@ async def _receive_messages( self._background_tasks.add(task) task.add_done_callback(self._raise_on_exception) task.add_done_callback(self._background_tasks.discard) - case "event": - event: agent_worker_pb2.Event = message.event + case "cloudEvent": + # The proto typing doesnt resolve this one + event = cast(cloudevent_pb2.CloudEvent, message.cloudEvent) # type: ignore task = asyncio.create_task(self._process_event(event)) self._background_tasks.add(task) task.add_done_callback(self._raise_on_exception) @@ -138,17 +135,10 @@ async def _receive_messages( self._background_tasks.add(task) task.add_done_callback(self._raise_on_exception) task.add_done_callback(self._background_tasks.discard) - case "cloudEvent": - task = asyncio.create_task(self._process_cloud_event(message.cloudEvent)) - self._background_tasks.add(task) - task.add_done_callback(self._raise_on_exception) - task.add_done_callback(self._background_tasks.discard) case "registerAgentTypeResponse" | "addSubscriptionResponse": logger.warning(f"Received unexpected message type: {oneofcase}") case None: logger.warning("Received empty message") - case other: - logger.error(f"Received unexpected message: {other}") async def _process_request(self, request: agent_worker_pb2.RpcRequest, client_id: int) -> None: # Deliver the message to a client given the target agent type. @@ -187,8 +177,8 @@ async def _process_response(self, response: agent_worker_pb2.RpcResponse, client future = self._pending_responses[client_id].pop(response.request_id) future.set_result(response) - async def _process_event(self, event: agent_worker_pb2.Event) -> None: - topic_id = TopicId(type=event.topic_type, source=event.topic_source) + async def _process_event(self, event: cloudevent_pb2.CloudEvent) -> None: + topic_id = TopicId(type=event.type, source=event.source) recipients = await self._subscription_manager.get_subscribed_recipients(topic_id) # Get the client ids of the recipients. async with self._agent_type_to_client_id_lock: @@ -201,7 +191,7 @@ async def _process_event(self, event: agent_worker_pb2.Event) -> None: logger.error(f"Agent {recipient.type} and its client not found for topic {topic_id}.") # Deliver the event to clients. for client_id in client_ids: - await self._send_queues[client_id].put(agent_worker_pb2.Message(event=event)) + await self._send_queues[client_id].put(agent_worker_pb2.Message(cloudEvent=event)) async def _process_register_agent_type_request( self, register_agent_type_req: agent_worker_pb2.RegisterAgentTypeRequest, client_id: int @@ -232,34 +222,46 @@ async def _process_add_subscription_request( self, add_subscription_req: agent_worker_pb2.AddSubscriptionRequest, client_id: int ) -> None: oneofcase = add_subscription_req.subscription.WhichOneof("subscription") + subscription: Subscription | None = None match oneofcase: case "typeSubscription": type_subscription_msg: agent_worker_pb2.TypeSubscription = ( add_subscription_req.subscription.typeSubscription ) - type_subscription = TypeSubscription( + subscription = TypeSubscription( topic_type=type_subscription_msg.topic_type, agent_type=type_subscription_msg.agent_type ) - try: - await self._subscription_manager.add_subscription(type_subscription) - subscription_ids = self._client_id_to_subscription_id_mapping.setdefault(client_id, set()) - subscription_ids.add(type_subscription.id) - success = True - error = None - except ValueError as e: - success = False - error = str(e) - # Send a response back to the client. - await self._send_queues[client_id].put( - agent_worker_pb2.Message( - addSubscriptionResponse=agent_worker_pb2.AddSubscriptionResponse( - request_id=add_subscription_req.request_id, success=success, error=error - ) - ) + + case "typePrefixSubscription": + type_prefix_subscription_msg: agent_worker_pb2.TypePrefixSubscription = ( + add_subscription_req.subscription.typePrefixSubscription + ) + subscription = TypePrefixSubscription( + topic_type_prefix=type_prefix_subscription_msg.topic_type_prefix, + agent_type=type_prefix_subscription_msg.agent_type, ) case None: logger.warning("Received empty subscription message") + if subscription is not None: + try: + await self._subscription_manager.add_subscription(subscription) + subscription_ids = self._client_id_to_subscription_id_mapping.setdefault(client_id, set()) + subscription_ids.add(subscription.id) + success = True + error = None + except ValueError as e: + success = False + error = str(e) + # Send a response back to the client. + await self._send_queues[client_id].put( + agent_worker_pb2.Message( + addSubscriptionResponse=agent_worker_pb2.AddSubscriptionResponse( + request_id=add_subscription_req.request_id, success=success, error=error + ) + ) + ) + async def GetState( # type: ignore self, request: agent_worker_pb2.AgentId, @@ -273,18 +275,3 @@ async def SaveState( # type: ignore context: grpc.aio.ServicerContext[agent_worker_pb2.AgentId, agent_worker_pb2.SaveStateResponse], ) -> agent_worker_pb2.SaveStateResponse: # type: ignore raise NotImplementedError("Method not implemented!") - - async def _process_cloud_event(self, cloud_event: cloudevent_pb2.CloudEvent) -> None: - logger.info(f"Processing CloudEvent: {cloud_event}") - - event = agent_worker_pb2.Event( - topic_type=cloud_event.type, - topic_source=cloud_event.source, - payload=agent_worker_pb2.Payload( - data_type=cloud_event.type, - data=cloud_event.proto_data.value, - data_content_type=JSON_DATA_CONTENT_TYPE, - ), - metadata=get_telemetry_grpc_metadata(), - ) - await self._process_event(event) diff --git a/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.py b/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.py deleted file mode 100644 index e5e7f4d59a1..00000000000 --- a/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: agent_events.proto -# Protobuf Python Version: 4.25.1 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x61gent_events.proto\x12\x06\x61gents\"2\n\x0bTextMessage\x12\x13\n\x0btextMessage\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"\x18\n\x05Input\x12\x0f\n\x07message\x18\x01 \x01(\t\"\x1f\n\x0eInputProcessed\x12\r\n\x05route\x18\x01 \x01(\t\"\x19\n\x06Output\x12\x0f\n\x07message\x18\x01 \x01(\t\"\x1e\n\rOutputWritten\x12\r\n\x05route\x18\x01 \x01(\t\"\x1a\n\x07IOError\x12\x0f\n\x07message\x18\x01 \x01(\t\"%\n\x12NewMessageReceived\x12\x0f\n\x07message\x18\x01 \x01(\t\"%\n\x11ResponseGenerated\x12\x10\n\x08response\x18\x01 \x01(\t\"\x1a\n\x07GoodBye\x12\x0f\n\x07message\x18\x01 \x01(\t\" \n\rMessageStored\x12\x0f\n\x07message\x18\x01 \x01(\t\";\n\x12\x43onversationClosed\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x14\n\x0cuser_message\x18\x02 \x01(\t\"\x1b\n\x08Shutdown\x12\x0f\n\x07message\x18\x01 \x01(\tB!\xaa\x02\x1eMicrosoft.AutoGen.Abstractionsb\x06proto3') - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agent_events_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - _globals['DESCRIPTOR']._options = None - _globals['DESCRIPTOR']._serialized_options = b'\252\002\036Microsoft.AutoGen.Abstractions' - _globals['_TEXTMESSAGE']._serialized_start=30 - _globals['_TEXTMESSAGE']._serialized_end=80 - _globals['_INPUT']._serialized_start=82 - _globals['_INPUT']._serialized_end=106 - _globals['_INPUTPROCESSED']._serialized_start=108 - _globals['_INPUTPROCESSED']._serialized_end=139 - _globals['_OUTPUT']._serialized_start=141 - _globals['_OUTPUT']._serialized_end=166 - _globals['_OUTPUTWRITTEN']._serialized_start=168 - _globals['_OUTPUTWRITTEN']._serialized_end=198 - _globals['_IOERROR']._serialized_start=200 - _globals['_IOERROR']._serialized_end=226 - _globals['_NEWMESSAGERECEIVED']._serialized_start=228 - _globals['_NEWMESSAGERECEIVED']._serialized_end=265 - _globals['_RESPONSEGENERATED']._serialized_start=267 - _globals['_RESPONSEGENERATED']._serialized_end=304 - _globals['_GOODBYE']._serialized_start=306 - _globals['_GOODBYE']._serialized_end=332 - _globals['_MESSAGESTORED']._serialized_start=334 - _globals['_MESSAGESTORED']._serialized_end=366 - _globals['_CONVERSATIONCLOSED']._serialized_start=368 - _globals['_CONVERSATIONCLOSED']._serialized_end=427 - _globals['_SHUTDOWN']._serialized_start=429 - _globals['_SHUTDOWN']._serialized_end=456 -# @@protoc_insertion_point(module_scope) diff --git a/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.pyi b/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.pyi deleted file mode 100644 index 01cfbafee51..00000000000 --- a/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2.pyi +++ /dev/null @@ -1,197 +0,0 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -""" - -import builtins -import google.protobuf.descriptor -import google.protobuf.message -import typing - -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -@typing.final -class TextMessage(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - TEXTMESSAGE_FIELD_NUMBER: builtins.int - SOURCE_FIELD_NUMBER: builtins.int - textMessage: builtins.str - source: builtins.str - def __init__( - self, - *, - textMessage: builtins.str = ..., - source: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["source", b"source", "textMessage", b"textMessage"]) -> None: ... - -global___TextMessage = TextMessage - -@typing.final -class Input(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - MESSAGE_FIELD_NUMBER: builtins.int - message: builtins.str - def __init__( - self, - *, - message: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... - -global___Input = Input - -@typing.final -class InputProcessed(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - ROUTE_FIELD_NUMBER: builtins.int - route: builtins.str - def __init__( - self, - *, - route: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["route", b"route"]) -> None: ... - -global___InputProcessed = InputProcessed - -@typing.final -class Output(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - MESSAGE_FIELD_NUMBER: builtins.int - message: builtins.str - def __init__( - self, - *, - message: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... - -global___Output = Output - -@typing.final -class OutputWritten(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - ROUTE_FIELD_NUMBER: builtins.int - route: builtins.str - def __init__( - self, - *, - route: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["route", b"route"]) -> None: ... - -global___OutputWritten = OutputWritten - -@typing.final -class IOError(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - MESSAGE_FIELD_NUMBER: builtins.int - message: builtins.str - def __init__( - self, - *, - message: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... - -global___IOError = IOError - -@typing.final -class NewMessageReceived(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - MESSAGE_FIELD_NUMBER: builtins.int - message: builtins.str - def __init__( - self, - *, - message: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... - -global___NewMessageReceived = NewMessageReceived - -@typing.final -class ResponseGenerated(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - RESPONSE_FIELD_NUMBER: builtins.int - response: builtins.str - def __init__( - self, - *, - response: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["response", b"response"]) -> None: ... - -global___ResponseGenerated = ResponseGenerated - -@typing.final -class GoodBye(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - MESSAGE_FIELD_NUMBER: builtins.int - message: builtins.str - def __init__( - self, - *, - message: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... - -global___GoodBye = GoodBye - -@typing.final -class MessageStored(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - MESSAGE_FIELD_NUMBER: builtins.int - message: builtins.str - def __init__( - self, - *, - message: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... - -global___MessageStored = MessageStored - -@typing.final -class ConversationClosed(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - USER_ID_FIELD_NUMBER: builtins.int - USER_MESSAGE_FIELD_NUMBER: builtins.int - user_id: builtins.str - user_message: builtins.str - def __init__( - self, - *, - user_id: builtins.str = ..., - user_message: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["user_id", b"user_id", "user_message", b"user_message"]) -> None: ... - -global___ConversationClosed = ConversationClosed - -@typing.final -class Shutdown(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - MESSAGE_FIELD_NUMBER: builtins.int - message: builtins.str - def __init__( - self, - *, - message: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... - -global___Shutdown = Shutdown diff --git a/python/packages/autogen-core/src/autogen_core/application/protos/agent_worker_pb2.py b/python/packages/autogen-core/src/autogen_core/application/protos/agent_worker_pb2.py index 0637e866c4d..319ee2c6365 100644 --- a/python/packages/autogen-core/src/autogen_core/application/protos/agent_worker_pb2.py +++ b/python/packages/autogen-core/src/autogen_core/application/protos/agent_worker_pb2.py @@ -16,7 +16,7 @@ from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x61gent_worker.proto\x12\x06\x61gents\x1a\x10\x63loudevent.proto\x1a\x19google/protobuf/any.proto\"\'\n\x07TopicId\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"$\n\x07\x41gentId\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\t\"E\n\x07Payload\x12\x11\n\tdata_type\x18\x01 \x01(\t\x12\x19\n\x11\x64\x61ta_content_type\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\"\x89\x02\n\nRpcRequest\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12$\n\x06source\x18\x02 \x01(\x0b\x32\x0f.agents.AgentIdH\x00\x88\x01\x01\x12\x1f\n\x06target\x18\x03 \x01(\x0b\x32\x0f.agents.AgentId\x12\x0e\n\x06method\x18\x04 \x01(\t\x12 \n\x07payload\x18\x05 \x01(\x0b\x32\x0f.agents.Payload\x12\x32\n\x08metadata\x18\x06 \x03(\x0b\x32 .agents.RpcRequest.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\t\n\x07_source\"\xb8\x01\n\x0bRpcResponse\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12 \n\x07payload\x18\x02 \x01(\x0b\x32\x0f.agents.Payload\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12\x33\n\x08metadata\x18\x04 \x03(\x0b\x32!.agents.RpcResponse.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xe4\x01\n\x05\x45vent\x12\x12\n\ntopic_type\x18\x01 \x01(\t\x12\x14\n\x0ctopic_source\x18\x02 \x01(\t\x12$\n\x06source\x18\x03 \x01(\x0b\x32\x0f.agents.AgentIdH\x00\x88\x01\x01\x12 \n\x07payload\x18\x04 \x01(\x0b\x32\x0f.agents.Payload\x12-\n\x08metadata\x18\x05 \x03(\x0b\x32\x1b.agents.Event.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\t\n\x07_source\"<\n\x18RegisterAgentTypeRequest\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\"^\n\x19RegisterAgentTypeResponse\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\x12\n\x05\x65rror\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_error\":\n\x10TypeSubscription\x12\x12\n\ntopic_type\x18\x01 \x01(\t\x12\x12\n\nagent_type\x18\x02 \x01(\t\"T\n\x0cSubscription\x12\x34\n\x10typeSubscription\x18\x01 \x01(\x0b\x32\x18.agents.TypeSubscriptionH\x00\x42\x0e\n\x0csubscription\"X\n\x16\x41\x64\x64SubscriptionRequest\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12*\n\x0csubscription\x18\x02 \x01(\x0b\x32\x14.agents.Subscription\"\\\n\x17\x41\x64\x64SubscriptionResponse\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\x12\n\x05\x65rror\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_error\"\x9d\x01\n\nAgentState\x12!\n\x08\x61gent_id\x18\x01 \x01(\x0b\x32\x0f.agents.AgentId\x12\x0c\n\x04\x65Tag\x18\x02 \x01(\t\x12\x15\n\x0b\x62inary_data\x18\x03 \x01(\x0cH\x00\x12\x13\n\ttext_data\x18\x04 \x01(\tH\x00\x12*\n\nproto_data\x18\x05 \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x42\x06\n\x04\x64\x61ta\"j\n\x10GetStateResponse\x12\'\n\x0b\x61gent_state\x18\x01 \x01(\x0b\x32\x12.agents.AgentState\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\x12\n\x05\x65rror\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_error\"B\n\x11SaveStateResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x12\n\x05\x65rror\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_error\"\xc6\x03\n\x07Message\x12%\n\x07request\x18\x01 \x01(\x0b\x32\x12.agents.RpcRequestH\x00\x12\'\n\x08response\x18\x02 \x01(\x0b\x32\x13.agents.RpcResponseH\x00\x12\x1e\n\x05\x65vent\x18\x03 \x01(\x0b\x32\r.agents.EventH\x00\x12\x44\n\x18registerAgentTypeRequest\x18\x04 \x01(\x0b\x32 .agents.RegisterAgentTypeRequestH\x00\x12\x46\n\x19registerAgentTypeResponse\x18\x05 \x01(\x0b\x32!.agents.RegisterAgentTypeResponseH\x00\x12@\n\x16\x61\x64\x64SubscriptionRequest\x18\x06 \x01(\x0b\x32\x1e.agents.AddSubscriptionRequestH\x00\x12\x42\n\x17\x61\x64\x64SubscriptionResponse\x18\x07 \x01(\x0b\x32\x1f.agents.AddSubscriptionResponseH\x00\x12,\n\ncloudEvent\x18\x08 \x01(\x0b\x32\x16.cloudevent.CloudEventH\x00\x42\t\n\x07message2\xb2\x01\n\x08\x41gentRpc\x12\x33\n\x0bOpenChannel\x12\x0f.agents.Message\x1a\x0f.agents.Message(\x01\x30\x01\x12\x35\n\x08GetState\x12\x0f.agents.AgentId\x1a\x18.agents.GetStateResponse\x12:\n\tSaveState\x12\x12.agents.AgentState\x1a\x19.agents.SaveStateResponseB!\xaa\x02\x1eMicrosoft.AutoGen.Abstractionsb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x61gent_worker.proto\x12\x06\x61gents\x1a\x10\x63loudevent.proto\x1a\x19google/protobuf/any.proto\"\'\n\x07TopicId\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"$\n\x07\x41gentId\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\t\"E\n\x07Payload\x12\x11\n\tdata_type\x18\x01 \x01(\t\x12\x19\n\x11\x64\x61ta_content_type\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\"\x89\x02\n\nRpcRequest\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12$\n\x06source\x18\x02 \x01(\x0b\x32\x0f.agents.AgentIdH\x00\x88\x01\x01\x12\x1f\n\x06target\x18\x03 \x01(\x0b\x32\x0f.agents.AgentId\x12\x0e\n\x06method\x18\x04 \x01(\t\x12 \n\x07payload\x18\x05 \x01(\x0b\x32\x0f.agents.Payload\x12\x32\n\x08metadata\x18\x06 \x03(\x0b\x32 .agents.RpcRequest.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\t\n\x07_source\"\xb8\x01\n\x0bRpcResponse\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12 \n\x07payload\x18\x02 \x01(\x0b\x32\x0f.agents.Payload\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12\x33\n\x08metadata\x18\x04 \x03(\x0b\x32!.agents.RpcResponse.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xe4\x01\n\x05\x45vent\x12\x12\n\ntopic_type\x18\x01 \x01(\t\x12\x14\n\x0ctopic_source\x18\x02 \x01(\t\x12$\n\x06source\x18\x03 \x01(\x0b\x32\x0f.agents.AgentIdH\x00\x88\x01\x01\x12 \n\x07payload\x18\x04 \x01(\x0b\x32\x0f.agents.Payload\x12-\n\x08metadata\x18\x05 \x03(\x0b\x32\x1b.agents.Event.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\t\n\x07_source\"<\n\x18RegisterAgentTypeRequest\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\"^\n\x19RegisterAgentTypeResponse\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\x12\n\x05\x65rror\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_error\":\n\x10TypeSubscription\x12\x12\n\ntopic_type\x18\x01 \x01(\t\x12\x12\n\nagent_type\x18\x02 \x01(\t\"G\n\x16TypePrefixSubscription\x12\x19\n\x11topic_type_prefix\x18\x01 \x01(\t\x12\x12\n\nagent_type\x18\x02 \x01(\t\"\x96\x01\n\x0cSubscription\x12\x34\n\x10typeSubscription\x18\x01 \x01(\x0b\x32\x18.agents.TypeSubscriptionH\x00\x12@\n\x16typePrefixSubscription\x18\x02 \x01(\x0b\x32\x1e.agents.TypePrefixSubscriptionH\x00\x42\x0e\n\x0csubscription\"X\n\x16\x41\x64\x64SubscriptionRequest\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12*\n\x0csubscription\x18\x02 \x01(\x0b\x32\x14.agents.Subscription\"\\\n\x17\x41\x64\x64SubscriptionResponse\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\x12\n\x05\x65rror\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_error\"\x9d\x01\n\nAgentState\x12!\n\x08\x61gent_id\x18\x01 \x01(\x0b\x32\x0f.agents.AgentId\x12\x0c\n\x04\x65Tag\x18\x02 \x01(\t\x12\x15\n\x0b\x62inary_data\x18\x03 \x01(\x0cH\x00\x12\x13\n\ttext_data\x18\x04 \x01(\tH\x00\x12*\n\nproto_data\x18\x05 \x01(\x0b\x32\x14.google.protobuf.AnyH\x00\x42\x06\n\x04\x64\x61ta\"j\n\x10GetStateResponse\x12\'\n\x0b\x61gent_state\x18\x01 \x01(\x0b\x32\x12.agents.AgentState\x12\x0f\n\x07success\x18\x02 \x01(\x08\x12\x12\n\x05\x65rror\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_error\"B\n\x11SaveStateResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x12\n\x05\x65rror\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_error\"\xa6\x03\n\x07Message\x12%\n\x07request\x18\x01 \x01(\x0b\x32\x12.agents.RpcRequestH\x00\x12\'\n\x08response\x18\x02 \x01(\x0b\x32\x13.agents.RpcResponseH\x00\x12,\n\ncloudEvent\x18\x03 \x01(\x0b\x32\x16.cloudevent.CloudEventH\x00\x12\x44\n\x18registerAgentTypeRequest\x18\x04 \x01(\x0b\x32 .agents.RegisterAgentTypeRequestH\x00\x12\x46\n\x19registerAgentTypeResponse\x18\x05 \x01(\x0b\x32!.agents.RegisterAgentTypeResponseH\x00\x12@\n\x16\x61\x64\x64SubscriptionRequest\x18\x06 \x01(\x0b\x32\x1e.agents.AddSubscriptionRequestH\x00\x12\x42\n\x17\x61\x64\x64SubscriptionResponse\x18\x07 \x01(\x0b\x32\x1f.agents.AddSubscriptionResponseH\x00\x42\t\n\x07message2\xb2\x01\n\x08\x41gentRpc\x12\x33\n\x0bOpenChannel\x12\x0f.agents.Message\x1a\x0f.agents.Message(\x01\x30\x01\x12\x35\n\x08GetState\x12\x0f.agents.AgentId\x1a\x18.agents.GetStateResponse\x12:\n\tSaveState\x12\x12.agents.AgentState\x1a\x19.agents.SaveStateResponseB!\xaa\x02\x1eMicrosoft.AutoGen.Abstractionsb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -54,20 +54,22 @@ _globals['_REGISTERAGENTTYPERESPONSE']._serialized_end=1067 _globals['_TYPESUBSCRIPTION']._serialized_start=1069 _globals['_TYPESUBSCRIPTION']._serialized_end=1127 - _globals['_SUBSCRIPTION']._serialized_start=1129 - _globals['_SUBSCRIPTION']._serialized_end=1213 - _globals['_ADDSUBSCRIPTIONREQUEST']._serialized_start=1215 - _globals['_ADDSUBSCRIPTIONREQUEST']._serialized_end=1303 - _globals['_ADDSUBSCRIPTIONRESPONSE']._serialized_start=1305 - _globals['_ADDSUBSCRIPTIONRESPONSE']._serialized_end=1397 - _globals['_AGENTSTATE']._serialized_start=1400 - _globals['_AGENTSTATE']._serialized_end=1557 - _globals['_GETSTATERESPONSE']._serialized_start=1559 - _globals['_GETSTATERESPONSE']._serialized_end=1665 - _globals['_SAVESTATERESPONSE']._serialized_start=1667 - _globals['_SAVESTATERESPONSE']._serialized_end=1733 - _globals['_MESSAGE']._serialized_start=1736 - _globals['_MESSAGE']._serialized_end=2190 - _globals['_AGENTRPC']._serialized_start=2193 - _globals['_AGENTRPC']._serialized_end=2371 + _globals['_TYPEPREFIXSUBSCRIPTION']._serialized_start=1129 + _globals['_TYPEPREFIXSUBSCRIPTION']._serialized_end=1200 + _globals['_SUBSCRIPTION']._serialized_start=1203 + _globals['_SUBSCRIPTION']._serialized_end=1353 + _globals['_ADDSUBSCRIPTIONREQUEST']._serialized_start=1355 + _globals['_ADDSUBSCRIPTIONREQUEST']._serialized_end=1443 + _globals['_ADDSUBSCRIPTIONRESPONSE']._serialized_start=1445 + _globals['_ADDSUBSCRIPTIONRESPONSE']._serialized_end=1537 + _globals['_AGENTSTATE']._serialized_start=1540 + _globals['_AGENTSTATE']._serialized_end=1697 + _globals['_GETSTATERESPONSE']._serialized_start=1699 + _globals['_GETSTATERESPONSE']._serialized_end=1805 + _globals['_SAVESTATERESPONSE']._serialized_start=1807 + _globals['_SAVESTATERESPONSE']._serialized_end=1873 + _globals['_MESSAGE']._serialized_start=1876 + _globals['_MESSAGE']._serialized_end=2298 + _globals['_AGENTRPC']._serialized_start=2301 + _globals['_AGENTRPC']._serialized_end=2479 # @@protoc_insertion_point(module_scope) diff --git a/python/packages/autogen-core/src/autogen_core/application/protos/agent_worker_pb2.pyi b/python/packages/autogen-core/src/autogen_core/application/protos/agent_worker_pb2.pyi index 522124ab889..79e384ab948 100644 --- a/python/packages/autogen-core/src/autogen_core/application/protos/agent_worker_pb2.pyi +++ b/python/packages/autogen-core/src/autogen_core/application/protos/agent_worker_pb2.pyi @@ -273,21 +273,43 @@ class TypeSubscription(google.protobuf.message.Message): global___TypeSubscription = TypeSubscription +@typing.final +class TypePrefixSubscription(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TOPIC_TYPE_PREFIX_FIELD_NUMBER: builtins.int + AGENT_TYPE_FIELD_NUMBER: builtins.int + topic_type_prefix: builtins.str + agent_type: builtins.str + def __init__( + self, + *, + topic_type_prefix: builtins.str = ..., + agent_type: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["agent_type", b"agent_type", "topic_type_prefix", b"topic_type_prefix"]) -> None: ... + +global___TypePrefixSubscription = TypePrefixSubscription + @typing.final class Subscription(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor TYPESUBSCRIPTION_FIELD_NUMBER: builtins.int + TYPEPREFIXSUBSCRIPTION_FIELD_NUMBER: builtins.int @property def typeSubscription(self) -> global___TypeSubscription: ... + @property + def typePrefixSubscription(self) -> global___TypePrefixSubscription: ... def __init__( self, *, typeSubscription: global___TypeSubscription | None = ..., + typePrefixSubscription: global___TypePrefixSubscription | None = ..., ) -> None: ... - def HasField(self, field_name: typing.Literal["subscription", b"subscription", "typeSubscription", b"typeSubscription"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["subscription", b"subscription", "typeSubscription", b"typeSubscription"]) -> None: ... - def WhichOneof(self, oneof_group: typing.Literal["subscription", b"subscription"]) -> typing.Literal["typeSubscription"] | None: ... + def HasField(self, field_name: typing.Literal["subscription", b"subscription", "typePrefixSubscription", b"typePrefixSubscription", "typeSubscription", b"typeSubscription"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["subscription", b"subscription", "typePrefixSubscription", b"typePrefixSubscription", "typeSubscription", b"typeSubscription"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["subscription", b"subscription"]) -> typing.Literal["typeSubscription", "typePrefixSubscription"] | None: ... global___Subscription = Subscription @@ -415,18 +437,17 @@ class Message(google.protobuf.message.Message): REQUEST_FIELD_NUMBER: builtins.int RESPONSE_FIELD_NUMBER: builtins.int - EVENT_FIELD_NUMBER: builtins.int + CLOUDEVENT_FIELD_NUMBER: builtins.int REGISTERAGENTTYPEREQUEST_FIELD_NUMBER: builtins.int REGISTERAGENTTYPERESPONSE_FIELD_NUMBER: builtins.int ADDSUBSCRIPTIONREQUEST_FIELD_NUMBER: builtins.int ADDSUBSCRIPTIONRESPONSE_FIELD_NUMBER: builtins.int - CLOUDEVENT_FIELD_NUMBER: builtins.int @property def request(self) -> global___RpcRequest: ... @property def response(self) -> global___RpcResponse: ... @property - def event(self) -> global___Event: ... + def cloudEvent(self) -> cloudevent_pb2.CloudEvent: ... @property def registerAgentTypeRequest(self) -> global___RegisterAgentTypeRequest: ... @property @@ -435,22 +456,19 @@ class Message(google.protobuf.message.Message): def addSubscriptionRequest(self) -> global___AddSubscriptionRequest: ... @property def addSubscriptionResponse(self) -> global___AddSubscriptionResponse: ... - @property - def cloudEvent(self) -> cloudevent_pb2.CloudEvent: ... def __init__( self, *, request: global___RpcRequest | None = ..., response: global___RpcResponse | None = ..., - event: global___Event | None = ..., + cloudEvent: cloudevent_pb2.CloudEvent | None = ..., registerAgentTypeRequest: global___RegisterAgentTypeRequest | None = ..., registerAgentTypeResponse: global___RegisterAgentTypeResponse | None = ..., addSubscriptionRequest: global___AddSubscriptionRequest | None = ..., addSubscriptionResponse: global___AddSubscriptionResponse | None = ..., - cloudEvent: cloudevent_pb2.CloudEvent | None = ..., ) -> None: ... - def HasField(self, field_name: typing.Literal["addSubscriptionRequest", b"addSubscriptionRequest", "addSubscriptionResponse", b"addSubscriptionResponse", "cloudEvent", b"cloudEvent", "event", b"event", "message", b"message", "registerAgentTypeRequest", b"registerAgentTypeRequest", "registerAgentTypeResponse", b"registerAgentTypeResponse", "request", b"request", "response", b"response"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["addSubscriptionRequest", b"addSubscriptionRequest", "addSubscriptionResponse", b"addSubscriptionResponse", "cloudEvent", b"cloudEvent", "event", b"event", "message", b"message", "registerAgentTypeRequest", b"registerAgentTypeRequest", "registerAgentTypeResponse", b"registerAgentTypeResponse", "request", b"request", "response", b"response"]) -> None: ... - def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["request", "response", "event", "registerAgentTypeRequest", "registerAgentTypeResponse", "addSubscriptionRequest", "addSubscriptionResponse", "cloudEvent"] | None: ... + def HasField(self, field_name: typing.Literal["addSubscriptionRequest", b"addSubscriptionRequest", "addSubscriptionResponse", b"addSubscriptionResponse", "cloudEvent", b"cloudEvent", "message", b"message", "registerAgentTypeRequest", b"registerAgentTypeRequest", "registerAgentTypeResponse", b"registerAgentTypeResponse", "request", b"request", "response", b"response"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["addSubscriptionRequest", b"addSubscriptionRequest", "addSubscriptionResponse", b"addSubscriptionResponse", "cloudEvent", b"cloudEvent", "message", b"message", "registerAgentTypeRequest", b"registerAgentTypeRequest", "registerAgentTypeResponse", b"registerAgentTypeResponse", "request", b"request", "response", b"response"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["request", "response", "cloudEvent", "registerAgentTypeRequest", "registerAgentTypeResponse", "addSubscriptionRequest", "addSubscriptionResponse"] | None: ... global___Message = Message diff --git a/python/packages/autogen-core/src/autogen_core/base/__init__.py b/python/packages/autogen-core/src/autogen_core/base/__init__.py index e4463e7eecc..8d95083ec88 100644 --- a/python/packages/autogen-core/src/autogen_core/base/__init__.py +++ b/python/packages/autogen-core/src/autogen_core/base/__init__.py @@ -16,6 +16,7 @@ from ._message_handler_context import MessageHandlerContext from ._serialization import ( JSON_DATA_CONTENT_TYPE, + PROTOBUF_DATA_CONTENT_TYPE, MessageSerializer, SerializationRegistry, UnknownPayload, @@ -43,6 +44,7 @@ "SubscriptionInstantiationContext", "MessageHandlerContext", "JSON_DATA_CONTENT_TYPE", + "PROTOBUF_DATA_CONTENT_TYPE", "MessageSerializer", "try_get_known_serializers_for_type", "UnknownPayload", diff --git a/python/packages/autogen-core/src/autogen_core/base/_agent_runtime.py b/python/packages/autogen-core/src/autogen_core/base/_agent_runtime.py index a8e7f009632..27c37ad9f34 100644 --- a/python/packages/autogen-core/src/autogen_core/base/_agent_runtime.py +++ b/python/packages/autogen-core/src/autogen_core/base/_agent_runtime.py @@ -55,6 +55,7 @@ async def publish_message( *, sender: AgentId | None = None, cancellation_token: CancellationToken | None = None, + message_id: str | None = None, ) -> None: """Publish a message to all agents in the given namespace, or if no namespace is provided, the namespace of the sender. @@ -64,7 +65,8 @@ async def publish_message( message (Any): The message to publish. topic (TopicId): The topic to publish the message to. sender (AgentId | None, optional): The agent which sent the message. Defaults to None. - cancellation_token (CancellationToken | None, optional): Token used to cancel an in progress . Defaults to None. + cancellation_token (CancellationToken | None, optional): Token used to cancel an in progress. Defaults to None. + message_id (str | None, optional): The message id. If None, a new message id will be generated. Defaults to None. This message id must be unique. and is recommended to be a UUID. Raises: UndeliverableException: If the message cannot be delivered. diff --git a/python/packages/autogen-core/src/autogen_core/base/_base_agent.py b/python/packages/autogen-core/src/autogen_core/base/_base_agent.py index 5d8e94225b3..70481705ca6 100644 --- a/python/packages/autogen-core/src/autogen_core/base/_base_agent.py +++ b/python/packages/autogen-core/src/autogen_core/base/_base_agent.py @@ -20,6 +20,7 @@ from ._subscription import Subscription, UnboundSubscription from ._subscription_context import SubscriptionInstantiationContext from ._topic import TopicId +from ._type_prefix_subscription import TypePrefixSubscription T = TypeVar("T", bound=Agent) @@ -149,6 +150,7 @@ async def register( factory: Callable[[], Self | Awaitable[Self]], *, skip_class_subscriptions: bool = False, + skip_direct_message_subscription: bool = False, ) -> AgentType: agent_type = AgentType(type) agent_type = await runtime.register_factory(type=agent_type, agent_factory=factory, expected_class=cls) @@ -166,6 +168,16 @@ async def register( for subscription in subscriptions: await runtime.add_subscription(subscription) + if not skip_direct_message_subscription: + # Additionally adds a special prefix subscription for this agent to receive direct messages + await runtime.add_subscription( + TypePrefixSubscription( + # The prefix MUST include ":" to avoid collisions with other agents + topic_type_prefix=agent_type.type + ":", + agent_type=agent_type.type, + ) + ) + # TODO: deduplication for _message_type, serializer in cls._handles_types(): runtime.add_message_serializer(serializer) diff --git a/python/packages/autogen-core/src/autogen_core/base/_message_context.py b/python/packages/autogen-core/src/autogen_core/base/_message_context.py index 0a2c2973bc0..c5c00559ed0 100644 --- a/python/packages/autogen-core/src/autogen_core/base/_message_context.py +++ b/python/packages/autogen-core/src/autogen_core/base/_message_context.py @@ -11,3 +11,4 @@ class MessageContext: topic_id: TopicId | None is_rpc: bool cancellation_token: CancellationToken + message_id: str diff --git a/python/packages/autogen-core/src/autogen_core/base/_serialization.py b/python/packages/autogen-core/src/autogen_core/base/_serialization.py index 51fd531feac..74e02864112 100644 --- a/python/packages/autogen-core/src/autogen_core/base/_serialization.py +++ b/python/packages/autogen-core/src/autogen_core/base/_serialization.py @@ -2,6 +2,7 @@ from dataclasses import asdict, dataclass, fields from typing import Any, ClassVar, Dict, List, Protocol, Sequence, TypeVar, cast, get_args, get_origin, runtime_checkable +from google.protobuf import any_pb2 from google.protobuf.message import Message from pydantic import BaseModel @@ -149,29 +150,35 @@ def serialize(self, message: PydanticT) -> bytes: ProtobufT = TypeVar("ProtobufT", bound=Message) +# This class serializes to and from a google.protobuf.Any message that has been serialized to a string class ProtobufMessageSerializer(MessageSerializer[ProtobufT]): def __init__(self, cls: type[ProtobufT]) -> None: self.cls = cls @property def data_content_type(self) -> str: - # TODO: This should be PROTOBUF_DATA_CONTENT_TYPE. There are currently - # a couple of hard coded places where the system assumes the - # content is JSON_DATA_CONTENT_TYPE which will need to be fixed - # first. - return JSON_DATA_CONTENT_TYPE + return PROTOBUF_DATA_CONTENT_TYPE @property def type_name(self) -> str: return _type_name(self.cls) def deserialize(self, payload: bytes) -> ProtobufT: - ret = self.cls() - ret.ParseFromString(payload) - return ret + # Parse payload into a proto any + any_proto = any_pb2.Any() + any_proto.ParseFromString(payload) + + destination_message = self.cls() + + if not any_proto.Unpack(destination_message): # type: ignore + raise ValueError(f"Failed to unpack payload into {self.cls}") + + return destination_message def serialize(self, message: ProtobufT) -> bytes: - return message.SerializeToString() + any_proto = any_pb2.Any() + any_proto.Pack(message) # type: ignore + return any_proto.SerializeToString() @dataclass diff --git a/python/packages/autogen-core/src/autogen_core/base/_type_prefix_subscription.py b/python/packages/autogen-core/src/autogen_core/base/_type_prefix_subscription.py new file mode 100644 index 00000000000..f0011916509 --- /dev/null +++ b/python/packages/autogen-core/src/autogen_core/base/_type_prefix_subscription.py @@ -0,0 +1,65 @@ +import uuid + +from ._agent_id import AgentId +from ._subscription import Subscription +from ._topic import TopicId +from .exceptions import CantHandleException + + +class TypePrefixSubscription(Subscription): + """This subscription matches on topics based on a prefix of the type and maps to agents using the source of the topic as the agent key. + + This subscription causes each source to have its own agent instance. + + Example: + + .. code-block:: python + + from autogen_core.components import TypePrefixSubscription + + subscription = TypePrefixSubscription(topic_type_prefix="t1", agent_type="a1") + + In this case: + + - A topic_id with type `t1` and source `s1` will be handled by an agent of type `a1` with key `s1` + - A topic_id with type `t1` and source `s2` will be handled by an agent of type `a1` with key `s2`. + - A topic_id with type `t1SUFFIX` and source `s2` will be handled by an agent of type `a1` with key `s2`. + + Args: + topic_type_prefix (str): Topic type prefix to match against + agent_type (str): Agent type to handle this subscription + """ + + def __init__(self, topic_type_prefix: str, agent_type: str): + self._topic_type_prefix = topic_type_prefix + self._agent_type = agent_type + self._id = str(uuid.uuid4()) + + @property + def id(self) -> str: + return self._id + + @property + def topic_type_prefix(self) -> str: + return self._topic_type_prefix + + @property + def agent_type(self) -> str: + return self._agent_type + + def is_match(self, topic_id: TopicId) -> bool: + return topic_id.type.startswith(self._topic_type_prefix) + + def map_to_agent(self, topic_id: TopicId) -> AgentId: + if not self.is_match(topic_id): + raise CantHandleException("TopicId does not match the subscription") + + return AgentId(type=self._agent_type, key=topic_id.source) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, TypePrefixSubscription): + return False + + return self.id == other.id or ( + self.agent_type == other.agent_type and self.topic_type_prefix == other.topic_type_prefix + ) diff --git a/python/packages/autogen-core/src/autogen_core/components/__init__.py b/python/packages/autogen-core/src/autogen_core/components/__init__.py index 9ad8bdb35a1..37d1ad48a06 100644 --- a/python/packages/autogen-core/src/autogen_core/components/__init__.py +++ b/python/packages/autogen-core/src/autogen_core/components/__init__.py @@ -2,7 +2,8 @@ The :mod:`autogen_core.components` module provides building blocks for creating single agents """ -from ._closure_agent import ClosureAgent +from ..base._type_prefix_subscription import TypePrefixSubscription +from ._closure_agent import ClosureAgent, ClosureContext from ._default_subscription import DefaultSubscription, default_subscription, type_subscription from ._default_topic import DefaultTopicId from ._image import Image @@ -15,6 +16,7 @@ "RoutedAgent", "TypeRoutedAgent", "ClosureAgent", + "ClosureContext", "message_handler", "event", "rpc", @@ -24,4 +26,5 @@ "DefaultTopicId", "default_subscription", "type_subscription", + "TypePrefixSubscription", ] diff --git a/python/packages/autogen-core/src/autogen_core/components/_closure_agent.py b/python/packages/autogen-core/src/autogen_core/components/_closure_agent.py index 1123c3ee40e..12e5faae6bf 100644 --- a/python/packages/autogen-core/src/autogen_core/components/_closure_agent.py +++ b/python/packages/autogen-core/src/autogen_core/components/_closure_agent.py @@ -1,32 +1,38 @@ +from __future__ import annotations + import inspect -from typing import Any, Awaitable, Callable, List, Mapping, Sequence, TypeVar, get_type_hints +from typing import Any, Awaitable, Callable, List, Mapping, Protocol, Sequence, TypeVar, get_type_hints + +from autogen_core.base._serialization import try_get_known_serializers_for_type +from autogen_core.base._subscription_context import SubscriptionInstantiationContext from ..base import ( - Agent, AgentId, AgentInstantiationContext, AgentMetadata, AgentRuntime, AgentType, + BaseAgent, + CancellationToken, MessageContext, Subscription, - SubscriptionInstantiationContext, - try_get_known_serializers_for_type, + TopicId, ) from ..base._type_helpers import get_types from ..base.exceptions import CantHandleException T = TypeVar("T") +ClosureAgentType = TypeVar("ClosureAgentType", bound="ClosureAgent") def get_handled_types_from_closure( - closure: Callable[[AgentRuntime, AgentId, T, MessageContext], Awaitable[Any]], + closure: Callable[[ClosureAgent, T, MessageContext], Awaitable[Any]], ) -> Sequence[type]: args = inspect.getfullargspec(closure)[0] - if len(args) != 4: + if len(args) != 3: raise AssertionError("Closure must have 4 arguments") - message_arg_name = args[2] + message_arg_name = args[1] type_hints = get_type_hints(closure) @@ -47,9 +53,30 @@ def get_handled_types_from_closure( return target_types -class ClosureAgent(Agent): +class ClosureContext(Protocol): + @property + def id(self) -> AgentId: ... + + async def send_message( + self, + message: Any, + recipient: AgentId, + *, + cancellation_token: CancellationToken | None = None, + ) -> Any: ... + + async def publish_message( + self, + message: Any, + topic_id: TopicId, + *, + cancellation_token: CancellationToken | None = None, + ) -> None: ... + + +class ClosureAgent(BaseAgent, ClosureContext): def __init__( - self, description: str, closure: Callable[[AgentRuntime, AgentId, T, MessageContext], Awaitable[Any]] + self, description: str, closure: Callable[[ClosureContext, T, MessageContext], Awaitable[Any]] ) -> None: try: runtime = AgentInstantiationContext.current_runtime() @@ -65,6 +92,7 @@ def __init__( handled_types = get_handled_types_from_closure(closure) self._expected_types = handled_types self._closure = closure + super().__init__(description) @property def metadata(self) -> AgentMetadata: @@ -88,7 +116,7 @@ async def on_message(self, message: Any, ctx: MessageContext) -> Any: raise CantHandleException( f"Message type {type(message)} not in target types {self._expected_types} of {self.id}" ) - return await self._closure(self._runtime, self._id, message, ctx) + return await self._closure(self, message, ctx) async def save_state(self) -> Mapping[str, Any]: raise ValueError("save_state not implemented for ClosureAgent") @@ -97,16 +125,28 @@ async def load_state(self, state: Mapping[str, Any]) -> None: raise ValueError("load_state not implemented for ClosureAgent") @classmethod - async def register( + async def register_closure( cls, runtime: AgentRuntime, type: str, - closure: Callable[[AgentRuntime, AgentId, T, MessageContext], Awaitable[Any]], + closure: Callable[[ClosureContext, T, MessageContext], Awaitable[Any]], *, + skip_class_subscriptions: bool = False, + skip_direct_message_subscription: bool = False, description: str = "", subscriptions: Callable[[], list[Subscription] | Awaitable[list[Subscription]]] | None = None, ) -> AgentType: - agent_type = AgentType(type) + def factory() -> ClosureAgent: + return ClosureAgent(description=description, closure=closure) + + agent_type = await cls.register( + runtime=runtime, + type=type, + factory=factory, # type: ignore + skip_class_subscriptions=skip_class_subscriptions, + skip_direct_message_subscription=skip_direct_message_subscription, + ) + subscriptions_list: List[Subscription] = [] if subscriptions is not None: with SubscriptionInstantiationContext.populate_context(agent_type): @@ -117,11 +157,6 @@ async def register( # just ignore mypy here subscriptions_list.extend(subscriptions_list_result) # type: ignore - agent_type = await runtime.register_factory( - type=agent_type, - agent_factory=lambda: ClosureAgent(description=description, closure=closure), - expected_class=cls, - ) for subscription in subscriptions_list: await runtime.add_subscription(subscription) diff --git a/python/packages/autogen-core/src/autogen_core/components/_type_subscription.py b/python/packages/autogen-core/src/autogen_core/components/_type_subscription.py index 92709a457ae..94def76595d 100644 --- a/python/packages/autogen-core/src/autogen_core/components/_type_subscription.py +++ b/python/packages/autogen-core/src/autogen_core/components/_type_subscription.py @@ -1,7 +1,6 @@ import uuid -from typing import TypeVar -from ..base import AgentId, BaseAgent, Subscription, TopicId +from ..base import AgentId, Subscription, TopicId from ..base.exceptions import CantHandleException @@ -59,6 +58,3 @@ def __eq__(self, other: object) -> bool: return False return self.id == other.id or (self.agent_type == other.agent_type and self.topic_type == other.topic_type) - - -BaseAgentType = TypeVar("BaseAgentType", bound="BaseAgent") diff --git a/python/packages/autogen-core/tests/protos/serialization_test.proto b/python/packages/autogen-core/tests/protos/serialization_test.proto new file mode 100644 index 00000000000..611100ccde1 --- /dev/null +++ b/python/packages/autogen-core/tests/protos/serialization_test.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package agents; + +message ProtoMessage { + string message = 1; +} +message NestingProtoMessage { + string message = 1; + ProtoMessage nested = 2; +} \ No newline at end of file diff --git a/python/packages/autogen-core/tests/protos/serialization_test_pb2.py b/python/packages/autogen-core/tests/protos/serialization_test_pb2.py new file mode 100644 index 00000000000..ebc4bfee701 --- /dev/null +++ b/python/packages/autogen-core/tests/protos/serialization_test_pb2.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: serialization_test.proto +# Protobuf Python Version: 4.25.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18serialization_test.proto\x12\x06\x61gents\"\x1f\n\x0cProtoMessage\x12\x0f\n\x07message\x18\x01 \x01(\t\"L\n\x13NestingProtoMessage\x12\x0f\n\x07message\x18\x01 \x01(\t\x12$\n\x06nested\x18\x02 \x01(\x0b\x32\x14.agents.ProtoMessageb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'serialization_test_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _globals['_PROTOMESSAGE']._serialized_start=36 + _globals['_PROTOMESSAGE']._serialized_end=67 + _globals['_NESTINGPROTOMESSAGE']._serialized_start=69 + _globals['_NESTINGPROTOMESSAGE']._serialized_end=145 +# @@protoc_insertion_point(module_scope) diff --git a/python/packages/autogen-core/tests/protos/serialization_test_pb2.pyi b/python/packages/autogen-core/tests/protos/serialization_test_pb2.pyi new file mode 100644 index 00000000000..b8a284663f6 --- /dev/null +++ b/python/packages/autogen-core/tests/protos/serialization_test_pb2.pyi @@ -0,0 +1,46 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class ProtoMessage(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + def __init__( + self, + *, + message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... + +global___ProtoMessage = ProtoMessage + +@typing.final +class NestingProtoMessage(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + NESTED_FIELD_NUMBER: builtins.int + message: builtins.str + @property + def nested(self) -> global___ProtoMessage: ... + def __init__( + self, + *, + message: builtins.str = ..., + nested: global___ProtoMessage | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["nested", b"nested"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["message", b"message", "nested", b"nested"]) -> None: ... + +global___NestingProtoMessage = NestingProtoMessage diff --git a/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2_grpc.py b/python/packages/autogen-core/tests/protos/serialization_test_pb2_grpc.py similarity index 100% rename from python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2_grpc.py rename to python/packages/autogen-core/tests/protos/serialization_test_pb2_grpc.py diff --git a/python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2_grpc.pyi b/python/packages/autogen-core/tests/protos/serialization_test_pb2_grpc.pyi similarity index 100% rename from python/packages/autogen-core/src/autogen_core/application/protos/agent_events_pb2_grpc.pyi rename to python/packages/autogen-core/tests/protos/serialization_test_pb2_grpc.pyi diff --git a/python/packages/autogen-core/tests/test_cancellation.py b/python/packages/autogen-core/tests/test_cancellation.py index d971ef50fc3..67852636f6b 100644 --- a/python/packages/autogen-core/tests/test_cancellation.py +++ b/python/packages/autogen-core/tests/test_cancellation.py @@ -59,7 +59,7 @@ async def on_new_message(self, message: MessageType, ctx: MessageContext) -> Mes async def test_cancellation_with_token() -> None: runtime = SingleThreadedAgentRuntime() - await runtime.register("long_running", LongRunningAgent) + await LongRunningAgent.register(runtime, "long_running", LongRunningAgent) agent_id = AgentId("long_running", key="default") token = CancellationToken() response = asyncio.create_task(runtime.send_message(MessageType(), recipient=agent_id, cancellation_token=token)) @@ -85,8 +85,9 @@ async def test_cancellation_with_token() -> None: async def test_nested_cancellation_only_outer_called() -> None: runtime = SingleThreadedAgentRuntime() - await runtime.register("long_running", LongRunningAgent) - await runtime.register( + await LongRunningAgent.register(runtime, "long_running", LongRunningAgent) + await NestingLongRunningAgent.register( + runtime, "nested", lambda: NestingLongRunningAgent(AgentId("long_running", key=AgentInstantiationContext.current_agent_id().key)), ) @@ -119,8 +120,9 @@ async def test_nested_cancellation_only_outer_called() -> None: async def test_nested_cancellation_inner_called() -> None: runtime = SingleThreadedAgentRuntime() - await runtime.register("long_running", LongRunningAgent) - await runtime.register( + await LongRunningAgent.register(runtime, "long_running", LongRunningAgent) + await NestingLongRunningAgent.register( + runtime, "nested", lambda: NestingLongRunningAgent(AgentId("long_running", key=AgentInstantiationContext.current_agent_id().key)), ) diff --git a/python/packages/autogen-core/tests/test_closure_agent.py b/python/packages/autogen-core/tests/test_closure_agent.py index a8731bca3b8..328fe237434 100644 --- a/python/packages/autogen-core/tests/test_closure_agent.py +++ b/python/packages/autogen-core/tests/test_closure_agent.py @@ -3,9 +3,8 @@ import pytest from autogen_core.application import SingleThreadedAgentRuntime -from autogen_core.base import AgentId, AgentRuntime, MessageContext -from autogen_core.components import ClosureAgent, DefaultSubscription -from autogen_core.components._default_topic import DefaultTopicId +from autogen_core.base import MessageContext +from autogen_core.components import ClosureAgent, ClosureContext, DefaultSubscription, DefaultTopicId @dataclass @@ -19,11 +18,11 @@ async def test_register_receives_publish() -> None: queue = asyncio.Queue[tuple[str, str]]() - async def log_message(_runtime: AgentRuntime, id: AgentId, message: Message, ctx: MessageContext) -> None: - key = id.key + async def log_message(closure_ctx: ClosureContext, message: Message, ctx: MessageContext) -> None: + key = closure_ctx.id.key await queue.put((key, message.content)) - await ClosureAgent.register(runtime, "name", log_message, subscriptions=lambda: [DefaultSubscription()]) + await ClosureAgent.register_closure(runtime, "name", log_message, subscriptions=lambda: [DefaultSubscription()]) runtime.start() await runtime.publish_message(Message("first message"), topic_id=DefaultTopicId()) diff --git a/python/packages/autogen-core/tests/test_intervention.py b/python/packages/autogen-core/tests/test_intervention.py index 6b3d18c7e0a..105df32988b 100644 --- a/python/packages/autogen-core/tests/test_intervention.py +++ b/python/packages/autogen-core/tests/test_intervention.py @@ -18,7 +18,7 @@ async def on_send(self, message: MessageType, *, sender: AgentId | None, recipie handler = DebugInterventionHandler() runtime = SingleThreadedAgentRuntime(intervention_handlers=[handler]) - await runtime.register("name", LoopbackAgent) + await LoopbackAgent.register(runtime, "name", LoopbackAgent) loopback = AgentId("name", key="default") runtime.start() @@ -42,7 +42,7 @@ async def on_send( handler = DropSendInterventionHandler() runtime = SingleThreadedAgentRuntime(intervention_handlers=[handler]) - await runtime.register("name", LoopbackAgent) + await LoopbackAgent.register(runtime, "name", LoopbackAgent) loopback = AgentId("name", key="default") runtime.start() @@ -66,7 +66,7 @@ async def on_response( handler = DropResponseInterventionHandler() runtime = SingleThreadedAgentRuntime(intervention_handlers=[handler]) - await runtime.register("name", LoopbackAgent) + await LoopbackAgent.register(runtime, "name", LoopbackAgent) loopback = AgentId("name", key="default") runtime.start() @@ -90,7 +90,7 @@ async def on_send( handler = ExceptionInterventionHandler() runtime = SingleThreadedAgentRuntime(intervention_handlers=[handler]) - await runtime.register("name", LoopbackAgent) + await LoopbackAgent.register(runtime, "name", LoopbackAgent) loopback = AgentId("name", key="default") runtime.start() @@ -117,7 +117,7 @@ async def on_response( handler = ExceptionInterventionHandler() runtime = SingleThreadedAgentRuntime(intervention_handlers=[handler]) - await runtime.register("name", LoopbackAgent) + await LoopbackAgent.register(runtime, "name", LoopbackAgent) loopback = AgentId("name", key="default") runtime.start() with pytest.raises(InterventionException): diff --git a/python/packages/autogen-core/tests/test_routed_agent.py b/python/packages/autogen-core/tests/test_routed_agent.py index 8da5bf6073f..cab1b1d467f 100644 --- a/python/packages/autogen-core/tests/test_routed_agent.py +++ b/python/packages/autogen-core/tests/test_routed_agent.py @@ -37,7 +37,8 @@ async def on_broadcast_message(self, message: MessageType, ctx: MessageContext) async def test_routed_agent(caplog: pytest.LogCaptureFixture) -> None: runtime = SingleThreadedAgentRuntime() with caplog.at_level(logging.INFO): - await runtime.register("loopback", LoopbackAgent, lambda: [TypeSubscription("default", "loopback")]) + await LoopbackAgent.register(runtime, "loopback", LoopbackAgent) + await runtime.add_subscription(TypeSubscription("default", "loopback")) runtime.start() await runtime.publish_message(UnhandledMessageType(), topic_id=TopicId("default", "default")) await runtime.stop_when_idle() @@ -47,7 +48,8 @@ async def test_routed_agent(caplog: pytest.LogCaptureFixture) -> None: @pytest.mark.asyncio async def test_message_handler_router() -> None: runtime = SingleThreadedAgentRuntime() - await runtime.register("counter", CounterAgent, lambda: [TypeSubscription("default", "counter")]) + await CounterAgent.register(runtime, "counter", CounterAgent) + await runtime.add_subscription(TypeSubscription("default", "counter")) agent_id = AgentId(type="counter", key="default") # Send a broadcast message. @@ -94,7 +96,7 @@ async def handler_two(self, message: TestMessage, ctx: MessageContext) -> None: @pytest.mark.asyncio async def test_routed_agent_message_matching() -> None: runtime = SingleThreadedAgentRuntime() - await runtime.register("message_match", RoutedAgentMessageCustomMatch) + await RoutedAgentMessageCustomMatch.register(runtime, "message_match", RoutedAgentMessageCustomMatch) agent_id = AgentId(type="message_match", key="default") agent = await runtime.try_get_underlying_agent_instance(agent_id, type=RoutedAgentMessageCustomMatch) @@ -134,7 +136,8 @@ async def on_event_two(self, message: TestMessage, ctx: MessageContext) -> None: @pytest.mark.asyncio async def test_event() -> None: runtime = SingleThreadedAgentRuntime() - await runtime.register("counter", EventAgent, lambda: [TypeSubscription("default", "counter")]) + await EventAgent.register(runtime, "counter", EventAgent) + await runtime.add_subscription(TypeSubscription("default", "counter")) agent_id = AgentId(type="counter", key="default") # Send a broadcast message. @@ -181,7 +184,8 @@ async def on_rpc_two(self, message: TestMessage, ctx: MessageContext) -> TestMes @pytest.mark.asyncio async def test_rpc() -> None: runtime = SingleThreadedAgentRuntime() - await runtime.register("counter", RPCAgent, lambda: [TypeSubscription("default", "counter")]) + await RPCAgent.register(runtime, "counter", RPCAgent) + await runtime.add_subscription(TypeSubscription("default", "counter")) agent_id = AgentId(type="counter", key="default") # Send an RPC message. diff --git a/python/packages/autogen-core/tests/test_runtime.py b/python/packages/autogen-core/tests/test_runtime.py index 0f56e42a7e0..b327be1461e 100644 --- a/python/packages/autogen-core/tests/test_runtime.py +++ b/python/packages/autogen-core/tests/test_runtime.py @@ -1,4 +1,3 @@ -import asyncio import logging import pytest @@ -7,16 +6,10 @@ AgentId, AgentInstantiationContext, AgentType, - Subscription, - SubscriptionInstantiationContext, TopicId, try_get_known_serializers_for_type, ) -from autogen_core.components import ( - DefaultTopicId, - TypeSubscription, - type_subscription, -) +from autogen_core.components import DefaultTopicId, TypeSubscription, type_subscription from opentelemetry.sdk.trace import TracerProvider from test_utils import ( CascadingAgent, @@ -146,82 +139,9 @@ async def test_register_receives_publish_cascade() -> None: async def test_register_factory_explicit_name() -> None: runtime = SingleThreadedAgentRuntime() - await runtime.register("name", LoopbackAgent, lambda: [TypeSubscription("default", "name")]) - runtime.start() - agent_id = AgentId("name", key="default") - topic_id = TopicId("default", "default") - await runtime.publish_message(MessageType(), topic_id=topic_id) - - await runtime.stop_when_idle() - - # Agent in default namespace should have received the message - long_running_agent = await runtime.try_get_underlying_agent_instance(agent_id, type=LoopbackAgent) - assert long_running_agent.num_calls == 1 - - # Agent in other namespace should not have received the message - other_long_running_agent: LoopbackAgent = await runtime.try_get_underlying_agent_instance( - AgentId("name", key="other"), type=LoopbackAgent - ) - assert other_long_running_agent.num_calls == 0 - - -@pytest.mark.asyncio -async def test_register_factory_context_var_name() -> None: - runtime = SingleThreadedAgentRuntime() - - await runtime.register( - "name", LoopbackAgent, lambda: [TypeSubscription("default", SubscriptionInstantiationContext.agent_type().type)] - ) - runtime.start() - agent_id = AgentId("name", key="default") - topic_id = TopicId("default", "default") - await runtime.publish_message(MessageType(), topic_id=topic_id) - - await runtime.stop_when_idle() - - # Agent in default namespace should have received the message - long_running_agent = await runtime.try_get_underlying_agent_instance(agent_id, type=LoopbackAgent) - assert long_running_agent.num_calls == 1 - - # Agent in other namespace should not have received the message - other_long_running_agent: LoopbackAgent = await runtime.try_get_underlying_agent_instance( - AgentId("name", key="other"), type=LoopbackAgent - ) - assert other_long_running_agent.num_calls == 0 - - -@pytest.mark.asyncio -async def test_register_factory_async() -> None: - runtime = SingleThreadedAgentRuntime() - - async def sub_factory() -> list[Subscription]: - await asyncio.sleep(0.1) - return [TypeSubscription("default", SubscriptionInstantiationContext.agent_type().type)] - - await runtime.register("name", LoopbackAgent, sub_factory) - runtime.start() - agent_id = AgentId("name", key="default") - topic_id = TopicId("default", "default") - await runtime.publish_message(MessageType(), topic_id=topic_id) - - await runtime.stop_when_idle() - - # Agent in default namespace should have received the message - long_running_agent = await runtime.try_get_underlying_agent_instance(agent_id, type=LoopbackAgent) - assert long_running_agent.num_calls == 1 - - # Agent in other namespace should not have received the message - other_long_running_agent: LoopbackAgent = await runtime.try_get_underlying_agent_instance( - AgentId("name", key="other"), type=LoopbackAgent - ) - assert other_long_running_agent.num_calls == 0 - - -@pytest.mark.asyncio -async def test_register_factory_direct_list() -> None: - runtime = SingleThreadedAgentRuntime() + await LoopbackAgent.register(runtime, "name", LoopbackAgent) + await runtime.add_subscription(TypeSubscription("default", "name")) - await runtime.register("name", LoopbackAgent, [TypeSubscription("default", "name")]) runtime.start() agent_id = AgentId("name", key="default") topic_id = TopicId("default", "default") diff --git a/python/packages/autogen-core/tests/test_serialization.py b/python/packages/autogen-core/tests/test_serialization.py index 3f3b0174c8b..f6ab2067c4d 100644 --- a/python/packages/autogen-core/tests/test_serialization.py +++ b/python/packages/autogen-core/tests/test_serialization.py @@ -8,9 +8,14 @@ SerializationRegistry, try_get_known_serializers_for_type, ) -from autogen_core.base._serialization import DataclassJsonMessageSerializer, PydanticJsonMessageSerializer +from autogen_core.base._serialization import ( + PROTOBUF_DATA_CONTENT_TYPE, + DataclassJsonMessageSerializer, + PydanticJsonMessageSerializer, +) from autogen_core.components import Image from PIL import Image as PILImage +from protos.serialization_test_pb2 import NestingProtoMessage, ProtoMessage from pydantic import BaseModel @@ -83,6 +88,30 @@ def test_nesting_dataclass_dataclass() -> None: serde.add_serializer(try_get_known_serializers_for_type(NestingDataclassMessage)) +def test_proto() -> None: + serde = SerializationRegistry() + serde.add_serializer(try_get_known_serializers_for_type(ProtoMessage)) + + message = ProtoMessage(message="hello") + name = serde.type_name(message) + data = serde.serialize(message, type_name=name, data_content_type=PROTOBUF_DATA_CONTENT_TYPE) + assert name == "ProtoMessage" + deserialized = serde.deserialize(data, type_name=name, data_content_type=PROTOBUF_DATA_CONTENT_TYPE) + assert deserialized.message == message.message + + +def test_nested_proto() -> None: + serde = SerializationRegistry() + serde.add_serializer(try_get_known_serializers_for_type(NestingProtoMessage)) + + message = NestingProtoMessage(message="hello", nested=ProtoMessage(message="world")) + name = serde.type_name(message) + data = serde.serialize(message, type_name=name, data_content_type=PROTOBUF_DATA_CONTENT_TYPE) + deserialized = serde.deserialize(data, type_name=name, data_content_type=PROTOBUF_DATA_CONTENT_TYPE) + assert deserialized.message == message.message + assert deserialized.nested.message == message.nested.message + + @dataclass class DataclassNestedUnionSyntaxOldMessage: message: Union[str, int] diff --git a/python/packages/autogen-core/tests/test_state.py b/python/packages/autogen-core/tests/test_state.py index 5d68447365f..7120a9baab4 100644 --- a/python/packages/autogen-core/tests/test_state.py +++ b/python/packages/autogen-core/tests/test_state.py @@ -24,7 +24,7 @@ async def load_state(self, state: Mapping[str, Any]) -> None: async def test_agent_can_save_state() -> None: runtime = SingleThreadedAgentRuntime() - await runtime.register("name1", StatefulAgent) + await StatefulAgent.register(runtime, "name1", StatefulAgent) agent1_id = AgentId("name1", key="default") agent1: StatefulAgent = await runtime.try_get_underlying_agent_instance(agent1_id, type=StatefulAgent) assert agent1.state == 0 @@ -44,7 +44,7 @@ async def test_agent_can_save_state() -> None: async def test_runtime_can_save_state() -> None: runtime = SingleThreadedAgentRuntime() - await runtime.register("name1", StatefulAgent) + await StatefulAgent.register(runtime, "name1", StatefulAgent) agent1_id = AgentId("name1", key="default") agent1: StatefulAgent = await runtime.try_get_underlying_agent_instance(agent1_id, type=StatefulAgent) assert agent1.state == 0 @@ -54,7 +54,7 @@ async def test_runtime_can_save_state() -> None: runtime_state = await runtime.save_state() runtime2 = SingleThreadedAgentRuntime() - await runtime2.register("name1", StatefulAgent) + await StatefulAgent.register(runtime2, "name1", StatefulAgent) agent2_id = AgentId("name1", key="default") agent2: StatefulAgent = await runtime2.try_get_underlying_agent_instance(agent2_id, type=StatefulAgent) diff --git a/python/packages/autogen-core/tests/test_subscription.py b/python/packages/autogen-core/tests/test_subscription.py index c339d549be5..91223acbbc8 100644 --- a/python/packages/autogen-core/tests/test_subscription.py +++ b/python/packages/autogen-core/tests/test_subscription.py @@ -27,7 +27,7 @@ def test_type_subscription_map() -> None: async def test_non_default_default_subscription() -> None: runtime = SingleThreadedAgentRuntime() - await runtime.register("MyAgent", LoopbackAgent) + await LoopbackAgent.register(runtime, "MyAgent", LoopbackAgent, skip_class_subscriptions=True) runtime.start() await runtime.publish_message(MessageType(), topic_id=DefaultTopicId()) await runtime.stop_when_idle() diff --git a/python/packages/autogen-core/tests/test_tool_agent.py b/python/packages/autogen-core/tests/test_tool_agent.py index 6184e9c78c8..bdbd3b96b72 100644 --- a/python/packages/autogen-core/tests/test_tool_agent.py +++ b/python/packages/autogen-core/tests/test_tool_agent.py @@ -43,7 +43,8 @@ async def _async_sleep_function(input: str) -> str: @pytest.mark.asyncio async def test_tool_agent() -> None: runtime = SingleThreadedAgentRuntime() - await runtime.register( + await ToolAgent.register( + runtime, "tool_agent", lambda: ToolAgent( description="Tool agent", @@ -143,7 +144,8 @@ def capabilities(self) -> ModelCapabilities: client = MockChatCompletionClient() tools: List[Tool] = [FunctionTool(_pass_function, name="pass", description="Pass function")] runtime = SingleThreadedAgentRuntime() - await runtime.register( + await ToolAgent.register( + runtime, "tool_agent", lambda: ToolAgent( description="Tool agent", diff --git a/python/packages/autogen-core/tests/test_utils/__init__.py b/python/packages/autogen-core/tests/test_utils/__init__.py index 92096550cd9..5de7519fc49 100644 --- a/python/packages/autogen-core/tests/test_utils/__init__.py +++ b/python/packages/autogen-core/tests/test_utils/__init__.py @@ -23,12 +23,14 @@ class LoopbackAgent(RoutedAgent): def __init__(self) -> None: super().__init__("A loop back agent.") self.num_calls = 0 + self.received_messages: list[Any] = [] @message_handler async def on_new_message( self, message: MessageType | ContentMessage, ctx: MessageContext ) -> MessageType | ContentMessage: self.num_calls += 1 + self.received_messages.append(message) return message diff --git a/python/packages/autogen-core/tests/test_worker_runtime.py b/python/packages/autogen-core/tests/test_worker_runtime.py index d58233c3ac8..2a7d3acdc38 100644 --- a/python/packages/autogen-core/tests/test_worker_runtime.py +++ b/python/packages/autogen-core/tests/test_worker_runtime.py @@ -1,22 +1,28 @@ import asyncio import logging import os -from typing import List +from typing import Any, List import pytest from autogen_core.application import WorkerAgentRuntime, WorkerAgentRuntimeHost from autogen_core.base import ( + PROTOBUF_DATA_CONTENT_TYPE, AgentId, AgentType, + MessageContext, + Subscription, TopicId, try_get_known_serializers_for_type, ) -from autogen_core.base._subscription import Subscription from autogen_core.components import ( DefaultTopicId, + RoutedAgent, TypeSubscription, + default_subscription, + event, type_subscription, ) +from protos.serialization_test_pb2 import ProtoMessage from test_utils import ( CascadingAgent, CascadingMessageType, @@ -360,7 +366,7 @@ async def get_subscribed_recipients() -> List[AgentId]: ) subscriptions1 = get_current_subscriptions() - assert len(subscriptions1) == 1 + assert len(subscriptions1) == 2 recipients1 = await get_subscribed_recipients() assert AgentId(type="worker1", key="default") in recipients1 @@ -388,7 +394,7 @@ async def get_subscribed_recipients() -> List[AgentId]: ) subscriptions3 = get_current_subscriptions() - assert len(subscriptions3) == 1 + assert len(subscriptions3) == 2 assert first_subscription_id not in [x.id for x in subscriptions3] recipients3 = await get_subscribed_recipients() @@ -401,6 +407,62 @@ async def get_subscribed_recipients() -> List[AgentId]: await worker1_2.stop() +@default_subscription +class ProtoReceivingAgent(RoutedAgent): + def __init__(self) -> None: + super().__init__("A loop back agent.") + self.num_calls = 0 + self.received_messages: list[Any] = [] + + @event + async def on_new_message(self, message: ProtoMessage, ctx: MessageContext) -> None: + self.num_calls += 1 + self.received_messages.append(message) + + +@pytest.mark.asyncio +async def test_proto_payloads() -> None: + host_address = "localhost:50057" + host = WorkerAgentRuntimeHost(address=host_address) + host.start() + receiver_runtime = WorkerAgentRuntime( + host_address=host_address, payload_serialization_format=PROTOBUF_DATA_CONTENT_TYPE + ) + receiver_runtime.start() + publisher_runtime = WorkerAgentRuntime( + host_address=host_address, payload_serialization_format=PROTOBUF_DATA_CONTENT_TYPE + ) + publisher_runtime.add_message_serializer(try_get_known_serializers_for_type(ProtoMessage)) + publisher_runtime.start() + + await ProtoReceivingAgent.register(receiver_runtime, "name", ProtoReceivingAgent) + + await publisher_runtime.publish_message(ProtoMessage(message="Hello!"), topic_id=DefaultTopicId()) + + await asyncio.sleep(2) + + # Agent in default namespace should have received the message + long_running_agent = await receiver_runtime.try_get_underlying_agent_instance( + AgentId("name", "default"), type=ProtoReceivingAgent + ) + assert long_running_agent.num_calls == 1 + assert long_running_agent.received_messages[0].message == "Hello!" + + # Agent in other namespace should not have received the message + other_long_running_agent = await receiver_runtime.try_get_underlying_agent_instance( + AgentId("name", key="other"), type=ProtoReceivingAgent + ) + assert other_long_running_agent.num_calls == 0 + assert len(other_long_running_agent.received_messages) == 0 + + await receiver_runtime.stop() + await publisher_runtime.stop() + await host.stop() + + +# TODO add tests for failure to deserialize + + @pytest.mark.asyncio async def test_grpc_max_message_size() -> None: default_max_size = 2**22 diff --git a/python/packages/autogen-studio/README.md b/python/packages/autogen-studio/README.md index c456b4837b9..00721035082 100644 --- a/python/packages/autogen-studio/README.md +++ b/python/packages/autogen-studio/README.md @@ -85,6 +85,7 @@ AutoGen Studio also takes several parameters to customize the application: - `--port ` argument to specify the port number. By default, it is set to `8080`. - `--reload` argument to enable auto-reloading of the server when changes are made to the code. By default, it is set to `False`. - `--database-uri` argument to specify the database URI. Example values include `sqlite:///database.sqlite` for SQLite and `postgresql+psycopg://user:password@localhost/dbname` for PostgreSQL. If this is not specified, the database URIL defaults to a `database.sqlite` file in the `--appdir` directory. +- `--upgrade-database` argument to upgrade the database schema to the latest version. By default, it is set to `False`. Now that you have AutoGen Studio installed and running, you are ready to explore its capabilities, including defining and modifying agent workflows, interacting with agents and sessions, and expanding agent skills. @@ -115,12 +116,11 @@ npm run start We welcome contributions to AutoGen Studio. We recommend the following general steps to contribute to the project: - Review the overall AutoGen project [contribution guide](https://github.com/microsoft/autogen?tab=readme-ov-file#contributing) -- Please review the AutoGen Studio [roadmap](https://github.com/microsoft/autogen/issues/737) to get a sense of the current priorities for the project. Help is appreciated especially with Studio issues tagged with `help-wanted` +- Please review the AutoGen Studio [roadmap](https://github.com/microsoft/autogen/issues/4006) to get a sense of the current priorities for the project. Help is appreciated especially with Studio issues tagged with `help-wanted` - Please initiate a discussion on the roadmap issue or a new issue to discuss your proposed contribution. -- Please review the autogenstudio dev branch here [dev branch](https://github.com/microsoft/autogen/tree/autogenstudio) and use as a base for your contribution. This way, your contribution will be aligned with the latest changes in the AutoGen Studio project. - Submit a pull request with your contribution! - If you are modifying AutoGen Studio, it has its own devcontainer. See instructions in `.devcontainer/README.md` to use it -- Please use the tag `studio` for any issues, questions, and PRs related to Studio +- Please use the tag `proj-studio` for any issues, questions, and PRs related to Studio ## FAQ diff --git a/python/packages/autogen-studio/autogenstudio/__init__.py b/python/packages/autogen-studio/autogenstudio/__init__.py index 83395091333..137cbad5a83 100644 --- a/python/packages/autogen-studio/autogenstudio/__init__.py +++ b/python/packages/autogen-studio/autogenstudio/__init__.py @@ -1,3 +1,18 @@ -from .datamodel import * +from .database.db_manager import DatabaseManager +from .datamodel import Agent, AgentConfig, Model, ModelConfig, Team, TeamConfig, Tool, ToolConfig +from .teammanager import TeamManager from .version import __version__ -from .teammanager import * + +__all__ = [ + "Tool", + "Model", + "DatabaseManager", + "Team", + "Agent", + "ToolConfig", + "ModelConfig", + "TeamConfig", + "AgentConfig", + "TeamManager", + "__version__", +] diff --git a/python/packages/autogen-studio/autogenstudio/cli.py b/python/packages/autogen-studio/autogenstudio/cli.py index b8612f4ad97..90f4331f0b7 100644 --- a/python/packages/autogen-studio/autogenstudio/cli.py +++ b/python/packages/autogen-studio/autogenstudio/cli.py @@ -15,7 +15,7 @@ def ui( host: str = "127.0.0.1", port: int = 8081, workers: int = 1, - reload: Annotated[bool, typer.Option("--reload")] = True, + reload: Annotated[bool, typer.Option("--reload")] = False, docs: bool = True, appdir: str = None, database_uri: Optional[str] = None, @@ -48,11 +48,7 @@ def ui( port=port, workers=workers, reload=reload, - reload_excludes=[ - "**/alembic/*", - "**/alembic.ini", - "**/versions/*" - ] if reload else None + reload_excludes=["**/alembic/*", "**/alembic.ini", "**/versions/*"] if reload else None, ) diff --git a/python/packages/autogen-studio/autogenstudio/components/__init__.py b/python/packages/autogen-studio/autogenstudio/components/__init__.py deleted file mode 100644 index ac3f3116bc2..00000000000 --- a/python/packages/autogen-studio/autogenstudio/components/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .agents.userproxy import UserProxyAgent diff --git a/python/packages/autogen-studio/autogenstudio/components/agents/__init__.py b/python/packages/autogen-studio/autogenstudio/components/agents/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/python/packages/autogen-studio/autogenstudio/components/agents/userproxy.py b/python/packages/autogen-studio/autogenstudio/components/agents/userproxy.py deleted file mode 100644 index cb0fba52e07..00000000000 --- a/python/packages/autogen-studio/autogenstudio/components/agents/userproxy.py +++ /dev/null @@ -1,47 +0,0 @@ -from typing import Callable, List, Optional, Sequence, Union, Awaitable -from inspect import iscoroutinefunction - -from autogen_agentchat.agents import BaseChatAgent -from autogen_agentchat.base import Response -from autogen_agentchat.messages import ChatMessage, TextMessage -from autogen_core.base import CancellationToken -import asyncio - - -class UserProxyAgent(BaseChatAgent): - """An agent that can represent a human user in a chat.""" - - def __init__( - self, - name: str, - description: Optional[str] = "a", - input_func: Optional[Union[Callable[..., str], - Callable[..., Awaitable[str]]]] = None - ) -> None: - super().__init__(name, description=description) - self.input_func = input_func or input - self._is_async = iscoroutinefunction( - input_func) if input_func else False - - @property - def produced_message_types(self) -> List[type[ChatMessage]]: - return [TextMessage] - - async def _get_input(self, prompt: str) -> str: - """Handle both sync and async input functions""" - if self._is_async: - return await self.input_func(prompt) - else: - return await asyncio.get_event_loop().run_in_executor(None, self.input_func, prompt) - - async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> Response: - - try: - user_input = await self._get_input("Enter your response: ") - return Response(chat_message=TextMessage(content=user_input, source=self.name)) - except Exception as e: - # Consider logging the error here - raise RuntimeError(f"Failed to get user input: {str(e)}") from e - - async def on_reset(self, cancellation_token: CancellationToken) -> None: - pass diff --git a/python/packages/autogen-studio/autogenstudio/database/__init__.py b/python/packages/autogen-studio/autogenstudio/database/__init__.py index 18e67140acc..acdf583557c 100644 --- a/python/packages/autogen-studio/autogenstudio/database/__init__.py +++ b/python/packages/autogen-studio/autogenstudio/database/__init__.py @@ -1,3 +1,3 @@ -from .db_manager import DatabaseManager -from .component_factory import ComponentFactory, Component +from .component_factory import Component, ComponentFactory from .config_manager import ConfigurationManager +from .db_manager import DatabaseManager diff --git a/python/packages/autogen-studio/autogenstudio/database/component_factory.py b/python/packages/autogen-studio/autogenstudio/database/component_factory.py index bd8fec61a36..595a11bbbfe 100644 --- a/python/packages/autogen-studio/autogenstudio/database/component_factory.py +++ b/python/packages/autogen-studio/autogenstudio/database/component_factory.py @@ -1,43 +1,46 @@ -import os -from pathlib import Path -from typing import Callable, List, Literal, Union, Optional, Dict, Any, Type -from datetime import datetime import json -from autogen_agentchat.task import MaxMessageTermination, TextMentionTermination, StopMessageTermination -import yaml import logging -from packaging import version +from datetime import datetime +from pathlib import Path +from typing import Callable, Dict, List, Literal, Optional, Union -from ..datamodel import ( - TeamConfig, AgentConfig, ModelConfig, ToolConfig, - TeamTypes, AgentTypes, ModelTypes, ToolTypes, - ComponentType, ComponentConfig, ComponentConfigInput, TerminationConfig, TerminationTypes, Response -) -from ..components import UserProxyAgent -from autogen_agentchat.agents import AssistantAgent +import aiofiles +import yaml +from autogen_agentchat.agents import AssistantAgent, UserProxyAgent +from autogen_agentchat.task import MaxMessageTermination, StopMessageTermination, TextMentionTermination from autogen_agentchat.teams import RoundRobinGroupChat, SelectorGroupChat -from autogen_ext.models import OpenAIChatCompletionClient from autogen_core.components.tools import FunctionTool +from autogen_ext.agents import MultimodalWebSurfer +from autogen_ext.models import OpenAIChatCompletionClient + +from ..datamodel.types import ( + AgentConfig, + AgentTypes, + ComponentConfig, + ComponentConfigInput, + ComponentTypes, + ModelConfig, + ModelTypes, + TeamConfig, + TeamTypes, + TerminationConfig, + TerminationTypes, + ToolConfig, + ToolTypes, +) +from ..utils.utils import Version logger = logging.getLogger(__name__) -# Type definitions for supported components TeamComponent = Union[RoundRobinGroupChat, SelectorGroupChat] -AgentComponent = Union[AssistantAgent] # Will grow with more agent types -# Will grow with more model types +AgentComponent = Union[AssistantAgent, MultimodalWebSurfer] ModelComponent = Union[OpenAIChatCompletionClient] ToolComponent = Union[FunctionTool] # Will grow with more tool types -TerminationComponent = Union[MaxMessageTermination, - StopMessageTermination, TextMentionTermination] - -# Config type definitions +TerminationComponent = Union[MaxMessageTermination, StopMessageTermination, TextMentionTermination] -Component = Union[TeamComponent, AgentComponent, ModelComponent, ToolComponent] +Component = Union[TeamComponent, AgentComponent, ModelComponent, ToolComponent, TerminationComponent] - -ReturnType = Literal['object', 'dict', 'config'] -Component = Union[RoundRobinGroupChat, SelectorGroupChat, - AssistantAgent, OpenAIChatCompletionClient, FunctionTool] +ReturnType = Literal["object", "dict", "config"] DEFAULT_SELECTOR_PROMPT = """You are in a role play game. The following roles are available: {roles}. @@ -48,18 +51,18 @@ Read the above conversation. Then select the next role from {participants} to play. Only return the role. """ -CONFIG_RETURN_TYPES = Literal['object', 'dict', 'config'] +CONFIG_RETURN_TYPES = Literal["object", "dict", "config"] class ComponentFactory: """Creates and manages agent components with versioned configuration loading""" SUPPORTED_VERSIONS = { - ComponentType.TEAM: ["1.0.0"], - ComponentType.AGENT: ["1.0.0"], - ComponentType.MODEL: ["1.0.0"], - ComponentType.TOOL: ["1.0.0"], - ComponentType.TERMINATION: ["1.0.0"] + ComponentTypes.TEAM: ["1.0.0"], + ComponentTypes.AGENT: ["1.0.0"], + ComponentTypes.MODEL: ["1.0.0"], + ComponentTypes.TOOL: ["1.0.0"], + ComponentTypes.TERMINATION: ["1.0.0"], } def __init__(self): @@ -68,10 +71,7 @@ def __init__(self): self._last_cache_clear = datetime.now() async def load( - self, - component: ComponentConfigInput, - input_func: Optional[Callable] = None, - return_type: ReturnType = 'object' + self, component: ComponentConfigInput, input_func: Optional[Callable] = None, return_type: ReturnType = "object" ) -> Union[Component, dict, ComponentConfig]: """ Universal loader for any component type @@ -103,24 +103,23 @@ async def load( ) # Return early if dict or config requested - if return_type == 'dict': + if return_type == "dict": return config.model_dump() - elif return_type == 'config': + elif return_type == "config": return config # Otherwise create and return component instance handlers = { - ComponentType.TEAM: lambda c: self.load_team(c, input_func), - ComponentType.AGENT: lambda c: self.load_agent(c, input_func), - ComponentType.MODEL: self.load_model, - ComponentType.TOOL: self.load_tool, - ComponentType.TERMINATION: self.load_termination + ComponentTypes.TEAM: lambda c: self.load_team(c, input_func), + ComponentTypes.AGENT: lambda c: self.load_agent(c, input_func), + ComponentTypes.MODEL: self.load_model, + ComponentTypes.TOOL: self.load_tool, + ComponentTypes.TERMINATION: self.load_termination, } handler = handlers.get(config.component_type) if not handler: - raise ValueError( - f"Unknown component type: {config.component_type}") + raise ValueError(f"Unknown component type: {config.component_type}") return await handler(config) @@ -128,7 +127,9 @@ async def load( logger.error(f"Failed to load component: {str(e)}") raise - async def load_directory(self, directory: Union[str, Path], return_type: ReturnType = 'object') -> List[Union[Component, dict, ComponentConfig]]: + async def load_directory( + self, directory: Union[str, Path], return_type: ReturnType = "object" + ) -> List[Union[Component, dict, ComponentConfig]]: """ Import all component configurations from a directory. """ @@ -137,13 +138,12 @@ async def load_directory(self, directory: Union[str, Path], return_type: ReturnT directory = Path(directory) # Using Path.iterdir() instead of os.listdir for path in list(directory.glob("*")): - if path.suffix.lower().endswith(('.json', '.yaml', '.yml')): + if path.suffix.lower().endswith((".json", ".yaml", ".yml")): try: component = await self.load(path, return_type=return_type) components.append(component) except Exception as e: - logger.info( - f"Failed to load component: {str(e)}, {path}") + logger.info(f"Failed to load component: {str(e)}, {path}") return components except Exception as e: @@ -156,14 +156,14 @@ def _dict_to_config(self, config_dict: dict) -> ComponentConfig: raise ValueError("component_type is required in configuration") config_types = { - ComponentType.TEAM: TeamConfig, - ComponentType.AGENT: AgentConfig, - ComponentType.MODEL: ModelConfig, - ComponentType.TOOL: ToolConfig, - ComponentType.TERMINATION: TerminationConfig # Add mapping for termination + ComponentTypes.TEAM: TeamConfig, + ComponentTypes.AGENT: AgentConfig, + ComponentTypes.MODEL: ModelConfig, + ComponentTypes.TOOL: ToolConfig, + ComponentTypes.TERMINATION: TerminationConfig, # Add mapping for termination } - component_type = ComponentType(config_dict["component_type"]) + component_type = ComponentTypes(config_dict["component_type"]) config_class = config_types.get(component_type) if not config_class: @@ -174,28 +174,44 @@ def _dict_to_config(self, config_dict: dict) -> ComponentConfig: async def load_termination(self, config: TerminationConfig) -> TerminationComponent: """Create termination condition instance from configuration.""" try: - if config.termination_type == TerminationTypes.MAX_MESSAGES: + if config.termination_type == TerminationTypes.COMBINATION: + if not config.conditions or len(config.conditions) < 2: + raise ValueError("Combination termination requires at least 2 conditions") + if not config.operator: + raise ValueError("Combination termination requires an operator (and/or)") + + # Load first two conditions + conditions = [await self.load_termination(cond) for cond in config.conditions[:2]] + result = conditions[0] & conditions[1] if config.operator == "and" else conditions[0] | conditions[1] + + # Process remaining conditions if any + for condition in config.conditions[2:]: + next_condition = await self.load_termination(condition) + result = result & next_condition if config.operator == "and" else result | next_condition + + return result + + elif config.termination_type == TerminationTypes.MAX_MESSAGES: + if config.max_messages is None: + raise ValueError("max_messages parameter required for MaxMessageTermination") return MaxMessageTermination(max_messages=config.max_messages) + elif config.termination_type == TerminationTypes.STOP_MESSAGE: return StopMessageTermination() + elif config.termination_type == TerminationTypes.TEXT_MENTION: if not config.text: - raise ValueError( - "text parameter required for TextMentionTermination") + raise ValueError("text parameter required for TextMentionTermination") return TextMentionTermination(text=config.text) + else: - raise ValueError( - f"Unsupported termination type: {config.termination_type}") + raise ValueError(f"Unsupported termination type: {config.termination_type}") + except Exception as e: logger.error(f"Failed to create termination condition: {str(e)}") - raise ValueError( - f"Termination condition creation failed: {str(e)}") - - async def load_team( - self, - config: TeamConfig, - input_func: Optional[Callable] = None - ) -> TeamComponent: + raise ValueError(f"Termination condition creation failed: {str(e)}") from e + + async def load_team(self, config: TeamConfig, input_func: Optional[Callable] = None) -> TeamComponent: """Create team instance from configuration.""" try: # Load participants (agents) with input_func @@ -216,33 +232,25 @@ async def load_team( # Create team based on type if config.team_type == TeamTypes.ROUND_ROBIN: - return RoundRobinGroupChat( - participants=participants, - termination_condition=termination - ) + return RoundRobinGroupChat(participants=participants, termination_condition=termination) elif config.team_type == TeamTypes.SELECTOR: if not model_client: - raise ValueError( - "SelectorGroupChat requires a model_client") + raise ValueError("SelectorGroupChat requires a model_client") selector_prompt = config.selector_prompt if config.selector_prompt else DEFAULT_SELECTOR_PROMPT return SelectorGroupChat( participants=participants, model_client=model_client, termination_condition=termination, - selector_prompt=selector_prompt + selector_prompt=selector_prompt, ) else: raise ValueError(f"Unsupported team type: {config.team_type}") except Exception as e: logger.error(f"Failed to create team {config.name}: {str(e)}") - raise ValueError(f"Team creation failed: {str(e)}") + raise ValueError(f"Team creation failed: {str(e)}") from e - async def load_agent( - self, - config: AgentConfig, - input_func: Optional[Callable] = None - ) -> AgentComponent: + async def load_agent(self, config: AgentConfig, input_func: Optional[Callable] = None) -> AgentComponent: """Create agent instance from configuration.""" try: # Load model client if specified @@ -263,7 +271,7 @@ async def load_agent( return UserProxyAgent( name=config.name, description=config.description or "A human user", - input_func=input_func # Pass through to UserProxyAgent + input_func=input_func, # Pass through to UserProxyAgent ) elif config.agent_type == AgentTypes.ASSISTANT: return AssistantAgent( @@ -271,15 +279,26 @@ async def load_agent( description=config.description or "A helpful assistant", model_client=model_client, tools=tools, - system_message=system_message + system_message=system_message, ) + elif config.agent_type == AgentTypes.MULTIMODAL_WEBSURFER: + return MultimodalWebSurfer( + name=config.name, + model_client=model_client, + headless=config.headless if config.headless is not None else True, + debug_dir=config.logs_dir if config.logs_dir is not None else "logs", + downloads_folder=config.logs_dir if config.logs_dir is not None else "logs", + to_save_screenshots=config.to_save_screenshots if config.to_save_screenshots is not None else False, + use_ocr=config.use_ocr if config.use_ocr is not None else False, + animate_actions=config.animate_actions if config.animate_actions is not None else False, + ) + else: - raise ValueError( - f"Unsupported agent type: {config.agent_type}") + raise ValueError(f"Unsupported agent type: {config.agent_type}") except Exception as e: logger.error(f"Failed to create agent {config.name}: {str(e)}") - raise ValueError(f"Agent creation failed: {str(e)}") + raise ValueError(f"Agent creation failed: {str(e)}") from e async def load_model(self, config: ModelConfig) -> ModelComponent: """Create model instance from configuration.""" @@ -291,20 +310,15 @@ async def load_model(self, config: ModelConfig) -> ModelComponent: return self._model_cache[cache_key] if config.model_type == ModelTypes.OPENAI: - model = OpenAIChatCompletionClient( - model=config.model, - api_key=config.api_key, - base_url=config.base_url - ) + model = OpenAIChatCompletionClient(model=config.model, api_key=config.api_key, base_url=config.base_url) self._model_cache[cache_key] = model return model else: - raise ValueError( - f"Unsupported model type: {config.model_type}") + raise ValueError(f"Unsupported model type: {config.model_type}") except Exception as e: logger.error(f"Failed to create model {config.model}: {str(e)}") - raise ValueError(f"Model creation failed: {str(e)}") + raise ValueError(f"Model creation failed: {str(e)}") from e async def load_tool(self, config: ToolConfig) -> ToolComponent: """Create tool instance from configuration.""" @@ -321,9 +335,7 @@ async def load_tool(self, config: ToolConfig) -> ToolComponent: if config.tool_type == ToolTypes.PYTHON_FUNCTION: tool = FunctionTool( - name=config.name, - description=config.description, - func=self._func_from_string(config.content) + name=config.name, description=config.description, func=self._func_from_string(config.content) ) self._tool_cache[cache_key] = tool return tool @@ -334,7 +346,6 @@ async def load_tool(self, config: ToolConfig) -> ToolComponent: logger.error(f"Failed to create tool '{config.name}': {str(e)}") raise - # Helper methods remain largely the same async def _load_from_file(self, path: Union[str, Path]) -> dict: """Load configuration from JSON or YAML file.""" path = Path(path) @@ -342,15 +353,16 @@ async def _load_from_file(self, path: Union[str, Path]) -> dict: raise FileNotFoundError(f"Config file not found: {path}") try: - with open(path) as f: - if path.suffix == '.json': - return json.load(f) - elif path.suffix in ('.yml', '.yaml'): - return yaml.safe_load(f) + async with aiofiles.open(path) as f: + content = await f.read() + if path.suffix == ".json": + return json.loads(content) + elif path.suffix in (".yml", ".yaml"): + return yaml.safe_load(content) else: raise ValueError(f"Unsupported file format: {path.suffix}") except Exception as e: - raise ValueError(f"Failed to load file {path}: {str(e)}") + raise ValueError(f"Failed to load file {path}: {str(e)}") from e def _func_from_string(self, content: str) -> callable: """Convert function string to callable.""" @@ -362,24 +374,25 @@ def _func_from_string(self, content: str) -> callable: return item raise ValueError("No function found in provided code") except Exception as e: - raise ValueError(f"Failed to create function: {str(e)}") + raise ValueError(f"Failed to create function: {str(e)}") from e - def _is_version_supported(self, component_type: ComponentType, ver: str) -> bool: + def _is_version_supported(self, component_type: ComponentTypes, ver: str) -> bool: """Check if version is supported for component type.""" try: - v = version.parse(ver) - return ver in self.SUPPORTED_VERSIONS[component_type] - except version.InvalidVersion: + version = Version(ver) + supported = [Version(v) for v in self.SUPPORTED_VERSIONS[component_type]] + return any(version == v for v in supported) + except ValueError: return False async def cleanup(self) -> None: """Cleanup resources and clear caches.""" for model in self._model_cache.values(): - if hasattr(model, 'cleanup'): + if hasattr(model, "cleanup"): await model.cleanup() for tool in self._tool_cache.values(): - if hasattr(tool, 'cleanup'): + if hasattr(tool, "cleanup"): await tool.cleanup() self._model_cache.clear() diff --git a/python/packages/autogen-studio/autogenstudio/database/config_manager.py b/python/packages/autogen-studio/autogenstudio/database/config_manager.py index be4fa8ad691..3cd1b43d81a 100644 --- a/python/packages/autogen-studio/autogenstudio/database/config_manager.py +++ b/python/packages/autogen-studio/autogenstudio/database/config_manager.py @@ -1,13 +1,11 @@ import logging -from typing import Optional, Union, Dict, Any, List from pathlib import Path +from typing import Any, Dict, List, Optional, Union + from loguru import logger -from ..datamodel import ( - Model, Team, Agent, Tool, - Response, ComponentTypes, LinkTypes, - ComponentConfigInput -) +from ..datamodel.db import Agent, LinkTypes, Model, Team, Tool +from ..datamodel.types import ComponentConfigInput, ComponentTypes, Response from .component_factory import ComponentFactory from .db_manager import DatabaseManager @@ -16,10 +14,10 @@ class ConfigurationManager: """Manages persistence and relationships of components using ComponentFactory for validation""" DEFAULT_UNIQUENESS_FIELDS = { - ComponentTypes.MODEL: ['model_type', 'model'], - ComponentTypes.TOOL: ['name'], - ComponentTypes.AGENT: ['agent_type', 'name'], - ComponentTypes.TEAM: ['team_type', 'name'] + ComponentTypes.MODEL: ["model_type", "model"], + ComponentTypes.TOOL: ["name"], + ComponentTypes.AGENT: ["agent_type", "name"], + ComponentTypes.TEAM: ["team_type", "name"], } def __init__(self, db_manager: DatabaseManager, uniqueness_fields: Dict[ComponentTypes, List[str]] = None): @@ -27,7 +25,9 @@ def __init__(self, db_manager: DatabaseManager, uniqueness_fields: Dict[Componen self.component_factory = ComponentFactory() self.uniqueness_fields = uniqueness_fields or self.DEFAULT_UNIQUENESS_FIELDS - async def import_component(self, component_config: ComponentConfigInput, user_id: str, check_exists: bool = False) -> Response: + async def import_component( + self, component_config: ComponentConfigInput, user_id: str, check_exists: bool = False + ) -> Response: """ Import a component configuration, validate it, and store the resulting component. @@ -41,23 +41,21 @@ async def import_component(self, component_config: ComponentConfigInput, user_id """ try: # Get validated config as dict - config = await self.component_factory.load(component_config, return_type='dict') + config = await self.component_factory.load(component_config, return_type="dict") # Get component type component_type = self._determine_component_type(config) if not component_type: - raise ValueError( - f"Unable to determine component type from config") + raise ValueError("Unable to determine component type from config") # Check existence if requested if check_exists: existing = self._check_exists(component_type, config, user_id) if existing: return Response( - message=self._format_exists_message( - component_type, config), + message=self._format_exists_message(component_type, config), status=True, - data={"id": existing.id} + data={"id": existing.id}, ) # Route to appropriate storage method @@ -70,8 +68,7 @@ async def import_component(self, component_config: ComponentConfigInput, user_id elif component_type == ComponentTypes.TOOL: return await self._store_tool(config, user_id) else: - raise ValueError( - f"Unsupported component type: {component_type}") + raise ValueError(f"Unsupported component type: {component_type}") except Exception as e: logger.error(f"Failed to import component: {str(e)}") @@ -90,23 +87,21 @@ async def import_directory(self, directory: Union[str, Path], user_id: str, chec Response containing import results for all files """ try: - configs = await self.component_factory.load_directory(directory, return_type='dict') + configs = await self.component_factory.load_directory(directory, return_type="dict") results = [] for config in configs: result = await self.import_component(config, user_id, check_exists) - results.append({ - "component": self._get_component_type(config), - "status": result.status, - "message": result.message, - "id": result.data.get("id") if result.status else None - }) - - return Response( - message="Directory import complete", - status=True, - data=results - ) + results.append( + { + "component": self._get_component_type(config), + "status": result.status, + "message": result.message, + "id": result.data.get("id") if result.status else None, + } + ) + + return Response(message="Directory import complete", status=True, data=results) except Exception as e: logger.error(f"Failed to import directory: {str(e)}") @@ -116,10 +111,7 @@ async def _store_team(self, config: dict, user_id: str, check_exists: bool = Fal """Store team component and manage its relationships with agents""" try: # Store the team - team_db = Team( - user_id=user_id, - config=config - ) + team_db = Team(user_id=user_id, config=config) team_result = self.db_manager.upsert(team_db) if not team_result.status: return team_result @@ -131,27 +123,17 @@ async def _store_team(self, config: dict, user_id: str, check_exists: bool = Fal if check_exists: # Check for existing agent agent_type = self._determine_component_type(participant) - existing_agent = self._check_exists( - agent_type, participant, user_id) + existing_agent = self._check_exists(agent_type, participant, user_id) if existing_agent: # Link existing agent - self.db_manager.link( - LinkTypes.TEAM_AGENT, - team_id, - existing_agent.id - ) - logger.info( - f"Linked existing agent to team: {existing_agent}") + self.db_manager.link(LinkTypes.TEAM_AGENT, team_id, existing_agent.id) + logger.info(f"Linked existing agent to team: {existing_agent}") continue # Store and link new agent agent_result = await self._store_agent(participant, user_id, check_exists) if agent_result.status: - self.db_manager.link( - LinkTypes.TEAM_AGENT, - team_id, - agent_result.data["id"] - ) + self.db_manager.link(LinkTypes.TEAM_AGENT, team_id, agent_result.data["id"]) return team_result @@ -163,10 +145,7 @@ async def _store_agent(self, config: dict, user_id: str, check_exists: bool = Fa """Store agent component and manage its relationships with tools and model""" try: # Store the agent - agent_db = Agent( - user_id=user_id, - config=config - ) + agent_db = Agent(user_id=user_id, config=config) agent_result = self.db_manager.upsert(agent_db) if not agent_result.status: return agent_result @@ -177,64 +156,39 @@ async def _store_agent(self, config: dict, user_id: str, check_exists: bool = Fa if "model_client" in config: if check_exists: # Check for existing model - model_type = self._determine_component_type( - config["model_client"]) - existing_model = self._check_exists( - model_type, config["model_client"], user_id) + model_type = self._determine_component_type(config["model_client"]) + existing_model = self._check_exists(model_type, config["model_client"], user_id) if existing_model: # Link existing model - self.db_manager.link( - LinkTypes.AGENT_MODEL, - agent_id, - existing_model.id - ) - logger.info( - f"Linked existing model to agent: {existing_model.config.model_type}") + self.db_manager.link(LinkTypes.AGENT_MODEL, agent_id, existing_model.id) + logger.info(f"Linked existing model to agent: {existing_model.config.model_type}") else: # Store and link new model model_result = await self._store_model(config["model_client"], user_id) if model_result.status: - self.db_manager.link( - LinkTypes.AGENT_MODEL, - agent_id, - model_result.data["id"] - ) + self.db_manager.link(LinkTypes.AGENT_MODEL, agent_id, model_result.data["id"]) else: # Store and link new model without checking model_result = await self._store_model(config["model_client"], user_id) if model_result.status: - self.db_manager.link( - LinkTypes.AGENT_MODEL, - agent_id, - model_result.data["id"] - ) + self.db_manager.link(LinkTypes.AGENT_MODEL, agent_id, model_result.data["id"]) # Handle tools for tool_config in config.get("tools", []): if check_exists: # Check for existing tool tool_type = self._determine_component_type(tool_config) - existing_tool = self._check_exists( - tool_type, tool_config, user_id) + existing_tool = self._check_exists(tool_type, tool_config, user_id) if existing_tool: # Link existing tool - self.db_manager.link( - LinkTypes.AGENT_TOOL, - agent_id, - existing_tool.id - ) - logger.info( - f"Linked existing tool to agent: {existing_tool.config.name}") + self.db_manager.link(LinkTypes.AGENT_TOOL, agent_id, existing_tool.id) + logger.info(f"Linked existing tool to agent: {existing_tool.config.name}") continue # Store and link new tool tool_result = await self._store_tool(tool_config, user_id) if tool_result.status: - self.db_manager.link( - LinkTypes.AGENT_TOOL, - agent_id, - tool_result.data["id"] - ) + self.db_manager.link(LinkTypes.AGENT_TOOL, agent_id, tool_result.data["id"]) return agent_result @@ -245,10 +199,7 @@ async def _store_agent(self, config: dict, user_id: str, check_exists: bool = Fa async def _store_model(self, config: dict, user_id: str) -> Response: """Store model component (leaf node - no relationships)""" try: - model_db = Model( - user_id=user_id, - config=config - ) + model_db = Model(user_id=user_id, config=config) return self.db_manager.upsert(model_db) except Exception as e: @@ -258,17 +209,16 @@ async def _store_model(self, config: dict, user_id: str) -> Response: async def _store_tool(self, config: dict, user_id: str) -> Response: """Store tool component (leaf node - no relationships)""" try: - tool_db = Tool( - user_id=user_id, - config=config - ) + tool_db = Tool(user_id=user_id, config=config) return self.db_manager.upsert(tool_db) except Exception as e: logger.error(f"Failed to store tool: {str(e)}") return Response(message=str(e), status=False) - def _check_exists(self, component_type: ComponentTypes, config: dict, user_id: str) -> Optional[Union[Model, Tool, Agent, Team]]: + def _check_exists( + self, component_type: ComponentTypes, config: dict, user_id: str + ) -> Optional[Union[Model, Tool, Agent, Team]]: """Check if component exists based on configured uniqueness fields.""" fields = self.uniqueness_fields.get(component_type, []) if not fields: @@ -278,17 +228,13 @@ def _check_exists(self, component_type: ComponentTypes, config: dict, user_id: s ComponentTypes.MODEL: Model, ComponentTypes.TOOL: Tool, ComponentTypes.AGENT: Agent, - ComponentTypes.TEAM: Team + ComponentTypes.TEAM: Team, }.get(component_type) - components = self.db_manager.get( - component_class, {"user_id": user_id}).data + components = self.db_manager.get(component_class, {"user_id": user_id}).data for component in components: - matches = all( - component.config.get(field) == config.get(field) - for field in fields - ) + matches = all(component.config.get(field) == config.get(field) for field in fields) if matches: return component diff --git a/python/packages/autogen-studio/autogenstudio/database/db_manager.py b/python/packages/autogen-studio/autogenstudio/database/db_manager.py index d5f17492ed3..2b9ec43e3bd 100644 --- a/python/packages/autogen-studio/autogenstudio/database/db_manager.py +++ b/python/packages/autogen-studio/autogenstudio/database/db_manager.py @@ -1,59 +1,77 @@ -from pathlib import Path import threading from datetime import datetime +from pathlib import Path from typing import Optional from loguru import logger -from sqlalchemy import exc, text, func +from sqlalchemy import exc, func, inspect, text from sqlmodel import Session, SQLModel, and_, create_engine, select + +from ..datamodel import LinkTypes, Response from .schema_manager import SchemaManager -from ..datamodel import ( - Response, - LinkTypes -) # from .dbutils import init_db_samples class DatabaseManager: - """A class to manage database operations""" - _init_lock = threading.Lock() - def __init__( - self, - engine_uri: str, - base_dir: Optional[Path | str] = None, - auto_upgrade: bool = True - ): + def __init__(self, engine_uri: str, base_dir: Optional[Path] = None): """ - Initialize DatabaseManager with optional custom base directory. + Initialize DatabaseManager with database connection settings. + Does not perform any database operations. Args: - engine_uri: Database connection URI - base_dir: Custom base directory for Alembic files. If None, uses current working directory - auto_upgrade: Whether to automatically upgrade schema when differences found + engine_uri: Database connection URI (e.g. sqlite:///db.sqlite3) + base_dir: Base directory for migration files. If None, uses current directory """ - # Convert string path to Path object if necessary - if isinstance(base_dir, str): - base_dir = Path(base_dir) - - connection_args = { - "check_same_thread": True - } if "sqlite" in engine_uri else {} + connection_args = {"check_same_thread": True} if "sqlite" in engine_uri else {} self.engine = create_engine(engine_uri, connect_args=connection_args) self.schema_manager = SchemaManager( engine=self.engine, base_dir=base_dir, - auto_upgrade=auto_upgrade, ) - # Check and upgrade on startup - upgraded, status = self.schema_manager.check_and_upgrade() - if upgraded: - logger.info("Database schema was upgraded automatically") - else: - logger.info(f"Schema status: {status}") + + def initialize_database(self, auto_upgrade: bool = False, force_init_alembic: bool = True) -> Response: + """ + Initialize database and migrations in the correct order. + + Args: + auto_upgrade: If True, automatically generate and apply migrations for schema changes + force_init_alembic: If True, reinitialize alembic configuration even if it exists + """ + if not self._init_lock.acquire(blocking=False): + return Response(message="Database initialization already in progress", status=False) + + try: + inspector = inspect(self.engine) + tables_exist = inspector.get_table_names() + + if not tables_exist: + # Fresh install - create tables and initialize migrations + logger.info("Creating database tables...") + SQLModel.metadata.create_all(self.engine) + + if self.schema_manager.initialize_migrations(force=force_init_alembic): + return Response(message="Database initialized successfully", status=True) + return Response(message="Failed to initialize migrations", status=False) + + # Handle existing database + if auto_upgrade: + logger.info("Checking database schema...") + if self.schema_manager.ensure_schema_up_to_date(): # <-- Use this instead + return Response(message="Database schema is up to date", status=True) + return Response(message="Database upgrade failed", status=False) + + return Response(message="Database is ready", status=True) + + except Exception as e: + error_msg = f"Database initialization failed: {str(e)}" + logger.error(error_msg) + return Response(message=error_msg, status=False) + finally: + self._init_lock.release() def reset_db(self, recreate_tables: bool = True): """ @@ -65,11 +83,7 @@ def reset_db(self, recreate_tables: bool = True): """ if not self._init_lock.acquire(blocking=False): logger.warning("Database reset already in progress") - return Response( - message="Database reset already in progress", - status=False, - data=None - ) + return Response(message="Database reset already in progress", status=False, data=None) try: # Dispose existing connections @@ -77,16 +91,16 @@ def reset_db(self, recreate_tables: bool = True): with Session(self.engine) as session: try: # Disable foreign key checks for SQLite - if 'sqlite' in str(self.engine.url): - session.exec(text('PRAGMA foreign_keys=OFF')) + if "sqlite" in str(self.engine.url): + session.exec(text("PRAGMA foreign_keys=OFF")) # Drop all tables SQLModel.metadata.drop_all(self.engine) logger.info("All tables dropped successfully") # Re-enable foreign key checks for SQLite - if 'sqlite' in str(self.engine.url): - session.exec(text('PRAGMA foreign_keys=ON')) + if "sqlite" in str(self.engine.url): + session.exec(text("PRAGMA foreign_keys=ON")) session.commit() @@ -99,48 +113,29 @@ def reset_db(self, recreate_tables: bool = True): if recreate_tables: logger.info("Recreating tables...") - self.create_db_and_tables() + self.initialize_database(auto_upgrade=False, force_init_alembic=True) return Response( message="Database reset successfully" if recreate_tables else "Database tables dropped successfully", status=True, - data=None + data=None, ) except Exception as e: error_msg = f"Error while resetting database: {str(e)}" logger.error(error_msg) - return Response( - message=error_msg, - status=False, - data=None - ) + return Response(message=error_msg, status=False, data=None) finally: if self._init_lock.locked(): self._init_lock.release() logger.info("Database reset lock released") - def create_db_and_tables(self): - """Create a new database and tables""" - with self._init_lock: - try: - SQLModel.metadata.create_all(self.engine) - logger.info("Database tables created successfully") - try: - # init_db_samples(self) - pass - except Exception as e: - logger.info( - "Error while initializing database samples: " + str(e)) - except Exception as e: - logger.info("Error while creating database tables:" + str(e)) - def upsert(self, model: SQLModel, return_json: bool = True): """Create or update an entity Args: model (SQLModel): The model instance to create or update - return_json (bool, optional): If True, returns the model as a dictionary. + return_json (bool, optional): If True, returns the model as a dictionary. If False, returns the SQLModel instance. Defaults to True. Returns: @@ -152,8 +147,7 @@ def upsert(self, model: SQLModel, return_json: bool = True): with Session(self.engine) as session: try: - existing_model = session.exec( - select(model_class).where(model_class.id == model.id)).first() + existing_model = session.exec(select(model_class).where(model_class.id == model.id)).first() if existing_model: model.updated_at = datetime.now() for key, value in model.model_dump().items(): @@ -166,8 +160,7 @@ def upsert(self, model: SQLModel, return_json: bool = True): session.refresh(model) except Exception as e: session.rollback() - logger.error("Error while updating/creating " + - str(model_class.__name__) + ": " + str(e)) + logger.error("Error while updating/creating " + str(model_class.__name__) + ": " + str(e)) status = False return Response( @@ -199,25 +192,21 @@ def get( try: statement = select(model_class) if filters: - conditions = [getattr(model_class, col) == - value for col, value in filters.items()] + conditions = [getattr(model_class, col) == value for col, value in filters.items()] statement = statement.where(and_(*conditions)) if hasattr(model_class, "created_at") and order: - order_by_clause = getattr( - model_class.created_at, order)() # Dynamically apply asc/desc + order_by_clause = getattr(model_class.created_at, order)() # Dynamically apply asc/desc statement = statement.order_by(order_by_clause) items = session.exec(statement).all() - result = [self._model_to_dict( - item) if return_json else item for item in items] + result = [self._model_to_dict(item) if return_json else item for item in items] status_message = f"{model_class.__name__} Retrieved Successfully" except Exception as e: session.rollback() status = False status_message = f"Error while fetching {model_class.__name__}" - logger.error("Error while getting items: " + - str(model_class.__name__) + " " + str(e)) + logger.error("Error while getting items: " + str(model_class.__name__) + " " + str(e)) return Response(message=status_message, status=status, data=result) @@ -230,8 +219,7 @@ def delete(self, model_class: SQLModel, filters: dict = None): try: statement = select(model_class) if filters: - conditions = [ - getattr(model_class, col) == value for col, value in filters.items()] + conditions = [getattr(model_class, col) == value for col, value in filters.items()] statement = statement.where(and_(*conditions)) rows = session.exec(statement).all() @@ -290,8 +278,7 @@ def link( select(link_table).where( and_( getattr(link_table, primary_id_field) == primary_id, - getattr( - link_table, secondary_id_field) == secondary_id + getattr(link_table, secondary_id_field) == secondary_id, ) ) ).first() @@ -302,37 +289,24 @@ def link( # Get the next sequence number if not provided if sequence is None: max_seq_result = session.exec( - select(func.max(link_table.sequence)).where( - getattr(link_table, primary_id_field) == primary_id - ) + select(func.max(link_table.sequence)).where(getattr(link_table, primary_id_field) == primary_id) ).first() sequence = 0 if max_seq_result is None else max_seq_result + 1 # Create new link - new_link = link_table(**{ - primary_id_field: primary_id, - secondary_id_field: secondary_id, - 'sequence': sequence - }) + new_link = link_table( + **{primary_id_field: primary_id, secondary_id_field: secondary_id, "sequence": sequence} + ) session.add(new_link) session.commit() - return Response( - message=f"Entities linked successfully with sequence {sequence}", - status=True - ) + return Response(message=f"Entities linked successfully with sequence {sequence}", status=True) except Exception as e: session.rollback() return Response(message=f"Error linking entities: {str(e)}", status=False) - def unlink( - self, - link_type: LinkTypes, - primary_id: int, - secondary_id: int, - sequence: Optional[int] = None - ): + def unlink(self, link_type: LinkTypes, primary_id: int, secondary_id: int, sequence: Optional[int] = None): """Unlink two entities and reorder sequences if needed.""" with Session(self.engine) as session: try: @@ -349,13 +323,12 @@ def unlink( statement = select(link_table).where( and_( getattr(link_table, primary_id_field) == primary_id, - getattr(link_table, secondary_id_field) == secondary_id + getattr(link_table, secondary_id_field) == secondary_id, ) ) if sequence is not None: - statement = statement.where( - link_table.sequence == sequence) + statement = statement.where(link_table.sequence == sequence) existing_link = session.exec(statement).first() @@ -379,10 +352,7 @@ def unlink( session.commit() - return Response( - message="Entities unlinked successfully and sequences reordered", - status=True - ) + return Response(message="Entities unlinked successfully and sequences reordered", status=True) except Exception as e: session.rollback() @@ -414,22 +384,14 @@ def get_linked_entities( .order_by(link_table.sequence) ).all() - result = [ - item.model_dump() if return_json else item for item in items] + result = [item.model_dump() if return_json else item for item in items] - return Response( - message="Linked entities retrieved successfully", - status=True, - data=result - ) + return Response(message="Linked entities retrieved successfully", status=True, data=result) except Exception as e: logger.error(f"Error getting linked entities: {str(e)}") - return Response( - message=f"Error getting linked entities: {str(e)}", - status=False, - data=[] - ) + return Response(message=f"Error getting linked entities: {str(e)}", status=False, data=[]) + # Add new close method async def close(self): diff --git a/python/packages/autogen-studio/autogenstudio/database/schema_manager.py b/python/packages/autogen-studio/autogenstudio/database/schema_manager.py index d4d7de42c8e..42d1f5f5dec 100644 --- a/python/packages/autogen-studio/autogenstudio/database/schema_manager.py +++ b/python/packages/autogen-studio/autogenstudio/database/schema_manager.py @@ -1,68 +1,71 @@ import os -from pathlib import Path import shutil -from typing import Optional, Tuple, List -from loguru import logger +from pathlib import Path +from typing import List, Optional, Tuple + +import sqlmodel from alembic import command +from alembic.autogenerate import compare_metadata from alembic.config import Config from alembic.runtime.migration import MigrationContext from alembic.script import ScriptDirectory -from alembic.autogenerate import compare_metadata -from sqlalchemy import Engine -from sqlmodel import SQLModel from alembic.util.exc import CommandError +from loguru import logger +from sqlalchemy import Engine, text +from sqlmodel import SQLModel class SchemaManager: """ Manages database schema validation and migrations using Alembic. - Provides automatic schema validation, migrations, and safe upgrades. - - Args: - engine: SQLAlchemy engine instance - auto_upgrade: Whether to automatically upgrade schema when differences found - init_mode: Controls initialization behavior: - - "none": No automatic initialization (raises error if not set up) - - "auto": Initialize if not present (default) - - "force": Always reinitialize, removing existing configuration + Operations are initiated explicitly by DatabaseManager. """ def __init__( self, engine: Engine, base_dir: Optional[Path] = None, - auto_upgrade: bool = True, - init_mode: str = "auto" ): - if init_mode not in ["none", "auto", "force"]: - raise ValueError("init_mode must be one of: none, auto, force") + """ + Initialize configuration only - no filesystem or DB operations. - self.engine = engine - self.auto_upgrade = auto_upgrade + Args: + engine: SQLAlchemy engine instance + base_dir: Base directory for Alembic files. If None, uses current working directory + """ + # Convert string path to Path object if necessary + if isinstance(base_dir, str): + base_dir = Path(base_dir) - # Use provided base_dir or default to class file location + self.engine = engine self.base_dir = base_dir or Path(__file__).parent - self.alembic_dir = self.base_dir / 'alembic' - self.alembic_ini_path = self.base_dir / 'alembic.ini' + self.alembic_dir = self.base_dir / "alembic" + self.alembic_ini_path = self.base_dir / "alembic.ini" - # Create base directory if it doesn't exist - self.base_dir.mkdir(parents=True, exist_ok=True) + def initialize_migrations(self, force: bool = False) -> bool: + try: + if force: + logger.info("Force reinitialization of migrations...") + self._cleanup_existing_alembic() + if not self._initialize_alembic(): + return False + else: + try: + self._validate_alembic_setup() + logger.info("Using existing Alembic configuration") + self._update_configuration() + except FileNotFoundError: + logger.info("Initializing new Alembic configuration") + if not self._initialize_alembic(): + return False + + # Only generate initial revision if alembic is properly initialized + logger.info("Creating initial migration...") + return self.generate_revision("Initial schema") is not None - # Initialize based on mode - if init_mode == "force": - self._cleanup_existing_alembic() - self._initialize_alembic() - else: - try: - self._validate_alembic_setup() - logger.info("Using existing Alembic configuration") - # Update existing configuration - self._update_configuration() - except FileNotFoundError: - if init_mode == "none": - raise - logger.info("Initializing new Alembic configuration") - self._initialize_alembic() + except Exception as e: + logger.error(f"Failed to initialize migrations: {e}") + return False def _update_configuration(self) -> None: """Updates existing Alembic configuration with current settings.""" @@ -70,11 +73,11 @@ def _update_configuration(self) -> None: # Update alembic.ini config_content = self._generate_alembic_ini_content() - with open(self.alembic_ini_path, 'w') as f: + with open(self.alembic_ini_path, "w") as f: f.write(config_content) # Update env.py - env_path = self.alembic_dir / 'env.py' + env_path = self.alembic_dir / "env.py" if env_path.exists(): self._update_env_py(env_path) else: @@ -82,37 +85,22 @@ def _update_configuration(self) -> None: def _cleanup_existing_alembic(self) -> None: """ - Safely removes existing Alembic configuration while preserving versions directory. + Completely remove existing Alembic configuration including versions. + For fresh initialization, we don't need to preserve anything. """ - logger.info( - "Cleaning up existing Alembic configuration while preserving versions...") - - # Create a backup of versions directory if it exists - if self.alembic_dir.exists() and (self.alembic_dir / 'versions').exists(): - logger.info("Preserving existing versions directory") + logger.info("Cleaning up existing Alembic configuration...") - # Remove alembic directory contents EXCEPT versions + # Remove entire alembic directory if it exists if self.alembic_dir.exists(): - for item in self.alembic_dir.iterdir(): - if item.name != 'versions': - try: - if item.is_dir(): - shutil.rmtree(item) - logger.info(f"Removed directory: {item}") - else: - item.unlink() - logger.info(f"Removed file: {item}") - except Exception as e: - logger.error(f"Failed to remove {item}: {e}") + import shutil + + shutil.rmtree(self.alembic_dir) + logger.info(f"Removed alembic directory: {self.alembic_dir}") # Remove alembic.ini if it exists if self.alembic_ini_path.exists(): - try: - self.alembic_ini_path.unlink() - logger.info( - f"Removed existing alembic.ini: {self.alembic_ini_path}") - except Exception as e: - logger.error(f"Failed to remove alembic.ini: {e}") + self.alembic_ini_path.unlink() + logger.info("Removed alembic.ini") def _ensure_alembic_setup(self, *, force: bool = False) -> None: """ @@ -124,51 +112,52 @@ def _ensure_alembic_setup(self, *, force: bool = False) -> None: try: self._validate_alembic_setup() if force: - logger.info( - "Force initialization requested. Cleaning up existing configuration...") + logger.info("Force initialization requested. Cleaning up existing configuration...") self._cleanup_existing_alembic() self._initialize_alembic() except FileNotFoundError: logger.info("Alembic configuration not found. Initializing...") if self.alembic_dir.exists(): - logger.warning( - "Found existing alembic directory but missing configuration") + logger.warning("Found existing alembic directory but missing configuration") self._cleanup_existing_alembic() self._initialize_alembic() logger.info("Alembic initialization complete") - def _initialize_alembic(self) -> None: - logger.info("Initializing Alembic configuration...") - - # Create directories first - self.alembic_dir.mkdir(exist_ok=True) - versions_dir = self.alembic_dir / 'versions' - versions_dir.mkdir(exist_ok=True) + def _initialize_alembic(self) -> bool: + """Initialize alembic structure and configuration""" + try: + # Ensure parent directory exists + self.alembic_dir.parent.mkdir(exist_ok=True) - # Create env.py BEFORE running command.init - env_path = self.alembic_dir / 'env.py' - if not env_path.exists(): - self._create_minimal_env_py(env_path) - logger.info("Created new env.py") + # Run alembic init to create fresh directory structure + logger.info("Initializing alembic directory structure...") - # Write alembic.ini - config_content = self._generate_alembic_ini_content() - with open(self.alembic_ini_path, 'w') as f: - f.write(config_content) - logger.info("Created alembic.ini") + # Create initial config file for alembic init + config_content = self._generate_alembic_ini_content() + with open(self.alembic_ini_path, "w") as f: + f.write(config_content) - # Now run alembic init - try: - config = self.get_alembic_config() + # Use the config we just created + config = Config(str(self.alembic_ini_path)) command.init(config, str(self.alembic_dir)) - logger.info("Initialized Alembic directory structure") - except CommandError as e: - if "already exists" not in str(e): - raise + + # Update script template after initialization + self.update_script_template() + + # Update env.py with our customizations + self._update_env_py(self.alembic_dir / "env.py") + + logger.info("Alembic initialization complete") + return True + + except Exception as e: + # Explicitly convert error to string + logger.error(f"Failed to initialize alembic: {str(e)}") + return False def _create_minimal_env_py(self, env_path: Path) -> None: """Creates a minimal env.py file for Alembic.""" - content = ''' + content = """ from logging.config import fileConfig from sqlalchemy import engine_from_config from sqlalchemy import pool @@ -201,7 +190,7 @@ def run_migrations_online() -> None: ) with connectable.connect() as connection: context.configure( - connection=connection, + connection=connection, target_metadata=target_metadata, compare_type=True ) @@ -211,9 +200,9 @@ def run_migrations_online() -> None: if context.is_offline_mode(): run_migrations_offline() else: - run_migrations_online()''' + run_migrations_online()""" - with open(env_path, 'w') as f: + with open(env_path, "w") as f: f.write(content) def _generate_alembic_ini_content(self) -> str: @@ -260,6 +249,29 @@ class = StreamHandler datefmt = %H:%M:%S """.strip() + def update_script_template(self): + """Update the Alembic script template to include SQLModel.""" + template_path = self.alembic_dir / "script.py.mako" + try: + with open(template_path, "r") as f: + content = f.read() + + # Add sqlmodel import to imports section + import_section = "from alembic import op\nimport sqlalchemy as sa" + new_imports = "from alembic import op\nimport sqlalchemy as sa\nimport sqlmodel" + + content = content.replace(import_section, new_imports) + + with open(template_path, "w") as f: + f.write(content) + + logger.info("Updated script template") + return True + + except Exception as e: + logger.error(f"Failed to update script template: {e}") + return False + def _update_env_py(self, env_path: Path) -> None: """ Updates the env.py file to use SQLModel metadata. @@ -268,27 +280,45 @@ def _update_env_py(self, env_path: Path) -> None: self._create_minimal_env_py(env_path) return try: - with open(env_path, 'r') as f: + with open(env_path, "r") as f: content = f.read() - # Add SQLModel import + # Add SQLModel import if not present if "from sqlmodel import SQLModel" not in content: content = "from sqlmodel import SQLModel\n" + content # Replace target_metadata + content = content.replace("target_metadata = None", "target_metadata = SQLModel.metadata") + + # Update both configure blocks properly content = content.replace( - "target_metadata = None", - "target_metadata = SQLModel.metadata" + """context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + )""", + """context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + compare_type=True, + )""", ) - # Add compare_type=True to context.configure - if "context.configure(" in content and "compare_type=True" not in content: - content = content.replace( - "context.configure(", - "context.configure(compare_type=True," - ) + content = content.replace( + """ context.configure( + connection=connection, target_metadata=target_metadata + )""", + """ context.configure( + connection=connection, + target_metadata=target_metadata, + compare_type=True, + )""", + ) - with open(env_path, 'w') as f: + with open(env_path, "w") as f: f.write(content) logger.info("Updated env.py with SQLModel metadata") @@ -297,6 +327,7 @@ def _update_env_py(self, env_path: Path) -> None: raise # Fixed: use keyword-only argument + def _ensure_alembic_setup(self, *, force: bool = False) -> None: """ Ensures Alembic is properly set up, initializing if necessary. @@ -307,32 +338,24 @@ def _ensure_alembic_setup(self, *, force: bool = False) -> None: try: self._validate_alembic_setup() if force: - logger.info( - "Force initialization requested. Cleaning up existing configuration...") + logger.info("Force initialization requested. Cleaning up existing configuration...") self._cleanup_existing_alembic() self._initialize_alembic() except FileNotFoundError: logger.info("Alembic configuration not found. Initializing...") if self.alembic_dir.exists(): - logger.warning( - "Found existing alembic directory but missing configuration") + logger.warning("Found existing alembic directory but missing configuration") self._cleanup_existing_alembic() self._initialize_alembic() logger.info("Alembic initialization complete") def _validate_alembic_setup(self) -> None: """Validates that Alembic is properly configured.""" - required_files = [ - self.alembic_ini_path, - self.alembic_dir / 'env.py', - self.alembic_dir / 'versions' - ] + required_files = [self.alembic_ini_path, self.alembic_dir / "env.py", self.alembic_dir / "versions"] missing = [f for f in required_files if not f.exists()] if missing: - raise FileNotFoundError( - f"Alembic configuration incomplete. Missing: {', '.join(str(f) for f in missing)}" - ) + raise FileNotFoundError(f"Alembic configuration incomplete. Missing: {', '.join(str(f) for f in missing)}") def get_alembic_config(self) -> Config: """ @@ -430,7 +453,7 @@ def upgrade_schema(self, revision: str = "head") -> bool: def check_and_upgrade(self) -> Tuple[bool, str]: """ - Checks schema status and upgrades if necessary (and auto_upgrade is True). + Checks schema status and upgrades if necessary. Returns: Tuple[bool, str]: (action_taken, status_message) @@ -438,13 +461,11 @@ def check_and_upgrade(self) -> Tuple[bool, str]: needs_upgrade, status = self.check_schema_status() if needs_upgrade: - if self.auto_upgrade: - if self.upgrade_schema(): - return True, "Schema was automatically upgraded" - else: - return False, "Automatic schema upgrade failed" + # Remove the auto_upgrade check since we explicitly called this method + if self.upgrade_schema(): + return True, "Schema was automatically upgraded" else: - return False, f"Schema needs upgrade but auto_upgrade is disabled. Status: {status}" + return False, "Automatic schema upgrade failed" return False, status @@ -460,11 +481,7 @@ def generate_revision(self, message: str = "auto") -> Optional[str]: """ try: config = self.get_alembic_config() - command.revision( - config, - message=message, - autogenerate=True - ) + command.revision(config, message=message, autogenerate=True) return self.get_head_revision() except Exception as e: @@ -512,25 +529,39 @@ def print_status(self) -> None: def ensure_schema_up_to_date(self) -> bool: """ - Ensures the database schema is up to date, generating and applying migrations if needed. - - Returns: - bool: True if schema is up to date or was successfully updated + Reset migrations and create fresh migration for current schema state. """ try: - # Check for unmigrated changes - differences = self.get_schema_differences() - if differences: - # Generate new migration - revision = self.generate_revision("auto-generated") - if not revision: - return False - logger.info(f"Generated new migration: {revision}") + logger.info("Resetting migrations and updating to current schema...") + + # 1. Clear the entire alembic directory + if self.alembic_dir.exists(): + shutil.rmtree(self.alembic_dir) + logger.info("Cleared alembic directory") + + # 2. Clear alembic_version table + with self.engine.connect() as connection: + connection.execute(text("DROP TABLE IF EXISTS alembic_version")) + connection.commit() + logger.info("Reset alembic version") + + # 3. Reinitialize alembic from scratch + if not self._initialize_alembic(): + logger.error("Failed to reinitialize alembic") + return False + + # 4. Generate fresh migration from current schema + revision = self.generate_revision("current_schema") + if not revision: + logger.error("Failed to generate new migration") + return False + logger.info(f"Generated fresh migration: {revision}") - # Apply any pending migrations - upgraded, status = self.check_and_upgrade() - if not upgraded and "needs upgrade" in status.lower(): + # 5. Apply the migration + if not self.upgrade_schema(): + logger.error("Failed to apply migration") return False + logger.info("Successfully applied migration") return True diff --git a/python/packages/autogen-studio/autogenstudio/datamodel/__init__.py b/python/packages/autogen-studio/autogenstudio/datamodel/__init__.py index 6b7b4098df4..0d46fb26334 100644 --- a/python/packages/autogen-studio/autogenstudio/datamodel/__init__.py +++ b/python/packages/autogen-studio/autogenstudio/datamodel/__init__.py @@ -1,2 +1,11 @@ -from .db import * -from .types import * +from .db import Agent, LinkTypes, Message, Model, Run, RunStatus, Session, Team, Tool +from .types import ( + AgentConfig, + ComponentConfigInput, + MessageConfig, + ModelConfig, + Response, + TeamConfig, + TeamResult, + ToolConfig, +) diff --git a/python/packages/autogen-studio/autogenstudio/datamodel/db.py b/python/packages/autogen-studio/autogenstudio/datamodel/db.py index 2f8210029a9..6395a535fd6 100644 --- a/python/packages/autogen-studio/autogenstudio/datamodel/db.py +++ b/python/packages/autogen-studio/autogenstudio/datamodel/db.py @@ -2,24 +2,28 @@ from datetime import datetime from enum import Enum -from typing import List, Optional, Union, Tuple, Type -from sqlalchemy import ForeignKey, Integer, UniqueConstraint -from sqlmodel import JSON, Column, DateTime, Field, SQLModel, func, Relationship, SQLModel +from typing import List, Optional, Tuple, Type, Union from uuid import UUID, uuid4 -from .types import ToolConfig, ModelConfig, AgentConfig, TeamConfig, MessageConfig, MessageMeta +from loguru import logger +from pydantic import BaseModel +from sqlalchemy import ForeignKey, Integer, UniqueConstraint +from sqlmodel import JSON, Column, DateTime, Field, Relationship, SQLModel, func + +from .types import AgentConfig, MessageConfig, MessageMeta, ModelConfig, TeamConfig, TeamResult, ToolConfig # added for python3.11 and sqlmodel 0.0.22 incompatibility if hasattr(SQLModel, "model_config"): SQLModel.model_config["protected_namespaces"] = () elif hasattr(SQLModel, "Config"): + class CustomSQLModel(SQLModel): class Config: protected_namespaces = () SQLModel = CustomSQLModel else: - print("Warning: Unable to set protected_namespaces.") + logger.warning("Unable to set protected_namespaces.") # pylint: disable=protected-access @@ -36,7 +40,7 @@ def model_class(self) -> Type[SQLModel]: ComponentTypes.TEAM: Team, ComponentTypes.AGENT: Agent, ComponentTypes.MODEL: Model, - ComponentTypes.TOOL: Tool + ComponentTypes.TOOL: Tool, }[self] @@ -51,7 +55,7 @@ def link_config(self) -> Tuple[Type[SQLModel], Type[SQLModel], Type[SQLModel]]: return { LinkTypes.AGENT_MODEL: (Agent, Model, AgentModelLink), LinkTypes.AGENT_TOOL: (Agent, Tool, AgentToolLink), - LinkTypes.TEAM_AGENT: (Team, Agent, TeamAgentLink) + LinkTypes.TEAM_AGENT: (Team, Agent, TeamAgentLink), }[self] @property @@ -70,40 +74,34 @@ def link_table(self) -> Type[SQLModel]: # type: ignore # link models class AgentToolLink(SQLModel, table=True): __table_args__ = ( - UniqueConstraint('agent_id', 'sequence', - name='unique_agent_tool_sequence'), - {'sqlite_autoincrement': True} + UniqueConstraint("agent_id", "sequence", name="unique_agent_tool_sequence"), + {"sqlite_autoincrement": True}, ) - agent_id: int = Field(default=None, primary_key=True, - foreign_key="agent.id") + agent_id: int = Field(default=None, primary_key=True, foreign_key="agent.id") tool_id: int = Field(default=None, primary_key=True, foreign_key="tool.id") sequence: Optional[int] = Field(default=0, primary_key=True) class AgentModelLink(SQLModel, table=True): __table_args__ = ( - UniqueConstraint('agent_id', 'sequence', - name='unique_agent_tool_sequence'), - {'sqlite_autoincrement': True} + UniqueConstraint("agent_id", "sequence", name="unique_agent_tool_sequence"), + {"sqlite_autoincrement": True}, ) - agent_id: int = Field(default=None, primary_key=True, - foreign_key="agent.id") - model_id: int = Field(default=None, primary_key=True, - foreign_key="model.id") + agent_id: int = Field(default=None, primary_key=True, foreign_key="agent.id") + model_id: int = Field(default=None, primary_key=True, foreign_key="model.id") sequence: Optional[int] = Field(default=0, primary_key=True) class TeamAgentLink(SQLModel, table=True): __table_args__ = ( - UniqueConstraint('agent_id', 'sequence', - name='unique_agent_tool_sequence'), - {'sqlite_autoincrement': True} + UniqueConstraint("agent_id", "sequence", name="unique_agent_tool_sequence"), + {"sqlite_autoincrement": True}, ) team_id: int = Field(default=None, primary_key=True, foreign_key="team.id") - agent_id: int = Field(default=None, primary_key=True, - foreign_key="agent.id") + agent_id: int = Field(default=None, primary_key=True, foreign_key="agent.id") sequence: Optional[int] = Field(default=0, primary_key=True) + # database models @@ -120,10 +118,8 @@ class Tool(SQLModel, table=True): ) # pylint: disable=not-callable user_id: Optional[str] = None version: Optional[str] = "0.0.1" - config: Union[ToolConfig, dict] = Field( - default_factory=ToolConfig, sa_column=Column(JSON)) - agents: List["Agent"] = Relationship( - back_populates="tools", link_model=AgentToolLink) + config: Union[ToolConfig, dict] = Field(default_factory=ToolConfig, sa_column=Column(JSON)) + agents: List["Agent"] = Relationship(back_populates="tools", link_model=AgentToolLink) class Model(SQLModel, table=True): @@ -139,10 +135,8 @@ class Model(SQLModel, table=True): ) # pylint: disable=not-callable user_id: Optional[str] = None version: Optional[str] = "0.0.1" - config: Union[ModelConfig, dict] = Field( - default_factory=ModelConfig, sa_column=Column(JSON)) - agents: List["Agent"] = Relationship( - back_populates="models", link_model=AgentModelLink) + config: Union[ModelConfig, dict] = Field(default_factory=ModelConfig, sa_column=Column(JSON)) + agents: List["Agent"] = Relationship(back_populates="models", link_model=AgentModelLink) class Team(SQLModel, table=True): @@ -158,10 +152,8 @@ class Team(SQLModel, table=True): ) # pylint: disable=not-callable user_id: Optional[str] = None version: Optional[str] = "0.0.1" - config: Union[TeamConfig, dict] = Field( - default_factory=TeamConfig, sa_column=Column(JSON)) - agents: List["Agent"] = Relationship( - back_populates="teams", link_model=TeamAgentLink) + config: Union[TeamConfig, dict] = Field(default_factory=TeamConfig, sa_column=Column(JSON)) + agents: List["Agent"] = Relationship(back_populates="teams", link_model=TeamAgentLink) class Agent(SQLModel, table=True): @@ -177,14 +169,10 @@ class Agent(SQLModel, table=True): ) # pylint: disable=not-callable user_id: Optional[str] = None version: Optional[str] = "0.0.1" - config: Union[AgentConfig, dict] = Field( - default_factory=AgentConfig, sa_column=Column(JSON)) - tools: List[Tool] = Relationship( - back_populates="agents", link_model=AgentToolLink) - models: List[Model] = Relationship( - back_populates="agents", link_model=AgentModelLink) - teams: List[Team] = Relationship( - back_populates="agents", link_model=TeamAgentLink) + config: Union[AgentConfig, dict] = Field(default_factory=AgentConfig, sa_column=Column(JSON)) + tools: List[Tool] = Relationship(back_populates="agents", link_model=AgentToolLink) + models: List[Model] = Relationship(back_populates="agents", link_model=AgentModelLink) + teams: List[Team] = Relationship(back_populates="agents", link_model=TeamAgentLink) class Message(SQLModel, table=True): @@ -200,17 +188,12 @@ class Message(SQLModel, table=True): ) # pylint: disable=not-callable user_id: Optional[str] = None version: Optional[str] = "0.0.1" - config: Union[MessageConfig, dict] = Field( - default_factory=MessageConfig, sa_column=Column(JSON)) + config: Union[MessageConfig, dict] = Field(default_factory=MessageConfig, sa_column=Column(JSON)) session_id: Optional[int] = Field( default=None, sa_column=Column(Integer, ForeignKey("session.id", ondelete="CASCADE")) ) - run_id: Optional[UUID] = Field( - default=None, foreign_key="run.id" - ) - - message_meta: Optional[Union[MessageMeta, dict]] = Field( - default={}, sa_column=Column(JSON)) + run_id: Optional[UUID] = Field(default=None, foreign_key="run.id") + message_meta: Optional[Union[MessageMeta, dict]] = Field(default={}, sa_column=Column(JSON)) class Session(SQLModel, table=True): @@ -226,9 +209,7 @@ class Session(SQLModel, table=True): ) # pylint: disable=not-callable user_id: Optional[str] = None version: Optional[str] = "0.0.1" - team_id: Optional[int] = Field( - default=None, sa_column=Column(Integer, ForeignKey("team.id", ondelete="CASCADE")) - ) + team_id: Optional[int] = Field(default=None, sa_column=Column(Integer, ForeignKey("team.id", ondelete="CASCADE"))) name: Optional[str] = None @@ -242,41 +223,59 @@ class RunStatus(str, Enum): class Run(SQLModel, table=True): """Represents a single execution run within a session""" - __table_args__ = {"sqlite_autoincrement": True} - # Primary key using UUID - id: UUID = Field( - default_factory=uuid4, - primary_key=True, - index=True - ) + __table_args__ = {"sqlite_autoincrement": True} - # Timestamps using the same pattern as other models + id: UUID = Field(default_factory=uuid4, primary_key=True, index=True) created_at: datetime = Field( - default_factory=datetime.now, - sa_column=Column(DateTime(timezone=True), server_default=func.now()) + default_factory=datetime.now, sa_column=Column(DateTime(timezone=True), server_default=func.now()) ) updated_at: datetime = Field( - default_factory=datetime.now, - sa_column=Column(DateTime(timezone=True), onupdate=func.now()) + default_factory=datetime.now, sa_column=Column(DateTime(timezone=True), onupdate=func.now()) ) - - # Foreign key to Session session_id: Optional[int] = Field( - default=None, - sa_column=Column( - Integer, - ForeignKey("session.id", ondelete="CASCADE"), - nullable=False - ) + default=None, sa_column=Column(Integer, ForeignKey("session.id", ondelete="CASCADE"), nullable=False) ) - - # Run status and metadata status: RunStatus = Field(default=RunStatus.CREATED) + + # Store the original user task + task: Union[MessageConfig, dict] = Field(default_factory=MessageConfig, sa_column=Column(JSON)) + + # Store TeamResult which contains TaskResult + team_result: Union[TeamResult, dict] = Field(default=None, sa_column=Column(JSON)) + error_message: Optional[str] = None + version: Optional[str] = "0.0.1" + messages: Union[List[Message], List[dict]] = Field(default_factory=list, sa_column=Column(JSON)) + + class Config: + json_encoders = {UUID: str, datetime: lambda v: v.isoformat()} + + +class GalleryConfig(SQLModel, table=False): + id: UUID = Field(default_factory=uuid4, primary_key=True, index=True) + title: Optional[str] = None + description: Optional[str] = None + run: Run + team: TeamConfig = None + tags: Optional[List[str]] = None + visibility: str = "public" # public, private, shared - # Metadata storage following pattern from Message model - run_meta: dict = Field(default={}, sa_column=Column(JSON)) + class Config: + json_encoders = {UUID: str, datetime: lambda v: v.isoformat()} - # Version tracking like other models + +class Gallery(SQLModel, table=True): + __table_args__ = {"sqlite_autoincrement": True} + id: Optional[int] = Field(default=None, primary_key=True) + created_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), server_default=func.now()), + ) + updated_at: datetime = Field( + default_factory=datetime.now, + sa_column=Column(DateTime(timezone=True), onupdate=func.now()), + ) + user_id: Optional[str] = None version: Optional[str] = "0.0.1" + config: Union[GalleryConfig, dict] = Field(default_factory=GalleryConfig, sa_column=Column(JSON)) diff --git a/python/packages/autogen-studio/autogenstudio/datamodel/types.py b/python/packages/autogen-studio/autogenstudio/datamodel/types.py index 5fec5d984be..795a10b5641 100644 --- a/python/packages/autogen-studio/autogenstudio/datamodel/types.py +++ b/python/packages/autogen-studio/autogenstudio/datamodel/types.py @@ -1,10 +1,10 @@ from datetime import datetime from enum import Enum from pathlib import Path -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, List, Literal, Optional, Union +from autogen_agentchat.base import TaskResult from pydantic import BaseModel -from autogen_agentchat.base._task import TaskResult class ModelTypes(str, Enum): @@ -18,6 +18,7 @@ class ToolTypes(str, Enum): class AgentTypes(str, Enum): ASSISTANT = "AssistantAgent" USERPROXY = "UserProxyAgent" + MULTIMODAL_WEBSURFER = "MultimodalWebSurfer" class TeamTypes(str, Enum): @@ -29,9 +30,10 @@ class TerminationTypes(str, Enum): MAX_MESSAGES = "MaxMessageTermination" STOP_MESSAGE = "StopMessageTermination" TEXT_MENTION = "TextMentionTermination" + COMBINATION = "CombinationTermination" -class ComponentType(str, Enum): +class ComponentTypes(str, Enum): TEAM = "team" AGENT = "agent" MODEL = "model" @@ -40,11 +42,9 @@ class ComponentType(str, Enum): class BaseConfig(BaseModel): - model_config = { - "protected_namespaces": () - } + model_config = {"protected_namespaces": ()} version: str = "1.0.0" - component_type: ComponentType + component_type: ComponentTypes class MessageConfig(BaseModel): @@ -58,7 +58,7 @@ class ModelConfig(BaseConfig): model_type: ModelTypes api_key: Optional[str] = None base_url: Optional[str] = None - component_type: ComponentType = ComponentType.MODEL + component_type: ComponentTypes = ComponentTypes.MODEL class ToolConfig(BaseConfig): @@ -66,7 +66,7 @@ class ToolConfig(BaseConfig): description: str content: str tool_type: ToolTypes - component_type: ComponentType = ComponentType.TOOL + component_type: ComponentTypes = ComponentTypes.TOOL class AgentConfig(BaseConfig): @@ -76,14 +76,23 @@ class AgentConfig(BaseConfig): model_client: Optional[ModelConfig] = None tools: Optional[List[ToolConfig]] = None description: Optional[str] = None - component_type: ComponentType = ComponentType.AGENT + component_type: ComponentTypes = ComponentTypes.AGENT + headless: Optional[bool] = None + logs_dir: Optional[str] = None + to_save_screenshots: Optional[bool] = None + use_ocr: Optional[bool] = None + animate_actions: Optional[bool] = None class TerminationConfig(BaseConfig): termination_type: TerminationTypes + # Fields for basic terminations max_messages: Optional[int] = None text: Optional[str] = None - component_type: ComponentType = ComponentType.TERMINATION + # Fields for combinations + operator: Optional[Literal["and", "or"]] = None + conditions: Optional[List["TerminationConfig"]] = None + component_type: ComponentTypes = ComponentTypes.TERMINATION class TeamConfig(BaseConfig): @@ -93,7 +102,7 @@ class TeamConfig(BaseConfig): model_client: Optional[ModelConfig] = None selector_prompt: Optional[str] = None termination_condition: Optional[TerminationConfig] = None - component_type: ComponentType = ComponentType.TEAM + component_type: ComponentTypes = ComponentTypes.TEAM class TeamResult(BaseModel): @@ -111,6 +120,7 @@ class MessageMeta(BaseModel): log: Optional[List[dict]] = None usage: Optional[List[dict]] = None + # web request/response data models @@ -126,12 +136,6 @@ class SocketMessage(BaseModel): type: str -ComponentConfig = Union[ - TeamConfig, - AgentConfig, - ModelConfig, - ToolConfig, - TerminationConfig -] +ComponentConfig = Union[TeamConfig, AgentConfig, ModelConfig, ToolConfig, TerminationConfig] ComponentConfigInput = Union[str, Path, dict, ComponentConfig] diff --git a/python/packages/autogen-studio/autogenstudio/profiler.py b/python/packages/autogen-studio/autogenstudio/profiler.py deleted file mode 100644 index 679a56917e2..00000000000 --- a/python/packages/autogen-studio/autogenstudio/profiler.py +++ /dev/null @@ -1,108 +0,0 @@ -# metrics - agent_frequency, execution_count, tool_count, - -from typing import Dict, List, Optional - -from .datamodel import Message, MessageMeta - - -class Profiler: - """ - Profiler class to profile agent task runs and compute metrics - for performance evaluation. - """ - - def __init__(self): - self.metrics: List[Dict] = [] - - def _is_code(self, message: Message) -> bool: - """ - Check if the message contains code. - - :param message: The message instance to check. - :return: True if the message contains code, False otherwise. - """ - content = message.get("message").get("content").lower() - return "```" in content - - def _is_tool(self, message: Message) -> bool: - """ - Check if the message uses a tool. - - :param message: The message instance to check. - :return: True if the message uses a tool, False otherwise. - """ - content = message.get("message").get("content").lower() - return "from skills import" in content - - def _is_code_execution(self, message: Message) -> bool: - """ - Check if the message indicates code execution. - - :param message: The message instance to check. - :return: dict with is_code and status keys. - """ - content = message.get("message").get("content").lower() - if "exitcode:" in content: - status = "exitcode: 0" in content - return {"is_code": True, "status": status} - else: - return {"is_code": False, "status": False} - - def _is_terminate(self, message: Message) -> bool: - """ - Check if the message indicates termination. - - :param message: The message instance to check. - :return: True if the message indicates termination, False otherwise. - """ - content = message.get("message").get("content").lower() - return "terminate" in content - - def profile(self, agent_message: Message): - """ - Profile the agent task run and compute metrics. - - :param agent: The agent instance that ran the task. - :param task: The task instance that was run. - """ - meta = MessageMeta(**agent_message.meta) - print(meta.log) - usage = meta.usage - messages = meta.messages - profile = [] - bar = [] - stats = {} - total_code_executed = 0 - success_code_executed = 0 - agents = [] - for message in messages: - agent = message.get("sender") - is_code = self._is_code(message) - is_tool = self._is_tool(message) - is_code_execution = self._is_code_execution(message) - total_code_executed += is_code_execution["is_code"] - success_code_executed += 1 if is_code_execution["status"] else 0 - - row = { - "agent": agent, - "tool_call": is_code, - "code_execution": is_code_execution, - "terminate": self._is_terminate(message), - } - bar_row = { - "agent": agent, - "tool_call": "tool call" if is_tool else "no tool call", - "code_execution": ( - "success" - if is_code_execution["status"] - else "failure" if is_code_execution["is_code"] else "no code" - ), - "message": 1, - } - profile.append(row) - bar.append(bar_row) - agents.append(agent) - code_success_rate = (success_code_executed / total_code_executed if total_code_executed > 0 else 0) * 100 - stats["code_success_rate"] = code_success_rate - stats["total_code_executed"] = total_code_executed - return {"profile": profile, "bar": bar, "stats": stats, "agents": set(agents), "usage": usage} diff --git a/python/packages/autogen-studio/autogenstudio/teammanager.py b/python/packages/autogen-studio/autogenstudio/teammanager.py index 6e9ef170441..9dcca06c8a3 100644 --- a/python/packages/autogen-studio/autogenstudio/teammanager.py +++ b/python/packages/autogen-studio/autogenstudio/teammanager.py @@ -1,50 +1,39 @@ -from typing import AsyncGenerator, Callable, Union, Optional import time -from .database import ComponentFactory, Component -from .datamodel import TeamResult, TaskResult, ComponentConfigInput -from autogen_agentchat.messages import InnerMessage, ChatMessage +from typing import AsyncGenerator, Callable, Optional, Union + +from autogen_agentchat.base import TaskResult +from autogen_agentchat.messages import AgentMessage, ChatMessage from autogen_core.base import CancellationToken +from .database import Component, ComponentFactory +from .datamodel import ComponentConfigInput, TeamResult + class TeamManager: def __init__(self) -> None: self.component_factory = ComponentFactory() - async def _create_team( - self, - team_config: ComponentConfigInput, - input_func: Optional[Callable] = None - ) -> Component: + async def _create_team(self, team_config: ComponentConfigInput, input_func: Optional[Callable] = None) -> Component: """Create team instance with common setup logic""" - return await self.component_factory.load( - team_config, - input_func=input_func - ) + return await self.component_factory.load(team_config, input_func=input_func) def _create_result(self, task_result: TaskResult, start_time: float) -> TeamResult: """Create TeamResult with timing info""" - return TeamResult( - task_result=task_result, - usage="", - duration=time.time() - start_time - ) + return TeamResult(task_result=task_result, usage="", duration=time.time() - start_time) async def run_stream( self, task: str, team_config: ComponentConfigInput, input_func: Optional[Callable] = None, - cancellation_token: Optional[CancellationToken] = None - ) -> AsyncGenerator[Union[InnerMessage, ChatMessage, TaskResult], None]: + cancellation_token: Optional[CancellationToken] = None, + ) -> AsyncGenerator[Union[AgentMessage, ChatMessage, TaskResult], None]: """Stream the team's execution results""" start_time = time.time() try: team = await self._create_team(team_config, input_func) - stream = team.run_stream( - task=task, - cancellation_token=cancellation_token - ) + stream = team.run_stream(task=task, cancellation_token=cancellation_token) async for message in stream: if cancellation_token and cancellation_token.is_cancelled(): @@ -63,15 +52,12 @@ async def run( task: str, team_config: ComponentConfigInput, input_func: Optional[Callable] = None, - cancellation_token: Optional[CancellationToken] = None + cancellation_token: Optional[CancellationToken] = None, ) -> TeamResult: """Original non-streaming run method with optional cancellation""" start_time = time.time() team = await self._create_team(team_config, input_func) - result = await team.run( - task=task, - cancellation_token=cancellation_token - ) + result = await team.run(task=task, cancellation_token=cancellation_token) return self._create_result(result, start_time) diff --git a/python/packages/autogen-studio/autogenstudio/utils/__init__.py b/python/packages/autogen-studio/autogenstudio/utils/__init__.py index 16281fe0b66..e69de29bb2d 100644 --- a/python/packages/autogen-studio/autogenstudio/utils/__init__.py +++ b/python/packages/autogen-studio/autogenstudio/utils/__init__.py @@ -1 +0,0 @@ -from .utils import * diff --git a/python/packages/autogen-studio/autogenstudio/utils/utils.py b/python/packages/autogen-studio/autogenstudio/utils/utils.py index 419a6e4a66d..38955bea5fc 100644 --- a/python/packages/autogen-studio/autogenstudio/utils/utils.py +++ b/python/packages/autogen-studio/autogenstudio/utils/utils.py @@ -10,7 +10,6 @@ from dotenv import load_dotenv from loguru import logger - from ..datamodel import Model from ..version import APP_NAME @@ -153,8 +152,7 @@ def get_modified_files(start_timestamp: float, end_timestamp: float, source_dir: for root, dirs, files in os.walk(source_dir): # Update directories and files to exclude those to be ignored dirs[:] = [d for d in dirs if d not in ignore_files] - files[:] = [f for f in files if f not in ignore_files and os.path.splitext(f)[ - 1] not in ignore_extensions] + files[:] = [f for f in files if f not in ignore_files and os.path.splitext(f)[1] not in ignore_extensions] for file in files: file_path = os.path.join(root, file) @@ -163,9 +161,7 @@ def get_modified_files(start_timestamp: float, end_timestamp: float, source_dir: # Verify if the file was modified within the given timestamp range if start_timestamp <= file_mtime <= end_timestamp: file_relative_path = ( - "files/user" + - file_path.split( - "files/user", 1)[1] if "files/user" in file_path else "" + "files/user" + file_path.split("files/user", 1)[1] if "files/user" in file_path else "" ) file_type = get_file_type(file_path) @@ -253,41 +249,27 @@ def sanitize_model(model: Model): model = model.model_dump() valid_keys = ["model", "base_url", "api_key", "api_type", "api_version"] # only add key if value is not None - sanitized_model = {k: v for k, v in model.items() if ( - v is not None and v != "") and k in valid_keys} + sanitized_model = {k: v for k, v in model.items() if (v is not None and v != "") and k in valid_keys} return sanitized_model -def test_model(model: Model): - """ - Test the model endpoint by sending a simple message to the model and returning the response. - """ +class Version: + def __init__(self, ver_str: str): + try: + # Split into major.minor.patch + self.major, self.minor, self.patch = map(int, ver_str.split(".")) + except (ValueError, AttributeError) as err: + raise ValueError(f"Invalid version format: {ver_str}. Expected: major.minor.patch") from err + + def __str__(self): + return f"{self.major}.{self.minor}.{self.patch}" + + def __eq__(self, other): + if isinstance(other, str): + other = Version(other) + return (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch) - print("Testing model", model) - - -# def summarize_chat_history(task: str, messages: List[Dict[str, str]], client: ModelClient): -# """ -# Summarize the chat history using the model endpoint and returning the response. -# """ -# summarization_system_prompt = f""" -# You are a helpful assistant that is able to review the chat history between a set of agents (userproxy agents, assistants etc) as they try to address a given TASK and provide a summary. Be SUCCINCT but also comprehensive enough to allow others (who cannot see the chat history) understand and recreate the solution. - -# The task requested by the user is: -# === -# {task} -# === -# The summary should focus on extracting the actual solution to the task from the chat history (assuming the task was addressed) such that any other agent reading the summary will understand what the actual solution is. Use a neutral tone and DO NOT directly mention the agents. Instead only focus on the actions that were carried out (e.g. do not say 'assistant agent generated some code visualization code ..' instead say say 'visualization code was generated ..'. The answer should be framed as a response to the user task. E.g. if the task is "What is the height of the Eiffel tower", the summary should be "The height of the Eiffel Tower is ..."). -# """ -# summarization_prompt = [ -# { -# "role": "system", -# "content": summarization_system_prompt, -# }, -# { -# "role": "user", -# "content": f"Summarize the following chat history. {str(messages)}", -# }, -# ] -# response = client.create(messages=summarization_prompt, cache_seed=None) -# return response.choices[0].message.content + def __gt__(self, other): + if isinstance(other, str): + other = Version(other) + return (self.major, self.minor, self.patch) > (other.major, other.minor, other.patch) diff --git a/python/packages/autogen-studio/autogenstudio/version.py b/python/packages/autogen-studio/autogenstudio/version.py index 1c67823dfd1..8534dc25990 100644 --- a/python/packages/autogen-studio/autogenstudio/version.py +++ b/python/packages/autogen-studio/autogenstudio/version.py @@ -1,3 +1,3 @@ -VERSION = "0.4.0.dev35" +VERSION = "0.4.0.dev38" __version__ = VERSION APP_NAME = "autogenstudio" diff --git a/python/packages/autogen-studio/autogenstudio/web/app.py b/python/packages/autogen-studio/autogenstudio/web/app.py index 8d62cf1adff..2e2ad333724 100644 --- a/python/packages/autogen-studio/autogenstudio/web/app.py +++ b/python/packages/autogen-studio/autogenstudio/web/app.py @@ -1,18 +1,19 @@ # api/app.py import os +from contextlib import asynccontextmanager +from typing import AsyncGenerator + # import logging from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles -from contextlib import asynccontextmanager -from typing import AsyncGenerator from loguru import logger -from .routes import sessions, runs, teams, agents, models, tools, ws -from .deps import init_managers, cleanup_managers +from ..version import VERSION from .config import settings +from .deps import cleanup_managers, init_managers from .initialization import AppInitializer -from ..version import VERSION +from .routes import agents, models, runs, sessions, teams, tools, ws # Configure logging # logger = logging.getLogger(__name__) @@ -54,6 +55,7 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: except Exception as e: logger.error(f"Error during shutdown: {str(e)}") + # Create FastAPI application app = FastAPI(lifespan=lifespan, debug=True) @@ -143,6 +145,7 @@ async def get_version(): "data": {"version": VERSION}, } + # Health check endpoint @@ -154,6 +157,7 @@ async def health_check(): "message": "Service is healthy", } + # Mount static file directories app.mount("/api", api) app.mount( @@ -172,7 +176,7 @@ async def internal_error_handler(request, exc): return { "status": False, "message": "Internal server error", - "detail": str(exc) if settings.API_DOCS else "Internal server error" + "detail": str(exc) if settings.API_DOCS else "Internal server error", } diff --git a/python/packages/autogen-studio/autogenstudio/web/deps.py b/python/packages/autogen-studio/autogenstudio/web/deps.py index 1767c004eee..d2e5b6fabb4 100644 --- a/python/packages/autogen-studio/autogenstudio/web/deps.py +++ b/python/packages/autogen-studio/autogenstudio/web/deps.py @@ -1,14 +1,14 @@ # api/deps.py -from typing import Optional -from fastapi import Depends, HTTPException, status import logging from contextlib import contextmanager +from typing import Optional -from ..database import DatabaseManager -from .managers.connection import WebSocketManager +from fastapi import Depends, HTTPException, status + +from ..database import ConfigurationManager, DatabaseManager from ..teammanager import TeamManager from .config import settings -from ..database import ConfigurationManager +from .managers.connection import WebSocketManager logger = logging.getLogger(__name__) @@ -25,17 +25,16 @@ def get_db_context(): """Provide a transactional scope around a series of operations.""" if not _db_manager: raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Database manager not initialized" + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Database manager not initialized" ) try: yield _db_manager except Exception as e: logger.error(f"Database operation failed: {str(e)}") raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Database operation failed" - ) + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Database operation failed" + ) from e + # Dependency providers @@ -44,8 +43,7 @@ async def get_db() -> DatabaseManager: """Dependency provider for database manager""" if not _db_manager: raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Database manager not initialized" + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Database manager not initialized" ) return _db_manager @@ -54,8 +52,7 @@ async def get_websocket_manager() -> WebSocketManager: """Dependency provider for connection manager""" if not _websocket_manager: raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Connection manager not initialized" + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Connection manager not initialized" ) return _websocket_manager @@ -63,12 +60,10 @@ async def get_websocket_manager() -> WebSocketManager: async def get_team_manager() -> TeamManager: """Dependency provider for team manager""" if not _team_manager: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Team manager not initialized" - ) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Team manager not initialized") return _team_manager + # Authentication dependency @@ -83,6 +78,7 @@ async def get_current_user( # Implement your user authentication here return "user_id" # Replace with actual user identification + # Manager initialization and cleanup @@ -94,20 +90,16 @@ async def init_managers(database_uri: str, config_dir: str, app_root: str) -> No try: # Initialize database manager - _db_manager = DatabaseManager( - engine_uri=database_uri, auto_upgrade=settings.UPGRADE_DATABASE, base_dir=app_root) - _db_manager.create_db_and_tables() + _db_manager = DatabaseManager(engine_uri=database_uri, base_dir=app_root) + _db_manager.initialize_database(auto_upgrade=settings.UPGRADE_DATABASE) # init default team config _team_config_manager = ConfigurationManager(db_manager=_db_manager) - import_result = await _team_config_manager.import_directory( - config_dir, settings.DEFAULT_USER_ID, check_exists=True) + await _team_config_manager.import_directory(config_dir, settings.DEFAULT_USER_ID, check_exists=True) # Initialize connection manager - _websocket_manager = WebSocketManager( - db_manager=_db_manager - ) + _websocket_manager = WebSocketManager(db_manager=_db_manager) logger.info("Connection manager initialized") # Initialize team manager @@ -149,6 +141,7 @@ async def cleanup_managers() -> None: logger.info("All managers cleaned up") + # Utility functions for dependency management @@ -157,19 +150,17 @@ def get_manager_status() -> dict: return { "database_manager": _db_manager is not None, "websocket_manager": _websocket_manager is not None, - "team_manager": _team_manager is not None + "team_manager": _team_manager is not None, } + # Combined dependencies async def get_managers(): """Get all managers in one dependency""" - return { - "db": await get_db(), - "connection": await get_websocket_manager(), - "team": await get_team_manager() - } + return {"db": await get_db(), "connection": await get_websocket_manager(), "team": await get_team_manager()} + # Error handling for manager operations @@ -183,19 +174,21 @@ def __init__(self, manager_name: str, operation: str, detail: str): self.detail = detail super().__init__(f"{manager_name} failed during {operation}: {detail}") + # Dependency for requiring specific managers def require_managers(*manager_names: str): """Decorator to require specific managers for a route""" + async def dependency(): status = get_manager_status() - missing = [name for name in manager_names if not status.get( - f"{name}_manager")] + missing = [name for name in manager_names if not status.get(f"{name}_manager")] if missing: raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, - detail=f"Required managers not available: {', '.join(missing)}" + detail=f"Required managers not available: {', '.join(missing)}", ) return True + return Depends(dependency) diff --git a/python/packages/autogen-studio/autogenstudio/web/initialization.py b/python/packages/autogen-studio/autogenstudio/web/initialization.py index e938d222062..00d8c0afa0b 100644 --- a/python/packages/autogen-studio/autogenstudio/web/initialization.py +++ b/python/packages/autogen-studio/autogenstudio/web/initialization.py @@ -2,15 +2,17 @@ import os from pathlib import Path from typing import Dict -from pydantic import BaseModel -from loguru import logger + from dotenv import load_dotenv +from loguru import logger +from pydantic import BaseModel from .config import Settings class _AppPaths(BaseModel): """Internal model representing all application paths""" + app_root: Path static_root: Path user_files: Path @@ -47,9 +49,7 @@ def _get_database_uri(self, app_root: Path) -> str: """Generate database URI based on settings or environment""" if db_uri := os.getenv("AUTOGENSTUDIO_DATABASE_URI"): return db_uri - return self.settings.DATABASE_URI.replace( - "./", str(app_root) + "/" - ) + return self.settings.DATABASE_URI.replace("./", str(app_root) + "/") def _init_paths(self) -> _AppPaths: """Initialize and return AppPaths instance""" @@ -60,14 +60,13 @@ def _init_paths(self) -> _AppPaths: user_files=app_root / "files" / "user", ui_root=self._app_path / "ui", config_dir=app_root / self.settings.CONFIG_DIR, - database_uri=self._get_database_uri(app_root) + database_uri=self._get_database_uri(app_root), ) def _create_directories(self) -> None: """Create all required directories""" self.app_root.mkdir(parents=True, exist_ok=True) - dirs = [self.static_root, self.user_files, - self.ui_root, self.config_dir] + dirs = [self.static_root, self.user_files, self.ui_root, self.config_dir] for path in dirs: path.mkdir(parents=True, exist_ok=True) diff --git a/python/packages/autogen-studio/autogenstudio/web/managers/connection.py b/python/packages/autogen-studio/autogenstudio/web/managers/connection.py index 6db34f06c8d..cc83995be6b 100644 --- a/python/packages/autogen-studio/autogenstudio/web/managers/connection.py +++ b/python/packages/autogen-studio/autogenstudio/web/managers/connection.py @@ -1,16 +1,18 @@ import asyncio -from autogen_agentchat.base._task import TaskResult -from fastapi import WebSocket, WebSocketDisconnect -from typing import Callable, Dict, Optional, Any -from uuid import UUID import logging from datetime import datetime, timezone +from typing import Any, Callable, Dict, Optional, Union +from uuid import UUID + +from autogen_agentchat.base._task import TaskResult +from autogen_agentchat.messages import AgentMessage, ChatMessage, MultiModalMessage, TextMessage +from autogen_core.base import CancellationToken +from autogen_core.components import Image as AGImage +from fastapi import WebSocket, WebSocketDisconnect -from ...datamodel import Run, RunStatus, TeamResult from ...database import DatabaseManager +from ...datamodel import Message, MessageConfig, Run, RunStatus, TeamResult from ...teammanager import TeamManager -from autogen_agentchat.messages import InnerMessage, ChatMessage, TextMessage -from autogen_core.base import CancellationToken logger = logging.getLogger(__name__) @@ -26,12 +28,20 @@ def __init__(self, db_manager: DatabaseManager): self._closed_connections: set[UUID] = set() self._input_responses: Dict[UUID, asyncio.Queue] = {} - self._cancel_message = TeamResult(task_result=TaskResult(messages=[TextMessage( - source="user", content="Run cancelled by user")], stop_reason="cancelled by user"), usage="", duration=0).model_dump() + self._cancel_message = TeamResult( + task_result=TaskResult( + messages=[TextMessage(source="user", content="Run cancelled by user")], stop_reason="cancelled by user" + ), + usage="", + duration=0, + ).model_dump() def _get_stop_message(self, reason: str) -> dict: - return TeamResult(task_result=TaskResult(messages=[TextMessage( - source="user", content=reason)], stop_reason=reason), usage="", duration=0).model_dump() + return TeamResult( + task_result=TaskResult(messages=[TextMessage(source="user", content=reason)], stop_reason=reason), + usage="", + duration=0, + ).model_dump() async def connect(self, websocket: WebSocket, run_id: UUID) -> bool: try: @@ -41,87 +51,118 @@ async def connect(self, websocket: WebSocket, run_id: UUID) -> bool: # Initialize input queue for this connection self._input_responses[run_id] = asyncio.Queue() - run = await self._get_run(run_id) - if run: - run.status = RunStatus.ACTIVE - self.db_manager.upsert(run) - - await self._send_message(run_id, { - "type": "system", - "status": "connected", - "timestamp": datetime.now(timezone.utc).isoformat() - }) + await self._send_message( + run_id, {"type": "system", "status": "connected", "timestamp": datetime.now(timezone.utc).isoformat()} + ) return True except Exception as e: logger.error(f"Connection error for run {run_id}: {e}") return False - async def start_stream( - self, - run_id: UUID, - team_manager: TeamManager, - task: str, - team_config: dict - ) -> None: + async def start_stream(self, run_id: UUID, task: str, team_config: dict) -> None: + """Start streaming task execution with proper run management""" if run_id not in self._connections or run_id in self._closed_connections: raise ValueError(f"No active connection for run {run_id}") + team_manager = TeamManager() cancellation_token = CancellationToken() self._cancellation_tokens[run_id] = cancellation_token + final_result = None try: - # Create input function for this run + # Update run with task and status + run = await self._get_run(run_id) + if run: + run.task = MessageConfig(content=task, source="user").model_dump() + run.status = RunStatus.ACTIVE + self.db_manager.upsert(run) + input_func = self.create_input_func(run_id) async for message in team_manager.run_stream( - task=task, - team_config=team_config, - input_func=input_func, # Pass the input function - cancellation_token=cancellation_token + task=task, team_config=team_config, input_func=input_func, cancellation_token=cancellation_token ): if cancellation_token.is_cancelled() or run_id in self._closed_connections: - logger.info( - f"Stream cancelled or connection closed for run {run_id}") + logger.info(f"Stream cancelled or connection closed for run {run_id}") break formatted_message = self._format_message(message) if formatted_message: await self._send_message(run_id, formatted_message) + # Save message if it's a content message + if isinstance(message, (AgentMessage, ChatMessage)): + await self._save_message(run_id, message) + # Capture final result if it's a TeamResult + elif isinstance(message, TeamResult): + final_result = message.model_dump() + if not cancellation_token.is_cancelled() and run_id not in self._closed_connections: - await self._update_run_status(run_id, RunStatus.COMPLETE) + if final_result: + await self._update_run(run_id, RunStatus.COMPLETE, team_result=final_result) + else: + logger.warning(f"No final result captured for completed run {run_id}") + await self._update_run_status(run_id, RunStatus.COMPLETE) else: - await self._send_message(run_id, { - "type": "completion", - "status": "cancelled", - "data": self._cancel_message, - "timestamp": datetime.now(timezone.utc).isoformat() - }) - await self._update_run_status(run_id, RunStatus.STOPPED) + await self._send_message( + run_id, + { + "type": "completion", + "status": "cancelled", + "data": self._cancel_message, + "timestamp": datetime.now(timezone.utc).isoformat(), + }, + ) + # Update run with cancellation result + await self._update_run(run_id, RunStatus.STOPPED, team_result=self._cancel_message) except Exception as e: logger.error(f"Stream error for run {run_id}: {e}") await self._handle_stream_error(run_id, e) - finally: self._cancellation_tokens.pop(run_id, None) + async def _save_message(self, run_id: UUID, message: Union[AgentMessage, ChatMessage]) -> None: + """Save a message to the database""" + run = await self._get_run(run_id) + if run: + db_message = Message( + session_id=run.session_id, + run_id=run_id, + config=message.model_dump(), + user_id=None, # You might want to pass this from somewhere + ) + self.db_manager.upsert(db_message) + + async def _update_run( + self, run_id: UUID, status: RunStatus, team_result: Optional[dict] = None, error: Optional[str] = None + ) -> None: + """Update run status and result""" + run = await self._get_run(run_id) + if run: + run.status = status + if team_result: + run.team_result = team_result + if error: + run.error_message = error + self.db_manager.upsert(run) + def create_input_func(self, run_id: UUID) -> Callable: """Creates an input function for a specific run""" - async def input_handler(prompt: str = "") -> str: - try: + async def input_handler(prompt: str = "", cancellation_token: Optional[CancellationToken] = None) -> str: + try: # Send input request to client - await self._send_message(run_id, { - "type": "input_request", - "prompt": prompt, - "data": { - "source": "system", - "content": prompt + await self._send_message( + run_id, + { + "type": "input_request", + "prompt": prompt, + "data": {"source": "system", "content": prompt}, + "timestamp": datetime.now(timezone.utc).isoformat(), }, - "timestamp": datetime.now(timezone.utc).isoformat() - }) + ) # Wait for response if run_id in self._input_responses: @@ -141,26 +182,37 @@ async def handle_input_response(self, run_id: UUID, response: str) -> None: if run_id in self._input_responses: await self._input_responses[run_id].put(response) else: - logger.warning( - f"Received input response for inactive run {run_id}") + logger.warning(f"Received input response for inactive run {run_id}") async def stop_run(self, run_id: UUID, reason: str) -> None: - """Stop a running task""" if run_id in self._cancellation_tokens: logger.info(f"Stopping run {run_id}") - # self._cancellation_tokens[run_id].cancel() - # Send final message if connection still exists and not closed - if run_id in self._connections and run_id not in self._closed_connections: - try: - await self._send_message(run_id, { - "type": "completion", - "status": "cancelled", - "data": self._get_stop_message(reason), - "timestamp": datetime.now(timezone.utc).isoformat() - }) - except Exception: - pass + stop_message = self._get_stop_message(reason) + + try: + # Update run record first + await self._update_run(run_id, status=RunStatus.STOPPED, team_result=stop_message) + + # Then handle websocket communication if connection is active + if run_id in self._connections and run_id not in self._closed_connections: + await self._send_message( + run_id, + { + "type": "completion", + "status": "cancelled", + "data": stop_message, + "timestamp": datetime.now(timezone.utc).isoformat(), + }, + ) + + # Finally cancel the token + self._cancellation_tokens[run_id].cancel() + + except Exception as e: + logger.error(f"Error stopping run {run_id}: {e}") + # We might want to force disconnect here if db update failed + # await self.disconnect(run_id) # Optional async def disconnect(self, run_id: UUID) -> None: """Clean up connection and associated resources""" @@ -185,8 +237,7 @@ async def _send_message(self, run_id: UUID, message: dict) -> None: message: Message dictionary to send """ if run_id in self._closed_connections: - logger.warning( - f"Attempted to send message to closed connection for run {run_id}") + logger.warning(f"Attempted to send message to closed connection for run {run_id}") return try: @@ -194,36 +245,36 @@ async def _send_message(self, run_id: UUID, message: dict) -> None: websocket = self._connections[run_id] await websocket.send_json(message) except WebSocketDisconnect: - logger.warning( - f"WebSocket disconnected while sending message for run {run_id}") + logger.warning(f"WebSocket disconnected while sending message for run {run_id}") await self.disconnect(run_id) except Exception as e: - logger.error( - f"Error sending message for run {run_id}: {e}, {message}") + logger.error(f"Error sending message for run {run_id}: {e}, {message}") # Don't try to send error message here to avoid potential recursive loop await self._update_run_status(run_id, RunStatus.ERROR, str(e)) await self.disconnect(run_id) async def _handle_stream_error(self, run_id: UUID, error: Exception) -> None: - """Handle stream errors with connection state awareness - - Args: - run_id: UUID of the run - error: Exception that occurred - """ + """Handle stream errors with proper run updates""" if run_id not in self._closed_connections: - try: - await self._send_message(run_id, { + error_result = TeamResult( + task_result=TaskResult( + messages=[TextMessage(source="system", content=str(error))], stop_reason="error" + ), + usage="", + duration=0, + ).model_dump() + + await self._send_message( + run_id, + { "type": "completion", "status": "error", - "error": str(error), - "timestamp": datetime.now(timezone.utc).isoformat() - }) - except Exception as send_error: - logger.error( - f"Failed to send error message for run {run_id}: {send_error}") + "data": error_result, + "timestamp": datetime.now(timezone.utc).isoformat(), + }, + ) - await self._update_run_status(run_id, RunStatus.ERROR, str(error)) + await self._update_run(run_id, RunStatus.ERROR, team_result=error_result, error=str(error)) def _format_message(self, message: Any) -> Optional[dict]: """Format message for WebSocket transmission @@ -235,11 +286,19 @@ def _format_message(self, message: Any) -> Optional[dict]: Optional[dict]: Formatted message or None if formatting fails """ try: - if isinstance(message, (InnerMessage, ChatMessage)): - return { - "type": "message", - "data": message.model_dump() - } + if isinstance(message, MultiModalMessage): + message_dump = message.model_dump() + message_dump["content"] = [ + message_dump["content"][0], + { + "url": f"data:image/png;base64,{message_dump['content'][1]['data']}", + "alt": "WebSurfer Screenshot", + }, + ] + return {"type": "message", "data": message_dump} + elif isinstance(message, (AgentMessage, ChatMessage)): + return {"type": "message", "data": message.model_dump()} + elif isinstance(message, TeamResult): return { "type": "result", @@ -260,16 +319,10 @@ async def _get_run(self, run_id: UUID) -> Optional[Run]: Returns: Optional[Run]: Run object if found, None otherwise """ - response = self.db_manager.get( - Run, filters={"id": run_id}, return_json=False) + response = self.db_manager.get(Run, filters={"id": run_id}, return_json=False) return response.data[0] if response.status and response.data else None - async def _update_run_status( - self, - run_id: UUID, - status: RunStatus, - error: Optional[str] = None - ) -> None: + async def _update_run_status(self, run_id: UUID, status: RunStatus, error: Optional[str] = None) -> None: """Update run status in database Args: @@ -285,14 +338,27 @@ async def _update_run_status( async def cleanup(self) -> None: """Clean up all active connections and resources when server is shutting down""" - logger.info( - f"Cleaning up {len(self.active_connections)} active connections") + logger.info(f"Cleaning up {len(self.active_connections)} active connections") try: # First cancel all running tasks for run_id in self.active_runs.copy(): if run_id in self._cancellation_tokens: self._cancellation_tokens[run_id].cancel() + run = await self._get_run(run_id) + if run and run.status == RunStatus.ACTIVE: + interrupted_result = TeamResult( + task_result=TaskResult( + messages=[TextMessage(source="system", content="Run interrupted by server shutdown")], + stop_reason="server_shutdown", + ), + usage="", + duration=0, + ).model_dump() + + run.status = RunStatus.STOPPED + run.team_result = interrupted_result + self.db_manager.upsert(run) # Then disconnect all websockets with timeout # 10 second timeout for entire cleanup diff --git a/python/packages/autogen-studio/autogenstudio/web/routes/agents.py b/python/packages/autogen-studio/autogenstudio/web/routes/agents.py index 183dbf2a5be..c0b029f6759 100644 --- a/python/packages/autogen-studio/autogenstudio/web/routes/agents.py +++ b/python/packages/autogen-studio/autogenstudio/web/routes/agents.py @@ -1,181 +1,51 @@ -# api/routes/agents.py -from fastapi import APIRouter, Depends, HTTPException from typing import Dict -from ..deps import get_db + +from fastapi import APIRouter, Depends, HTTPException + +from ...database import DatabaseManager # Add this import from ...datamodel import Agent, Model, Tool +from ..deps import get_db router = APIRouter() @router.get("/") -async def list_agents( - user_id: str, - db=Depends(get_db) -) -> Dict: +async def list_agents(user_id: str, db: DatabaseManager = Depends(get_db)) -> Dict: """List all agents for a user""" response = db.get(Agent, filters={"user_id": user_id}) - return { - "status": True, - "data": response.data - } + return {"status": True, "data": response.data} @router.get("/{agent_id}") -async def get_agent( - agent_id: int, - user_id: str, - db=Depends(get_db) -) -> Dict: +async def get_agent(agent_id: int, user_id: str, db: DatabaseManager = Depends(get_db)) -> Dict: """Get a specific agent""" - response = db.get( - Agent, - filters={"id": agent_id, "user_id": user_id} - ) + response = db.get(Agent, filters={"id": agent_id, "user_id": user_id}) if not response.status or not response.data: raise HTTPException(status_code=404, detail="Agent not found") - return { - "status": True, - "data": response.data[0] - } + return {"status": True, "data": response.data[0]} @router.post("/") -async def create_agent( - agent: Agent, - db=Depends(get_db) -) -> Dict: +async def create_agent(agent: Agent, db: DatabaseManager = Depends(get_db)) -> Dict: """Create a new agent""" response = db.upsert(agent) if not response.status: raise HTTPException(status_code=400, detail=response.message) - return { - "status": True, - "data": response.data - } + return {"status": True, "data": response.data} @router.delete("/{agent_id}") -async def delete_agent( - agent_id: int, - user_id: str, - db=Depends(get_db) -) -> Dict: +async def delete_agent(agent_id: int, user_id: str, db: DatabaseManager = Depends(get_db)) -> Dict: """Delete an agent""" - response = db.delete( - filters={"id": agent_id, "user_id": user_id}, - model_class=Agent - ) - return { - "status": True, - "message": "Agent deleted successfully" - } + db.delete(filters={"id": agent_id, "user_id": user_id}, model_class=Agent) + return {"status": True, "message": "Agent deleted successfully"} + # Agent-Model link endpoints @router.post("/{agent_id}/models/{model_id}") -async def link_agent_model( - agent_id: int, - model_id: int, - db=Depends(get_db) -) -> Dict: +async def link_agent_model(agent_id: int, model_id: int, db: DatabaseManager = Depends(get_db)) -> Dict: """Link a model to an agent""" - response = db.link( - link_type="agent_model", - primary_id=agent_id, - secondary_id=model_id - ) - return { - "status": True, - "message": "Model linked to agent successfully" - } - - -@router.delete("/{agent_id}/models/{model_id}") -async def unlink_agent_model( - agent_id: int, - model_id: int, - db=Depends(get_db) -) -> Dict: - """Unlink a model from an agent""" - response = db.unlink( - link_type="agent_model", - primary_id=agent_id, - secondary_id=model_id - ) - return { - "status": True, - "message": "Model unlinked from agent successfully" - } - - -@router.get("/{agent_id}/models") -async def get_agent_models( - agent_id: int, - db=Depends(get_db) -) -> Dict: - """Get all models linked to an agent""" - response = db.get_linked_entities( - link_type="agent_model", - primary_id=agent_id, - return_json=True - ) - return { - "status": True, - "data": response.data - } - -# Agent-Tool link endpoints - - -@router.post("/{agent_id}/tools/{tool_id}") -async def link_agent_tool( - agent_id: int, - tool_id: int, - db=Depends(get_db) -) -> Dict: - """Link a tool to an agent""" - response = db.link( - link_type="agent_tool", - primary_id=agent_id, - secondary_id=tool_id - ) - return { - "status": True, - "message": "Tool linked to agent successfully" - } - - -@router.delete("/{agent_id}/tools/{tool_id}") -async def unlink_agent_tool( - agent_id: int, - tool_id: int, - db=Depends(get_db) -) -> Dict: - """Unlink a tool from an agent""" - response = db.unlink( - link_type="agent_tool", - primary_id=agent_id, - secondary_id=tool_id - ) - return { - "status": True, - "message": "Tool unlinked from agent successfully" - } - - -@router.get("/{agent_id}/tools") -async def get_agent_tools( - agent_id: int, - db=Depends(get_db) -) -> Dict: - """Get all tools linked to an agent""" - response = db.get_linked_entities( - link_type="agent_tool", - primary_id=agent_id, - return_json=True - ) - return { - "status": True, - "data": response.data - } + db.link(link_type="agent_model", primary_id=agent_id, secondary_id=model_id) + return {"status": True, "message": "Model linked to agent successfully"} diff --git a/python/packages/autogen-studio/autogenstudio/web/routes/gallery.py b/python/packages/autogen-studio/autogenstudio/web/routes/gallery.py new file mode 100644 index 00000000000..0b0451e53ef --- /dev/null +++ b/python/packages/autogen-studio/autogenstudio/web/routes/gallery.py @@ -0,0 +1,62 @@ +# api/routes/gallery.py +from fastapi import APIRouter, Depends, HTTPException + +from ...database import DatabaseManager +from ...datamodel import Gallery, GalleryConfig, Response, Run, Session +from ..deps import get_db + +router = APIRouter() + + +@router.post("/") +async def create_gallery_entry( + gallery_data: GalleryConfig, user_id: str, db: DatabaseManager = Depends(get_db) +) -> Response: + # First validate that user owns all runs + for run in gallery_data.runs: + run_result = db.get(Run, filters={"id": run.id}) + if not run_result.status or not run_result.data: + raise HTTPException(status_code=404, detail=f"Run {run.id} not found") + + # Get associated session to check ownership + session_result = db.get(Session, filters={"id": run_result.data[0].session_id}) + if not session_result.status or not session_result.data or session_result.data[0].user_id != user_id: + raise HTTPException(status_code=403, detail=f"Not authorized to add run {run.id} to gallery") + + # Create gallery entry + gallery = Gallery(user_id=user_id, config=gallery_data) + result = db.upsert(gallery) + return result + + +@router.get("/{gallery_id}") +async def get_gallery_entry(gallery_id: int, user_id: str, db: DatabaseManager = Depends(get_db)) -> Response: + result = db.get(Gallery, filters={"id": gallery_id}) + if not result.status or not result.data: + raise HTTPException(status_code=404, detail="Gallery entry not found") + + gallery = result.data[0] + if gallery.config["visibility"] != "public" and gallery.user_id != user_id: + raise HTTPException(status_code=403, detail="Not authorized to view this gallery entry") + + return result + + +@router.get("/") +async def list_gallery_entries(user_id: str, db: DatabaseManager = Depends(get_db)) -> Response: + result = db.get(Gallery, filters={"user_id": user_id}) + return result + + +@router.delete("/{gallery_id}") +async def delete_gallery_entry(gallery_id: int, user_id: str, db: DatabaseManager = Depends(get_db)) -> Response: + # Check ownership first + result = db.get(Gallery, filters={"id": gallery_id}) + if not result.status or not result.data: + raise HTTPException(status_code=404, detail="Gallery entry not found") + + if result.data[0].user_id != user_id: + raise HTTPException(status_code=403, detail="Not authorized to delete this gallery entry") + + # Delete if authorized + return db.delete(Gallery, filters={"id": gallery_id}) diff --git a/python/packages/autogen-studio/autogenstudio/web/routes/models.py b/python/packages/autogen-studio/autogenstudio/web/routes/models.py index 9b57e625545..f041e52cb93 100644 --- a/python/packages/autogen-studio/autogenstudio/web/routes/models.py +++ b/python/packages/autogen-studio/autogenstudio/web/routes/models.py @@ -1,95 +1,42 @@ # api/routes/models.py -from fastapi import APIRouter, Depends, HTTPException from typing import Dict + +from fastapi import APIRouter, Depends, HTTPException from openai import OpenAIError -from ..deps import get_db + from ...datamodel import Model -from ...utils import test_model +from ..deps import get_db router = APIRouter() @router.get("/") -async def list_models( - user_id: str, - db=Depends(get_db) -) -> Dict: +async def list_models(user_id: str, db=Depends(get_db)) -> Dict: """List all models for a user""" response = db.get(Model, filters={"user_id": user_id}) - return { - "status": True, - "data": response.data - } + return {"status": True, "data": response.data} @router.get("/{model_id}") -async def get_model( - model_id: int, - user_id: str, - db=Depends(get_db) -) -> Dict: +async def get_model(model_id: int, user_id: str, db=Depends(get_db)) -> Dict: """Get a specific model""" - response = db.get( - Model, - filters={"id": model_id, "user_id": user_id} - ) + response = db.get(Model, filters={"id": model_id, "user_id": user_id}) if not response.status or not response.data: raise HTTPException(status_code=404, detail="Model not found") - return { - "status": True, - "data": response.data[0] - } + return {"status": True, "data": response.data[0]} @router.post("/") -async def create_model( - model: Model, - db=Depends(get_db) -) -> Dict: +async def create_model(model: Model, db=Depends(get_db)) -> Dict: """Create a new model""" response = db.upsert(model) if not response.status: raise HTTPException(status_code=400, detail=response.message) - return { - "status": True, - "data": response.data - } + return {"status": True, "data": response.data} @router.delete("/{model_id}") -async def delete_model( - model_id: int, - user_id: str, - db=Depends(get_db) -) -> Dict: +async def delete_model(model_id: int, user_id: str, db=Depends(get_db)) -> Dict: """Delete a model""" - response = db.delete( - filters={"id": model_id, "user_id": user_id}, - model_class=Model - ) - return { - "status": True, - "message": "Model deleted successfully" - } - - -@router.post("/test") -async def test_model_endpoint(model: Model) -> Dict: - """Test a model configuration""" - try: - response = test_model(model) - return { - "status": True, - "message": "Model tested successfully", - "data": response - } - except OpenAIError as e: - raise HTTPException( - status_code=400, - detail=f"OpenAI API error: {str(e)}" - ) - except Exception as e: - raise HTTPException( - status_code=500, - detail=f"Error testing model: {str(e)}" - ) + db.delete(filters={"id": model_id, "user_id": user_id}, model_class=Model) + return {"status": True, "message": "Model deleted successfully"} diff --git a/python/packages/autogen-studio/autogenstudio/web/routes/runs.py b/python/packages/autogen-studio/autogenstudio/web/routes/runs.py index 7fb5e7475ac..9644099de6f 100644 --- a/python/packages/autogen-studio/autogenstudio/web/routes/runs.py +++ b/python/packages/autogen-studio/autogenstudio/web/routes/runs.py @@ -1,14 +1,12 @@ # /api/runs routes -from fastapi import APIRouter, Body, Depends, HTTPException -from uuid import UUID from typing import Dict +from uuid import UUID +from fastapi import APIRouter, Body, Depends, HTTPException from pydantic import BaseModel -from ..deps import get_db, get_websocket_manager, get_team_manager -from ...datamodel import Run, Session, Message, Team, RunStatus, MessageConfig -from ...teammanager import TeamManager -from autogen_core.base import CancellationToken +from ...datamodel import Message, MessageConfig, Run, RunStatus, Session, Team +from ..deps import get_db, get_team_manager, get_websocket_manager router = APIRouter() @@ -23,54 +21,45 @@ async def create_run( request: CreateRunRequest, db=Depends(get_db), ) -> Dict: - """Create a new run""" + """Create a new run with initial state""" session_response = db.get( - Session, - filters={"id": request.session_id, "user_id": request.user_id}, - return_json=False + Session, filters={"id": request.session_id, "user_id": request.user_id}, return_json=False ) if not session_response.status or not session_response.data: raise HTTPException(status_code=404, detail="Session not found") try: - - run = db.upsert(Run(session_id=request.session_id), return_json=False) - return { - "status": run.status, - "data": {"run_id": str(run.data.id)} - } - - # } + # Create run with default state + run = db.upsert( + Run( + session_id=request.session_id, + status=RunStatus.CREATED, + task=None, # Will be set when run starts + team_result=None, + ), + return_json=False, + ) + return {"status": run.status, "data": {"run_id": str(run.data.id)}} except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException(status_code=500, detail=str(e)) from e -@router.post("/{run_id}/start") -async def start_run( - run_id: UUID, - message: Message = Body(...), - ws_manager=Depends(get_websocket_manager), - team_manager=Depends(get_team_manager), - db=Depends(get_db), -) -> Dict: - """Start streaming task execution""" +# We might want to add these endpoints: - if isinstance(message.config, dict): - message.config = MessageConfig(**message.config) - session = db.get(Session, filters={ - "id": message.session_id}, return_json=False) +@router.get("/{run_id}") +async def get_run(run_id: UUID, db=Depends(get_db)) -> Dict: + """Get run details including task and result""" + run = db.get(Run, filters={"id": run_id}, return_json=False) + if not run.status or not run.data: + raise HTTPException(status_code=404, detail="Run not found") - team = db.get( - Team, filters={"id": session.data[0].team_id}, return_json=False) + return {"status": True, "data": run.data[0]} - try: - await ws_manager.start_stream(run_id, team_manager, message.config.content, team.data[0].config) - return { - "status": True, - "message": "Stream started successfully", - "data": {"run_id": str(run_id)} - } - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) +@router.get("/{run_id}/messages") +async def get_run_messages(run_id: UUID, db=Depends(get_db)) -> Dict: + """Get all messages for a run""" + messages = db.get(Message, filters={"run_id": run_id}, order="created_at asc", return_json=False) + + return {"status": True, "data": messages.data} diff --git a/python/packages/autogen-studio/autogenstudio/web/routes/sessions.py b/python/packages/autogen-studio/autogenstudio/web/routes/sessions.py index f74ee628815..4ca31e4f704 100644 --- a/python/packages/autogen-studio/autogenstudio/web/routes/sessions.py +++ b/python/packages/autogen-studio/autogenstudio/web/routes/sessions.py @@ -1,72 +1,45 @@ # api/routes/sessions.py -from fastapi import APIRouter, Depends, HTTPException from typing import Dict + +from fastapi import APIRouter, Depends, HTTPException +from loguru import logger + +from ...datamodel import Message, Run, Session from ..deps import get_db -from ...datamodel import Session, Message router = APIRouter() @router.get("/") -async def list_sessions( - user_id: str, - db=Depends(get_db) -) -> Dict: +async def list_sessions(user_id: str, db=Depends(get_db)) -> Dict: """List all sessions for a user""" response = db.get(Session, filters={"user_id": user_id}) - return { - "status": True, - "data": response.data - } + return {"status": True, "data": response.data} @router.get("/{session_id}") -async def get_session( - session_id: int, - user_id: str, - db=Depends(get_db) -) -> Dict: +async def get_session(session_id: int, user_id: str, db=Depends(get_db)) -> Dict: """Get a specific session""" - response = db.get( - Session, - filters={"id": session_id, "user_id": user_id} - ) + response = db.get(Session, filters={"id": session_id, "user_id": user_id}) if not response.status or not response.data: raise HTTPException(status_code=404, detail="Session not found") - return { - "status": True, - "data": response.data[0] - } + return {"status": True, "data": response.data[0]} @router.post("/") -async def create_session( - session: Session, - db=Depends(get_db) -) -> Dict: +async def create_session(session: Session, db=Depends(get_db)) -> Dict: """Create a new session""" response = db.upsert(session) if not response.status: raise HTTPException(status_code=400, detail=response.message) - return { - "status": True, - "data": response.data - } + return {"status": True, "data": response.data} @router.put("/{session_id}") -async def update_session( - session_id: int, - user_id: str, - session: Session, - db=Depends(get_db) -) -> Dict: +async def update_session(session_id: int, user_id: str, session: Session, db=Depends(get_db)) -> Dict: """Update an existing session""" # First verify the session belongs to user - existing = db.get( - Session, - filters={"id": session_id, "user_id": user_id} - ) + existing = db.get(Session, filters={"id": session_id, "user_id": user_id}) if not existing.status or not existing.data: raise HTTPException(status_code=404, detail="Session not found") @@ -75,40 +48,74 @@ async def update_session( if not response.status: raise HTTPException(status_code=400, detail=response.message) - return { - "status": True, - "data": response.data, - "message": "Session updated successfully" - } + return {"status": True, "data": response.data, "message": "Session updated successfully"} @router.delete("/{session_id}") -async def delete_session( - session_id: int, - user_id: str, - db=Depends(get_db) -) -> Dict: +async def delete_session(session_id: int, user_id: str, db=Depends(get_db)) -> Dict: """Delete a session""" - response = db.delete( - filters={"id": session_id, "user_id": user_id}, - model_class=Session - ) - return { - "status": True, - "message": "Session deleted successfully" - } - - -@router.get("/{session_id}/messages") -async def list_messages( - session_id: int, - user_id: str, - db=Depends(get_db) -) -> Dict: - """List all messages for a session""" - filters = {"session_id": session_id, "user_id": user_id} - response = db.get(Message, filters=filters, order="asc") - return { - "status": True, - "data": response.data - } + db.delete(filters={"id": session_id, "user_id": user_id}, model_class=Session) + return {"status": True, "message": "Session deleted successfully"} + + +@router.get("/{session_id}/runs") +async def list_session_runs(session_id: int, user_id: str, db=Depends(get_db)) -> Dict: + """Get complete session history organized by runs""" + + try: + # 1. Verify session exists and belongs to user + session = db.get(Session, filters={"id": session_id, "user_id": user_id}, return_json=False) + if not session.status: + raise HTTPException(status_code=500, detail="Database error while fetching session") + if not session.data: + raise HTTPException(status_code=404, detail="Session not found or access denied") + + # 2. Get ordered runs for session + runs = db.get(Run, filters={"session_id": session_id}, order="asc", return_json=False) + if not runs.status: + raise HTTPException(status_code=500, detail="Database error while fetching runs") + + # 3. Build response with messages per run + run_data = [] + if runs.data: # It's ok to have no runs + for run in runs.data: + try: + # Get messages for this specific run + messages = db.get(Message, filters={"run_id": run.id}, order="asc", return_json=False) + if not messages.status: + logger.error(f"Failed to fetch messages for run {run.id}") + # Continue processing other runs even if one fails + messages.data = [] + + run_data.append( + { + "id": str(run.id), + "created_at": run.created_at, + "status": run.status, + "task": run.task, + "team_result": run.team_result, + "messages": messages.data or [], + } + ) + except Exception as e: + logger.error(f"Error processing run {run.id}: {str(e)}") + # Include run with error state instead of failing entirely + run_data.append( + { + "id": str(run.id), + "created_at": run.created_at, + "status": "ERROR", + "task": run.task, + "team_result": None, + "messages": [], + "error": f"Failed to process run: {str(e)}", + } + ) + + return {"status": True, "data": {"runs": run_data}} + + except HTTPException: + raise # Re-raise HTTP exceptions + except Exception as e: + logger.error(f"Unexpected error in list_messages: {str(e)}") + raise HTTPException(status_code=500, detail="Internal server error while fetching session data") from e diff --git a/python/packages/autogen-studio/autogenstudio/web/routes/teams.py b/python/packages/autogen-studio/autogenstudio/web/routes/teams.py index 854c195d3c7..50b9273c8a2 100644 --- a/python/packages/autogen-studio/autogenstudio/web/routes/teams.py +++ b/python/packages/autogen-studio/autogenstudio/web/routes/teams.py @@ -1,146 +1,41 @@ # api/routes/teams.py -from fastapi import APIRouter, Depends, HTTPException from typing import Dict -from ..deps import get_db + +from fastapi import APIRouter, Depends, HTTPException + from ...datamodel import Team +from ..deps import get_db router = APIRouter() @router.get("/") -async def list_teams( - user_id: str, - db=Depends(get_db) -) -> Dict: +async def list_teams(user_id: str, db=Depends(get_db)) -> Dict: """List all teams for a user""" response = db.get(Team, filters={"user_id": user_id}) - return { - "status": True, - "data": response.data - } + return {"status": True, "data": response.data} @router.get("/{team_id}") -async def get_team( - team_id: int, - user_id: str, - db=Depends(get_db) -) -> Dict: +async def get_team(team_id: int, user_id: str, db=Depends(get_db)) -> Dict: """Get a specific team""" - response = db.get( - Team, - filters={"id": team_id, "user_id": user_id} - ) + response = db.get(Team, filters={"id": team_id, "user_id": user_id}) if not response.status or not response.data: raise HTTPException(status_code=404, detail="Team not found") - return { - "status": True, - "data": response.data[0] - } + return {"status": True, "data": response.data[0]} @router.post("/") -async def create_team( - team: Team, - db=Depends(get_db) -) -> Dict: +async def create_team(team: Team, db=Depends(get_db)) -> Dict: """Create a new team""" response = db.upsert(team) if not response.status: raise HTTPException(status_code=400, detail=response.message) - return { - "status": True, - "data": response.data - } + return {"status": True, "data": response.data} @router.delete("/{team_id}") -async def delete_team( - team_id: int, - user_id: str, - db=Depends(get_db) -) -> Dict: +async def delete_team(team_id: int, user_id: str, db=Depends(get_db)) -> Dict: """Delete a team""" - response = db.delete( - filters={"id": team_id, "user_id": user_id}, - model_class=Team - ) - return { - "status": True, - "message": "Team deleted successfully" - } - -# Team-Agent link endpoints - - -@router.post("/{team_id}/agents/{agent_id}") -async def link_team_agent( - team_id: int, - agent_id: int, - db=Depends(get_db) -) -> Dict: - """Link an agent to a team""" - response = db.link( - link_type="team_agent", - primary_id=team_id, - secondary_id=agent_id - ) - return { - "status": True, - "message": "Agent linked to team successfully" - } - - -@router.post("/{team_id}/agents/{agent_id}/{sequence_id}") -async def link_team_agent_sequence( - team_id: int, - agent_id: int, - sequence_id: int, - db=Depends(get_db) -) -> Dict: - """Link an agent to a team with sequence""" - response = db.link( - link_type="team_agent", - primary_id=team_id, - secondary_id=agent_id, - sequence_id=sequence_id - ) - return { - "status": True, - "message": "Agent linked to team with sequence successfully" - } - - -@router.delete("/{team_id}/agents/{agent_id}") -async def unlink_team_agent( - team_id: int, - agent_id: int, - db=Depends(get_db) -) -> Dict: - """Unlink an agent from a team""" - response = db.unlink( - link_type="team_agent", - primary_id=team_id, - secondary_id=agent_id - ) - return { - "status": True, - "message": "Agent unlinked from team successfully" - } - - -@router.get("/{team_id}/agents") -async def get_team_agents( - team_id: int, - db=Depends(get_db) -) -> Dict: - """Get all agents linked to a team""" - response = db.get_linked_entities( - link_type="team_agent", - primary_id=team_id, - return_json=True - ) - return { - "status": True, - "data": response.data - } + db.delete(filters={"id": team_id, "user_id": user_id}, model_class=Team) + return {"status": True, "message": "Team deleted successfully"} diff --git a/python/packages/autogen-studio/autogenstudio/web/routes/tools.py b/python/packages/autogen-studio/autogenstudio/web/routes/tools.py index d73b626038a..da2ae7733b2 100644 --- a/python/packages/autogen-studio/autogenstudio/web/routes/tools.py +++ b/python/packages/autogen-studio/autogenstudio/web/routes/tools.py @@ -1,103 +1,41 @@ # api/routes/tools.py -from fastapi import APIRouter, Depends, HTTPException from typing import Dict -from ..deps import get_db + +from fastapi import APIRouter, Depends, HTTPException + from ...datamodel import Tool +from ..deps import get_db router = APIRouter() @router.get("/") -async def list_tools( - user_id: str, - db=Depends(get_db) -) -> Dict: +async def list_tools(user_id: str, db=Depends(get_db)) -> Dict: """List all tools for a user""" response = db.get(Tool, filters={"user_id": user_id}) - return { - "status": True, - "data": response.data - } + return {"status": True, "data": response.data} @router.get("/{tool_id}") -async def get_tool( - tool_id: int, - user_id: str, - db=Depends(get_db) -) -> Dict: +async def get_tool(tool_id: int, user_id: str, db=Depends(get_db)) -> Dict: """Get a specific tool""" - response = db.get( - Tool, - filters={"id": tool_id, "user_id": user_id} - ) + response = db.get(Tool, filters={"id": tool_id, "user_id": user_id}) if not response.status or not response.data: raise HTTPException(status_code=404, detail="Tool not found") - return { - "status": True, - "data": response.data[0] - } + return {"status": True, "data": response.data[0]} @router.post("/") -async def create_tool( - tool: Tool, - db=Depends(get_db) -) -> Dict: +async def create_tool(tool: Tool, db=Depends(get_db)) -> Dict: """Create a new tool""" response = db.upsert(tool) if not response.status: raise HTTPException(status_code=400, detail=response.message) - return { - "status": True, - "data": response.data - } + return {"status": True, "data": response.data} @router.delete("/{tool_id}") -async def delete_tool( - tool_id: int, - user_id: str, - db=Depends(get_db) -) -> Dict: +async def delete_tool(tool_id: int, user_id: str, db=Depends(get_db)) -> Dict: """Delete a tool""" - response = db.delete( - filters={"id": tool_id, "user_id": user_id}, - model_class=Tool - ) - return { - "status": True, - "message": "Tool deleted successfully" - } - - -@router.post("/{tool_id}/test") -async def test_tool( - tool_id: int, - user_id: str, - db=Depends(get_db) -) -> Dict: - """Test a tool configuration""" - # Get tool - tool_response = db.get( - Tool, - filters={"id": tool_id, "user_id": user_id} - ) - if not tool_response.status or not tool_response.data: - raise HTTPException(status_code=404, detail="Tool not found") - - tool = tool_response.data[0] - - try: - # Implement tool testing logic here - # This would depend on the tool type and configuration - return { - "status": True, - "message": "Tool tested successfully", - "data": {"tool_id": tool_id} - } - except Exception as e: - raise HTTPException( - status_code=500, - detail=f"Error testing tool: {str(e)}" - ) + db.delete(filters={"id": tool_id, "user_id": user_id}, model_class=Tool) + return {"status": True, "message": "Tool deleted successfully"} diff --git a/python/packages/autogen-studio/autogenstudio/web/routes/ws.py b/python/packages/autogen-studio/autogenstudio/web/routes/ws.py index 75152036fc2..c7c45175a31 100644 --- a/python/packages/autogen-studio/autogenstudio/web/routes/ws.py +++ b/python/packages/autogen-studio/autogenstudio/web/routes/ws.py @@ -1,17 +1,17 @@ # api/ws.py -from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Depends, HTTPException -from typing import Dict -from uuid import UUID -import logging +import asyncio import json from datetime import datetime +from uuid import UUID + +from fastapi import APIRouter, Depends, WebSocket, WebSocketDisconnect +from loguru import logger -from ..deps import get_websocket_manager, get_db, get_team_manager from ...datamodel import Run, RunStatus +from ..deps import get_db, get_websocket_manager from ..managers import WebSocketManager router = APIRouter() -logger = logging.getLogger(__name__) @router.websocket("/runs/{run_id}") @@ -20,12 +20,12 @@ async def run_websocket( run_id: UUID, ws_manager: WebSocketManager = Depends(get_websocket_manager), db=Depends(get_db), - team_manager=Depends(get_team_manager) ): """WebSocket endpoint for run communication""" # Verify run exists and is in valid state run_response = db.get(Run, filters={"id": run_id}, return_json=False) if not run_response.status or not run_response.data: + logger.warning(f"Run not found: {run_id}") await websocket.close(code=4004, reason="Run not found") return @@ -48,18 +48,32 @@ async def run_websocket( raw_message = await websocket.receive_text() message = json.loads(raw_message) - if message.get("type") == "stop": - print(f"Received stop request for run {run_id}") - reason = message.get( - "reason") or "User requested stop/cancellation" + if message.get("type") == "start": + # Handle start message + logger.info(f"Received start request for run {run_id}") + task = message.get("task") + team_config = message.get("team_config") + if task and team_config: + # await ws_manager.start_stream(run_id, task, team_config) + asyncio.create_task(ws_manager.start_stream(run_id, task, team_config)) + else: + logger.warning(f"Invalid start message format for run {run_id}") + await websocket.send_json( + { + "type": "error", + "error": "Invalid start message format", + "timestamp": datetime.utcnow().isoformat(), + } + ) + + elif message.get("type") == "stop": + logger.info(f"Received stop request for run {run_id}") + reason = message.get("reason") or "User requested stop/cancellation" await ws_manager.stop_run(run_id, reason=reason) break elif message.get("type") == "ping": - await websocket.send_json({ - "type": "pong", - "timestamp": datetime.utcnow().isoformat() - }) + await websocket.send_json({"type": "pong", "timestamp": datetime.utcnow().isoformat()}) elif message.get("type") == "input_response": # Handle input response from client @@ -67,16 +81,13 @@ async def run_websocket( if response is not None: await ws_manager.handle_input_response(run_id, response) else: - logger.warning( - f"Invalid input response format for run {run_id}") + logger.warning(f"Invalid input response format for run {run_id}") except json.JSONDecodeError: logger.warning(f"Invalid JSON received: {raw_message}") - await websocket.send_json({ - "type": "error", - "error": "Invalid message format", - "timestamp": datetime.utcnow().isoformat() - }) + await websocket.send_json( + {"type": "error", "error": "Invalid message format", "timestamp": datetime.utcnow().isoformat()} + ) except WebSocketDisconnect: logger.info(f"WebSocket disconnected for run {run_id}") diff --git a/python/packages/autogen-studio/frontend/package.json b/python/packages/autogen-studio/frontend/package.json index f205a7a1e44..822c0696531 100644 --- a/python/packages/autogen-studio/frontend/package.json +++ b/python/packages/autogen-studio/frontend/package.json @@ -17,8 +17,6 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@ant-design/charts": "^2.2.3", - "@ant-design/plots": "^2.2.2", "@dagrejs/dagre": "^1.1.4", "@headlessui/react": "^2.2.0", "@heroicons/react": "^2.0.18", @@ -38,7 +36,7 @@ "gatsby-source-filesystem": "^5.14.0", "gatsby-transformer-sharp": "^5.14.0", "install": "^0.13.0", - "lucide-react": "^0.456.0", + "lucide-react": "^0.460.0", "postcss": "^8.4.49", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/python/packages/autogen-studio/frontend/src/components/types/datamodel.ts b/python/packages/autogen-studio/frontend/src/components/types/datamodel.ts index 4cb20e37d79..e408a1d8456 100644 --- a/python/packages/autogen-studio/frontend/src/components/types/datamodel.ts +++ b/python/packages/autogen-studio/frontend/src/components/types/datamodel.ts @@ -6,6 +6,7 @@ export interface RequestUsage { export interface ImageContent { url: string; alt?: string; + data?: string; } export interface FunctionCall { @@ -95,38 +96,25 @@ export interface BaseConfig { version?: string; } -// WebSocket message types -export type ThreadStatus = - | "streaming" - | "complete" - | "error" - | "cancelled" - | "awaiting_input" - | "timeout"; - export interface WebSocketMessage { - type: "message" | "result" | "completion" | "input_request"; - data?: { - source?: string; - models_usage?: RequestUsage | null; - content?: string; - task_result?: TaskResult; - }; - status?: ThreadStatus; + type: "message" | "result" | "completion" | "input_request" | "error"; + data?: AgentMessageConfig | TaskResult; + status?: RunStatus; error?: string; timestamp?: string; } export interface TaskResult { messages: AgentMessageConfig[]; - usage: string; - duration: number; stop_reason?: string; } export type ModelTypes = "OpenAIChatCompletionClient"; -export type AgentTypes = "AssistantAgent" | "CodingAssistantAgent"; +export type AgentTypes = + | "AssistantAgent" + | "CodingAssistantAgent" + | "MultimodalWebSurfer"; export type TeamTypes = "RoundRobinGroupChat" | "SelectorGroupChat"; @@ -187,3 +175,28 @@ export interface TeamConfig extends BaseConfig { export interface Team extends DBModel { config: TeamConfig; } + +export interface TeamResult { + task_result: TaskResult; + usage: string; + duration: number; +} + +export interface Run { + id: string; + created_at: string; + status: RunStatus; + task: AgentMessageConfig; + team_result: TeamResult | null; + messages: Message[]; // Change to Message[] + error_message?: string; +} + +export type RunStatus = + | "created" + | "active" // covers 'streaming' + | "awaiting_input" + | "timeout" + | "complete" + | "error" + | "stopped"; diff --git a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/agentflow.tsx b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/agentflow.tsx index 142af966f78..c076223ed47 100644 --- a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/agentflow.tsx +++ b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/agentflow.tsx @@ -10,7 +10,6 @@ import { Node, Edge, Background, - Controls, NodeTypes, useReactFlow, ReactFlowProvider, @@ -24,16 +23,16 @@ import { AgentMessageConfig, AgentConfig, TeamConfig, + Run, } from "../../../../types/datamodel"; -import { ThreadState } from "../types"; -import { CustomEdge } from "./edge"; +import { CustomEdge, CustomEdgeData } from "./edge"; import { useConfigStore } from "../../../../../hooks/store"; import { AgentFlowToolbar } from "./toolbar"; +import { EdgeMessageModal } from "./edgemessagemodal"; interface AgentFlowProps { teamConfig: TeamConfig; - messages: AgentMessageConfig[]; - threadState: ThreadState; + run: Run; } interface MessageSequence { @@ -51,40 +50,37 @@ interface BidirectionalPattern { const NODE_DIMENSIONS = { default: { width: 170, height: 100 }, - end: { width: 120, height: 80 }, + end: { width: 170, height: 80 }, + task: { width: 170, height: 100 }, }; const getLayoutedElements = ( nodes: Node[], - edges: Edge[], + edges: CustomEdge[], direction: "TB" | "LR" ) => { const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({})); - // Updated graph settings g.setGraph({ rankdir: direction, - nodesep: 80, - ranksep: 120, - ranker: "tight-tree", + nodesep: 110, + ranksep: 100, + ranker: "network-simplex", marginx: 30, marginy: 30, }); - // Add nodes (unchanged) nodes.forEach((node) => { const dimensions = node.data.type === "end" ? NODE_DIMENSIONS.end : NODE_DIMENSIONS.default; g.setNode(node.id, { ...node, ...dimensions }); }); - // Create a map to track bidirectional edges const bidirectionalPairs = new Map< string, { source: string; target: string }[] >(); - // First pass - identify bidirectional pairs edges.forEach((edge) => { const forwardKey = `${edge.source}->${edge.target}`; const reverseKey = `${edge.target}->${edge.source}`; @@ -99,33 +95,19 @@ const getLayoutedElements = ( }); }); - // Second pass - add edges with weights bidirectionalPairs.forEach((pairs, pairKey) => { if (pairs.length === 2) { - // Bidirectional edge const [first, second] = pairs; - g.setEdge(first.source, first.target, { - weight: 2, - minlen: 1, - }); - g.setEdge(second.source, second.target, { - weight: 1, - minlen: 1, - }); + g.setEdge(first.source, first.target, { weight: 2, minlen: 1 }); + g.setEdge(second.source, second.target, { weight: 1, minlen: 1 }); } else { - // Regular edge const edge = pairs[0]; - g.setEdge(edge.source, edge.target, { - weight: 1, - minlen: 1, - }); + g.setEdge(edge.source, edge.target, { weight: 1, minlen: 1 }); } }); - // Run layout Dagre.layout(g); - // Position nodes const positionedNodes = nodes.map((node) => { const { x, y } = g.node(node.id); const dimensions = @@ -139,7 +121,6 @@ const getLayoutedElements = ( }; }); - // Create a map of node positions for edge calculations const nodePositions = new Map( positionedNodes.map((node) => [ node.id, @@ -150,7 +131,6 @@ const getLayoutedElements = ( ]) ); - // Process edges with positions const positionedEdges = edges.map((edge) => { const sourcePos = nodePositions.get(edge.source)!; const targetPos = nodePositions.get(edge.target)!; @@ -164,10 +144,7 @@ const getLayoutedElements = ( }; }); - return { - nodes: positionedNodes, - edges: positionedEdges, - }; + return { nodes: positionedNodes, edges: positionedEdges }; }; const createNode = ( @@ -175,11 +152,11 @@ const createNode = ( type: "user" | "agent" | "end", agentConfig?: AgentConfig, isActive: boolean = false, - threadState?: ThreadState + run?: Run ): Node => { - const isStreamingOrWaiting = - threadState?.status === "streaming" || - threadState?.status === "awaiting_input"; + const isProcessing = + run?.status === "active" || run?.status === "awaiting_input"; + if (type === "user") { return { id, @@ -193,7 +170,7 @@ const createNode = ( isActive, status: "", reason: "", - draggable: !isStreamingOrWaiting, + draggable: !isProcessing, }, }; } @@ -206,8 +183,8 @@ const createNode = ( data: { type: "end", label: "End", - status: threadState?.status, - reason: threadState?.reason || "", + status: run?.status, + reason: run?.error_message || "", agentType: "", description: "", isActive: false, @@ -216,6 +193,23 @@ const createNode = ( }; } + // if (type === "task") { + // return { + // id, + // type: "agentNode", + // position: { x: 0, y: 0 }, + // data: { + // type: "task", + // label: "Task", + // description: run?.task.content || "", + // isActive: false, + // status: null, + // reason: null, + // draggable: false, + // }, + // }; + // } + return { id, type: "agentNode", @@ -228,7 +222,7 @@ const createNode = ( isActive, status: "", reason: "", - draggable: !isStreamingOrWaiting, + draggable: !isProcessing, }, }; }; @@ -241,23 +235,28 @@ const edgeTypes = { custom: CustomEdge, }; -const AgentFlow: React.FC = ({ - teamConfig, - messages, - threadState, -}) => { +const AgentFlow: React.FC = ({ teamConfig, run }) => { const { fitView } = useReactFlow(); const [nodes, setNodes] = useState([]); - const [edges, setEdges] = useState([]); + const [edges, setEdges] = useState([]); const [shouldRefit, setShouldRefit] = useState(false); const [isFullscreen, setIsFullscreen] = useState(false); - // Get settings from store - const { agentFlow: settings, setAgentFlowSettings } = useConfigStore(); + const { agentFlow: settings } = useConfigStore(); + const [modalOpen, setModalOpen] = useState(false); + const [selectedEdge, setSelectedEdge] = useState(null); + + const handleEdgeClick = useCallback((edge: CustomEdge) => { + if (!edge.data?.messages) return; // Early return if no data/messages + + setSelectedEdge(edge); + setModalOpen(true); + }, []); const onNodesChange = useCallback((changes: NodeChange[]) => { setNodes((nds) => applyNodeChanges(changes, nds)); }, []); + const flowWrapper = useRef(null); useEffect(() => { @@ -265,36 +264,38 @@ const AgentFlow: React.FC = ({ const timeoutId = setTimeout(() => { fitView({ padding: 0.2, duration: 200 }); setShouldRefit(false); - }, 100); // Increased delay slightly + }, 100); return () => clearTimeout(timeoutId); } }, [shouldRefit, fitView]); - // Process messages into nodes and edges const processMessages = useCallback( (messages: AgentMessageConfig[]) => { - if (messages.length === 0) return { nodes: [], edges: [] }; + if (!run.task) return { nodes: [], edges: [] }; const nodeMap = new Map(); const transitionCounts = new Map(); const bidirectionalPatterns = new Map(); - // Process first message source - const firstAgentConfig = teamConfig.participants.find( - (p) => p.name === messages[0].source - ); - nodeMap.set( - messages[0].source, - createNode( + // Add first message node if it exists + if (messages.length > 0) { + const firstAgentConfig = teamConfig.participants.find( + (p) => p.name === messages[0].source + ); + nodeMap.set( messages[0].source, - messages[0].source === "user" ? "user" : "agent", - firstAgentConfig, - false - ) - ); + createNode( + messages[0].source, + messages[0].source === "user" ? "user" : "agent", + firstAgentConfig, + false, + run + ) + ); + } - // Group messages by transitions + // Process message transitions for (let i = 0; i < messages.length - 1; i++) { const currentMsg = messages[i]; const nextMsg = messages[i + 1]; @@ -319,7 +320,6 @@ const AgentFlow: React.FC = ({ transition.messages.push(currentMsg); } - // Ensure all nodes are in the nodeMap if (!nodeMap.has(nextMsg.source)) { const agentConfig = teamConfig.participants.find( (p) => p.name === nextMsg.source @@ -330,13 +330,14 @@ const AgentFlow: React.FC = ({ nextMsg.source, nextMsg.source === "user" ? "user" : "agent", agentConfig, - false + false, + run ) ); } } - // Identify bidirectional patterns + // Process bidirectional patterns transitionCounts.forEach((transition, key) => { const [source, target] = key.split("->"); const reverseKey = `${target}->${source}`; @@ -352,10 +353,9 @@ const AgentFlow: React.FC = ({ }); // Create edges with bidirectional routing - const newEdges: Edge[] = []; + const newEdges: CustomEdge[] = []; const processedKeys = new Set(); - // Helper function to create edge label based on settings const createEdgeLabel = (transition: MessageSequence) => { if (!settings.showLabels) return ""; if (transition.totalTokens > 0) { @@ -374,7 +374,6 @@ const AgentFlow: React.FC = ({ const bidirectionalPattern = bidirectionalPatterns.get(patternKey); if (bidirectionalPattern) { - // Create paired edges for bidirectional pattern const forwardKey = `${source}->${target}`; const reverseKey = `${target}->${source}`; @@ -386,7 +385,7 @@ const AgentFlow: React.FC = ({ isSecondary: boolean, edgeId: string, pairedEdgeId: string - ) => ({ + ): CustomEdge => ({ id: edgeId, source: transition.source, target: transition.target, @@ -421,7 +420,6 @@ const AgentFlow: React.FC = ({ processedKeys.add(forwardKey); processedKeys.add(reverseKey); } else { - // Handle regular edges (including self-loops) newEdges.push({ id: `${transition.source}-${transition.target}-${key}`, source: transition.source, @@ -432,7 +430,7 @@ const AgentFlow: React.FC = ({ messages: settings.showMessages ? transition.messages : [], }, animated: - threadState?.status === "streaming" && + run.status === "active" && key === Array.from(transitionCounts.keys()).pop(), style: { stroke: "#2563eb", @@ -442,25 +440,22 @@ const AgentFlow: React.FC = ({ } }); - // Handle end node logic - if (threadState && messages.length > 0) { + // Add end node if run is complete/error/stopped + if (run && messages.length > 0) { const lastMessage = messages[messages.length - 1]; - if (["complete", "error", "cancelled"].includes(threadState.status)) { - nodeMap.set( - "end", - createNode("end", "end", undefined, false, threadState) - ); + if (["complete", "error", "stopped"].includes(run.status)) { + nodeMap.set("end", createNode("end", "end", undefined, false, run)); - const edgeColor = - { - complete: "#2563eb", - cancelled: "red", - error: "red", - streaming: "#2563eb", - awaiting_input: "#2563eb", - timeout: "red", - }[threadState.status] || "#2563eb"; + const edgeColor = { + complete: "#2563eb", + stopped: "red", + error: "red", + active: "#2563eb", + awaiting_input: "#2563eb", + timeout: "red", + created: "#2563eb", + }[run.status]; newEdges.push({ id: `${lastMessage.source}-end`, @@ -481,12 +476,9 @@ const AgentFlow: React.FC = ({ } } - return { - nodes: Array.from(nodeMap.values()), - edges: newEdges, - }; + return { nodes: Array.from(nodeMap.values()), edges: newEdges }; }, - [teamConfig.participants, threadState, settings] + [teamConfig.participants, run, settings] ); const handleToggleFullscreen = useCallback(() => { @@ -508,7 +500,9 @@ const AgentFlow: React.FC = ({ }, [isFullscreen, handleToggleFullscreen]); useEffect(() => { - const { nodes: newNodes, edges: newEdges } = processMessages(messages); + const { nodes: newNodes, edges: newEdges } = processMessages( + run.messages.map((m) => m.config) + ); const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements( newNodes, newEdges, @@ -518,17 +512,22 @@ const AgentFlow: React.FC = ({ setNodes(layoutedNodes); setEdges(layoutedEdges); - if (messages.length > 0) { + if (run.messages.length > 0) { setTimeout(() => { fitView({ padding: 0.2, duration: 200 }); }, 50); } - }, [messages, processMessages, settings.direction, threadState, fitView]); + }, [run.messages, processMessages, settings.direction, run.status, fitView]); - // Define common ReactFlow props const reactFlowProps = { nodes, - edges, + edges: edges.map((edge) => ({ + ...edge, + data: { + ...edge.data, + onClick: () => handleEdgeClick(edge), + }, + })), nodeTypes, edgeTypes, defaultViewport: { x: 0, y: 0, zoom: 1 }, @@ -538,7 +537,6 @@ const AgentFlow: React.FC = ({ proOptions: { hideAttribution: true }, }; - // Define common toolbar props const toolbarProps = useMemo( () => ({ isFullscreen, @@ -547,16 +545,16 @@ const AgentFlow: React.FC = ({ }), [isFullscreen, handleToggleFullscreen, fitView] ); + return (
- {/* Backdrop when fullscreen */} {isFullscreen && (
= ({ {settings.showGrid && } - {/* */}
+ { + setModalOpen(false); + setSelectedEdge(null); + }} + edge={selectedEdge} + />
); }; diff --git a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/agentnode.tsx b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/agentnode.tsx index 5f9215a38ca..626a31499db 100644 --- a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/agentnode.tsx +++ b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/agentnode.tsx @@ -8,7 +8,7 @@ import { Bot, Flag, } from "lucide-react"; -import { ThreadStatus } from "../../../../types/datamodel"; +import { RunStatus } from "../../../../types/datamodel"; export type NodeType = "agent" | "user" | "end"; @@ -18,7 +18,7 @@ export interface AgentNodeData { agentType?: string; description?: string; isActive?: boolean; - status?: ThreadStatus | null; + status?: RunStatus | null; reason?: string | null; draggable: boolean; } @@ -54,7 +54,7 @@ function AgentNode({ data, isConnectable }: AgentNodeProps) { return ; case "error": return ; - case "cancelled": + case "stopped": return ; default: return null; @@ -66,7 +66,7 @@ function AgentNode({ data, isConnectable }: AgentNodeProps) { if (data.type === "end") { return { - wrapper: `relative min-w-[120px] shadow rounded-lg overflow-hidden ${activeStyles}`, + wrapper: `relative min-w-[180px] shadow rounded-lg overflow-hidden ${activeStyles}`, border: data.status === "complete" ? "var(--accent)" @@ -77,7 +77,7 @@ function AgentNode({ data, isConnectable }: AgentNodeProps) { } return { - wrapper: `min-w-[150px] rounded-lg shadow overflow-hidden ${activeStyles}`, + wrapper: `min-w-[180px] rounded-lg shadow overflow-hidden ${activeStyles}`, border: undefined, }; }; diff --git a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/edge.tsx b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/edge.tsx index b569110e9be..17446552b13 100644 --- a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/edge.tsx +++ b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/edge.tsx @@ -1,38 +1,23 @@ import React from "react"; -import { Tooltip } from "antd"; import { AgentMessageConfig } from "../../../../types/datamodel"; import { + Edge, EdgeLabelRenderer, type EdgeProps, getSmoothStepPath, } from "@xyflow/react"; -import { RenderMessage } from "../rendermessage"; -interface EdgeTooltipContentProps { - messages: AgentMessageConfig[]; -} +const EDGE_OFFSET = 140; -interface CustomEdgeData { +export interface CustomEdgeData extends Record { label?: string; messages: AgentMessageConfig[]; routingType?: "primary" | "secondary"; bidirectionalPair?: string; + onClick?: () => void; } -const EdgeTooltipContent: React.FC = ({ - messages, -}) => { - return ( -
-
{messages.length} messages
-
- {messages.map((message, index) => ( - - ))} -
-
- ); -}; +export type CustomEdge = Edge; interface CustomEdgeProps extends Omit { data: CustomEdgeData; @@ -63,27 +48,24 @@ export const CustomEdge: React.FC = ({ let labelX = 0; let labelY = 0; - if (isSelfLoop) { - const rightOffset = 120; - const verticalOffset = sourceY - targetY; - const verticalPadding = 6; - const radius = 8; + if (data.routingType === "secondary" || isSelfLoop) { + // Calculate the midpoint and offset + const midY = (sourceY + targetY) / 2; + const offset = EDGE_OFFSET; + // Create path that goes out from output, right, up, left, into input edgePath = ` - M ${sourceX} ${targetY - verticalPadding} - L ${sourceX + rightOffset - radius} ${targetY - verticalPadding} - Q ${sourceX + rightOffset} ${targetY - verticalPadding} ${ - sourceX + rightOffset - } ${targetY - verticalPadding + radius} - L ${sourceX + rightOffset} ${sourceY + verticalPadding - radius} - Q ${sourceX + rightOffset} ${sourceY + verticalPadding} ${ - sourceX + rightOffset - radius - } ${sourceY + verticalPadding} - L ${sourceX} ${sourceY + verticalPadding} + M ${sourceX},${sourceY} + L ${sourceX},${sourceY + 10} + L ${sourceX + offset},${sourceY + 10} + L ${sourceX + offset},${targetY - 10} + L ${targetX},${targetY - 10} + L ${targetX},${targetY} `; - labelX = sourceX + rightOffset + 10; - labelY = targetY + verticalOffset / 2; + // Set label position + labelX = sourceX + offset; + labelY = midY; } else { [edgePath, labelX, labelY] = getSmoothStepPath({ sourceX, @@ -98,7 +80,7 @@ export const CustomEdge: React.FC = ({ if (!data.routingType || isSelfLoop) return { x, y }; // Make vertical separation more pronounced - const verticalOffset = data.routingType === "secondary" ? -35 : 35; // Increased from 20 to 35 + const verticalOffset = data.routingType === "secondary" ? -35 : 35; const horizontalOffset = data.routingType === "secondary" ? -25 : 25; // Calculate edge angle to determine if it's more horizontal or vertical @@ -109,7 +91,7 @@ export const CustomEdge: React.FC = ({ // Always apply some vertical offset const basePosition = { x: isMoreHorizontal ? x : x + horizontalOffset, - y: y + (data.routingType === "secondary" ? -35 : 35), // Always apply vertical offset + y: y + (data.routingType === "secondary" ? -20 : 20), }; return basePosition; @@ -126,6 +108,7 @@ export const CustomEdge: React.FC = ({ style={{ ...style, strokeWidth: finalStrokeWidth, + stroke: data.routingType === "secondary" ? "#0891b2" : style.stroke, }} markerEnd={markerEnd} /> @@ -136,29 +119,23 @@ export const CustomEdge: React.FC = ({ position: "absolute", transform: `translate(-50%, -50%) translate(${labelPosition.x}px,${labelPosition.y}px)`, pointerEvents: "all", - // Add a slight transition for smooth updates - transition: "transform 0.2s ease-in-out", + transition: "all 0.2s ease-in-out", }} + onClick={data.onClick} > - 0 ? ( - - ) : ( - data?.label - ) - } - overlayStyle={{ maxWidth: "none" }} +
-
- {data.label} -
- + {messageCount > 0 && ( + ({messageCount}) + )} + {data.label} +
)} diff --git a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/edgemessagemodal.tsx b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/edgemessagemodal.tsx new file mode 100644 index 00000000000..0c5d2443f27 --- /dev/null +++ b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/agentflow/edgemessagemodal.tsx @@ -0,0 +1,103 @@ +// edgemessagemodal.tsx +import React, { useState, useMemo } from "react"; +import { Modal, Input } from "antd"; +import { RenderMessage } from "../rendermessage"; +import { CustomEdge } from "./edge"; + +const { Search } = Input; + +interface EdgeMessageModalProps { + open: boolean; + onClose: () => void; + edge: CustomEdge | null; +} + +export const EdgeMessageModal: React.FC = ({ + open, + onClose, + edge, +}) => { + const [searchTerm, setSearchTerm] = useState(""); + + const totalTokens = useMemo(() => { + if (!edge?.data?.messages) return 0; + return edge.data.messages.reduce((acc, msg) => { + const promptTokens = msg.models_usage?.prompt_tokens || 0; + const completionTokens = msg.models_usage?.completion_tokens || 0; + return acc + promptTokens + completionTokens; + }, 0); + }, [edge?.data?.messages]); + + const filteredMessages = useMemo(() => { + if (!edge?.data?.messages) return []; + if (!searchTerm) return edge.data.messages; + + return edge.data.messages.filter( + (msg) => + typeof msg.content === "string" && + msg.content.toLowerCase().includes(searchTerm.toLowerCase()) + ); + }, [edge?.data?.messages, searchTerm]); + + if (!edge) return null; + + return ( + +
+ {edge.source} → {edge.target} +
+
+ {edge.data && ( + + {edge.data.messages.length} message + {`${edge.data.messages.length > 1 ? "s" : ""}`} + + )} + {totalTokens.toLocaleString()} tokens +
+ {edge.data && edge.data.messages.length > 0 && ( +
+ {" "} + The above represents the number of times the {`${edge.target}`}{" "} + node sent a message{" "} + after{" "} + the {`${edge.source}`} node.{" "} +
+ )} + + } + open={open} + onCancel={onClose} + width={800} + footer={null} + > +
+ setSearchTerm(e.target.value)} + allowClear + className="sticky top-0 z-10" + /> + +
+ {filteredMessages.map((msg, idx) => ( + + ))} + + {filteredMessages.length === 0 && ( +
+ No messages found +
+ )} +
+
+
+ ); +}; diff --git a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/chat.tsx b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/chat.tsx index 473c90b4f98..b78aa50dab3 100644 --- a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/chat.tsx +++ b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/chat.tsx @@ -4,200 +4,119 @@ import { getServerUrl } from "../../../utils"; import { SessionManager } from "../../shared/session/manager"; import { IStatus } from "../../../types/app"; import { + Run, Message, - ThreadStatus, WebSocketMessage, + TeamConfig, + AgentMessageConfig, + RunStatus, + TeamResult, } from "../../../types/datamodel"; import { useConfigStore } from "../../../../hooks/store"; import { appContext } from "../../../../hooks/provider"; import ChatInput from "./chatinput"; -import { ModelUsage, ThreadState, TIMEOUT_CONFIG } from "./types"; -import { MessageList } from "./messagelist"; import TeamManager from "../../shared/team/manager"; import { teamAPI } from "../../shared/team/api"; +import { sessionAPI } from "../../shared/session/api"; +import RunView from "./runview"; +import { TIMEOUT_CONFIG } from "./types"; const logo = require("../../../../images/landing/welcome.svg").default; -export default function ChatView({ - initMessages, -}: { - initMessages: Message[]; -}) { +export default function ChatView() { const serverUrl = getServerUrl(); const [loading, setLoading] = React.useState(false); const [error, setError] = React.useState({ status: true, message: "All good", }); - const [messages, setMessages] = React.useState(initMessages); - const [threadMessages, setThreadMessages] = React.useState< - Record - >({}); - const chatContainerRef = React.useRef(null); - const timeoutRefs = React.useRef>({}); + // Core state + const [existingRuns, setExistingRuns] = React.useState([]); + const [currentRun, setCurrentRun] = React.useState(null); + + const chatContainerRef = React.useRef(null); + + // Context and config const { user } = React.useContext(appContext); const { session, sessions } = useConfigStore(); - const [activeSockets, setActiveSockets] = React.useState< - Record - >({}); - const activeSocketsRef = React.useRef>({}); + const [activeSocket, setActiveSocket] = React.useState( + null + ); + const [teamConfig, setTeamConfig] = React.useState(null); + + const inputTimeoutRef = React.useRef(null); + const activeSocketRef = React.useRef(null); + + // Create a Message object from AgentMessageConfig + const createMessage = ( + config: AgentMessageConfig, + runId: string, + sessionId: number + ): Message => ({ + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + config, + session_id: sessionId, + run_id: runId, + user_id: user?.email || undefined, + }); - const [teamConfig, setTeamConfig] = React.useState(null); + // Load existing runs when session changes + const loadSessionRuns = async () => { + if (!session?.id || !user?.email) return; + + try { + const response = await sessionAPI.getSessionRuns(session.id, user.email); + setExistingRuns(response.runs); + } catch (error) { + console.error("Error loading session runs:", error); + message.error("Failed to load chat history"); + } + }; React.useEffect(() => { - if (chatContainerRef.current) { - chatContainerRef.current.scrollTo({ - top: chatContainerRef.current.scrollHeight, - behavior: "smooth", - }); + if (session?.id) { + loadSessionRuns(); + } else { + setExistingRuns([]); + setCurrentRun(null); } - }, [messages, threadMessages]); + }, [session?.id]); + // Load team config React.useEffect(() => { - if (session && session.team_id && user && user.email) { - teamAPI.getTeam(session.team_id, user?.email).then((team) => { + if (session?.team_id && user?.email) { + teamAPI.getTeam(session.team_id, user.email).then((team) => { setTeamConfig(team.config); - // console.log("Team Config", team.config); }); } }, [session]); - const updateSocket = (runId: string, socket: WebSocket | null) => { - if (socket) { - activeSocketsRef.current[runId] = socket; - setActiveSockets((prev) => ({ ...prev, [runId]: socket })); - } else { - delete activeSocketsRef.current[runId]; - setActiveSockets((prev) => { - const next = { ...prev }; - delete next[runId]; - return next; - }); - } - }; - React.useEffect(() => { - return () => { - Object.values(activeSockets).forEach((socket) => socket.close()); - }; - }, [activeSockets]); - - const handleTimeoutForRun = (runId: string) => { - const socket = activeSocketsRef.current[runId]; - if (socket && socket.readyState === WebSocket.OPEN) { - // Send stop message to backend, just like when user clicks stop - socket.send( - JSON.stringify({ - type: "stop", - reason: TIMEOUT_CONFIG.DEFAULT_MESSAGE, - }) - ); - } - - // Update thread state with timeout reason - setThreadMessages((prev) => { - const currentThread = prev[runId]; - if (!currentThread) return prev; - - return { - ...prev, - [runId]: { - ...currentThread, - status: "cancelled", // Use existing cancelled status - reason: "Input request timed out after 3 minutes", - isExpanded: true, - inputRequest: currentThread.inputRequest - ? { - prompt: currentThread.inputRequest.prompt, - isPending: true, - } - : undefined, - }, - }; - }); - - if (timeoutRefs.current[runId]) { - clearTimeout(timeoutRefs.current[runId]); - delete timeoutRefs.current[runId]; - } - }; - - const handleInputResponse = async (runId: string, response: string) => { - // Clear timeout when response is received - if (timeoutRefs.current[runId]) { - clearTimeout(timeoutRefs.current[runId]); - delete timeoutRefs.current[runId]; - } - - if (response === "TIMEOUT") { - handleTimeoutForRun(runId); - return; - } - - const socket = activeSockets[runId]; - if (socket && socket.readyState === WebSocket.OPEN) { - try { - socket.send( - JSON.stringify({ - type: "input_response", - response: response, - }) - ); - - setThreadMessages((prev) => ({ - ...prev, - [runId]: { - ...prev[runId], - status: "streaming", - inputRequest: undefined, - }, - })); - } catch (error) { - console.error("Error sending input response:", error); - message.error("Failed to send response"); - - setThreadMessages((prev) => ({ - ...prev, - [runId]: { - ...prev[runId], - status: "error", - reason: "Failed to send input response", - }, - })); + setTimeout(() => { + if (chatContainerRef.current && existingRuns.length > 0) { + // Scroll to bottom to show latest run + chatContainerRef.current.scrollTo({ + top: chatContainerRef.current.scrollHeight, + behavior: "auto", // Use 'auto' instead of 'smooth' for initial load + }); } - } else { - message.error("Connection lost. Please try again."); - } - }; + }, 450); + }, [existingRuns.length, currentRun?.messages]); - const getBaseUrl = (url: string): string => { - try { - // Remove protocol (http:// or https://) - let baseUrl = url.replace(/(^\w+:|^)\/\//, ""); - - // Handle both localhost and production cases - if (baseUrl.startsWith("localhost")) { - // For localhost, keep the port if it exists - baseUrl = baseUrl.replace("/api", ""); - } else if (baseUrl === "/api") { - // For production where url is just '/api' - baseUrl = window.location.host; - } else { - // For other cases, remove '/api' and trailing slash - baseUrl = baseUrl.replace("/api", "").replace(/\/$/, ""); + // Cleanup socket on unmount + React.useEffect(() => { + return () => { + if (inputTimeoutRef.current) { + clearTimeout(inputTimeoutRef.current); } - - return baseUrl; - } catch (error) { - console.error("Error processing server URL:", error); - throw new Error("Invalid server URL configuration"); - } - }; + activeSocket?.close(); + }; + }, [activeSocket]); const createRun = async (sessionId: number): Promise => { const payload = { session_id: sessionId, user_id: user?.email || "" }; - const response = await fetch(`${serverUrl}/runs/`, { method: "POST", headers: { "Content-Type": "application/json" }, @@ -212,334 +131,343 @@ export default function ChatView({ return data.data.run_id; }; - const startRun = async (runId: string, query: string) => { - const messagePayload = { - user_id: user?.email, - session_id: session?.id, - config: { - content: query, - source: "user", - }, - }; + const handleWebSocketMessage = (message: WebSocketMessage) => { + setCurrentRun((current) => { + if (!current || !session?.id) return null; - const response = await fetch(`${serverUrl}/runs/${runId}/start`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(messagePayload), - }); + console.log("WebSocket message:", message); - if (!response.ok) { - throw new Error("Failed to start run"); - } + switch (message.type) { + case "error": + if (inputTimeoutRef.current) { + clearTimeout(inputTimeoutRef.current); + inputTimeoutRef.current = null; + } + if (activeSocket) { + activeSocket.close(); + setActiveSocket(null); + activeSocketRef.current = null; + } + console.log("Error: ", message.error); - return await response.json(); - }; + case "message": + if (!message.data) return current; - const connectWebSocket = (runId: string, query: string) => { - const baseUrl = getBaseUrl(serverUrl); - const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:"; - const wsUrl = `${wsProtocol}//${baseUrl}/api/ws/runs/${runId}`; + // Create new Message object from websocket data + const newMessage = createMessage( + message.data as AgentMessageConfig, + current.id, + session.id + ); - const socket = new WebSocket(wsUrl); - let isClosing = false; + return { + ...current, + messages: [...current.messages, newMessage], + }; - const clearTimeoutForRun = () => { - if (timeoutRefs.current[runId]) { - clearTimeout(timeoutRefs.current[runId]); - delete timeoutRefs.current[runId]; - } - }; + case "input_request": + if (inputTimeoutRef.current) { + clearTimeout(inputTimeoutRef.current); + } - const closeSocket = () => { - if (!isClosing && socket.readyState !== WebSocket.CLOSED) { - isClosing = true; - socket.close(); - updateSocket(runId, null); - } - }; + inputTimeoutRef.current = setTimeout(() => { + const socket = activeSocketRef.current; + console.log("Input timeout", socket); + + if (socket?.readyState === WebSocket.OPEN) { + socket.send( + JSON.stringify({ + type: "stop", + reason: TIMEOUT_CONFIG.DEFAULT_MESSAGE, + code: TIMEOUT_CONFIG.WEBSOCKET_CODE, + }) + ); + setCurrentRun((prev) => + prev + ? { + ...prev, + status: "stopped", + error_message: TIMEOUT_CONFIG.DEFAULT_MESSAGE, + } + : null + ); + } + }, TIMEOUT_CONFIG.DURATION_MS); - socket.onopen = async () => { - try { - updateSocket(runId, socket); - - setThreadMessages((prev) => ({ - ...prev, - [runId]: { - messages: [], - status: "streaming", - isExpanded: true, - }, - })); - - setMessages((prev: Message[]) => - prev.map((msg: Message) => { - if (msg.run_id === runId && msg.config.source === "bot") { - return { - ...msg, - config: { - ...msg.config, - content: "Starting...", - }, - }; + return { + ...current, + status: "awaiting_input", + }; + case "result": + case "completion": + // When run completes, move it to existingRuns + const status: RunStatus = + message.status === "complete" + ? "complete" + : message.status === "error" + ? "error" + : "stopped"; + + const isTeamResult = (data: any): data is TeamResult => { + return ( + data && + "task_result" in data && + "usage" in data && + "duration" in data + ); + }; + + const updatedRun = { + ...current, + status, + team_result: + message.data && isTeamResult(message.data) ? message.data : null, + }; + + // Add to existing runs if complete + if (status === "complete") { + if (inputTimeoutRef.current) { + clearTimeout(inputTimeoutRef.current); + inputTimeoutRef.current = null; } - return msg; - }) - ); + if (activeSocket) { + activeSocket.close(); + setActiveSocket(null); + activeSocketRef.current = null; + } + setExistingRuns((prev) => [...prev, updatedRun]); + return null; + } - await startRun(runId, query); - } catch (error) { - closeSocket(); - setThreadMessages((prev) => ({ - ...prev, - [runId]: { - ...prev[runId], - status: "error", - isExpanded: true, - }, - })); + return updatedRun; + + default: + return current; } - }; + }); + }; - socket.onmessage = (event) => { - const message: WebSocketMessage = JSON.parse(event.data); + const handleError = (error: any) => { + console.error("Error:", error); + message.error("Error during request processing"); - switch (message.type) { - case "input_request": - clearTimeoutForRun(); + setCurrentRun((current) => { + if (!current) return null; - timeoutRefs.current[runId] = setTimeout(() => { - handleTimeoutForRun(runId); - }, TIMEOUT_CONFIG.DURATION_MS); + const errorRun = { + ...current, + status: "error" as const, + error_message: + error instanceof Error ? error.message : "Unknown error occurred", + }; - setThreadMessages((prev) => ({ - ...prev, - [runId]: { - ...prev[runId], - status: "awaiting_input", - inputRequest: { - prompt: message.data?.content || "", - isPending: false, - }, - }, - })); - break; + // Add failed run to existing runs + setExistingRuns((prev) => [...prev, errorRun]); + return null; // Clear current run + }); - case "message": - clearTimeoutForRun(); - - setThreadMessages((prev) => { - const currentThread = prev[runId] || { - messages: [], - status: "streaming", - isExpanded: true, - }; - - const models_usage: ModelUsage | undefined = message.data - ?.models_usage - ? { - prompt_tokens: message.data.models_usage.prompt_tokens, - completion_tokens: - message.data.models_usage.completion_tokens, - } - : undefined; - - return { - ...prev, - [runId]: { - ...currentThread, - messages: [ - ...currentThread.messages, - { - source: message.data?.source || "", - content: message.data?.content || "", - models_usage, - }, - ], - status: "streaming", - }, - }; - }); - break; + setError({ + status: false, + message: + error instanceof Error ? error.message : "Unknown error occurred", + }); + }; - case "result": - case "completion": - clearTimeoutForRun(); - - setThreadMessages((prev) => { - const currentThread = prev[runId]; - if (!currentThread) return prev; - - const status: ThreadStatus = message.status || "complete"; - const reason = - message.data?.task_result?.stop_reason || - (message.error ? `Error: ${message.error}` : undefined); - - return { - ...prev, - [runId]: { - ...currentThread, - status, - reason, - isExpanded: true, - finalResult: message.data?.task_result?.messages - ?.filter((msg: any) => msg.content !== "TERMINATE") - .pop(), - }, - }; - }); - closeSocket(); - break; - } - }; + const handleInputResponse = async (response: string) => { + if (!activeSocketRef.current || !currentRun) return; - socket.onclose = (event) => { - clearTimeoutForRun(); - - if (!isClosing) { - updateSocket(runId, null); - - setThreadMessages((prev) => { - const thread = prev[runId]; - if (thread && thread.status === "streaming") { - return { - ...prev, - [runId]: { - ...thread, - status: - event.code === TIMEOUT_CONFIG.WEBSOCKET_CODE - ? "timeout" - : "complete", - reason: event.reason || "Connection closed", - }, - }; - } - return prev; - }); - } - }; + if (activeSocketRef.current.readyState !== WebSocket.OPEN) { + console.error( + "Socket not in OPEN state:", + activeSocketRef.current.readyState + ); + handleError(new Error("WebSocket connection not available")); + return; + } - socket.onerror = (error) => { - clearTimeoutForRun(); + // Clear timeout when response received + if (inputTimeoutRef.current) { + clearTimeout(inputTimeoutRef.current); + inputTimeoutRef.current = null; + } - setThreadMessages((prev) => { - const thread = prev[runId]; - if (!thread) return prev; + try { + console.log("Sending input response:", response); + activeSocketRef.current.send( + JSON.stringify({ + type: "input_response", + response: response, + }) + ); + setCurrentRun((current) => { + if (!current) return null; return { - ...prev, - [runId]: { - ...thread, - status: "error", - reason: "WebSocket connection error occurred", - isExpanded: true, - }, + ...current, + status: "active", }; }); - - closeSocket(); - }; - - return socket; + } catch (error) { + handleError(error); + } }; - const cancelRun = async (runId: string) => { - const socket = activeSockets[runId]; - if (socket && socket.readyState === WebSocket.OPEN) { - socket.send( - JSON.stringify({ type: "stop", reason: "Cancelled by user" }) - ); + const handleCancel = async () => { + if (!activeSocketRef.current || !currentRun) return; - setThreadMessages((prev) => ({ - ...prev, - [runId]: { - ...prev[runId], - status: "cancelled", + // Clear timeout when manually cancelled + if (inputTimeoutRef.current) { + clearTimeout(inputTimeoutRef.current); + inputTimeoutRef.current = null; + } + try { + activeSocketRef.current.send( + JSON.stringify({ + type: "stop", reason: "Cancelled by user", - isExpanded: true, - }, - })); + }) + ); + + setCurrentRun((current) => { + if (!current) return null; + return { + ...current, + status: "stopped", + }; + }); + } catch (error) { + handleError(error); } }; - // Clean up timeouts when component unmounts - React.useEffect(() => { - return () => { - Object.entries(timeoutRefs.current).forEach(([_, timeout]) => - clearTimeout(timeout) - ); - timeoutRefs.current = {}; - }; - }, []); - const runTask = async (query: string) => { setError(null); setLoading(true); - if (!session?.id) { + // Add explicit cleanup + if (activeSocket) { + activeSocket.close(); + setActiveSocket(null); + activeSocketRef.current = null; + } + if (inputTimeoutRef.current) { + clearTimeout(inputTimeoutRef.current); + inputTimeoutRef.current = null; + } + + if (!session?.id || !teamConfig) { + // Add teamConfig check setLoading(false); return; } - let runId: string | null = null; - try { - runId = (await createRun(session.id)) + ""; - - const userMessage: Message = { - config: { + const runId = await createRun(session.id); + + // Initialize run state BEFORE websocket connection + setCurrentRun({ + id: runId, + created_at: new Date().toISOString(), + status: "created", // Start with created status + messages: [], + task: { content: query, source: "user", }, - session_id: session.id, - run_id: runId, - }; - - const botMessage: Message = { - config: { - content: "Thinking...", - source: "bot", - }, - session_id: session.id, - run_id: runId, - }; - - setMessages((prev) => [...prev, userMessage, botMessage]); - connectWebSocket(runId, query); // Now passing query to connectWebSocket - } catch (err) { - console.error("Error:", err); - message.error("Error during request processing"); - - if (runId) { - if (activeSockets[runId]) { - activeSockets[runId].close(); - } - - setThreadMessages((prev) => ({ - ...prev, - [runId!]: { - ...prev[runId!], - status: "error", - isExpanded: true, - }, - })); - } - - setError({ - status: false, - message: err instanceof Error ? err.message : "Unknown error occurred", + team_result: null, + error_message: undefined, }); + + // Setup WebSocket + const socket = setupWebSocket(runId, query); + setActiveSocket(socket); + activeSocketRef.current = socket; + } catch (error) { + handleError(error); } finally { setLoading(false); } }; - React.useEffect(() => { - // session changed - if (session) { - setMessages([]); - setThreadMessages({}); + const setupWebSocket = (runId: string, query: string): WebSocket => { + if (!session || !session.id) { + throw new Error("Invalid session configuration"); } - }, [session]); + // Close existing socket if any + if (activeSocket?.readyState === WebSocket.OPEN) { + activeSocket.close(); + } + + const baseUrl = getBaseUrl(serverUrl); + const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:"; + const wsUrl = `${wsProtocol}//${baseUrl}/api/ws/runs/${runId}`; + + const socket = new WebSocket(wsUrl); + + // Initialize current run + setCurrentRun({ + id: runId, + created_at: new Date().toISOString(), + status: "active", + task: createMessage( + { content: query, source: "user" }, + runId, + session.id || 0 + ).config, + team_result: null, + messages: [], + error_message: undefined, + }); + + socket.onopen = () => { + // Send start message with teamConfig + socket.send( + JSON.stringify({ + type: "start", + task: query, + team_config: teamConfig, + }) + ); + }; + + socket.onmessage = (event) => { + try { + const message = JSON.parse(event.data); + handleWebSocketMessage(message); + } catch (error) { + console.error("WebSocket message parsing error:", error); + } + }; + + socket.onclose = () => { + activeSocketRef.current = null; + setActiveSocket(null); + }; + + socket.onerror = (error) => { + handleError(error); + }; + + return socket; + }; + + // Helper for WebSocket URL + const getBaseUrl = (url: string): string => { + try { + let baseUrl = url.replace(/(^\w+:|^)\/\//, ""); + if (baseUrl.startsWith("localhost")) { + baseUrl = baseUrl.replace("/api", ""); + } else if (baseUrl === "/api") { + baseUrl = window.location.host; + } else { + baseUrl = baseUrl.replace("/api", "").replace(/\/$/, ""); + } + return baseUrl; + } catch (error) { + console.error("Error processing server URL:", error); + throw new Error("Invalid server URL configuration"); + } + }; return (
@@ -549,45 +477,62 @@ export default function ChatView({
+
- +
+ {" "} + {" "} +
+ {sessions !== null && sessions?.length === 0 ? ( +
+
+ Welcome + Welcome! Create a session to get started! +
+
+ ) : ( + <> + {teamConfig && ( + <> + {/* Existing Runs */} + {existingRuns.map((run, index) => ( + + ))} + + {/* Current Run */} + {currentRun && ( + + )} + + )} + + )}
- {sessions !== null && sessions?.length === 0 ? ( -
-
- Welcome - Welcome! Create a session to get started! -
+ {session && ( +
+
- ) : ( - <> - {session && ( -
- thread.status === "awaiting_input" - )} // Disable input while waiting for user input - /> -
- )} - )}
diff --git a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/threadview.tsx b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/inputrequest.tsx similarity index 59% rename from python/packages/autogen-studio/frontend/src/components/views/playground/chat/threadview.tsx rename to python/packages/autogen-studio/frontend/src/components/views/playground/chat/inputrequest.tsx index 355b277d4c4..d9318892500 100644 --- a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/threadview.tsx +++ b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/inputrequest.tsx @@ -12,15 +12,6 @@ import { ThreadState, TIMEOUT_CONFIG } from "./types"; import { RenderMessage } from "./rendermessage"; import LoadingDots from "../../shared/atoms"; -interface ThreadViewProps { - thread: ThreadState; - isStreaming: boolean; - runId: string; - onCancel: (runId: string) => void; - onInputResponse: (runId: string, response: string) => void; - threadContainerRef: (el: HTMLDivElement | null) => void; -} - interface InputRequestProps { prompt: string; onSubmit: (response: string) => void; @@ -170,105 +161,4 @@ const InputRequestView: React.FC = ({ ); }; -const ThreadView: React.FC = ({ - thread, - isStreaming, - runId, - onCancel, - onInputResponse, - threadContainerRef, -}) => { - const isAwaitingInput = thread.status === "awaiting_input"; - const isTimedOut = thread.status === "timeout"; - - const getStatusIcon = () => { - switch (thread.status) { - case "streaming": - return ; - case "awaiting_input": - return ; - case "complete": - return ; - case "error": - return ; - case "timeout": - return ; - default: - return null; - } - }; - - const getStatusText = () => { - if (isStreaming) { - return ( - <> - Agents working - - - ); - } - if (isAwaitingInput) return "Waiting for your input"; - if (isTimedOut) return TIMEOUT_CONFIG.DEFAULT_MESSAGE; - if (thread.reason) - return ( - <> - Stop Reason: - {thread.reason} - - ); - return null; - }; - - const handleTimeout = () => { - if (thread.inputRequest) { - onInputResponse(runId, "TIMEOUT"); - } - }; - - return ( -
-
-
- {getStatusIcon()} - {getStatusText()} -
- {(isStreaming || isAwaitingInput) && ( - - )} -
- -
-
- {thread.messages.map((threadMsg, threadIndex) => ( -
- -
- ))} - - {thread.inputRequest && ( - onInputResponse(runId, response)} - disabled={!isAwaitingInput || isTimedOut} - onTimeout={handleTimeout} - /> - )} -
-
-
- ); -}; - -export default ThreadView; +export default InputRequestView; diff --git a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/messagelist.tsx b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/messagelist.tsx deleted file mode 100644 index d25a508e7be..00000000000 --- a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/messagelist.tsx +++ /dev/null @@ -1,289 +0,0 @@ -import React from "react"; -import { ThreadState } from "./types"; -import { - AgentMessageConfig, - Message, - TeamConfig, -} from "../../../types/datamodel"; -import { RenderMessage } from "./rendermessage"; -import { - StopCircle, - User, - Network, - MessageSquare, - Loader2, - CheckCircle, - AlertTriangle, - TriangleAlertIcon, - GroupIcon, -} from "lucide-react"; -import AgentFlow from "./agentflow/agentflow"; -import ThreadView from "./threadview"; -import LoadingDots from "../../shared/atoms"; - -interface MessageListProps { - messages: Message[]; - threadMessages: Record; - setThreadMessages: React.Dispatch< - React.SetStateAction> - >; - onRetry: (content: string) => void; - onCancel: (runId: string) => void; - onInputResponse: (runId: string, response: string) => void; - loading?: boolean; - teamConfig?: TeamConfig; -} - -interface MessagePair { - userMessage: Message; - botMessage: Message; -} - -export const MessageList: React.FC = ({ - messages, - threadMessages, - setThreadMessages, - onRetry, - onCancel, - onInputResponse, // New prop - loading = false, - teamConfig, -}) => { - const messagePairs = React.useMemo(() => { - const pairs: MessagePair[] = []; - for (let i = 0; i < messages.length; i += 2) { - if (messages[i] && messages[i + 1]) { - pairs.push({ - userMessage: messages[i], - botMessage: messages[i + 1], - }); - } - } - return pairs; - }, [messages]); - - // Create a ref map to store refs for each thread container - const threadContainerRefs = React.useRef< - Record - >({}); - - // Effect to handle scrolling when thread messages update - React.useEffect(() => { - Object.entries(threadMessages).forEach(([runId, thread]) => { - if (thread.isExpanded && threadContainerRefs.current[runId]) { - const container = threadContainerRefs.current[runId]; - if (container) { - container.scrollTo({ - top: container.scrollHeight, - behavior: "smooth", - }); - } - } - }); - }, [threadMessages]); - - const toggleThread = (runId: string) => { - setThreadMessages((prev) => ({ - ...prev, - [runId]: { - ...prev[runId], - isExpanded: !prev[runId]?.isExpanded, - }, - })); - }; - - const calculateThreadTokens = (messages: AgentMessageConfig[]) => { - return messages.reduce((total, msg) => { - if (!msg.models_usage) return total; - return ( - total + - (msg.models_usage.prompt_tokens || 0) + - (msg.models_usage.completion_tokens || 0) - ); - }, 0); - }; - - const getStatusIcon = (status: ThreadState["status"]) => { - switch (status) { - case "streaming": - return ( -
- {" "} - Processing{" "} - -
- ); - case "awaiting_input": // New status - return ( -
- {" "} - Waiting for your input -
- ); - case "complete": - return ( -
- {" "} - Task completed -
- ); - case "error": - return ( -
- {" "} - An error occurred. -
- ); - case "cancelled": - return ( -
- {" "} - Task was cancelled. -
- ); - default: - return null; - } - }; - - return ( -
- {messagePairs.map(({ userMessage, botMessage }, pairIndex) => { - const isLast = pairIndex === messagePairs.length - 1; - const thread = threadMessages[botMessage.run_id]; - const hasThread = thread && thread.messages.length > 0; - const isStreaming = thread?.status === "streaming"; - const isAwaitingInput = thread?.status === "awaiting_input"; // New check - - const isFirstMessage = pairIndex === 0; - - return ( -
- {/* User message */} - { -
- {/*
Task Run 1.
*/} -
- Run {pairIndex + 1} - {!isFirstMessage && ( - <> - {" "} - |{" "} - {" "} - Note: Each run does not share data with previous runs in - the same session yet.{" "} - - )} -
-
- } -
-
- You -
- -
-
-
- -
-
- - {/* Team response */} -
-
-
- -
- - Agent Team - -
- - {/* Main response container */} -
-
-
- {getStatusIcon(thread?.status)}{" "} - {!isAwaitingInput && thread?.finalResult?.content} -
-
- - {/* Thread section */} - {hasThread && ( -
-
-
- -
- -
- {calculateThreadTokens(thread.messages)} tokens |{" "} - {thread.messages.length} messages -
-
- -
-
- {thread.isExpanded && ( - - (threadContainerRefs.current[botMessage.run_id] = - el) - } - /> - )} -
-
- {teamConfig && thread.isExpanded && ( - - )} -
-
-
- )} -
-
-
- ); - })} - - {messages.length === 0 && !loading && ( -
-
Send a message to begin!
-
- )} -
- ); -}; diff --git a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/rendermessage.tsx b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/rendermessage.tsx index 54035bf7e66..d9f9346fce2 100644 --- a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/rendermessage.tsx +++ b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/rendermessage.tsx @@ -1,11 +1,77 @@ -import React from "react"; -import { User, Bot } from "lucide-react"; +import React, { useState, memo } from "react"; +import { User, Bot, Maximize2, Minimize2 } from "lucide-react"; import { AgentMessageConfig, FunctionCall, FunctionExecutionResult, ImageContent, } from "../../../types/datamodel"; +import { ClickableImage, TruncatableText } from "../../shared/atoms"; + +const TEXT_THRESHOLD = 400; +const JSON_THRESHOLD = 800; + +// Helper function to get image source from either format +const getImageSource = (item: ImageContent): string => { + if (item.url) { + return item.url; + } + if (item.data) { + // Assume PNG if no type specified - we can enhance this later if needed + return `data:image/png;base64,${item.data}`; + } + // Fallback placeholder if neither url nor data is present + return "/api/placeholder/400/320"; +}; + +const RenderMultiModal: React.FC<{ content: (string | ImageContent)[] }> = ({ + content, +}) => ( +
+ {content.map((item, index) => + typeof item === "string" ? ( + + ) : ( + + ) + )} +
+); +const RenderToolCall: React.FC<{ content: FunctionCall[] }> = ({ content }) => ( +
+ {content.map((call) => ( +
+
Function: {call.name}
+ +
+ ))} +
+); + +const RenderToolResult: React.FC<{ content: FunctionExecutionResult[] }> = ({ + content, +}) => ( +
+ {content.map((result) => ( +
+
Result ID: {result.call_id}
+ +
+ ))} +
+); export const messageUtils = { isToolCallContent(content: unknown): content is FunctionCall[] { @@ -25,7 +91,9 @@ export const messageUtils = { return content.every( (item) => typeof item === "string" || - (typeof item === "object" && item !== null && "url" in item) + (typeof item === "object" && + item !== null && + ("url" in item || "data" in item)) ); }, @@ -53,58 +121,12 @@ interface MessageProps { className?: string; } -const RenderToolCall: React.FC<{ content: FunctionCall[] }> = ({ content }) => ( -
- {content.map((call) => ( -
-
Function: {call.name}
-
-          {JSON.stringify(JSON.parse(call.arguments), null, 2)}
-        
-
- ))} -
-); - -const RenderMultiModal: React.FC<{ content: (string | ImageContent)[] }> = ({ - content, -}) => ( -
- {content.map((item, index) => - typeof item === "string" ? ( -

{item}

- ) : ( - {item.alt - ) - )} -
-); - -const RenderToolResult: React.FC<{ content: FunctionExecutionResult[] }> = ({ - content, -}) => ( -
- {content.map((result) => ( -
-
Result ID: {result.call_id}
-
-          {result.content}
-        
-
- ))} -
-); - export const RenderMessage: React.FC = ({ message, isLast = false, className = "", }) => { + if (!message) return null; const isUser = messageUtils.isUser(message.source); const content = message.content; @@ -142,7 +164,10 @@ export const RenderMessage: React.FC = ({ ) : messageUtils.isFunctionExecutionResult(content) ? ( ) : ( -
{content}
+ )} diff --git a/python/packages/autogen-studio/frontend/src/components/views/playground/chat/runview.tsx b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/runview.tsx new file mode 100644 index 00000000000..5dd9ba8c550 --- /dev/null +++ b/python/packages/autogen-studio/frontend/src/components/views/playground/chat/runview.tsx @@ -0,0 +1,271 @@ +import React, { useState, useRef, useEffect } from "react"; +import { + StopCircle, + MessageSquare, + Loader2, + CheckCircle, + AlertTriangle, + TriangleAlertIcon, + GroupIcon, +} from "lucide-react"; +import { Run, Message, TeamConfig } from "../../../types/datamodel"; +import AgentFlow from "./agentflow/agentflow"; +import { RenderMessage } from "./rendermessage"; +import InputRequestView from "./inputrequest"; +import { Tooltip } from "antd"; +import { LoadingDots } from "../../shared/atoms"; + +interface RunViewProps { + run: Run; + teamConfig?: TeamConfig; + onInputResponse?: (response: string) => void; + onCancel?: () => void; + isFirstRun?: boolean; +} + +const RunView: React.FC = ({ + run, + onInputResponse, + onCancel, + teamConfig, + isFirstRun = false, +}) => { + const [isExpanded, setIsExpanded] = useState(true); + const threadContainerRef = useRef(null); + const isActive = run.status === "active" || run.status === "awaiting_input"; + + // Replace existing scroll effect with this simpler one + useEffect(() => { + setTimeout(() => { + if (threadContainerRef.current) { + threadContainerRef.current.scrollTo({ + top: threadContainerRef.current.scrollHeight, + behavior: "smooth", + }); + } + }, 450); + }, [run.messages]); // Only depend on messages changing + + const calculateThreadTokens = (messages: Message[]) => { + return messages.reduce((total, msg) => { + if (!msg.config.models_usage) return total; + return ( + total + + (msg.config.models_usage.prompt_tokens || 0) + + (msg.config.models_usage.completion_tokens || 0) + ); + }, 0); + }; + + const getStatusIcon = (status: Run["status"]) => { + switch (status) { + case "active": + return ( +
+ + Processing + +
+ ); + case "awaiting_input": + return ( +
+ + Waiting for your input + +
+ ); + case "complete": + return ( +
+ + Task completed +
+ ); + case "error": + return ( +
+ + {run.error_message || "An error occurred"} +
+ ); + case "stopped": + return ( +
+ + Task was stopped +
+ ); + default: + return null; + } + }; + + return ( +
+ {/* Run Header */} +
+
+ +
ID: {run.id}
+
Created: {new Date(run.created_at).toLocaleString()}
+
Status: {run.status}
+
+ } + > + Run ...{run.id.slice(-6)} + + {!isFirstRun && ( + <> + {" "} + |{" "} + + Note: Each run does not share data with previous runs in the same + session yet. + + )} +
+
+ + {/* User Message */} +
+
+ +
+
+ + {/* Team Response */} +
+
+
+ +
+ Agent Team +
+ +
+ {/* Main Response Container */} +
+
+
{getStatusIcon(run.status)}
+ + {/* Cancel Button - More prominent placement */} + {isActive && onCancel && ( + + )} +
+ + {/* Final Response */} + {run.status !== "awaiting_input" && run.status !== "active" && ( +
+
+ Stop reason: {run.team_result?.task_result?.stop_reason} +
+ {run.messages[run.messages.length - 1]?.config?.content + ""} +
+ )} +
+ + {/* Thread Section */} +
+ {run.messages.length > 0 && ( +
+
+
+ +
+ +
+ {calculateThreadTokens(run.messages)} tokens |{" "} + {run.messages.length} messages +
+
+ + {isExpanded && ( +
+ {/* Messages Thread */} +
+
+ {" "} + {" "} +
+ {run.messages.map((msg, idx) => ( +
+ +
+ ))} + + {/* Input Request UI */} + {run.status === "awaiting_input" && onInputResponse && ( +
+ +
+ )} +
+
{" "} +
+ {getStatusIcon(run.status)} +
+
+
+ + {/* Agent Flow Visualization */} +
+ {teamConfig && ( + + )} +
+
+ )} +
+ )} +
+
+
+ + ); +}; + +export default RunView; diff --git a/python/packages/autogen-studio/frontend/src/components/views/shared/atoms.tsx b/python/packages/autogen-studio/frontend/src/components/views/shared/atoms.tsx index 15dbc8c6f5a..fcd95ca7609 100644 --- a/python/packages/autogen-studio/frontend/src/components/views/shared/atoms.tsx +++ b/python/packages/autogen-studio/frontend/src/components/views/shared/atoms.tsx @@ -1,5 +1,5 @@ -import React from "react"; -import { Loader2 } from "lucide-react"; +import React, { memo, useState } from "react"; +import { Loader2, Maximize2, Minimize2, X } from "lucide-react"; export const LoadingIndicator = ({ size = 16 }: { size: number }) => (
@@ -40,4 +40,127 @@ export const LoadingDots = ({ size = 8 }) => { ); }; -export default LoadingDots; +export const TruncatableText = memo( + ({ + content, + isJson = false, + className = "", + jsonThreshold = 1000, + textThreshold = 500, + }: { + content: string; + isJson?: boolean; + className?: string; + jsonThreshold?: number; + textThreshold?: number; + }) => { + const [isExpanded, setIsExpanded] = useState(false); + const threshold = isJson ? jsonThreshold : textThreshold; + const shouldTruncate = content.length > threshold; + + const toggleExpand = () => { + setIsExpanded(!isExpanded); + }; + + const displayContent = + shouldTruncate && !isExpanded + ? content.slice(0, threshold) + "..." + : content; + + return ( +
+
+ {displayContent} + {shouldTruncate && !isExpanded && ( +
+ )} +
+ + {shouldTruncate && ( +
+ +
+ )} +
+ ); + } +); + +const FullScreenImage: React.FC<{ + src: string; + alt: string; + onClose: () => void; +}> = ({ src, alt, onClose }) => { + return ( +
+ + {alt} e.stopPropagation()} + /> +
+ ); +}; + +export const ClickableImage: React.FC<{ + src: string; + alt: string; + className?: string; +}> = ({ src, alt, className = "" }) => { + const [isFullScreen, setIsFullScreen] = useState(false); + + return ( + <> + {alt} setIsFullScreen(true)} + /> + {isFullScreen && ( + setIsFullScreen(false)} + /> + )} + + ); +}; diff --git a/python/packages/autogen-studio/frontend/src/components/views/shared/markdown.tsx b/python/packages/autogen-studio/frontend/src/components/views/shared/markdown.tsx index c0068043b19..c98f86ea11e 100644 --- a/python/packages/autogen-studio/frontend/src/components/views/shared/markdown.tsx +++ b/python/packages/autogen-studio/frontend/src/components/views/shared/markdown.tsx @@ -1,20 +1,18 @@ +import React, { ReactNode } from "react"; import Markdown from "react-markdown"; -import React from "react"; interface MarkdownViewProps { - children: string; + content: string; className?: string; } export const MarkdownView: React.FC = ({ - children, + content, className = "", }) => { return ( -
- {children} +
+ {content}
); }; diff --git a/python/packages/autogen-studio/frontend/src/components/views/shared/session/api.ts b/python/packages/autogen-studio/frontend/src/components/views/shared/session/api.ts index cccd6ad61a7..f34843e239b 100644 --- a/python/packages/autogen-studio/frontend/src/components/views/shared/session/api.ts +++ b/python/packages/autogen-studio/frontend/src/components/views/shared/session/api.ts @@ -1,4 +1,4 @@ -import { Session } from "../../../types/datamodel"; +import { Session, SessionRuns } from "../../../types/datamodel"; import { getServerUrl } from "../../../utils"; export class SessionAPI { @@ -83,6 +83,23 @@ export class SessionAPI { return data.data; } + // session runs with messages + async getSessionRuns( + sessionId: number, + userId: string + ): Promise { + const response = await fetch( + `${this.getBaseUrl()}/sessions/${sessionId}/runs?user_id=${userId}`, + { + headers: this.getHeaders(), + } + ); + const data = await response.json(); + if (!data.status) + throw new Error(data.message || "Failed to fetch session runs"); + return data.data; // Returns { runs: RunMessage[] } + } + async deleteSession(sessionId: number, userId: string): Promise { const response = await fetch( `${this.getBaseUrl()}/sessions/${sessionId}?user_id=${userId}`, diff --git a/python/packages/autogen-studio/frontend/src/pages/index.tsx b/python/packages/autogen-studio/frontend/src/pages/index.tsx index e5424857448..a1b59107409 100644 --- a/python/packages/autogen-studio/frontend/src/pages/index.tsx +++ b/python/packages/autogen-studio/frontend/src/pages/index.tsx @@ -8,7 +8,7 @@ const IndexPage = ({ data }: any) => { return (
- +
); diff --git a/python/packages/autogen-studio/frontend/yarn.lock b/python/packages/autogen-studio/frontend/yarn.lock index 03ca9a83ff8..889c3d92de8 100644 --- a/python/packages/autogen-studio/frontend/yarn.lock +++ b/python/packages/autogen-studio/frontend/yarn.lock @@ -15,27 +15,6 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@ant-design/charts-util@0.0.1-alpha.6": - version "0.0.1-alpha.6" - resolved "https://registry.yarnpkg.com/@ant-design/charts-util/-/charts-util-0.0.1-alpha.6.tgz#09903d28a15d86cc73fafef5383a5a3f932811bb" - integrity sha512-roZobGkUJ0WqULPiQkX/2j01r6Cn0W6WTVpszq9u8dZKwyrSDr+UgfA/hDmrwOm9TWD9HAxe7aRHnvC06dux8w== - -"@ant-design/charts-util@0.0.1-alpha.7": - version "0.0.1-alpha.7" - resolved "https://registry.yarnpkg.com/@ant-design/charts-util/-/charts-util-0.0.1-alpha.7.tgz#39152b7106970faa226ba857fae64a0eb32f30b9" - integrity sha512-Yh0o6EdO6SvdSnStFZMbnUzjyymkVzV+TQ9ymVW9hlVgO/fUkUII3JYSdV+UVcFnYwUF0YiDKuSTLCZNAzg2bQ== - dependencies: - lodash "^4.17.21" - -"@ant-design/charts@^2.2.3": - version "2.2.3" - resolved "https://registry.yarnpkg.com/@ant-design/charts/-/charts-2.2.3.tgz#ca484a70877ee9c7013951f477fcba661e842525" - integrity sha512-gjyOJwAvRH3NztbR4R7bQ+wVfA/jRT+EgtSEQxQtjka4w0srMIftXgaoxoycgCEHgO1eUg20RWXx0mMFKyWIfg== - dependencies: - "@ant-design/graphs" "^2.0.0" - "@ant-design/plots" "^2.1.3" - lodash "^4.17.21" - "@ant-design/colors@^7.0.0", "@ant-design/colors@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.1.0.tgz#60eadfa2e21871d8948dac5d50b9f056062f8af3" @@ -53,9 +32,9 @@ rc-util "^5.38.0" "@ant-design/cssinjs@^1.21.0", "@ant-design/cssinjs@^1.21.1": - version "1.21.1" - resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.21.1.tgz#7320813c5f747e0cde52c388eff5198d78d57230" - integrity sha512-tyWnlK+XH7Bumd0byfbCiZNK43HEubMoCcu9VxwsAwiHdHTgWa+tMN0/yvxa+e8EzuFP1WdUNNPclRpVtD33lg== + version "1.22.0" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.22.0.tgz#c2eea2490d2405e55c64f3c1af1e09524d62a228" + integrity sha512-W9XSFeRPR0mAN3OuxfuS/xhENCYKf+8s+QyNNER0FSWoK9OpISTag6CCweg6lq0hASQ/2Vcza0Z8/kGivCP0Ng== dependencies: "@babel/runtime" "^7.11.1" "@emotion/hash" "^0.8.0" @@ -63,7 +42,7 @@ classnames "^2.3.1" csstype "^3.1.3" rc-util "^5.35.0" - stylis "^4.3.3" + stylis "^4.3.4" "@ant-design/fast-color@^2.0.6": version "2.0.6" @@ -72,18 +51,6 @@ dependencies: "@babel/runtime" "^7.24.7" -"@ant-design/graphs@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@ant-design/graphs/-/graphs-2.0.0.tgz#dd0b39c05ffcb42c9368df0515fdc0a1917f26d8" - integrity sha512-giwe60AHwcQp5mXKQrsDU2/34cKOJQoc4rYPB9N1CqaGFcEWqOi6Kiz7O9s0QRwPBpzyP/boyP01a9qb03ycKw== - dependencies: - "@ant-design/charts-util" "0.0.1-alpha.7" - "@antv/g6" "^5.0.24" - "@antv/g6-extension-react" "^0.1.7" - "@antv/graphin" "^3.0.2" - lodash "^4.17.21" - styled-components "^6.1.13" - "@ant-design/icons-svg@^4.4.0": version "4.4.2" resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz#ed2be7fb4d82ac7e1d45a54a5b06d6cecf8be6f6" @@ -100,18 +67,6 @@ classnames "^2.2.6" rc-util "^5.31.1" -"@ant-design/plots@^2.1.3", "@ant-design/plots@^2.2.2": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@ant-design/plots/-/plots-2.3.2.tgz#5ca93f502c9b865297e9029745e216c0962c45e9" - integrity sha512-shFV2DTQcbQDtzBwpMagG2pnKy3+I4igws6VQvM7m8UIZtWFSwkWpjCnOl7Xefqgnov/M0C9HbaVGCGc9ZfIqA== - dependencies: - "@ant-design/charts-util" "0.0.1-alpha.6" - "@antv/event-emitter" "^0.1.3" - "@antv/g" "^6.0.0" - "@antv/g2" "^5.1.18" - "@antv/g2-extension-plot" "^0.2.0" - lodash "^4.17.21" - "@ant-design/react-slick@~1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-1.1.2.tgz#f84ce3e4d0dc941f02b16f1d1d6d7a371ffbb4f1" @@ -123,520 +78,6 @@ resize-observer-polyfill "^1.5.1" throttle-debounce "^5.0.0" -"@antv/algorithm@^0.1.26": - version "0.1.26" - resolved "https://registry.yarnpkg.com/@antv/algorithm/-/algorithm-0.1.26.tgz#e3f5e7f1d8db5b415c3f31e32b119cbcafc8f5de" - integrity sha512-DVhcFSQ8YQnMNW34Mk8BSsfc61iC1sAnmcfYoXTAshYHuU50p/6b7x3QYaGctDNKWGvi1ub7mPcSY0bK+aN0qg== - dependencies: - "@antv/util" "^2.0.13" - tslib "^2.0.0" - -"@antv/component@^2.0.0", "@antv/component@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@antv/component/-/component-2.1.1.tgz#c91daad673913b6efb3c6503dca7ba123346197b" - integrity sha512-V0UCq3Bekqtjw5WedexT1tHM/9x5BY0UAaU7G/5A2NhRfp9GuaQ8xGWLMSWlCQiJSRZWhPIA7RoOSw4Y/W+7UA== - dependencies: - "@antv/g" "^6.1.2" - "@antv/scale" "^0.4.3" - "@antv/util" "^3.3.5" - svg-path-parser "^1.1.0" - -"@antv/coord@^0.4.6": - version "0.4.7" - resolved "https://registry.yarnpkg.com/@antv/coord/-/coord-0.4.7.tgz#3ef6c6e3f9ca0f024b90888549946061f35df77a" - integrity sha512-UTbrMLhwJUkKzqJx5KFnSRpU3BqrdLORJbwUbHK2zHSCT3q3bjcFA//ZYLVfIlwqFDXp/hzfMyRtp0c77A9ZVA== - dependencies: - "@antv/scale" "^0.4.12" - "@antv/util" "^2.0.13" - gl-matrix "^3.4.3" - -"@antv/event-emitter@^0.1.3": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@antv/event-emitter/-/event-emitter-0.1.3.tgz#3e06323b9dcd55a3241ddc7c5458cfabd2095164" - integrity sha512-4ddpsiHN9Pd4UIlWuKVK1C4IiZIdbwQvy9i7DUSI3xNJ89FPUFt8lxDYj8GzzfdllV0NkJTRxnG+FvLk0llidg== - -"@antv/g-camera-api@2.0.18": - version "2.0.18" - resolved "https://registry.yarnpkg.com/@antv/g-camera-api/-/g-camera-api-2.0.18.tgz#69c9e42e74627504a720534259cbd22b12b15cf1" - integrity sha512-N3Gd/kCSCoPZfTocAI0i1uu3VmKrVR6FAq+9QSLEHFdFpg6+Pvqjx32QeOgl3uhE5hNiG/FUZ7KWXDNSnKJS1w== - dependencies: - "@antv/g-lite" "2.1.4" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - gl-matrix "^3.4.3" - tslib "^2.5.3" - -"@antv/g-camera-api@2.0.21": - version "2.0.21" - resolved "https://registry.yarnpkg.com/@antv/g-camera-api/-/g-camera-api-2.0.21.tgz#fb312a5db79addb978dee32fc2bb91c59ba384b5" - integrity sha512-cU903cmIBEyVX6hk7bmoltnnORnRd+KnRQsFzWv+Gg8l99bVOEqVa6/YE+Geh9Gt0JVUr+k06KVs8V40IVkz5Q== - dependencies: - "@antv/g-lite" "2.2.2" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - gl-matrix "^3.4.3" - tslib "^2.5.3" - -"@antv/g-canvas@^2.0.0": - version "2.0.20" - resolved "https://registry.yarnpkg.com/@antv/g-canvas/-/g-canvas-2.0.20.tgz#a79a6960f408a8e3b1982468b6659303b79f5f2d" - integrity sha512-o6aB/I4g6Xi3em6cwdgPQhm/3X1uf+KaMgyQKgB/FekWAD6kxf0qwQRmn0SggUCMcfpJDOrvKmifdIfeDV1zpw== - dependencies: - "@antv/g-lite" "2.1.4" - "@antv/g-plugin-canvas-path-generator" "2.0.15" - "@antv/g-plugin-canvas-picker" "2.0.17" - "@antv/g-plugin-canvas-renderer" "2.1.4" - "@antv/g-plugin-dom-interaction" "2.1.4" - "@antv/g-plugin-html-renderer" "2.1.4" - "@antv/g-plugin-image-loader" "2.0.15" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - tslib "^2.5.3" - -"@antv/g-canvas@^2.0.24": - version "2.0.25" - resolved "https://registry.yarnpkg.com/@antv/g-canvas/-/g-canvas-2.0.25.tgz#4558fe57b5b6ca823ab6461eb3ce302927443670" - integrity sha512-dHpEghB4f+Ts9b3vNzRf43xZ/V6xhLNYOT/0ZbK30fV495bo3Jn+sL1SSjeMnaTJkU6heBso/9yBn1ns2U7/Vw== - dependencies: - "@antv/g-lite" "2.2.2" - "@antv/g-plugin-canvas-path-generator" "2.1.2" - "@antv/g-plugin-canvas-picker" "2.1.4" - "@antv/g-plugin-canvas-renderer" "2.2.4" - "@antv/g-plugin-dom-interaction" "2.1.7" - "@antv/g-plugin-html-renderer" "2.1.7" - "@antv/g-plugin-image-loader" "2.1.4" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - tslib "^2.5.3" - -"@antv/g-dom-mutation-observer-api@2.0.15": - version "2.0.15" - resolved "https://registry.yarnpkg.com/@antv/g-dom-mutation-observer-api/-/g-dom-mutation-observer-api-2.0.15.tgz#035199a7dca34514308edb723bfc59f0560b10ac" - integrity sha512-wHlTNJuVw899GaiWIBVtW6KG5Naf/xZXzK6GSX0z5QlcmNx5tZhUWFNreLebbADrHbr9XBNN2B5m3ndCX7500w== - dependencies: - "@antv/g-lite" "2.1.4" - "@babel/runtime" "^7.25.6" - -"@antv/g-dom-mutation-observer-api@2.0.18": - version "2.0.18" - resolved "https://registry.yarnpkg.com/@antv/g-dom-mutation-observer-api/-/g-dom-mutation-observer-api-2.0.18.tgz#4ace5f4932dc3ce594912800487d6581ab193c18" - integrity sha512-LOriTfw9iSJVQv24VpBKnwWTy2Axv1JxnOpfC6siV8M1D+5cfv+fBmWs6cIQKxM9p7RZSkpaYFQWM27+sH0AvA== - dependencies: - "@antv/g-lite" "2.2.2" - "@babel/runtime" "^7.25.6" - -"@antv/g-lite@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@antv/g-lite/-/g-lite-2.1.4.tgz#28ec3e971ff86c031b51750bb2765525dce79cda" - integrity sha512-RPs+NdvbD1WQpdoSB8pb7Ugrauxc0a2z4QJxvNWhjVPJqnB6GUlnLDKpbd+2vqzOf4GhObAfAdPbOXjA1HkHRw== - dependencies: - "@antv/g-math" "3.0.0" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - d3-color "^3.1.0" - eventemitter3 "^5.0.1" - gl-matrix "^3.4.3" - rbush "^3.0.1" - tslib "^2.5.3" - -"@antv/g-lite@2.2.2": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@antv/g-lite/-/g-lite-2.2.2.tgz#154d60f68f842c1f3e45f91d8741ae2edd152c5a" - integrity sha512-Ffk7Jar+n6lUA+TEvoEaN30rAWe5l6Ybic7lucA90PKiyCsN0w+qVGJzbvskamuwp3RmcSZDNwQGP8vg374dCA== - dependencies: - "@antv/g-math" "3.0.0" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - d3-color "^3.1.0" - eventemitter3 "^5.0.1" - gl-matrix "^3.4.3" - rbush "^3.0.1" - tslib "^2.5.3" - -"@antv/g-math@3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@antv/g-math/-/g-math-3.0.0.tgz#834d993391546e39ae5a30452572fdc49a7c57ec" - integrity sha512-AkmiNIEL1vgqTPeGY2wtsMdBBqKFwF7SKSgs+D1iOS/rqYMsXdhp/HvtuQ5tx/HdawE/ZzTiicIYopc520ADZw== - dependencies: - "@antv/util" "^3.3.5" - gl-matrix "^3.4.3" - tslib "^2.5.3" - -"@antv/g-plugin-canvas-path-generator@2.0.15": - version "2.0.15" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-canvas-path-generator/-/g-plugin-canvas-path-generator-2.0.15.tgz#7edbf403802cf5343f8145cba4c3c3854091d1aa" - integrity sha512-QsPYUdlgSvCOu6e6KrCdfaZPH3H15hY6kFJDGRSrtydAS/IM/YqgXxsw8Xl2nByCjSg48alwNuFoajgu+7dgzA== - dependencies: - "@antv/g-lite" "2.1.4" - "@antv/g-math" "3.0.0" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - tslib "^2.5.3" - -"@antv/g-plugin-canvas-path-generator@2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-canvas-path-generator/-/g-plugin-canvas-path-generator-2.1.2.tgz#b96f5a572eebb66f2607414fe180f84c9fb67667" - integrity sha512-ILaKEQvbAZNkRhbE3kWxd0EszDAb3TE0HQKNfur7YuVkQMgACDj6jbVUULFPcGpcw/pQbWM8nD1RRf24hfFAlw== - dependencies: - "@antv/g-lite" "2.2.2" - "@antv/g-math" "3.0.0" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - tslib "^2.5.3" - -"@antv/g-plugin-canvas-picker@2.0.17": - version "2.0.17" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-canvas-picker/-/g-plugin-canvas-picker-2.0.17.tgz#a213d9d7a0a6613c7456f65514fa1d7efb3fc312" - integrity sha512-A36VnA25QCh38xhnuVHwK5/NKp5dAQ0Y1ZoQ6JTZHNWox+ZChjSDbfimtQOVQQaixoX4O6JFtFroUgno7vHIkg== - dependencies: - "@antv/g-lite" "2.1.4" - "@antv/g-math" "3.0.0" - "@antv/g-plugin-canvas-path-generator" "2.0.15" - "@antv/g-plugin-canvas-renderer" "2.1.4" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - gl-matrix "^3.4.3" - tslib "^2.5.3" - -"@antv/g-plugin-canvas-picker@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-canvas-picker/-/g-plugin-canvas-picker-2.1.4.tgz#963dfe2a8a6771ab969910e52a7d1e1a6ef7f0ed" - integrity sha512-L/L7wiZVCnLumIsE1DU14t4kwnwOLyQlsB3ZE/Q6ZVEf6UiZp1ARrGR8zzz+yUPxeWiJqduVnBRLOkyZkdjOoQ== - dependencies: - "@antv/g-lite" "2.2.2" - "@antv/g-math" "3.0.0" - "@antv/g-plugin-canvas-path-generator" "2.1.2" - "@antv/g-plugin-canvas-renderer" "2.2.4" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - gl-matrix "^3.4.3" - tslib "^2.5.3" - -"@antv/g-plugin-canvas-renderer@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-canvas-renderer/-/g-plugin-canvas-renderer-2.1.4.tgz#677449301512e30ae4ec6975689b19f3e8241f41" - integrity sha512-VtcaCZP04zsjocKNVaHTE0323GZh5J+y9ZwF316mfwD3+Ji97ueRIrXDmi0oVmnC64T3N3JKwOy+xoKOd/mw6g== - dependencies: - "@antv/g-lite" "2.1.4" - "@antv/g-math" "3.0.0" - "@antv/g-plugin-canvas-path-generator" "2.0.15" - "@antv/g-plugin-image-loader" "2.0.15" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - gl-matrix "^3.4.3" - tslib "^2.5.3" - -"@antv/g-plugin-canvas-renderer@2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-canvas-renderer/-/g-plugin-canvas-renderer-2.2.4.tgz#3ac4227c16dfc52f074bc8343a9eea4afc99d403" - integrity sha512-cmqVUB+aAglj/gYvc8Zcqjwe9PXOapyTE6Q9ChYVnlStP8rMdyXgdfACNa7pi7T1TWoq2etuEp29t4k+oGKLqQ== - dependencies: - "@antv/g-lite" "2.2.2" - "@antv/g-math" "3.0.0" - "@antv/g-plugin-canvas-path-generator" "2.1.2" - "@antv/g-plugin-image-loader" "2.1.4" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - gl-matrix "^3.4.3" - tslib "^2.5.3" - -"@antv/g-plugin-dom-interaction@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-dom-interaction/-/g-plugin-dom-interaction-2.1.4.tgz#56c4657ffddfa0a3ab86a4c7cb91df2b87c85b2f" - integrity sha512-ewWyGj4ipYMeh9yyoZVjyOOyfGDvzIVFZ1TrY1LYh75UsOQGbCIF/IdeBCOYxM/hEi9nQAVlxgc5g6AOH6CCXw== - dependencies: - "@antv/g-lite" "2.1.4" - "@babel/runtime" "^7.25.6" - tslib "^2.5.3" - -"@antv/g-plugin-dom-interaction@2.1.7": - version "2.1.7" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-dom-interaction/-/g-plugin-dom-interaction-2.1.7.tgz#93ffb590701999b742628936390a5890b81fab80" - integrity sha512-wrlcFlWsXq9Pa6ju5de8V0TzIR2GivBOkRyHQuNba6kpUC1RAT0EHpTpPCJML+Jys8nt8A0ppssSF4E0jZ1MpQ== - dependencies: - "@antv/g-lite" "2.2.2" - "@babel/runtime" "^7.25.6" - tslib "^2.5.3" - -"@antv/g-plugin-dragndrop@^2.0.0": - version "2.0.15" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-dragndrop/-/g-plugin-dragndrop-2.0.15.tgz#8e710a8f66ed971c8d500cae82664de913405e05" - integrity sha512-221a2JbupxPxaLgDOTp3rXvf2824CeotlQWaQFkDbt7MPAKSOseC4di2WscH6pNqR4uaXaDzoswo3rNRgWcaBw== - dependencies: - "@antv/g-lite" "2.1.4" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - tslib "^2.5.3" - -"@antv/g-plugin-dragndrop@^2.0.18": - version "2.0.18" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-dragndrop/-/g-plugin-dragndrop-2.0.18.tgz#1e7df5d6e40a29befc4e563ceb19f2f6d6b972e0" - integrity sha512-kjRj/yoWXh5J0Db/gtB3TFNlR4x3dMWvlRz6+7M3ka/mlrdMS0SGFLZfbWq6xvN+TzyiDXZdzSwA0A0OkA5Jww== - dependencies: - "@antv/g-lite" "2.2.2" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - tslib "^2.5.3" - -"@antv/g-plugin-html-renderer@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-html-renderer/-/g-plugin-html-renderer-2.1.4.tgz#e5604a9890495fa33e9f02f7648ec0d83cd8a5f5" - integrity sha512-a/1m1xOpAWusFtWsKq4ohJUWFrMbjc1g0LCPz1mu2D0r9AMl9fXhPaPmm5XapTkWBHHpOiJKEV8wsNVgkMK5Zw== - dependencies: - "@antv/g-lite" "2.1.4" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - gl-matrix "^3.4.3" - tslib "^2.5.3" - -"@antv/g-plugin-html-renderer@2.1.7": - version "2.1.7" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-html-renderer/-/g-plugin-html-renderer-2.1.7.tgz#ec4874d86a887809e9ef2b096132d6a50c1b53d4" - integrity sha512-MPquXo9MT9QHUdbvgwcBr9msNv9Qh2slOKpc1fzwWG7/aLpmOAxTAhd3+Sjj8JuVXHYw25Q3seI8lMJSGwTWvw== - dependencies: - "@antv/g-lite" "2.2.2" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - gl-matrix "^3.4.3" - tslib "^2.5.3" - -"@antv/g-plugin-image-loader@2.0.15": - version "2.0.15" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-image-loader/-/g-plugin-image-loader-2.0.15.tgz#7928c4abf7ff283deddde78bc35a34bc46a53932" - integrity sha512-iP991rOUoJHUyX8GoJ5ASRexZyhEJfMvfTZoWIpduTcH2EiYJh/luLwKedlUQ71haF3/TraEJPE+0LzGnxOeJQ== - dependencies: - "@antv/g-lite" "2.1.4" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - gl-matrix "^3.4.3" - tslib "^2.5.3" - -"@antv/g-plugin-image-loader@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-image-loader/-/g-plugin-image-loader-2.1.4.tgz#0b4f5e00a0941178db4efd54afc182b47c6ce543" - integrity sha512-dRikFBY/GNvMW7ecbDerChgnKnjZQoSNONR8uL/bZ1vurTZTd9HK1FYz2QDjLIRePWNTlfcWmux1RooSKfo1OA== - dependencies: - "@antv/g-lite" "2.2.2" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - gl-matrix "^3.4.3" - tslib "^2.5.3" - -"@antv/g-plugin-svg-picker@2.0.20": - version "2.0.20" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-svg-picker/-/g-plugin-svg-picker-2.0.20.tgz#5401d37816a1bb6c8457fabfe38bc0eddeb2b383" - integrity sha512-gML0upmK24Bqr8REsuW8ZZqlKNHSEDtYlycFof36HDcKVNDWIf22ff5N3op1rCHkbny6XXjkIXoKulfQM6GHOA== - dependencies: - "@antv/g-lite" "2.2.2" - "@antv/g-plugin-svg-renderer" "2.2.2" - "@babel/runtime" "^7.25.6" - tslib "^2.5.3" - -"@antv/g-plugin-svg-renderer@2.2.2": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@antv/g-plugin-svg-renderer/-/g-plugin-svg-renderer-2.2.2.tgz#c48bd91df47e73ebce22a9e8eab63cedecc221ff" - integrity sha512-fWQ5gVSxcZr+Ip95wVeHY2WtoWnsyGDqj+lkxK1aAq0uzqI2qIVyawUc86R87Bv4UVY9b0B5KCtig5F5SGGwAg== - dependencies: - "@antv/g-lite" "2.2.2" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - gl-matrix "^3.4.3" - tslib "^2.5.3" - -"@antv/g-svg@^2.0.11": - version "2.0.20" - resolved "https://registry.yarnpkg.com/@antv/g-svg/-/g-svg-2.0.20.tgz#6f8377bd310381102b80930756a99e97f565b5aa" - integrity sha512-KMb5VzQ3nZpBBwH+sRIJGh/umfEpv4SmDWwV/Yy4nAn0X91yq++qEiIDCnq9QSXHOESV6Si7XJbo4WtX5G7TPw== - dependencies: - "@antv/g-lite" "2.2.2" - "@antv/g-plugin-dom-interaction" "2.1.7" - "@antv/g-plugin-svg-picker" "2.0.20" - "@antv/g-plugin-svg-renderer" "2.2.2" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - tslib "^2.5.3" - -"@antv/g-web-animations-api@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@antv/g-web-animations-api/-/g-web-animations-api-2.1.4.tgz#aaa2be6014fc58b9d8f6724b053b7893eae4cc18" - integrity sha512-KLPrP3HsnKDKdoBF3ijiXY8QJx8FrdRRPFmz1uVVIzyfJa7UVX5HOi7soK7Nh6Grg3eiy99Y9y9U1IQe41J5fQ== - dependencies: - "@antv/g-lite" "2.1.4" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - tslib "^2.5.3" - -"@antv/g-web-animations-api@2.1.7": - version "2.1.7" - resolved "https://registry.yarnpkg.com/@antv/g-web-animations-api/-/g-web-animations-api-2.1.7.tgz#c56868ebaa8ddc7aca79c9314f6a211f91847039" - integrity sha512-yx7ZwLUgiglCe7sSisloWesO8gwgNwTGeDE0fTHhk1kZmO0BxZ8q6/VxlmLgXTyqdRso+6wfsTo2HukMURJSNw== - dependencies: - "@antv/g-lite" "2.2.2" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - tslib "^2.5.3" - -"@antv/g2-extension-plot@^0.2.0": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@antv/g2-extension-plot/-/g2-extension-plot-0.2.1.tgz#664a3cdf2d4d708ed1231ee90d984812b6226d29" - integrity sha512-WNv/LIUNJLwlfG8XXmKUbje9PbImtJqh36UDvuOk/uu+kmP/uMyHAXsBuu0yCOWdQgBVTVwoxszxJOCnY4mVfg== - dependencies: - "@antv/g2" "^5.1.8" - "@antv/util" "^3.3.5" - d3-array "^3.2.4" - d3-hierarchy "^3.1.2" - -"@antv/g2@^5.1.18", "@antv/g2@^5.1.8": - version "5.2.7" - resolved "https://registry.yarnpkg.com/@antv/g2/-/g2-5.2.7.tgz#fa131b0343a1a8a771b255d9efd57e2ded4a7503" - integrity sha512-bOU7ZJfa735KCqIsWWwlFtn3pc8TwJIckBhy7X8PFcxTuMIXzgqOt7vbMMdF4psBHMyIIOCDAo8zf9rGhgjEzA== - dependencies: - "@antv/component" "^2.0.0" - "@antv/coord" "^0.4.6" - "@antv/event-emitter" "^0.1.3" - "@antv/g" "^6.0.0" - "@antv/g-canvas" "^2.0.0" - "@antv/g-plugin-dragndrop" "^2.0.0" - "@antv/scale" "^0.4.12" - "@antv/util" "^3.3.5" - d3-array "^3.2.4" - d3-dsv "^3.0.1" - d3-force "^3.0.0" - d3-format "^3.1.0" - d3-geo "^3.1.0" - d3-hierarchy "^3.1.2" - d3-path "^3.1.0" - d3-scale-chromatic "^3.0.0" - d3-shape "^3.2.0" - flru "^1.0.2" - fmin "^0.0.2" - pdfast "^0.2.0" - -"@antv/g6-extension-react@^0.1.7": - version "0.1.7" - resolved "https://registry.yarnpkg.com/@antv/g6-extension-react/-/g6-extension-react-0.1.7.tgz#d0cf5cb2584383c47bd4d6fd3a5caeeca3943e11" - integrity sha512-fKk1weq2odHSTi5i8iSg9/keDPbufryA2TZ2X2j+qkSAwxJ7WtURagV/7/CUN9r1tMMk1eoiuzQZXdvc72a1GA== - dependencies: - "@antv/g" "^6.0.13" - "@antv/g-svg" "^2.0.11" - "@antv/react-g" "^2.0.14" - -"@antv/g6@^5.0.24", "@antv/g6@^5.0.28": - version "5.0.30" - resolved "https://registry.yarnpkg.com/@antv/g6/-/g6-5.0.30.tgz#0e241100bc0519ba0bf2c0ab11cb80a576f1796d" - integrity sha512-QEpNNAz/DcSnyHMJJ1UNSVjKgbfJ0zhwHcq8I/+f/mZl87oK1GlcRi8FCVcPGBb+W3OH8xfH5GIjPxEPma1kxg== - dependencies: - "@antv/algorithm" "^0.1.26" - "@antv/component" "^2.1.1" - "@antv/event-emitter" "^0.1.3" - "@antv/g" "^6.1.7" - "@antv/g-canvas" "^2.0.24" - "@antv/g-plugin-dragndrop" "^2.0.18" - "@antv/graphlib" "^2.0.3" - "@antv/hierarchy" "^0.6.14" - "@antv/layout" "1.2.14-beta.9" - "@antv/util" "^3.3.10" - bubblesets-js "^2.3.4" - hull.js "^1.0.6" - -"@antv/g@6.1.7", "@antv/g@^6.0.13", "@antv/g@^6.1.7": - version "6.1.7" - resolved "https://registry.yarnpkg.com/@antv/g/-/g-6.1.7.tgz#3003b2532c4287f2208537bcca87bffd0e44c30a" - integrity sha512-qv8YnBKqX3Yjs85U9OnBa6E92tNAI3cKrBhDrI5EikzjVPqfcVQLx0P5Zo8uzCYt7m9jFpJCi/iaGvWX/fA14Q== - dependencies: - "@antv/g-camera-api" "2.0.21" - "@antv/g-dom-mutation-observer-api" "2.0.18" - "@antv/g-lite" "2.2.2" - "@antv/g-web-animations-api" "2.1.7" - "@babel/runtime" "^7.25.6" - -"@antv/g@^6.0.0", "@antv/g@^6.1.2": - version "6.1.4" - resolved "https://registry.yarnpkg.com/@antv/g/-/g-6.1.4.tgz#4279fc065ab2fe4581874fddb06c46ce5c58c841" - integrity sha512-YHiYqu1nmYFdzImvfHA8E2ZhwjLHljNsQ0D1jT5ONV/CX5S7PMINpjfB/6D+HqZuDS/umLGmfPqpk1lqjvPW9A== - dependencies: - "@antv/g-camera-api" "2.0.18" - "@antv/g-dom-mutation-observer-api" "2.0.15" - "@antv/g-lite" "2.1.4" - "@antv/g-web-animations-api" "2.1.4" - "@babel/runtime" "^7.25.6" - -"@antv/graphin@^3.0.2": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@antv/graphin/-/graphin-3.0.4.tgz#33f4ead798d8f1fa1bf885247c68ca94d18e17c4" - integrity sha512-7ce6RDI5Z6ud93yiyS7b+mmFrHJhlkwwNo53kb7P7KoCsnV7ioMONDE6Gw0ROeMSR6TwHtxGZUhHw9wxnPp82Q== - dependencies: - "@antv/g6" "^5.0.28" - -"@antv/graphlib@^2.0.0", "@antv/graphlib@^2.0.3": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@antv/graphlib/-/graphlib-2.0.3.tgz#493e05872851c897e2133b0968cf5c6f4f6c022e" - integrity sha512-EtQR+DIfsYy28tumTnH560v7yIzXZq0nSgFBZh76mMiV1oHEN1L4p6JKu7IMtILH14mDqzmYYYFetYoAODoQUw== - dependencies: - "@antv/event-emitter" "^0.1.3" - -"@antv/hierarchy@^0.6.14": - version "0.6.14" - resolved "https://registry.yarnpkg.com/@antv/hierarchy/-/hierarchy-0.6.14.tgz#4e8b4966c9c2a44aaa6f9da7008c4bd44d490385" - integrity sha512-V3uknf7bhynOqQDw2sg+9r9DwZ9pc6k/EcqyTFdfXB1+ydr7urisP0MipIuimucvQKN+Qkd+d6w601r1UIroqQ== - -"@antv/layout@1.2.14-beta.9": - version "1.2.14-beta.9" - resolved "https://registry.yarnpkg.com/@antv/layout/-/layout-1.2.14-beta.9.tgz#5c66a0f22158c545aabd1654a50bfc8c3bf93f98" - integrity sha512-wPlwBFMtq2lWZFc89/7Lzb8fjHnyKVZZ9zBb2h+zZIP0YWmVmHRE8+dqCiPKOyOGUXEdDtn813f1g107dCHZlg== - dependencies: - "@antv/event-emitter" "^0.1.3" - "@antv/graphlib" "^2.0.0" - "@antv/util" "^3.3.2" - "@naoak/workerize-transferable" "^0.1.0" - comlink "^4.4.1" - d3-force "^3.0.0" - d3-force-3d "^3.0.5" - d3-octree "^1.0.2" - d3-quadtree "^3.0.1" - dagre "^0.8.5" - ml-matrix "^6.10.4" - tslib "^2.5.0" - -"@antv/react-g@^2.0.14": - version "2.0.23" - resolved "https://registry.yarnpkg.com/@antv/react-g/-/react-g-2.0.23.tgz#74df2fb27eba3144fd914baf51b9607233b4b670" - integrity sha512-Cur5/B6kRRK7kxj5USsEobKcGAGoWG9fDltVJ/3m95kMN95Ayx2rbVFxc5NavTrnmhxO4OUkRIyb0PDBFApfQA== - dependencies: - "@antv/g" "6.1.7" - "@antv/util" "^3.3.5" - "@babel/runtime" "^7.25.6" - gl-matrix "^3.4.3" - react-reconciler "^0.26.2" - scheduler "^0.20.2" - tslib "^2.5.3" - -"@antv/scale@^0.4.12", "@antv/scale@^0.4.3": - version "0.4.16" - resolved "https://registry.yarnpkg.com/@antv/scale/-/scale-0.4.16.tgz#60557470668ccfe5217e482a01f05c0cbb706b62" - integrity sha512-5wg/zB5kXHxpTV5OYwJD3ja6R8yTiqIOkjOhmpEJiowkzRlbEC/BOyMvNUq5fqFIHnMCE9woO7+c3zxEQCKPjw== - dependencies: - "@antv/util" "^3.3.7" - color-string "^1.5.5" - fecha "^4.2.1" - -"@antv/util@^2.0.13": - version "2.0.17" - resolved "https://registry.yarnpkg.com/@antv/util/-/util-2.0.17.tgz#e8ef42aca7892815b229269f3dd10c6b3c7597a9" - integrity sha512-o6I9hi5CIUvLGDhth0RxNSFDRwXeywmt6ExR4+RmVAzIi48ps6HUy+svxOCayvrPBN37uE6TAc2KDofRo0nK9Q== - dependencies: - csstype "^3.0.8" - tslib "^2.0.3" - -"@antv/util@^3.3.10", "@antv/util@^3.3.2", "@antv/util@^3.3.5", "@antv/util@^3.3.7": - version "3.3.10" - resolved "https://registry.yarnpkg.com/@antv/util/-/util-3.3.10.tgz#6fb2560c0f42df61f824e1f995a1ed1bdb00eb9a" - integrity sha512-basGML3DFA3O87INnzvDStjzS+n0JLEhRnRsDzP9keiXz8gT1z/fTdmJAZFOzMMWxy+HKbi7NbSt0+8vz/OsBQ== - dependencies: - fast-deep-equal "^3.1.3" - gl-matrix "^3.3.0" - tslib "^2.3.1" - "@ardatan/relay-compiler@12.0.0": version "12.0.0" resolved "https://registry.yarnpkg.com/@ardatan/relay-compiler/-/relay-compiler-12.0.0.tgz#2e4cca43088e807adc63450e8cab037020e91106" @@ -770,10 +211,10 @@ regexpu-core "^6.1.1" semver "^6.3.1" -"@babel/helper-define-polyfill-provider@^0.6.2": - version "0.6.2" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz#18594f789c3594acb24cfdb4a7f7b7d2e8bd912d" - integrity sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ== +"@babel/helper-define-polyfill-provider@^0.6.2", "@babel/helper-define-polyfill-provider@^0.6.3": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz#f4f2792fae2ef382074bc2d713522cf24e6ddb21" + integrity sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg== dependencies: "@babel/helper-compilation-targets" "^7.22.6" "@babel/helper-plugin-utils" "^7.22.5" @@ -1634,7 +1075,7 @@ "@babel/plugin-transform-modules-commonjs" "^7.25.9" "@babel/plugin-transform-typescript" "^7.25.9" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.6", "@babel/runtime@^7.25.7", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.26.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== @@ -1698,23 +1139,6 @@ resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== -"@emotion/is-prop-valid@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz#d4175076679c6a26faa92b03bb786f9e52612337" - integrity sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw== - dependencies: - "@emotion/memoize" "^0.8.1" - -"@emotion/memoize@^0.8.1": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17" - integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== - -"@emotion/unitless@0.8.1": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" - integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== - "@emotion/unitless@^0.7.5": version "0.7.5" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" @@ -1770,9 +1194,9 @@ "@floating-ui/dom" "^1.0.0" "@floating-ui/react@^0.26.16": - version "0.26.27" - resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.26.27.tgz#402f7b4b2702650662705fe9cbe0f1d5607846a1" - integrity sha512-jLP72x0Kr2CgY6eTYi/ra3VA9LOkTo4C+DUTrbFgFOExKy3omYVmwMjNKqxAHdsnyLS96BIDLcO2SlnsNf8KUQ== + version "0.26.28" + resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.26.28.tgz#93f44ebaeb02409312e9df9507e83aab4a8c0dc7" + integrity sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw== dependencies: "@floating-ui/react-dom" "^2.1.2" "@floating-ui/utils" "^0.2.8" @@ -2009,9 +1433,9 @@ "@tanstack/react-virtual" "^3.8.1" "@heroicons/react@^2.0.18": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.1.5.tgz#1e13f34976cc542deae92353c01c8b3d7942e9ba" - integrity sha512-FuzFN+BsHa+7OxbvAERtgBTNeZpUjgM/MIizfVkSCL2/edriN0Hx/DWRCR//aPYwO5QX/YlgLGXk+E3PcfZwjA== + version "2.2.0" + resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.2.0.tgz#0c05124af50434a800773abec8d3af6a297d904b" + integrity sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ== "@humanwhocodes/config-array@^0.5.0": version "0.5.0" @@ -2091,20 +1515,6 @@ dependencies: "@lezer/common" "^1.0.0" -"@ljharb/resumer@~0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@ljharb/resumer/-/resumer-0.0.1.tgz#8a940a9192dd31f6a1df17564bbd26dc6ad3e68d" - integrity sha512-skQiAOrCfO7vRTq53cxznMpks7wS1va95UCidALlOVWqvBAzwPVErwizDwoMqNVMEn1mDq0utxZd02eIrvF1lw== - dependencies: - "@ljharb/through" "^2.3.9" - -"@ljharb/through@^2.3.9", "@ljharb/through@~2.3.9": - version "2.3.13" - resolved "https://registry.yarnpkg.com/@ljharb/through/-/through-2.3.13.tgz#b7e4766e0b65aa82e529be945ab078de79874edc" - integrity sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ== - dependencies: - call-bind "^1.0.7" - "@lmdb/lmdb-darwin-arm64@2.5.2": version "2.5.2" resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.2.tgz#bc66fa43286b5c082e8fee0eacc17995806b6fbe" @@ -2248,11 +1658,6 @@ resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz#0aa5502d547b57abfc4ac492de68e2006e417242" integrity sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ== -"@naoak/workerize-transferable@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@naoak/workerize-transferable/-/workerize-transferable-0.1.0.tgz#864cc8241b977bffd8661c0be1441da9b4bfb633" - integrity sha512-fDLfuP71IPNP5+zSfxFb52OHgtjZvauRJWbVnpzQ7G7BjcbLjTny0OW1d3ZO806XKpLWNKmeeW3MhE0sy8iwYQ== - "@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": version "5.1.1-v1" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129" @@ -2562,88 +1967,94 @@ "@parcel/source-map" "^2.1.1" chalk "^4.1.0" -"@parcel/watcher-android-arm64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz#c2c19a3c442313ff007d2d7a9c2c1dd3e1c9ca84" - integrity sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg== +"@parcel/watcher-android-arm64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz#e32d3dda6647791ee930556aee206fcd5ea0fb7a" + integrity sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ== -"@parcel/watcher-darwin-arm64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz#c817c7a3b4f3a79c1535bfe54a1c2818d9ffdc34" - integrity sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA== +"@parcel/watcher-darwin-arm64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz#0d9e680b7e9ec1c8f54944f1b945aa8755afb12f" + integrity sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw== -"@parcel/watcher-darwin-x64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz#1a3f69d9323eae4f1c61a5f480a59c478d2cb020" - integrity sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg== +"@parcel/watcher-darwin-x64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz#f9f1d5ce9d5878d344f14ef1856b7a830c59d1bb" + integrity sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA== -"@parcel/watcher-freebsd-x64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz#0d67fef1609f90ba6a8a662bc76a55fc93706fc8" - integrity sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w== +"@parcel/watcher-freebsd-x64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz#2b77f0c82d19e84ff4c21de6da7f7d096b1a7e82" + integrity sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw== -"@parcel/watcher-linux-arm-glibc@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz#ce5b340da5829b8e546bd00f752ae5292e1c702d" - integrity sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA== +"@parcel/watcher-linux-arm-glibc@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz#92ed322c56dbafa3d2545dcf2803334aee131e42" + integrity sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA== -"@parcel/watcher-linux-arm64-glibc@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz#6d7c00dde6d40608f9554e73998db11b2b1ff7c7" - integrity sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA== +"@parcel/watcher-linux-arm-musl@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz#cd48e9bfde0cdbbd2ecd9accfc52967e22f849a4" + integrity sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA== -"@parcel/watcher-linux-arm64-musl@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz#bd39bc71015f08a4a31a47cd89c236b9d6a7f635" - integrity sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA== +"@parcel/watcher-linux-arm64-glibc@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz#7b81f6d5a442bb89fbabaf6c13573e94a46feb03" + integrity sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA== -"@parcel/watcher-linux-x64-glibc@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz#0ce29966b082fb6cdd3de44f2f74057eef2c9e39" - integrity sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg== +"@parcel/watcher-linux-arm64-musl@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz#dcb8ff01077cdf59a18d9e0a4dff7a0cfe5fd732" + integrity sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q== -"@parcel/watcher-linux-x64-musl@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz#d2ebbf60e407170bb647cd6e447f4f2bab19ad16" - integrity sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ== +"@parcel/watcher-linux-x64-glibc@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz#2e254600fda4e32d83942384d1106e1eed84494d" + integrity sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw== -"@parcel/watcher-win32-arm64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz#eb4deef37e80f0b5e2f215dd6d7a6d40a85f8adc" - integrity sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg== +"@parcel/watcher-linux-x64-musl@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz#01fcea60fedbb3225af808d3f0a7b11229792eef" + integrity sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA== -"@parcel/watcher-win32-ia32@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz#94fbd4b497be39fd5c8c71ba05436927842c9df7" - integrity sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw== +"@parcel/watcher-win32-arm64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz#87cdb16e0783e770197e52fb1dc027bb0c847154" + integrity sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig== -"@parcel/watcher-win32-x64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz#4bf920912f67cae5f2d264f58df81abfea68dadf" - integrity sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A== +"@parcel/watcher-win32-ia32@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz#778c39b56da33e045ba21c678c31a9f9d7c6b220" + integrity sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA== + +"@parcel/watcher-win32-x64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz#33873876d0bbc588aacce38e90d1d7480ce81cb7" + integrity sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw== "@parcel/watcher@^2.0.7": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.4.1.tgz#a50275151a1bb110879c6123589dba90c19f1bf8" - integrity sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA== + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.0.tgz#5c88818b12b8de4307a9d3e6dc3e28eba0dfbd10" + integrity sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ== dependencies: detect-libc "^1.0.3" is-glob "^4.0.3" micromatch "^4.0.5" node-addon-api "^7.0.0" optionalDependencies: - "@parcel/watcher-android-arm64" "2.4.1" - "@parcel/watcher-darwin-arm64" "2.4.1" - "@parcel/watcher-darwin-x64" "2.4.1" - "@parcel/watcher-freebsd-x64" "2.4.1" - "@parcel/watcher-linux-arm-glibc" "2.4.1" - "@parcel/watcher-linux-arm64-glibc" "2.4.1" - "@parcel/watcher-linux-arm64-musl" "2.4.1" - "@parcel/watcher-linux-x64-glibc" "2.4.1" - "@parcel/watcher-linux-x64-musl" "2.4.1" - "@parcel/watcher-win32-arm64" "2.4.1" - "@parcel/watcher-win32-ia32" "2.4.1" - "@parcel/watcher-win32-x64" "2.4.1" + "@parcel/watcher-android-arm64" "2.5.0" + "@parcel/watcher-darwin-arm64" "2.5.0" + "@parcel/watcher-darwin-x64" "2.5.0" + "@parcel/watcher-freebsd-x64" "2.5.0" + "@parcel/watcher-linux-arm-glibc" "2.5.0" + "@parcel/watcher-linux-arm-musl" "2.5.0" + "@parcel/watcher-linux-arm64-glibc" "2.5.0" + "@parcel/watcher-linux-arm64-musl" "2.5.0" + "@parcel/watcher-linux-x64-glibc" "2.5.0" + "@parcel/watcher-linux-x64-musl" "2.5.0" + "@parcel/watcher-win32-arm64" "2.5.0" + "@parcel/watcher-win32-ia32" "2.5.0" + "@parcel/watcher-win32-x64" "2.5.0" "@parcel/workers@2.8.3": version "2.8.3" @@ -2766,19 +2177,7 @@ classnames "^2.3.2" rc-util "^5.24.4" -"@rc-component/trigger@^2.0.0", "@rc-component/trigger@^2.1.1": - version "2.2.3" - resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.2.3.tgz#b47e945115e2d0a7f7e067dbb9ed76c91c1b4385" - integrity sha512-X1oFIpKoXAMXNDYCviOmTfuNuYxE4h5laBsyCqVAVMjNHxoF3/uiyA7XdegK1XbCvBbCZ6P6byWrEoDRpKL8+A== - dependencies: - "@babel/runtime" "^7.23.2" - "@rc-component/portal" "^1.1.0" - classnames "^2.3.2" - rc-motion "^2.0.0" - rc-resize-observer "^1.3.1" - rc-util "^5.38.0" - -"@rc-component/trigger@^2.2.5": +"@rc-component/trigger@^2.0.0", "@rc-component/trigger@^2.1.1", "@rc-component/trigger@^2.2.5": version "2.2.5" resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.2.5.tgz#5ebe383e563e667b3fa24b6b32afedbab378a92e" integrity sha512-F1EJ4KjFpGAHAjuKvOyZB/6IZDkVx0bHl0M4fQM5wXcmm7lgTgVSSnR3bXwdmS6jOJGHOqfDxIJW3WUvwMIXhQ== @@ -2791,55 +2190,55 @@ rc-util "^5.38.0" "@react-aria/focus@^3.17.1": - version "3.18.4" - resolved "https://registry.yarnpkg.com/@react-aria/focus/-/focus-3.18.4.tgz#a6e95896bc8680d1b5bcd855e983fc2c195a1a55" - integrity sha512-91J35077w9UNaMK1cpMUEFRkNNz0uZjnSwiyBCFuRdaVuivO53wNC9XtWSDNDdcO5cGy87vfJRVAiyoCn/mjqA== + version "3.19.0" + resolved "https://registry.yarnpkg.com/@react-aria/focus/-/focus-3.19.0.tgz#82b9a5b83f023b943a7970df3d059f49d61df05d" + integrity sha512-hPF9EXoUQeQl1Y21/rbV2H4FdUR2v+4/I0/vB+8U3bT1CJ+1AFj1hc/rqx2DqEwDlEwOHN+E4+mRahQmlybq0A== dependencies: - "@react-aria/interactions" "^3.22.4" - "@react-aria/utils" "^3.25.3" - "@react-types/shared" "^3.25.0" + "@react-aria/interactions" "^3.22.5" + "@react-aria/utils" "^3.26.0" + "@react-types/shared" "^3.26.0" "@swc/helpers" "^0.5.0" clsx "^2.0.0" -"@react-aria/interactions@^3.21.3", "@react-aria/interactions@^3.22.4": - version "3.22.4" - resolved "https://registry.yarnpkg.com/@react-aria/interactions/-/interactions-3.22.4.tgz#88ed61ab6a485f869bc1f65ae6688d48ca96064b" - integrity sha512-E0vsgtpItmknq/MJELqYJwib+YN18Qag8nroqwjk1qOnBa9ROIkUhWJerLi1qs5diXq9LHKehZDXRlwPvdEFww== +"@react-aria/interactions@^3.21.3", "@react-aria/interactions@^3.22.5": + version "3.22.5" + resolved "https://registry.yarnpkg.com/@react-aria/interactions/-/interactions-3.22.5.tgz#9cd8c93b8b6988f1d315d3efb450119d1432bbb8" + integrity sha512-kMwiAD9E0TQp+XNnOs13yVJghiy8ET8L0cbkeuTgNI96sOAp/63EJ1FSrDf17iD8sdjt41LafwX/dKXW9nCcLQ== dependencies: - "@react-aria/ssr" "^3.9.6" - "@react-aria/utils" "^3.25.3" - "@react-types/shared" "^3.25.0" + "@react-aria/ssr" "^3.9.7" + "@react-aria/utils" "^3.26.0" + "@react-types/shared" "^3.26.0" "@swc/helpers" "^0.5.0" -"@react-aria/ssr@^3.9.6": - version "3.9.6" - resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.9.6.tgz#a9e8b351acdc8238f2b5215b0ce904636c6ea690" - integrity sha512-iLo82l82ilMiVGy342SELjshuWottlb5+VefO3jOQqQRNYnJBFpUSadswDPbRimSgJUZuFwIEYs6AabkP038fA== +"@react-aria/ssr@^3.9.7": + version "3.9.7" + resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.9.7.tgz#d89d129f7bbc5148657e6c952ac31c9353183770" + integrity sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg== dependencies: "@swc/helpers" "^0.5.0" -"@react-aria/utils@^3.25.3": - version "3.25.3" - resolved "https://registry.yarnpkg.com/@react-aria/utils/-/utils-3.25.3.tgz#cad9bffc07b045cdc283df2cb65c18747acbf76d" - integrity sha512-PR5H/2vaD8fSq0H/UB9inNbc8KDcVmW6fYAfSWkkn+OAdhTTMVKqXXrZuZBWyFfSD5Ze7VN6acr4hrOQm2bmrA== +"@react-aria/utils@^3.26.0": + version "3.26.0" + resolved "https://registry.yarnpkg.com/@react-aria/utils/-/utils-3.26.0.tgz#833cbfa33e75d15835b757791b3f754432d2f948" + integrity sha512-LkZouGSjjQ0rEqo4XJosS4L3YC/zzQkfRM3KoqK6fUOmUJ9t0jQ09WjiF+uOoG9u+p30AVg3TrZRUWmoTS+koQ== dependencies: - "@react-aria/ssr" "^3.9.6" - "@react-stately/utils" "^3.10.4" - "@react-types/shared" "^3.25.0" + "@react-aria/ssr" "^3.9.7" + "@react-stately/utils" "^3.10.5" + "@react-types/shared" "^3.26.0" "@swc/helpers" "^0.5.0" clsx "^2.0.0" -"@react-stately/utils@^3.10.4": - version "3.10.4" - resolved "https://registry.yarnpkg.com/@react-stately/utils/-/utils-3.10.4.tgz#310663a834b67048d305e1680ed258130092fe51" - integrity sha512-gBEQEIMRh5f60KCm7QKQ2WfvhB2gLUr9b72sqUdIZ2EG+xuPgaIlCBeSicvjmjBvYZwOjoOEnmIkcx2GHp/HWw== +"@react-stately/utils@^3.10.5": + version "3.10.5" + resolved "https://registry.yarnpkg.com/@react-stately/utils/-/utils-3.10.5.tgz#47bb91cd5afd1bafe39353614e5e281b818ebccc" + integrity sha512-iMQSGcpaecghDIh3mZEpZfoFH3ExBwTtuBEcvZ2XnGzCgQjeYXcMdIUwAfVQLXFTdHUHGF6Gu6/dFrYsCzySBQ== dependencies: "@swc/helpers" "^0.5.0" -"@react-types/shared@^3.25.0": - version "3.25.0" - resolved "https://registry.yarnpkg.com/@react-types/shared/-/shared-3.25.0.tgz#7223baf72256e918a3c29081bb1ecc6fad4fbf58" - integrity sha512-OZSyhzU6vTdW3eV/mz5i6hQwQUhkRs7xwY2d1aqPvTdMe0+2cY7Fwp45PAiwYLEj73i9ro2FxF9qC4DvHGSCgQ== +"@react-types/shared@^3.26.0": + version "3.26.0" + resolved "https://registry.yarnpkg.com/@react-types/shared/-/shared-3.26.0.tgz#21a8b579f0097ee78de18e3e580421ced89e4c8c" + integrity sha512-6FuPqvhmjjlpEDLTiYx29IJCbCNWPlsyO+ZUmCUXzhUv2ttShOXfw8CmeHWHftT/b2KweAWuzqSlfeXPR76jpw== "@rtsao/scc@^1.1.0": version "1.1.0" @@ -3216,10 +2615,10 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== -"@types/node@*", "@types/node@>=10.0.0": - version "22.8.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.8.6.tgz#e8a0c0871623283d8b3ef7d7b9b1bfdfd3028e22" - integrity sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw== +"@types/node@*", "@types/node@>=10.0.0", "@types/node@^22.9.0": + version "22.9.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.2.tgz#51e58f2bf29cc38f529dacbb0eafca890464138e" + integrity sha512-wwuxAVEbsRvDD9x7buvAl7DyQ7Oj+va/d/Veug7higYzp9MF0CINbfWTBgDFMpcVwcdUiYuNmX2KfnvY3N70mw== dependencies: undici-types "~6.19.8" @@ -3228,13 +2627,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== -"@types/node@^22.9.0": - version "22.9.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.0.tgz#b7f16e5c3384788542c72dc3d561a7ceae2c0365" - integrity sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ== - dependencies: - undici-types "~6.19.8" - "@types/node@^8.5.7": version "8.10.66" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3" @@ -3306,11 +2698,6 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== -"@types/stylis@4.2.5": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@types/stylis/-/stylis-4.2.5.tgz#1daa6456f40959d06157698a653a9ab0a70281df" - integrity sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw== - "@types/tmp@^0.0.33": version "0.0.33" resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.0.33.tgz#1073c4bc824754ae3d10cfab88ab0237ba964e4d" @@ -3432,125 +2819,125 @@ dependencies: resolve "^1.10.0" -"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" - integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== +"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.12.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" + integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== dependencies: - "@webassemblyjs/helper-numbers" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-numbers" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" -"@webassemblyjs/floating-point-hex-parser@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" - integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== +"@webassemblyjs/floating-point-hex-parser@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb" + integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA== -"@webassemblyjs/helper-api-error@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" - integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== +"@webassemblyjs/helper-api-error@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7" + integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ== -"@webassemblyjs/helper-buffer@1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6" - integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw== +"@webassemblyjs/helper-buffer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b" + integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA== -"@webassemblyjs/helper-numbers@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" - integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== +"@webassemblyjs/helper-numbers@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d" + integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA== dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.11.6" - "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/floating-point-hex-parser" "1.13.2" + "@webassemblyjs/helper-api-error" "1.13.2" "@xtuc/long" "4.2.2" -"@webassemblyjs/helper-wasm-bytecode@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" - integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== +"@webassemblyjs/helper-wasm-bytecode@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b" + integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA== -"@webassemblyjs/helper-wasm-section@1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf" - integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g== +"@webassemblyjs/helper-wasm-section@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348" + integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw== dependencies: - "@webassemblyjs/ast" "1.12.1" - "@webassemblyjs/helper-buffer" "1.12.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/wasm-gen" "1.14.1" -"@webassemblyjs/ieee754@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" - integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== +"@webassemblyjs/ieee754@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba" + integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" - integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== +"@webassemblyjs/leb128@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0" + integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw== dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" - integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== +"@webassemblyjs/utf8@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" + integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== "@webassemblyjs/wasm-edit@^1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" - integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== - dependencies: - "@webassemblyjs/ast" "1.12.1" - "@webassemblyjs/helper-buffer" "1.12.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/helper-wasm-section" "1.12.1" - "@webassemblyjs/wasm-gen" "1.12.1" - "@webassemblyjs/wasm-opt" "1.12.1" - "@webassemblyjs/wasm-parser" "1.12.1" - "@webassemblyjs/wast-printer" "1.12.1" - -"@webassemblyjs/wasm-gen@1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547" - integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w== - dependencies: - "@webassemblyjs/ast" "1.12.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/ieee754" "1.11.6" - "@webassemblyjs/leb128" "1.11.6" - "@webassemblyjs/utf8" "1.11.6" - -"@webassemblyjs/wasm-opt@1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5" - integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg== - dependencies: - "@webassemblyjs/ast" "1.12.1" - "@webassemblyjs/helper-buffer" "1.12.1" - "@webassemblyjs/wasm-gen" "1.12.1" - "@webassemblyjs/wasm-parser" "1.12.1" - -"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" - integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== - dependencies: - "@webassemblyjs/ast" "1.12.1" - "@webassemblyjs/helper-api-error" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/ieee754" "1.11.6" - "@webassemblyjs/leb128" "1.11.6" - "@webassemblyjs/utf8" "1.11.6" - -"@webassemblyjs/wast-printer@1.12.1": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac" - integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA== - dependencies: - "@webassemblyjs/ast" "1.12.1" + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" + integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/helper-wasm-section" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-opt" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + "@webassemblyjs/wast-printer" "1.14.1" + +"@webassemblyjs/wasm-gen@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570" + integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" + +"@webassemblyjs/wasm-opt@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b" + integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + +"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.12.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" + integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-api-error" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" + +"@webassemblyjs/wast-printer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07" + integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw== + dependencies: + "@webassemblyjs/ast" "1.14.1" "@xtuc/long" "4.2.2" "@xtuc/ieee754@^1.2.0": @@ -3586,9 +2973,9 @@ d3-zoom "^3.0.0" abortcontroller-polyfill@^1.1.9: - version "1.7.5" - resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz#6738495f4e901fbb57b6c0611d0c75f76c485bed" - integrity sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ== + version "1.7.6" + resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.6.tgz#7be8d35b5ed7dfa1a51b36f221720b23deb13f36" + integrity sha512-Zypm+LjYdWAzvuypZvDN0smUJrhOurcuBWhhMRBExqVLRvdjp3Z9mASxKyq19K+meZMshwjjy5S0lkm388zE4Q== accepts@~1.3.4, accepts@~1.3.8: version "1.3.8" @@ -3676,20 +3063,6 @@ ajv@^8.0.0, ajv@^8.0.1, ajv@^8.9.0: json-schema-traverse "^1.0.0" require-from-string "^2.0.2" -align-text@^0.1.1, align-text@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" - integrity sha512-GrTZLRpmp6wIC2ztrWW9MjjTgSKccffgFagbNDOX95/dcjEcYZibYTeaOntySQLcdw1ztBoFkviiUvTMbb9MYg== - dependencies: - kind-of "^3.0.2" - longest "^1.0.1" - repeat-string "^1.5.2" - -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - integrity sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg== - anser@^2.1.1: version "2.3.0" resolved "https://registry.yarnpkg.com/anser/-/anser-2.3.0.tgz#9b30c8cffe864e646eebd6cd578ba67f2dfe8b2e" @@ -3744,11 +3117,6 @@ ansi-regex@^6.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== - ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -3769,9 +3137,9 @@ ansi-styles@^6.1.0: integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== antd@^5.22.1: - version "5.22.1" - resolved "https://registry.yarnpkg.com/antd/-/antd-5.22.1.tgz#726c9d4465a1f59079ef60896418fcdbb07a796c" - integrity sha512-itq8AZwe3IfawZH6SMM5XdbTz1xXGTTqA7sNN0qpEdxcoTpD5nRsCBAMIy+PhwcWFobgFc6ZlF8d7f8eicn0SQ== + version "5.22.2" + resolved "https://registry.yarnpkg.com/antd/-/antd-5.22.2.tgz#9f5d38c09685c018c368329f1a1107d5417536d6" + integrity sha512-vihhiJbm9VG3d6boUeD1q2MXMax+qBrXhgqCEC+45v8iGUF6m4Ct+lFiCW4oWaN3EABOsbVA6Svy3Rj/QkQFKw== dependencies: "@ant-design/colors" "^7.1.0" "@ant-design/cssinjs" "^1.21.1" @@ -3794,7 +3162,7 @@ antd@^5.22.1: rc-dialog "~9.6.0" rc-drawer "~7.2.0" rc-dropdown "~4.2.0" - rc-field-form "~2.5.0" + rc-field-form "~2.5.1" rc-image "~7.11.0" rc-input "~1.6.3" rc-input-number "~9.3.0" @@ -3803,7 +3171,7 @@ antd@^5.22.1: rc-motion "^2.9.3" rc-notification "~5.6.2" rc-pagination "~4.3.0" - rc-picker "~4.8.0" + rc-picker "~4.8.1" rc-progress "~4.0.0" rc-rate "~2.13.0" rc-resize-observer "^1.4.0" @@ -4097,12 +3465,12 @@ babel-plugin-macros@^3.1.0: resolve "^1.19.0" babel-plugin-polyfill-corejs2@^0.4.10: - version "0.4.11" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz#30320dfe3ffe1a336c15afdcdafd6fd615b25e33" - integrity sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q== + version "0.4.12" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz#ca55bbec8ab0edeeef3d7b8ffd75322e210879a9" + integrity sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og== dependencies: "@babel/compat-data" "^7.22.6" - "@babel/helper-define-polyfill-provider" "^0.6.2" + "@babel/helper-define-polyfill-provider" "^0.6.3" semver "^6.3.1" babel-plugin-polyfill-corejs3@^0.10.6: @@ -4114,11 +3482,11 @@ babel-plugin-polyfill-corejs3@^0.10.6: core-js-compat "^3.38.0" babel-plugin-polyfill-regenerator@^0.6.1: - version "0.6.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz#addc47e240edd1da1058ebda03021f382bba785e" - integrity sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg== + version "0.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz#abeb1f3f1c762eace37587f42548b08b57789bc8" + integrity sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q== dependencies: - "@babel/helper-define-polyfill-provider" "^0.6.2" + "@babel/helper-define-polyfill-provider" "^0.6.3" babel-plugin-remove-graphql-queries@^5.14.0: version "5.14.0" @@ -4230,9 +3598,9 @@ bare-path@^2.0.0, bare-path@^2.1.0: bare-os "^2.1.0" bare-stream@^2.0.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/bare-stream/-/bare-stream-2.3.2.tgz#3bc62b429bcf850d2f265719b7a49ee0630a3ae4" - integrity sha512-EFZHSIBkDgSHIwj2l2QZfP4U5OcD4xFAOwhSb/vlr9PIqyGJGvB/nfClJbcnh3EY4jtPE4zsb5ztae96bVF79A== + version "2.4.0" + resolved "https://registry.yarnpkg.com/bare-stream/-/bare-stream-2.4.0.tgz#2bd49033b69f69a8e2be80c0676c6e27967789d8" + integrity sha512-sd96/aZ8LjF1uJbEHzIo1LrERPKRFPEy1nZ1eOILftBxrVsFDAQkimHIIq87xrHcubzjNeETsD9PwN0wp+vLiQ== dependencies: streamx "^2.20.0" @@ -4365,11 +3733,6 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" -bubblesets-js@^2.3.4: - version "2.3.4" - resolved "https://registry.yarnpkg.com/bubblesets-js/-/bubblesets-js-2.3.4.tgz#8e1230b29c309e3327a05630fe02df3d96596ab6" - integrity sha512-DyMjHmpkS2+xcFNtyN00apJYL3ESdp9fTrkDr5+9Qg/GPqFmcWgGsK1akZnttE1XFxJ/VMy4DNNGMGYtmFp1Sg== - buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -4440,7 +3803,7 @@ cacheable-request@^7.0.2: normalize-url "^6.0.1" responselike "^2.0.0" -call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7, call-bind@~1.0.2: +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== @@ -4469,11 +3832,6 @@ camelcase-css@^2.0.1: resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== -camelcase@^1.0.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" - integrity sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g== - camelcase@^5.0.0: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -4484,11 +3842,6 @@ camelcase@^6.2.0, camelcase@^6.3.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -camelize@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3" - integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ== - caniuse-api@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" @@ -4500,9 +3853,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001669: - version "1.0.30001676" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz#fe133d41fe74af8f7cc93b8a714c3e86a86e6f04" - integrity sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw== + version "1.0.30001683" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001683.tgz#7f026a2d5d319a9cf8915a1451173052caaadc81" + integrity sha512-iqmNnThZ0n70mNwvxpEC2nBJ037ZHZUoBI5Gorh1Mw6IlEAZujEoU1tXA628iZfzm7R9FvFzxbfdgml82a3k8Q== capital-case@^1.0.4: version "1.0.4" @@ -4518,25 +3871,6 @@ ccount@^2.0.0: resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== -center-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" - integrity sha512-Baz3aNe2gd2LP2qk5U+sDk/m4oSuwSDcBfayTCTBoWpfIGO5XFxPmjILQII4NGiZjD6DoDI6kf7gKaxkf7s3VQ== - dependencies: - align-text "^0.1.3" - lazy-cache "^1.0.3" - -chalk@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -4629,7 +3963,7 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chokidar@^3.4.2, chokidar@^3.5.3: +chokidar@^3.4.2, chokidar@^3.5.3, chokidar@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -4695,15 +4029,6 @@ clipboardy@^4.0.0: is-wsl "^3.1.0" is64bit "^2.0.0" -cliui@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" - integrity sha512-GIOYRizG+TGoc7Wgc1LiOTLare95R3mzKgoln+Q/lE4ceiYH19gUpl0l0Ffq4lJDEf3FxujMe6IBfOCs7pfqNA== - dependencies: - center-align "^0.1.1" - right-align "^0.1.1" - wordwrap "0.0.2" - cliui@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" @@ -4763,7 +4088,7 @@ color-name@^1.0.0, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^1.5.5, color-string@^1.9.0: +color-string@^1.9.0: version "1.9.1" resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== @@ -4796,11 +4121,6 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -comlink@^4.4.1: - version "4.4.2" - resolved "https://registry.yarnpkg.com/comlink/-/comlink-4.4.2.tgz#cbbcd82742fbebc06489c28a183eedc5c60a2bca" - integrity sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g== - comma-separated-tokens@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" @@ -4811,11 +4131,6 @@ command-exists@^1.2.4: resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== -commander@7, commander@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== - commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -4826,6 +4141,11 @@ commander@^4.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + common-tags@1.8.2, common-tags@^1.8.2: version "1.8.2" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" @@ -4922,11 +4242,6 @@ content-type@~1.0.4, content-type@~1.0.5: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -contour_plot@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/contour_plot/-/contour_plot-0.0.1.tgz#475870f032b8e338412aa5fc507880f0bf495c77" - integrity sha512-Nil2HI76Xux6sVGORvhSS8v66m+/h5CwFkBJDO+U5vWaMdNC0yXNCsGDPbzPhvqOEU5koebhdEvD372LI+IyLw== - convert-hrtime@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/convert-hrtime/-/convert-hrtime-3.0.0.tgz#62c7593f5809ca10be8da858a6d2f702bcda00aa" @@ -5048,9 +4363,9 @@ cross-fetch@^3.1.5: node-fetch "^2.6.12" cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.5.tgz#910aac880ff5243da96b728bc6521a5f6c2f2f82" - integrity sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug== + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" @@ -5061,11 +4376,6 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== -css-color-keywords@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" - integrity sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg== - css-declaration-sorter@^6.3.1: version "6.4.1" resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz#28beac7c20bad7f1775be3a7129d7eae409a3a71" @@ -5116,15 +4426,6 @@ css-selector-parser@^1.0.0: resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-1.4.1.tgz#03f9cb8a81c3e5ab2c51684557d5aaf6d2569759" integrity sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g== -css-to-react-native@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.2.0.tgz#cdd8099f71024e149e4f6fe17a7d46ecd55f1e32" - integrity sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ== - dependencies: - camelize "^1.0.0" - css-color-keywords "^1.0.0" - postcss-value-parser "^4.0.2" - css-tree@^1.1.2, css-tree@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" @@ -5204,24 +4505,12 @@ csso@^4.2.0: dependencies: css-tree "^1.1.2" -csstype@3.1.3, csstype@^3.0.2, csstype@^3.0.8, csstype@^3.1.3: +csstype@^3.0.2, csstype@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== -"d3-array@2.5.0 - 3", d3-array@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5" - integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== - dependencies: - internmap "1 - 2" - -d3-binarytree@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/d3-binarytree/-/d3-binarytree-1.0.2.tgz#ed43ebc13c70fbabfdd62df17480bc5a425753cc" - integrity sha512-cElUNH+sHu95L04m92pG73t2MEJXKu+GeKUN1TJkFsu93E5W8E9Sc3kHEGJKgenGvj19m6upSn2EunvMgMD2Yw== - -"d3-color@1 - 3", d3-color@^3.1.0: +"d3-color@1 - 3": version "3.1.0" resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== @@ -5239,57 +4528,11 @@ d3-binarytree@1: d3-dispatch "1 - 3" d3-selection "3" -d3-dsv@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73" - integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q== - dependencies: - commander "7" - iconv-lite "0.6" - rw "1" - "d3-ease@1 - 3": version "3.0.1" resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4" integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== -d3-force-3d@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/d3-force-3d/-/d3-force-3d-3.0.5.tgz#9c8931b49acc3554f9110e128bc580cd3ab830f2" - integrity sha512-tdwhAhoTYZY/a6eo9nR7HP3xSW/C6XvJTbeRpR92nlPzH6OiE+4MliN9feuSFd0tPtEUo+191qOhCTWx3NYifg== - dependencies: - d3-binarytree "1" - d3-dispatch "1 - 3" - d3-octree "1" - d3-quadtree "1 - 3" - d3-timer "1 - 3" - -d3-force@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-3.0.0.tgz#3e2ba1a61e70888fe3d9194e30d6d14eece155c4" - integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg== - dependencies: - d3-dispatch "1 - 3" - d3-quadtree "1 - 3" - d3-timer "1 - 3" - -d3-format@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" - integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== - -d3-geo@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-3.1.1.tgz#6027cf51246f9b2ebd64f99e01dc7c3364033a4d" - integrity sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q== - dependencies: - d3-array "2.5.0 - 3" - -d3-hierarchy@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz#b01cd42c1eed3d46db77a5966cf726f8c09160c6" - integrity sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA== - "d3-interpolate@1 - 3": version "3.0.1" resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" @@ -5297,41 +4540,11 @@ d3-hierarchy@^3.1.2: dependencies: d3-color "1 - 3" -d3-octree@1, d3-octree@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/d3-octree/-/d3-octree-1.0.2.tgz#b39026b82701e45c7163e34ee056dc492035a017" - integrity sha512-Qxg4oirJrNXauiuC94uKMbgxwnhdda9xRLl9ihq45srlJ4Ga3CSgqGcAL8iW7N5CIv4Oz8x3E734ulxyvHPvwA== - -d3-path@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526" - integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== - -"d3-quadtree@1 - 3", d3-quadtree@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz#6dca3e8be2b393c9a9d514dabbd80a92deef1a4f" - integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw== - -d3-scale-chromatic@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz#34c39da298b23c20e02f1a4b239bd0f22e7f1314" - integrity sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ== - dependencies: - d3-color "1 - 3" - d3-interpolate "1 - 3" - "d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31" integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== -d3-shape@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5" - integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== - dependencies: - d3-path "^3.1.0" - "d3-timer@1 - 3": version "3.0.1" resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" @@ -5367,14 +4580,6 @@ d@1, d@^1.0.1, d@^1.0.2: es5-ext "^0.10.64" type "^2.7.2" -dagre@^0.8.5: - version "0.8.5" - resolved "https://registry.yarnpkg.com/dagre/-/dagre-0.8.5.tgz#ba30b0055dac12b6c1fcc247817442777d06afee" - integrity sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw== - dependencies: - graphlib "^2.1.8" - lodash "^4.17.15" - damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" @@ -5440,7 +4645,7 @@ debug@^3.0.1, debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" -decamelize@^1.0.0, decamelize@^1.2.0: +decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== @@ -5464,18 +4669,6 @@ decompress-response@^6.0.0: dependencies: mimic-response "^3.1.0" -deep-equal@~1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.2.tgz#78a561b7830eef3134c7f6f3a3d6af272a678761" - integrity sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg== - dependencies: - is-arguments "^1.1.1" - is-date-object "^1.0.5" - is-regex "^1.1.4" - object-is "^1.1.5" - object-keys "^1.1.1" - regexp.prototype.flags "^1.5.1" - deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -5496,7 +4689,7 @@ defer-to-connect@^2.0.0, defer-to-connect@^2.0.1: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== -define-data-property@^1.0.1, define-data-property@^1.1.1, define-data-property@^1.1.4: +define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== @@ -5519,11 +4712,6 @@ define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: has-property-descriptors "^1.0.0" object-keys "^1.1.1" -defined@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.1.tgz#c0b9db27bfaffd95d6f61399419b893df0f91ebf" - integrity sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q== - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -5719,13 +4907,6 @@ dotenv@^8.6.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== -dotignore@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/dotignore/-/dotignore-0.1.2.tgz#f942f2200d28c3a76fbdd6f0ee9f3257c8a2e905" - integrity sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw== - dependencies: - minimatch "^3.0.4" - duplexer@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" @@ -5742,9 +4923,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.5.41: - version "1.5.50" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz#d9ba818da7b2b5ef1f3dd32bce7046feb7e93234" - integrity sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw== + version "1.5.64" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.64.tgz#ac8c4c89075d35a1514b620f47dfe48a71ec3697" + integrity sha512-IXEuxU+5ClW2IGEYFC2T7szbyVgehupCWQe5GNh+H065CD6U6IFN0s4KeAMFGNmQolRU4IV7zGBWSYMmZ8uuqQ== emoji-regex@^8.0.0: version "8.0.0" @@ -5856,9 +5037,9 @@ error-stack-parser@^2.0.6, error-stack-parser@^2.1.4: stackframe "^1.3.4" es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3: - version "1.23.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" - integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + version "1.23.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.5.tgz#f4599a4946d57ed467515ed10e4f157289cd52fb" + integrity sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ== dependencies: array-buffer-byte-length "^1.0.1" arraybuffer.prototype.slice "^1.0.3" @@ -5875,7 +5056,7 @@ es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23 function.prototype.name "^1.1.6" get-intrinsic "^1.2.4" get-symbol-description "^1.0.2" - globalthis "^1.0.3" + globalthis "^1.0.4" gopd "^1.0.1" has-property-descriptors "^1.0.2" has-proto "^1.0.3" @@ -5891,10 +5072,10 @@ es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23 is-string "^1.0.7" is-typed-array "^1.1.13" is-weakref "^1.0.2" - object-inspect "^1.13.1" + object-inspect "^1.13.3" object-keys "^1.1.1" object.assign "^4.1.5" - regexp.prototype.flags "^1.5.2" + regexp.prototype.flags "^1.5.3" safe-array-concat "^1.1.2" safe-regex-test "^1.0.3" string.prototype.trim "^1.2.9" @@ -5920,9 +5101,9 @@ es-errors@^1.2.1, es-errors@^1.3.0: integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-iterator-helpers@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.1.0.tgz#f6d745d342aea214fe09497e7152170dc333a7a6" - integrity sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw== + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz#2f1a3ab998b30cb2d10b195b587c6d9ebdebf152" + integrity sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q== dependencies: call-bind "^1.0.7" define-properties "^1.2.1" @@ -5932,6 +5113,7 @@ es-iterator-helpers@^1.1.0: function-bind "^1.1.2" get-intrinsic "^1.2.4" globalthis "^1.0.4" + gopd "^1.0.1" has-property-descriptors "^1.0.2" has-proto "^1.0.3" has-symbols "^1.0.3" @@ -6358,11 +5540,6 @@ event-source-polyfill@1.0.31: resolved "https://registry.yarnpkg.com/event-source-polyfill/-/event-source-polyfill-1.0.31.tgz#45fb0a6fc1375b2ba597361ba4287ffec5bf2e0c" integrity sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA== -eventemitter3@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" - integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== - events@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -6487,7 +5664,7 @@ fast-fifo@^1.2.0, fast-fifo@^1.3.2: resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== -fast-glob@^3.2.9, fast-glob@^3.3.0: +fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -6550,11 +5727,6 @@ fbjs@^3.0.0: setimmediate "^1.0.5" ua-parser-js "^1.0.35" -fecha@^4.2.1: - version "4.2.3" - resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" - integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== - figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -6677,32 +5849,16 @@ flat@^5.0.2: integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== flatted@^3.2.9: - version "3.3.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" - integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== - -flru@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/flru/-/flru-1.0.2.tgz#1ae514c62b8b035ffff9ca9e4563ddcc817f4845" - integrity sha512-kWyh8ADvHBFz6ua5xYOPnUroZTT/bwWfrCeL0Wj1dzG4/YOmOcfJ99W8dOVyyynJN35rZ9aCOtHChqQovV7yog== - -fmin@^0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/fmin/-/fmin-0.0.2.tgz#59bbb40d43ffdc1c94cd00a568c41f95f1973017" - integrity sha512-sSi6DzInhl9d8yqssDfGZejChO8d2bAGIpysPsvYsxFe898z89XhCZg6CPNV3nhUhFefeC/AXZK2bAJxlBjN6A== - dependencies: - contour_plot "^0.0.1" - json2module "^0.0.3" - rollup "^0.25.8" - tape "^4.5.1" - uglify-js "^2.6.2" + version "3.3.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" + integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== follow-redirects@^1.15.6: version "1.15.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== -for-each@^0.3.3, for-each@~0.3.3: +for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== @@ -7388,11 +6544,6 @@ github-slugger@^2.0.0: resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-2.0.0.tgz#52cf2f9279a21eb6c59dd385b410f0c0adda8f1a" integrity sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw== -gl-matrix@^3.3.0, gl-matrix@^3.4.3: - version "3.4.3" - resolved "https://registry.yarnpkg.com/gl-matrix/-/gl-matrix-3.4.3.tgz#fc1191e8320009fd4d20e9339595c6041ddc22c9" - integrity sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA== - glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -7424,7 +6575,7 @@ glob@^10.3.10: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" -glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@^7.2.3, glob@~7.2.3: +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@^7.2.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -7540,13 +6691,6 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== -graphlib@^2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da" - integrity sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A== - dependencies: - lodash "^4.17.15" - graphql-compose@^9.0.10: version "9.0.11" resolved "https://registry.yarnpkg.com/graphql-compose/-/graphql-compose-9.0.11.tgz#c3b5a3378b62be0deb9ea43d147b756010f6d611" @@ -7555,9 +6699,9 @@ graphql-compose@^9.0.10: graphql-type-json "0.3.2" graphql-http@^1.19.0: - version "1.22.1" - resolved "https://registry.yarnpkg.com/graphql-http/-/graphql-http-1.22.1.tgz#3857ac75366e55db189cfe09ade9cc4c4f2cfd09" - integrity sha512-4Jor+LRbA7SfSaw7dfDUs2UBzvWg3cKrykfHRgKsOIvQaLuf+QOcG2t3Mx5N9GzSNJcuqMqJWz0ta5+BryEmXg== + version "1.22.3" + resolved "https://registry.yarnpkg.com/graphql-http/-/graphql-http-1.22.3.tgz#5da03ee564b847e585fa08e326a50dbd4c8fbc0a" + integrity sha512-sgUz/2DZt+QvY6WrpAsAXUvhnIkp2eX9jN78V8DAtFcpZi/nfDrzDt2byYjyoJzRcWuqhE0K63g1QMewt73U6A== graphql-tag@^2.11.0, graphql-tag@^2.12.6: version "2.12.6" @@ -7593,13 +6737,6 @@ gzip-size@^6.0.0: dependencies: duplexer "^0.1.2" -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== - dependencies: - ansi-regex "^2.0.0" - has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" @@ -7639,15 +6776,10 @@ has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" -has@~1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.4.tgz#2eb2860e000011dae4f1406a86fe80e530fb2ec6" - integrity sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ== - hash-wasm@^4.11.0: - version "4.11.0" - resolved "https://registry.yarnpkg.com/hash-wasm/-/hash-wasm-4.11.0.tgz#7d1479b114c82e48498fdb1d2462a687d00386d5" - integrity sha512-HVusNXlVqHe0fzIzdQOGolnFN6mX/fqcrSAOcTBXdvzrXVHwTz11vXeKRmkR5gTuwVpvHZEIyKoePDvuAR+XwQ== + version "4.12.0" + resolved "https://registry.yarnpkg.com/hash-wasm/-/hash-wasm-4.12.0.tgz#f9f1a9f9121e027a9acbf6db5d59452ace1ef9bb" + integrity sha512-+/2B2rYLb48I/evdOIhP+K/DD2ca2fgBjp6O+GBEnCDk2e4rpeXIK8GvIyRPjTezgmWn9gmKwkQjjx6BtqDHVQ== hasha@^5.2.2: version "5.2.2" @@ -7857,11 +6989,6 @@ http2-wrapper@^2.1.10: quick-lru "^5.1.1" resolve-alpn "^1.2.0" -hull.js@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/hull.js/-/hull.js-1.0.6.tgz#75f013e8171eb9a871b4a94887e89eb555461d0e" - integrity sha512-TC7e9sHYOaCVms0sn2hN7buxnaGfcl9h5EPVoVX9DTPoMpqQiS9bf3tmGDgiNaMVHBD91RAvWjCxrJ5Jx8BI5A== - human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -7879,13 +7006,6 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6: - version "0.6.3" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" - integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" @@ -7942,7 +7062,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -7995,11 +7115,6 @@ internal-slot@^1.0.7: hasown "^2.0.0" side-channel "^1.0.4" -"internmap@1 - 2": - version "2.0.3" - resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" - integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== - invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -8038,19 +7153,6 @@ is-alphanumerical@^2.0.0: is-alphabetical "^2.0.0" is-decimal "^2.0.0" -is-any-array@^2.0.0, is-any-array@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-any-array/-/is-any-array-2.0.1.tgz#9233242a9c098220290aa2ec28f82ca7fa79899e" - integrity sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ== - -is-arguments@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - is-array-buffer@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" @@ -8098,11 +7200,6 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - is-buffer@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" @@ -8275,13 +7372,13 @@ is-promise@^2.2.2: integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== is-reference@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-3.0.2.tgz#154747a01f45cd962404ee89d43837af2cba247c" - integrity sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg== + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-3.0.3.tgz#9ef7bf9029c70a67b2152da4adf57c23d718910f" + integrity sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw== dependencies: - "@types/estree" "*" + "@types/estree" "^1.0.6" -is-regex@^1.1.4, is-regex@~1.1.4: +is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== @@ -8493,7 +7590,7 @@ jest-worker@^27.4.5, jest-worker@^27.5.1: merge-stream "^2.0.0" supports-color "^8.0.0" -jiti@^1.20.0, jiti@^1.21.0: +jiti@^1.20.0, jiti@^1.21.6: version "1.21.6" resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268" integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== @@ -8564,13 +7661,6 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json2module@^0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/json2module/-/json2module-0.0.3.tgz#00fb5f4a9b7adfc3f0647c29cb17bcd1979be9b2" - integrity sha512-qYGxqrRrt4GbB8IEOy1jJGypkNsjWoIMlZt4bAsmUScCA507Hbc2p1JOhBzqn45u3PWafUgH2OnzyNU7udO/GA== - dependencies: - rw "^1.3.2" - json2mq@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a" @@ -8616,13 +7706,6 @@ keyv@^4.0.0, keyv@^4.5.3: dependencies: json-buffer "3.0.1" -kind-of@^3.0.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== - dependencies: - is-buffer "^1.1.5" - kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" @@ -8662,11 +7745,6 @@ latest-version@^7.0.0: dependencies: package-json "^8.1.0" -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - integrity sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ== - levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -8857,11 +7935,6 @@ longest-streak@^3.0.0: resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== -longest@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - integrity sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg== - loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -8927,10 +8000,10 @@ lru-queue@^0.1.0: dependencies: es5-ext "~0.10.2" -lucide-react@^0.456.0: - version "0.456.0" - resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.456.0.tgz#14906c3355cc65d3380b7b2294b331aeda1bb392" - integrity sha512-DIIGJqTT5X05sbAsQ+OhA8OtJYyD4NsEMCA/HQW/Y6ToPQ7gwbtujIoeAaup4HpHzV35SQOarKAWH8LYglB6eA== +lucide-react@^0.460.0: + version "0.460.0" + resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.460.0.tgz#5681364b6bd94d1d475944f0385239c0b1408e35" + integrity sha512-BVtq/DykVeIvRTJvRAgCsOwaGL8Un3Bxh8MbDxMhEWlZay3T4IpEKDEpwt5KZ0KJMHzgm6jrltxlT5eXOWXDHg== make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: version "3.1.0" @@ -9168,9 +8241,9 @@ mdast-util-to-markdown@^1.0.0, mdast-util-to-markdown@^1.3.0, mdast-util-to-mark zwitch "^2.0.0" mdast-util-to-markdown@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.1.tgz#6fdb72cd54ee4e6745e138db003609978a322e94" - integrity sha512-OrkcCoqAkEg9b1ykXBrA0ehRc8H4fGU/03cACmW2xXzau1+dIdS+qJugh1Cqex3hMumSBgSE/5pc7uqP12nLAw== + version "2.1.2" + resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz#f910ffe60897f04bb4b7e7ee434486f76288361b" + integrity sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA== dependencies: "@types/mdast" "^4.0.0" "@types/unist" "^3.0.0" @@ -9293,9 +8366,9 @@ micromark-core-commonmark@^1.0.0, micromark-core-commonmark@^1.0.1: uvu "^0.5.0" micromark-core-commonmark@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz#9a45510557d068605c6e9a80f282b2bb8581e43d" - integrity sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA== + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz#6a45bbb139e126b3f8b361a10711ccc7c6e15e93" + integrity sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w== dependencies: decode-named-character-reference "^1.0.0" devlop "^1.0.0" @@ -9390,9 +8463,9 @@ micromark-factory-destination@^1.0.0: micromark-util-types "^1.0.0" micromark-factory-destination@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz#857c94debd2c873cba34e0445ab26b74f6a6ec07" - integrity sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz#8fef8e0f7081f0474fbdd92deb50c990a0264639" + integrity sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA== dependencies: micromark-util-character "^2.0.0" micromark-util-symbol "^2.0.0" @@ -9409,9 +8482,9 @@ micromark-factory-label@^1.0.0: uvu "^0.5.0" micromark-factory-label@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz#17c5c2e66ce39ad6f4fc4cbf40d972f9096f726a" - integrity sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz#5267efa97f1e5254efc7f20b459a38cb21058ba1" + integrity sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg== dependencies: devlop "^1.0.0" micromark-util-character "^2.0.0" @@ -9441,9 +8514,9 @@ micromark-factory-space@^1.0.0: micromark-util-types "^1.0.0" micromark-factory-space@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz#5e7afd5929c23b96566d0e1ae018ae4fcf81d030" - integrity sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz#36d0212e962b2b3121f8525fc7a3c7c029f334fc" + integrity sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg== dependencies: micromark-util-character "^2.0.0" micromark-util-types "^2.0.0" @@ -9459,9 +8532,9 @@ micromark-factory-title@^1.0.0: micromark-util-types "^1.0.0" micromark-factory-title@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz#726140fc77892af524705d689e1cf06c8a83ea95" - integrity sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz#237e4aa5d58a95863f01032d9ee9b090f1de6e94" + integrity sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw== dependencies: micromark-factory-space "^2.0.0" micromark-util-character "^2.0.0" @@ -9479,9 +8552,9 @@ micromark-factory-whitespace@^1.0.0: micromark-util-types "^1.0.0" micromark-factory-whitespace@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz#9e92eb0f5468083381f923d9653632b3cfb5f763" - integrity sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz#06b26b2983c4d27bfcc657b33e25134d4868b0b1" + integrity sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ== dependencies: micromark-factory-space "^2.0.0" micromark-util-character "^2.0.0" @@ -9497,9 +8570,9 @@ micromark-util-character@^1.0.0: micromark-util-types "^1.0.0" micromark-util-character@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.0.tgz#31320ace16b4644316f6bf057531689c71e2aee1" - integrity sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ== + version "2.1.1" + resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6" + integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== dependencies: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" @@ -9512,9 +8585,9 @@ micromark-util-chunked@^1.0.0: micromark-util-symbol "^1.0.0" micromark-util-chunked@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz#e51f4db85fb203a79dbfef23fd41b2f03dc2ef89" - integrity sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz#47fbcd93471a3fccab86cff03847fc3552db1051" + integrity sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA== dependencies: micromark-util-symbol "^2.0.0" @@ -9528,9 +8601,9 @@ micromark-util-classify-character@^1.0.0: micromark-util-types "^1.0.0" micromark-util-classify-character@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz#8c7537c20d0750b12df31f86e976d1d951165f34" - integrity sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz#d399faf9c45ca14c8b4be98b1ea481bced87b629" + integrity sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q== dependencies: micromark-util-character "^2.0.0" micromark-util-symbol "^2.0.0" @@ -9545,9 +8618,9 @@ micromark-util-combine-extensions@^1.0.0: micromark-util-types "^1.0.0" micromark-util-combine-extensions@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz#75d6ab65c58b7403616db8d6b31315013bfb7ee5" - integrity sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz#2a0f490ab08bff5cc2fd5eec6dd0ca04f89b30a9" + integrity sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg== dependencies: micromark-util-chunked "^2.0.0" micromark-util-types "^2.0.0" @@ -9560,9 +8633,9 @@ micromark-util-decode-numeric-character-reference@^1.0.0: micromark-util-symbol "^1.0.0" micromark-util-decode-numeric-character-reference@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz#2698bbb38f2a9ba6310e359f99fcb2b35a0d2bd5" - integrity sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ== + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz#fcf15b660979388e6f118cdb6bf7d79d73d26fe5" + integrity sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw== dependencies: micromark-util-symbol "^2.0.0" @@ -9577,9 +8650,9 @@ micromark-util-decode-string@^1.0.0: micromark-util-symbol "^1.0.0" micromark-util-decode-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz#7dfa3a63c45aecaa17824e656bcdb01f9737154a" - integrity sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz#6cb99582e5d271e84efca8e61a807994d7161eb2" + integrity sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ== dependencies: decode-named-character-reference "^1.0.0" micromark-util-character "^2.0.0" @@ -9592,9 +8665,9 @@ micromark-util-encode@^1.0.0: integrity sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw== micromark-util-encode@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz#0921ac7953dc3f1fd281e3d1932decfdb9382ab1" - integrity sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8" + integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== micromark-util-events-to-acorn@^1.0.0: version "1.2.3" @@ -9616,9 +8689,9 @@ micromark-util-html-tag-name@^1.0.0: integrity sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q== micromark-util-html-tag-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz#ae34b01cbe063363847670284c6255bb12138ec4" - integrity sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz#e40403096481986b41c106627f98f72d4d10b825" + integrity sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA== micromark-util-normalize-identifier@^1.0.0: version "1.1.0" @@ -9628,9 +8701,9 @@ micromark-util-normalize-identifier@^1.0.0: micromark-util-symbol "^1.0.0" micromark-util-normalize-identifier@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz#91f9a4e65fe66cc80c53b35b0254ad67aa431d8b" - integrity sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz#c30d77b2e832acf6526f8bf1aa47bc9c9438c16d" + integrity sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q== dependencies: micromark-util-symbol "^2.0.0" @@ -9642,9 +8715,9 @@ micromark-util-resolve-all@^1.0.0: micromark-util-types "^1.0.0" micromark-util-resolve-all@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz#189656e7e1a53d0c86a38a652b284a252389f364" - integrity sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz#e1a2d62cdd237230a2ae11839027b19381e31e8b" + integrity sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg== dependencies: micromark-util-types "^2.0.0" @@ -9658,9 +8731,9 @@ micromark-util-sanitize-uri@^1.0.0, micromark-util-sanitize-uri@^1.1.0: micromark-util-symbol "^1.0.0" micromark-util-sanitize-uri@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz#ec8fbf0258e9e6d8f13d9e4770f9be64342673de" - integrity sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7" + integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== dependencies: micromark-util-character "^2.0.0" micromark-util-encode "^2.0.0" @@ -9677,9 +8750,9 @@ micromark-util-subtokenize@^1.0.0: uvu "^0.5.0" micromark-util-subtokenize@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz#76129c49ac65da6e479c09d0ec4b5f29ec6eace5" - integrity sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q== + version "2.0.3" + resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.3.tgz#70ffb99a454bd8c913c8b709c3dc97baefb65f96" + integrity sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg== dependencies: devlop "^1.0.0" micromark-util-chunked "^2.0.0" @@ -9692,9 +8765,9 @@ micromark-util-symbol@^1.0.0: integrity sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag== micromark-util-symbol@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz#12225c8f95edf8b17254e47080ce0862d5db8044" - integrity sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8" + integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== micromark-util-types@^1.0.0, micromark-util-types@^1.0.1: version "1.1.0" @@ -9702,9 +8775,9 @@ micromark-util-types@^1.0.0, micromark-util-types@^1.0.1: integrity sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg== micromark-util-types@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.0.tgz#63b4b7ffeb35d3ecf50d1ca20e68fc7caa36d95e" - integrity sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w== + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.1.tgz#a3edfda3022c6c6b55bfb049ef5b75d70af50709" + integrity sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ== micromark@^3.0.0: version "3.2.0" @@ -9730,9 +8803,9 @@ micromark@^3.0.0: uvu "^0.5.0" micromark@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.0.tgz#84746a249ebd904d9658cfabc1e8e5f32cbc6249" - integrity sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ== + version "4.0.1" + resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.1.tgz#294c2f12364759e5f9e925a767ae3dfde72223ff" + integrity sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw== dependencies: "@types/debug" "^4.0.0" debug "^4.0.0" @@ -9752,7 +8825,7 @@ micromark@^4.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" -micromatch@^4.0.4, micromatch@^4.0.5: +micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== @@ -9835,7 +8908,7 @@ minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6, minimist@~1.2.8: +minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -9862,49 +8935,6 @@ mkdirp@^0.5.1, mkdirp@^0.5.4: dependencies: minimist "^1.2.6" -ml-array-max@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/ml-array-max/-/ml-array-max-1.2.4.tgz#2373e2b7e51c8807e456cc0ef364c5863713623b" - integrity sha512-BlEeg80jI0tW6WaPyGxf5Sa4sqvcyY6lbSn5Vcv44lp1I2GR6AWojfUvLnGTNsIXrZ8uqWmo8VcG1WpkI2ONMQ== - dependencies: - is-any-array "^2.0.0" - -ml-array-min@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/ml-array-min/-/ml-array-min-1.2.3.tgz#662f027c400105816b849cc3cd786915d0801495" - integrity sha512-VcZ5f3VZ1iihtrGvgfh/q0XlMobG6GQ8FsNyQXD3T+IlstDv85g8kfV0xUG1QPRO/t21aukaJowDzMTc7j5V6Q== - dependencies: - is-any-array "^2.0.0" - -ml-array-rescale@^1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/ml-array-rescale/-/ml-array-rescale-1.3.7.tgz#c4d129320d113a732e62dd963dc1695bba9a5340" - integrity sha512-48NGChTouvEo9KBctDfHC3udWnQKNKEWN0ziELvY3KG25GR5cA8K8wNVzracsqSW1QEkAXjTNx+ycgAv06/1mQ== - dependencies: - is-any-array "^2.0.0" - ml-array-max "^1.2.4" - ml-array-min "^1.2.3" - -ml-matrix@^6.10.4: - version "6.12.0" - resolved "https://registry.yarnpkg.com/ml-matrix/-/ml-matrix-6.12.0.tgz#def6a0574b5fdc54a753033830e784a17399e270" - integrity sha512-AGfR+pWaC0GmzjUnB6BfwhndPEUGz0i7QUYdqNuw1zhTov/vSRJ9pP2hs6BoGpaSbtXgrKjZz2zjD1M0xuur6A== - dependencies: - is-any-array "^2.0.1" - ml-array-rescale "^1.3.7" - -mock-property@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/mock-property/-/mock-property-1.0.3.tgz#3e37c50a56609d548cabd56559fde3dd8767b10c" - integrity sha512-2emPTb1reeLLYwHxyVx993iYyCHEiRRO+y8NFXFPL5kl5q14sgTK76cXyEKkeKCHeRw35SfdkUJ10Q1KfHuiIQ== - dependencies: - define-data-property "^1.1.1" - functions-have-names "^1.2.3" - gopd "^1.0.1" - has-property-descriptors "^1.0.0" - hasown "^2.0.0" - isarray "^2.0.5" - moment@^2.29.4: version "2.30.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" @@ -10170,23 +9200,10 @@ object-hash@^3.0.0: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== -object-inspect@^1.13.1: - version "1.13.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" - integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== - -object-inspect@~1.12.3: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== - -object-is@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" - integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" +object-inspect@^1.13.1, object-inspect@^1.13.3: + version "1.13.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.3.tgz#f14c183de51130243d6d18ae149375ff50ea488a" + integrity sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA== object-keys@^1.1.1: version "1.1.1" @@ -10529,11 +9546,6 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pdfast@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/pdfast/-/pdfast-0.2.0.tgz#8cbc556e1bf2522177787c0de2e0d4373ba885c9" - integrity sha512-cq6TTu6qKSFUHwEahi68k/kqN2mfepjkGrG9Un70cgdRRKLKY6Rf8P8uvP2NvZktaQZNF3YE7agEkLj0vGK9bA== - peek-readable@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.1.0.tgz#4ece1111bf5c2ad8867c314c81356847e8a62e72" @@ -10664,7 +9676,7 @@ postcss-js@^4.0.1: dependencies: camelcase-css "^2.0.1" -postcss-load-config@^4.0.1: +postcss-load-config@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3" integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== @@ -10746,20 +9758,20 @@ postcss-modules-extract-imports@^3.0.0: integrity sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q== postcss-modules-local-by-default@^4.0.0: - version "4.0.5" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz#f1b9bd757a8edf4d8556e8d0f4f894260e3df78f" - integrity sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw== + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.1.0.tgz#b0db6bc81ffc7bdc52eb0f84d6ca0bedf0e36d21" + integrity sha512-rm0bdSv4jC3BDma3s9H19ZddW0aHX6EoqwDYU2IfZhRN+53QrufTRo2IdkAbRqLx4R2IYbZnbjKKxg4VN5oU9Q== dependencies: icss-utils "^5.0.0" - postcss-selector-parser "^6.0.2" + postcss-selector-parser "^7.0.0" postcss-value-parser "^4.1.0" postcss-modules-scope@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz#a43d28289a169ce2c15c00c4e64c0858e43457d5" - integrity sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ== + version "3.2.1" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz#1bbccddcb398f1d7a511e0a2d1d047718af4078c" + integrity sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA== dependencies: - postcss-selector-parser "^6.0.4" + postcss-selector-parser "^7.0.0" postcss-modules-values@^4.0.0: version "4.0.0" @@ -10768,7 +9780,7 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" -postcss-nested@^6.0.1: +postcss-nested@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.2.0.tgz#4c2d22ab5f20b9cb61e2c5c5915950784d068131" integrity sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ== @@ -10869,7 +9881,7 @@ postcss-selector-parser@6.0.10: cssesc "^3.0.0" util-deprecate "^1.0.2" -postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9, postcss-selector-parser@^6.1.1: +postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9, postcss-selector-parser@^6.1.1, postcss-selector-parser@^6.1.2: version "6.1.2" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== @@ -10877,6 +9889,14 @@ postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.2, postcss-selecto cssesc "^3.0.0" util-deprecate "^1.0.2" +postcss-selector-parser@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz#41bd8b56f177c093ca49435f65731befe25d6b9c" + integrity sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss-svgo@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d" @@ -10892,30 +9912,12 @@ postcss-unique-selectors@^5.1.1: dependencies: postcss-selector-parser "^6.0.5" -postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: +postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@8.4.38: - version "8.4.38" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" - integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== - dependencies: - nanoid "^3.3.7" - picocolors "^1.0.0" - source-map-js "^1.2.0" - -postcss@^8.2.15, postcss@^8.2.9, postcss@^8.4.23, postcss@^8.4.24: - version "8.4.47" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365" - integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ== - dependencies: - nanoid "^3.3.7" - picocolors "^1.1.0" - source-map-js "^1.2.1" - -postcss@^8.4.49: +postcss@^8.2.15, postcss@^8.2.9, postcss@^8.4.24, postcss@^8.4.47, postcss@^8.4.49: version "8.4.49" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19" integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== @@ -11085,11 +10087,6 @@ quick-lru@^5.1.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== -quickselect@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-2.0.0.tgz#f19680a486a5eefb581303e023e98faaf25dd018" - integrity sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw== - randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -11120,13 +10117,6 @@ raw-loader@^4.0.2: loader-utils "^2.0.0" schema-utils "^3.0.0" -rbush@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/rbush/-/rbush-3.0.1.tgz#5fafa8a79b3b9afdfe5008403a720cc1de882ecf" - integrity sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w== - dependencies: - quickselect "^2.0.0" - rc-cascader@~3.30.0: version "3.30.0" resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.30.0.tgz#da3e35cadcc00c58c62a6757eca6c7147ff94ea8" @@ -11189,7 +10179,7 @@ rc-dropdown@~4.2.0: classnames "^2.2.6" rc-util "^5.17.0" -rc-field-form@~2.5.0: +rc-field-form@~2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-2.5.1.tgz#30f3c529f86aec6af27589052df9c66cec94ceb4" integrity sha512-33hunXwynQJyeae7LS3hMGTXNeRBjiPyPYgB0824EbmLHiXC1EBGyUwRh6xjLRy9c+en5WARYN0gJz5+JAqwig== @@ -11293,7 +10283,7 @@ rc-pagination@~4.3.0: classnames "^2.3.2" rc-util "^5.38.0" -rc-picker@~4.8.0: +rc-picker@~4.8.1: version "4.8.1" resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-4.8.1.tgz#105cfae323bf1db5e9f9e6fdc773ff3250e837de" integrity sha512-lj9hXXMSkbjFUIhfQh8XH698ybxnoBOfq7pdM1FvfSyDwdFhdQa7dvsIYwo6Uz7Zp1wVkfw5rOJO3MpdWzoHsg== @@ -11429,9 +10419,9 @@ rc-tooltip@~6.2.1: classnames "^2.3.1" rc-tree-select@~5.24.4: - version "5.24.4" - resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.24.4.tgz#6789b8761daf53a8b91b75e9e6c4f720a7e8cc92" - integrity sha512-MzljkSkk7weKOcE853UtYlXB6uyUEzcEQhhpaCwE6jQPbmBUgGiRURuKWpYUnM/dXrwTTlCK969M6Pgjj35MLA== + version "5.24.5" + resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.24.5.tgz#a1bf85c7d5e4979880cfb0748bb6bab937ed3483" + integrity sha512-PnyR8LZJWaiEFw0SHRqo4MNQWyyZsyMs8eNmo68uXZWjxc7QqeWcjPPoONN0rc90c3HZqGF9z+Roz+GLzY5GXA== dependencies: "@babel/runtime" "^7.25.7" classnames "2.x" @@ -11468,9 +10458,9 @@ rc-util@^5.0.1, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2. react-is "^18.2.0" rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: - version "3.14.8" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.14.8.tgz#abf6e8809b7f5c955aa7f59c2a9d57443e9942fd" - integrity sha512-8D0KfzpRYi6YZvlOWIxiOm9BGt4Wf2hQyEaM6RXlDDiY2NhLheuYI+RA+7ZaZj1lq+XQqy3KHlaeeXQfzI5fGg== + version "3.15.0" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.15.0.tgz#45c5b1ef1363287214e0a3c17af29ee0c3764764" + integrity sha512-dF2YQztqrU3ijAeWOqscTshCEr7vpimzSqAVjO1AyAmaqcHulaXpnGR0ptK5PXfxTUy48VkJOiglMIxlkYGs0w== dependencies: "@babel/runtime" "^7.20.0" classnames "^2.2.6" @@ -11556,15 +10546,6 @@ react-markdown@^9.0.1: unist-util-visit "^5.0.0" vfile "^6.0.0" -react-reconciler@^0.26.2: - version "0.26.2" - resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.26.2.tgz#bbad0e2d1309423f76cf3c3309ac6c96e05e9d91" - integrity sha512-nK6kgY28HwrMNwDnMui3dvm3rCFjZrcGiuwLc5COUipBK5hWHLOxMJhSnSomirqWwjPBJKV1QcbkI0VJr7Gl1Q== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" - react-refresh@^0.14.0: version "0.14.2" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" @@ -11655,7 +10636,7 @@ redux@4.2.1: dependencies: "@babel/runtime" "^7.9.2" -reflect.getprototypeof@^1.0.4: +reflect.getprototypeof@^1.0.4, reflect.getprototypeof@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz#3ab04c32a8390b770712b7a8633972702d278859" integrity sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg== @@ -11697,7 +10678,7 @@ regenerator-transform@^0.15.2: dependencies: "@babel/runtime" "^7.8.4" -regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2: +regexp.prototype.flags@^1.5.2, regexp.prototype.flags@^1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz#b3ae40b1d2499b8350ab2c3fe6ef3845d3a96f42" integrity sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ== @@ -11713,14 +10694,14 @@ regexpp@^3.1.0: integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== regexpu-core@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.1.1.tgz#b469b245594cb2d088ceebc6369dceb8c00becac" - integrity sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw== + version "6.2.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.2.0.tgz#0e5190d79e542bf294955dccabae04d3c7d53826" + integrity sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA== dependencies: regenerate "^1.4.2" regenerate-unicode-properties "^10.2.0" regjsgen "^0.8.0" - regjsparser "^0.11.0" + regjsparser "^0.12.0" unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.1.0" @@ -11743,10 +10724,10 @@ regjsgen@^0.8.0: resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.8.0.tgz#df23ff26e0c5b300a6470cad160a9d090c3a37ab" integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q== -regjsparser@^0.11.0: - version "0.11.2" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.11.2.tgz#7404ad42be00226d72bcf1f003f1f441861913d8" - integrity sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA== +regjsparser@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.12.0.tgz#0e846df6c6530586429377de56e0475583b088dc" + integrity sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ== dependencies: jsesc "~3.0.2" @@ -11846,11 +10827,6 @@ renderkid@^2.0.4: lodash "^4.17.21" strip-ansi "^3.0.1" -repeat-string@^1.5.2: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -11898,7 +10874,7 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve@^1.1.7, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.22.2, resolve@^1.22.4, resolve@~1.22.6: +resolve@^1.1.7, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.22.4, resolve@^1.22.8: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -11948,13 +10924,6 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -right-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" - integrity sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg== - dependencies: - align-text "^0.1.1" - rimraf@^2.6.2: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -11969,15 +10938,6 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" -rollup@^0.25.8: - version "0.25.8" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.25.8.tgz#bf6ce83b87510d163446eeaa577ed6a6fc5835e0" - integrity sha512-a2S4Bh3bgrdO4BhKr2E4nZkjTvrJ2m2bWjMTzVYtoqSCn0HnuxosXnaJUHrMEziOWr3CzL9GjilQQKcyCQpJoA== - dependencies: - chalk "^1.1.1" - minimist "^1.2.0" - source-map-support "^0.3.2" - run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -11990,11 +10950,6 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rw@1, rw@^1.3.2: - version "1.3.3" - resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" - integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ== - rxjs@^6.6.0: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" @@ -12038,7 +10993,7 @@ safe-regex-test@^1.0.3: es-errors "^1.3.0" is-regex "^1.1.4" -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": +"safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -12048,14 +11003,6 @@ sax@^1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== -scheduler@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler@^0.23.2: version "0.23.2" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" @@ -12231,11 +11178,6 @@ shallow-compare@^1.2.2: resolved "https://registry.yarnpkg.com/shallow-compare/-/shallow-compare-1.2.2.tgz#fa4794627bf455a47c4f56881d8a6132d581ffdb" integrity sha512-LUMFi+RppPlrHzbqmFnINTrazo0lPNwhcgzuAXVVcfy/mqPDrQmHAyz5bvV0gDAuRFrk804V0HpQ6u9sZ0tBeg== -shallowequal@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" - integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== - sharp@^0.32.6: version "0.32.6" resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.32.6.tgz#6ad30c0b7cd910df65d5f355f774aa4fce45732a" @@ -12399,18 +11341,11 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-js@^1.2.0, source-map-js@^1.2.1: +source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== -source-map-support@^0.3.2: - version "0.3.3" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.3.3.tgz#34900977d5ba3f07c7757ee72e73bb1a9b53754f" - integrity sha512-9O4+y9n64RewmFoKUZ/5Tx9IHIcXM6Q+RTSw6ehnqybUz4a7iwR3Eaw80uLtqqQ5D0C+5H03D4KKGo9PdP33Gg== - dependencies: - source-map "0.1.32" - source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -12419,13 +11354,6 @@ source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@0.1.32: - version "0.1.32" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.32.tgz#c8b6c167797ba4740a8ea33252162ff08591b266" - integrity sha512-htQyLrrRLkQ87Zfrir4/yN+vAUd6DNjVayEjTSHXu29AYQJw57I4/xEL/M6p6E/woPNJwvZt6rVlzc7gFEJccQ== - dependencies: - amdefine ">=0.0.4" - source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -12436,11 +11364,6 @@ source-map@^0.7.0, source-map@^0.7.3: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== -source-map@~0.5.1: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== - space-separated-tokens@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" @@ -12501,9 +11424,9 @@ streamsearch@^1.1.0: integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== streamx@^2.15.0, streamx@^2.20.0: - version "2.20.1" - resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.20.1.tgz#471c4f8b860f7b696feb83d5b125caab2fdbb93c" - integrity sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA== + version "2.20.2" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.20.2.tgz#6a8911959d6f307c19781a1d19ecd94b5f042d78" + integrity sha512-aDGDLU+j9tJcUdPGOaHmVF1u/hhI+CsGkT02V3OKlHDV7IukOI+nTWAGkiZEKCO35rWN1wIr4tS7YFr1f4qSvA== dependencies: fast-fifo "^1.3.2" queue-tick "^1.0.1" @@ -12599,7 +11522,7 @@ string.prototype.repeat@^1.0.0: define-properties "^1.1.3" es-abstract "^1.17.5" -string.prototype.trim@^1.2.9, string.prototype.trim@~1.2.8: +string.prototype.trim@^1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== @@ -12656,7 +11579,7 @@ stringify-entities@^4.0.0: dependencies: ansi-regex "^5.0.1" -strip-ansi@^3.0.0, strip-ansi@^3.0.1: +strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== @@ -12751,21 +11674,6 @@ style-to-object@^1.0.0: dependencies: inline-style-parser "0.2.4" -styled-components@^6.1.13: - version "6.1.13" - resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-6.1.13.tgz#2d777750b773b31469bd79df754a32479e9f475e" - integrity sha512-M0+N2xSnAtwcVAQeFEsGWFFxXDftHUD7XrKla06QbpUMmbmtFBMMTcKWvFXtWxuD5qQkB8iU5gk6QASlx2ZRMw== - dependencies: - "@emotion/is-prop-valid" "1.2.2" - "@emotion/unitless" "0.8.1" - "@types/stylis" "4.2.5" - css-to-react-native "3.2.0" - csstype "3.1.3" - postcss "8.4.38" - shallowequal "1.1.0" - stylis "4.3.2" - tslib "2.6.2" - stylehacks@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.1.tgz#7934a34eb59d7152149fa69d6e9e56f2fc34bcc9" @@ -12774,17 +11682,12 @@ stylehacks@^5.1.1: browserslist "^4.21.4" postcss-selector-parser "^6.0.4" -stylis@4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.2.tgz#8f76b70777dd53eb669c6f58c997bf0a9972e444" - integrity sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg== - -stylis@^4.3.3: +stylis@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.4.tgz#ca5c6c4a35c4784e4e93a2a24dc4e9fa075250a4" integrity sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now== -sucrase@^3.32.0: +sucrase@^3.35.0: version "3.35.0" resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== @@ -12802,11 +11705,6 @@ sudo-prompt@^8.2.0: resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-8.2.5.tgz#cc5ef3769a134bb94b24a631cc09628d4d53603e" integrity sha512-rlBo3HU/1zAJUrkY6jNxDOC9eVYliG6nS4JA8u8KAshITd07tafMc/Br7xQwCSseXwJ2iCcHCE8SNWX3q8Z+kw== -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== - supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -12833,11 +11731,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -svg-path-parser@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/svg-path-parser/-/svg-path-parser-1.1.0.tgz#e16b4b39df0d2b0d39e8347db79fdda1453a6046" - integrity sha512-jGCUqcQyXpfe38R7RFfhrMyfXcBmpMNJI/B+4CE9/Unkh98UporAc461GTthv+TVDuZXsBx7/WiwJb1Oh4tt4A== - svgo@^2.7.0: version "2.8.0" resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" @@ -12880,32 +11773,32 @@ table@^6.0.9: strip-ansi "^6.0.1" tailwindcss@^3.4.14: - version "3.4.14" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.14.tgz#6dd23a7f54ec197b19159e91e3bb1e55e7aa73ac" - integrity sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA== + version "3.4.15" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.15.tgz#04808bf4bf1424b105047d19e7d4bfab368044a9" + integrity sha512-r4MeXnfBmSOuKUWmXe6h2CcyfzJCEk4F0pptO5jlnYSIViUkVmsawj80N5h2lO3gwcmSb4n3PuN+e+GC1Guylw== dependencies: "@alloc/quick-lru" "^5.2.0" arg "^5.0.2" - chokidar "^3.5.3" + chokidar "^3.6.0" didyoumean "^1.2.2" dlv "^1.1.3" - fast-glob "^3.3.0" + fast-glob "^3.3.2" glob-parent "^6.0.2" is-glob "^4.0.3" - jiti "^1.21.0" + jiti "^1.21.6" lilconfig "^2.1.0" - micromatch "^4.0.5" + micromatch "^4.0.8" normalize-path "^3.0.0" object-hash "^3.0.0" - picocolors "^1.0.0" - postcss "^8.4.23" + picocolors "^1.1.1" + postcss "^8.4.47" postcss-import "^15.1.0" postcss-js "^4.0.1" - postcss-load-config "^4.0.1" - postcss-nested "^6.0.1" - postcss-selector-parser "^6.0.11" - resolve "^1.22.2" - sucrase "^3.32.0" + postcss-load-config "^4.0.2" + postcss-nested "^6.2.0" + postcss-selector-parser "^6.1.2" + resolve "^1.22.8" + sucrase "^3.35.0" tapable@^1.0.0: version "1.1.3" @@ -12917,28 +11810,6 @@ tapable@^2.1.1, tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -tape@^4.5.1: - version "4.17.0" - resolved "https://registry.yarnpkg.com/tape/-/tape-4.17.0.tgz#de89f3671ddc5dad178d04c28dc6b0183f42268e" - integrity sha512-KCuXjYxCZ3ru40dmND+oCLsXyuA8hoseu2SS404Px5ouyS0A99v8X/mdiLqsR5MTAyamMBN7PRwt2Dv3+xGIxw== - dependencies: - "@ljharb/resumer" "~0.0.1" - "@ljharb/through" "~2.3.9" - call-bind "~1.0.2" - deep-equal "~1.1.1" - defined "~1.0.1" - dotignore "~0.1.2" - for-each "~0.3.3" - glob "~7.2.3" - has "~1.0.3" - inherits "~2.0.4" - is-regex "~1.1.4" - minimist "~1.2.8" - mock-property "~1.0.0" - object-inspect "~1.12.3" - resolve "~1.22.6" - string.prototype.trim "~1.2.8" - tar-fs@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" @@ -13129,17 +12000,12 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== - tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.5.3, tslib@^2.8.0: +tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.8.0: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -13224,9 +12090,9 @@ typed-array-byte-length@^1.0.1: is-typed-array "^1.1.13" typed-array-byte-offset@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" - integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.3.tgz#3fa9f22567700cc86aaf86a1e7176f74b59600f2" + integrity sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw== dependencies: available-typed-arrays "^1.0.7" call-bind "^1.0.7" @@ -13234,6 +12100,7 @@ typed-array-byte-offset@^1.0.2: gopd "^1.0.1" has-proto "^1.0.3" is-typed-array "^1.1.13" + reflect.getprototypeof "^1.0.6" typed-array-length@^1.0.6: version "1.0.6" @@ -13260,30 +12127,15 @@ typedarray@^0.0.6: integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== typescript@^5.3.3: - version "5.6.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" - integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== + version "5.7.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6" + integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg== ua-parser-js@^1.0.35: version "1.0.39" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.39.tgz#bfc07f361549bf249bd8f4589a4cccec18fd2018" integrity sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw== -uglify-js@^2.6.2: - version "2.8.29" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" - integrity sha512-qLq/4y2pjcU3vhlhseXGGJ7VbFO4pBANu0kwl8VCa9KEI0V8VfZIx2Fy3w01iSTA/pGwKZSmu/+I4etLNDdt5w== - dependencies: - source-map "~0.5.1" - yargs "~3.10.0" - optionalDependencies: - uglify-to-browserify "~1.0.0" - -uglify-to-browserify@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" - integrity sha512-vb2s1lYx2xBtUgy+ta+b2J/GLVUR+wmpINwHePmPRhOsIVCG2wDzKJ0n14GslH1BifsqVzSOwQhRaCAsZ/nI4Q== - unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" @@ -13829,21 +12681,11 @@ wildcard@^2.0.0: resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== -window-size@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" - integrity sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg== - word-wrap@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - integrity sha512-xSBsCeh+g+dinoBv3GAOWM4LcVVO68wLXRanibtBSdUvkGWQRGeE9P7IwU9EmDDi4jA6L44lz15CGMwdw9N5+Q== - "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -13960,9 +12802,9 @@ yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yaml@^2.0.0, yaml@^2.3.4: - version "2.6.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.0.tgz#14059ad9d0b1680d0f04d3a60fe00f3a857303c3" - integrity sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ== + version "2.6.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.1.tgz#42f2b1ba89203f374609572d5349fb8686500773" + integrity sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg== yargs-parser@^18.1.2: version "18.1.3" @@ -13989,16 +12831,6 @@ yargs@^15.3.1, yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@~3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" - integrity sha512-QFzUah88GAGy9lyDKGBqZdkYApt63rCXYBGYnEP4xDJPXNqXXnBDACnbrXnViV6jRSqAePwrATi2i8mfYm4L1A== - dependencies: - camelcase "^1.0.2" - cliui "^2.1.0" - decamelize "^1.0.0" - window-size "0.1.0" - yarn@^1.22.22: version "1.22.22" resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.22.tgz#ac34549e6aa8e7ead463a7407e1c7390f61a6610" diff --git a/python/packages/autogen-studio/notebooks/tutorial.ipynb b/python/packages/autogen-studio/notebooks/tutorial.ipynb index 8e749f74383..432f9458765 100644 --- a/python/packages/autogen-studio/notebooks/tutorial.ipynb +++ b/python/packages/autogen-studio/notebooks/tutorial.ipynb @@ -16,17 +16,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "task_result=TaskResult(messages=[TextMessage(source='user', models_usage=None, content='What is the weather in New York?'), ToolCallMessage(source='writing_agent', models_usage=RequestUsage(prompt_tokens=65, completion_tokens=15), content=[FunctionCall(id='call_x8C5nib1PJkMZGQ6zrNUlfa0', arguments='{\"city\":\"New York\"}', name='get_weather')]), ToolCallResultMessage(source='writing_agent', models_usage=None, content=[FunctionExecutionResult(content='The weather in New York is 73 degrees and Sunny.', call_id='call_x8C5nib1PJkMZGQ6zrNUlfa0')]), TextMessage(source='writing_agent', models_usage=RequestUsage(prompt_tokens=97, completion_tokens=14), content='The weather in New York is currently 73 degrees and sunny.'), TextMessage(source='writing_agent', models_usage=RequestUsage(prompt_tokens=123, completion_tokens=13), content='Would you like to know the weather in any other city?')], stop_reason='Maximum number of messages 5 reached, current message count: 5') usage='' duration=1.9984567165374756\n" - ] - } - ], + "outputs": [], "source": [ "from autogenstudio.teammanager import TeamManager \n", " \n", @@ -37,22 +29,9 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "source='user' models_usage=None content='What is the weather in New York?'\n", - "source='writing_agent' models_usage=RequestUsage(prompt_tokens=65, completion_tokens=15) content=[FunctionCall(id='call_Gwnfsa8ndnOsXTvRECTr92hr', arguments='{\"city\":\"New York\"}', name='get_weather')]\n", - "source='writing_agent' models_usage=None content=[FunctionExecutionResult(content='The weather in New York is 73 degrees and Sunny.', call_id='call_Gwnfsa8ndnOsXTvRECTr92hr')]\n", - "source='writing_agent' models_usage=RequestUsage(prompt_tokens=97, completion_tokens=14) content='The weather in New York is currently 73 degrees and sunny.'\n", - "source='writing_agent' models_usage=RequestUsage(prompt_tokens=123, completion_tokens=14) content='The weather in New York is currently 73 degrees and sunny.'\n", - "task_result=TaskResult(messages=[TextMessage(source='user', models_usage=None, content='What is the weather in New York?'), ToolCallMessage(source='writing_agent', models_usage=RequestUsage(prompt_tokens=65, completion_tokens=15), content=[FunctionCall(id='call_Gwnfsa8ndnOsXTvRECTr92hr', arguments='{\"city\":\"New York\"}', name='get_weather')]), ToolCallResultMessage(source='writing_agent', models_usage=None, content=[FunctionExecutionResult(content='The weather in New York is 73 degrees and Sunny.', call_id='call_Gwnfsa8ndnOsXTvRECTr92hr')]), TextMessage(source='writing_agent', models_usage=RequestUsage(prompt_tokens=97, completion_tokens=14), content='The weather in New York is currently 73 degrees and sunny.'), TextMessage(source='writing_agent', models_usage=RequestUsage(prompt_tokens=123, completion_tokens=14), content='The weather in New York is currently 73 degrees and sunny.')], stop_reason='Maximum number of messages 5 reached, current message count: 5') usage='' duration=2.363379955291748\n" - ] - } - ], + "outputs": [], "source": [ "result_stream = wm.run_stream(task=\"What is the weather in New York?\", team_config=\"team.json\") \n", "async for response in result_stream:\n", @@ -74,28 +53,32 @@ "metadata": {}, "outputs": [ { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO [alembic.runtime.migration] Context impl SQLiteImpl.\n", - "INFO [alembic.runtime.migration] Will assume non-transactional DDL.\n", - "\u001b[32m2024-11-14 09:06:25.242\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mautogenstudio.database.schema_manager\u001b[0m:\u001b[36mupgrade_schema\u001b[0m:\u001b[36m390\u001b[0m - \u001b[1mSchema upgraded successfully to head\u001b[0m\n", - "\u001b[32m2024-11-14 09:06:25.243\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mautogenstudio.database.db_manager\u001b[0m:\u001b[36m__init__\u001b[0m:\u001b[36m34\u001b[0m - \u001b[1mDatabase schema was upgraded automatically\u001b[0m\n", - "\u001b[32m2024-11-14 09:06:25.244\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mautogenstudio.database.db_manager\u001b[0m:\u001b[36mcreate_db_and_tables\u001b[0m:\u001b[36m108\u001b[0m - \u001b[1mDatabase tables created successfully\u001b[0m\n" - ] + "data": { + "text/plain": [ + "Response(message='Database is ready', status=True, data=None)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ "from autogenstudio.database import DatabaseManager \n", + "import os \n", + "# delete database\n", + "# if os.path.exists(\"test.db\"):\n", + "# os.remove(\"test.db\") \n", "\n", + "os.makedirs(\"test\", exist_ok=True)\n", "# create a database\n", - "dbmanager = DatabaseManager(engine_uri=\"sqlite:///test.db\")\n", - "dbmanager.create_db_and_tables() " + "dbmanager = DatabaseManager(engine_uri=\"sqlite:///test.db\", base_dir=\"test\")\n", + "dbmanager.initialize_database() " ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -145,14 +128,14 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "53 teams in database\n" + "2 teams in database\n" ] } ], @@ -172,7 +155,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -194,17 +177,9 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "message='Directory import complete' status=True data=[{'component': 'team', 'status': True, 'message': 'Team Created Successfully', 'id': 54}]\n" - ] - } - ], + "outputs": [], "source": [ "result = await config_manager.import_directory(\".\", user_id=\"user_id\", check_exists=False)\n", "print(result)" @@ -212,17 +187,9 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "54 teams in database\n" - ] - } - ], + "outputs": [], "source": [ "all_teams = dbmanager.get(Team)\n", "print(len(all_teams.data), \"teams in database\")" @@ -237,7 +204,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -282,22 +249,9 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "source='user' models_usage=None content='Plan a 3 day trip to Nepal.'\n", - "source='planner_agent' models_usage=RequestUsage(prompt_tokens=45, completion_tokens=54) content=\"Consider starting your 3-day trip to Nepal with a cultural tour in Kathmandu Valley, followed by an exploration of Pokhara's natural beauty on the second day, and finally, indulge in a thrilling safari at Chitwan National Park on the third day.\"\n", - "source='local_agent' models_usage=RequestUsage(prompt_tokens=116, completion_tokens=54) content=\"Consider starting your 3-day trip to Nepal with a cultural tour in Kathmandu Valley, followed by an exploration of Pokhara's natural beauty on the second day, and finally, indulge in a thrilling safari at Chitwan National Park on the third day.\"\n", - "source='language_agent' models_usage=RequestUsage(prompt_tokens=201, completion_tokens=45) content=\"Your travel plan lacks a mention of dealing with potential language barriers; it might be useful to learn some phrases in Nepali, as it's the official language of Nepal, or have a local translation app handy during your trip.\"\n", - "source='travel_summary_agent' models_usage=RequestUsage(prompt_tokens=270, completion_tokens=237) content='Day 1: Start your adventure in the capital city, Kathmandu. Take a guided tour of Kathmandu Valley to explore its UNESCO World Heritage Sites, such as the Durbar Squares, Swayambhunath Stupa, and Boudhanath Stupa. Engage with the locals and sample some traditional Nepalese cuisine.\\n\\nDay 2: Proceed to Pokhara, known for its stunning natural beauty. Visit the iconic Phewa Lake and enjoy a boat ride, then trek to the Peace Pagoda for a panoramic view of the city. Round off the day with a visit to the fascinating Pokhara Mountain Museum.\\n\\nDay 3: Travel to Chitwan National Park for a memorable safari. Explore the diverse wildlife and lush vegetation that make the park a UNESCO World Heritage site. Be on the lookout for rhinos, Bengal tigers, and a multitude of bird species.\\n\\nNote: Communication is key to enjoying your trip. The official language of Nepal is Nepali. It can be helpful to learn a few basic phrases or carry a translation app to help you interact with the local people and enrich your cultural experience.\\n\\nTERMINATE.'\n", - "TaskResult(messages=[TextMessage(source='user', models_usage=None, content='Plan a 3 day trip to Nepal.'), TextMessage(source='planner_agent', models_usage=RequestUsage(prompt_tokens=45, completion_tokens=54), content=\"Consider starting your 3-day trip to Nepal with a cultural tour in Kathmandu Valley, followed by an exploration of Pokhara's natural beauty on the second day, and finally, indulge in a thrilling safari at Chitwan National Park on the third day.\"), TextMessage(source='local_agent', models_usage=RequestUsage(prompt_tokens=116, completion_tokens=54), content=\"Consider starting your 3-day trip to Nepal with a cultural tour in Kathmandu Valley, followed by an exploration of Pokhara's natural beauty on the second day, and finally, indulge in a thrilling safari at Chitwan National Park on the third day.\"), TextMessage(source='language_agent', models_usage=RequestUsage(prompt_tokens=201, completion_tokens=45), content=\"Your travel plan lacks a mention of dealing with potential language barriers; it might be useful to learn some phrases in Nepali, as it's the official language of Nepal, or have a local translation app handy during your trip.\"), TextMessage(source='travel_summary_agent', models_usage=RequestUsage(prompt_tokens=270, completion_tokens=237), content='Day 1: Start your adventure in the capital city, Kathmandu. Take a guided tour of Kathmandu Valley to explore its UNESCO World Heritage Sites, such as the Durbar Squares, Swayambhunath Stupa, and Boudhanath Stupa. Engage with the locals and sample some traditional Nepalese cuisine.\\n\\nDay 2: Proceed to Pokhara, known for its stunning natural beauty. Visit the iconic Phewa Lake and enjoy a boat ride, then trek to the Peace Pagoda for a panoramic view of the city. Round off the day with a visit to the fascinating Pokhara Mountain Museum.\\n\\nDay 3: Travel to Chitwan National Park for a memorable safari. Explore the diverse wildlife and lush vegetation that make the park a UNESCO World Heritage site. Be on the lookout for rhinos, Bengal tigers, and a multitude of bird species.\\n\\nNote: Communication is key to enjoying your trip. The official language of Nepal is Nepali. It can be helpful to learn a few basic phrases or carry a translation app to help you interact with the local people and enrich your cultural experience.\\n\\nTERMINATE.')], stop_reason=\"Text 'TERMINATE' mentioned\")\n" - ] - } - ], + "outputs": [], "source": [ "\n", "result = group_chat.run_stream(task=\"Plan a 3 day trip to Nepal.\")\n", @@ -317,7 +271,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -330,19 +284,9 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "source='user' models_usage=None content='hello there'\n", - "source='user_agent' models_usage=None content='Hello World thereEnter your response: '\n", - "TaskResult(messages=[TextMessage(source='user', models_usage=None, content='hello there'), TextMessage(source='user_agent', models_usage=None, content='Hello World thereEnter your response: ')], stop_reason=None)\n" - ] - } - ], + "outputs": [], "source": [ "from autogen_core.base import CancellationToken \n", "cancellation_token = CancellationToken()\n", diff --git a/python/packages/autogen-studio/pyproject.toml b/python/packages/autogen-studio/pyproject.toml index fc3acee48e0..3a067d5dfc4 100644 --- a/python/packages/autogen-studio/pyproject.toml +++ b/python/packages/autogen-studio/pyproject.toml @@ -23,7 +23,8 @@ dependencies = [ "pydantic-settings", "fastapi", "typer", - "uvicorn", + "uvicorn", + "aiofiles", "python-dotenv", "websockets", "numpy < 2.0.0", @@ -69,3 +70,21 @@ filterwarnings = [ [project.scripts] autogenstudio = "autogenstudio.cli:run" + + +[tool.ruff] +extend = "../../pyproject.toml" +exclude = ["build", "dist"] +include = [ + "autogenstudio/**" +] + +[tool.ruff.lint] +ignore = ["B008"] + + +[tool.poe.tasks] +fmt = "ruff format" +format.ref = "fmt" +lint = "ruff check" +test = "pytest -n 0" \ No newline at end of file diff --git a/python/packages/autogen-studio/tests/test_component_factory.py b/python/packages/autogen-studio/tests/test_component_factory.py index d676941feee..4fc4aed8c66 100644 --- a/python/packages/autogen-studio/tests/test_component_factory.py +++ b/python/packages/autogen-studio/tests/test_component_factory.py @@ -6,10 +6,10 @@ from autogen_agentchat.task import MaxMessageTermination, StopMessageTermination, TextMentionTermination from autogen_core.components.tools import FunctionTool -from autogenstudio.datamodel import ( +from autogenstudio.datamodel.types import ( AgentConfig, ModelConfig, TeamConfig, ToolConfig, TerminationConfig, ModelTypes, AgentTypes, TeamTypes, TerminationTypes, ToolTypes, - ComponentType + ComponentTypes ) from autogenstudio.database import ComponentFactory @@ -41,7 +41,7 @@ def calculator(a: int, b: int, operation: str = '+') -> int: raise ValueError("Invalid operation") """, tool_type=ToolTypes.PYTHON_FUNCTION, - component_type=ComponentType.TOOL, + component_type=ComponentTypes.TOOL, version="1.0.0" ) @@ -52,7 +52,7 @@ def sample_model_config(): model_type=ModelTypes.OPENAI, model="gpt-4", api_key="test-key", - component_type=ComponentType.MODEL, + component_type=ComponentTypes.MODEL, version="1.0.0" ) @@ -65,7 +65,7 @@ def sample_agent_config(sample_model_config: ModelConfig, sample_tool_config: To system_message="You are a helpful assistant", model_client=sample_model_config, tools=[sample_tool_config], - component_type=ComponentType.AGENT, + component_type=ComponentTypes.AGENT, version="1.0.0" ) @@ -75,7 +75,7 @@ def sample_termination_config(): return TerminationConfig( termination_type=TerminationTypes.MAX_MESSAGES, max_messages=10, - component_type=ComponentType.TERMINATION, + component_type=ComponentTypes.TERMINATION, version="1.0.0" ) @@ -88,7 +88,7 @@ def sample_team_config(sample_agent_config: AgentConfig, sample_termination_conf participants=[sample_agent_config], termination_condition=sample_termination_config, model_client=sample_model_config, - component_type=ComponentType.TEAM, + component_type=ComponentTypes.TEAM, version="1.0.0" ) @@ -115,7 +115,7 @@ async def test_load_tool_invalid_config(component_factory: ComponentFactory): description="", content="", tool_type=ToolTypes.PYTHON_FUNCTION, - component_type=ComponentType.TOOL, + component_type=ComponentTypes.TOOL, version="1.0.0" )) @@ -125,7 +125,7 @@ async def test_load_tool_invalid_config(component_factory: ComponentFactory): description="Invalid function", content="def invalid_func(): return invalid syntax", tool_type=ToolTypes.PYTHON_FUNCTION, - component_type=ComponentType.TOOL, + component_type=ComponentTypes.TOOL, version="1.0.0" ) with pytest.raises(ValueError): @@ -154,7 +154,7 @@ async def test_load_termination(component_factory: ComponentFactory): max_msg_config = TerminationConfig( termination_type=TerminationTypes.MAX_MESSAGES, max_messages=5, - component_type=ComponentType.TERMINATION, + component_type=ComponentTypes.TERMINATION, version="1.0.0" ) termination = await component_factory.load_termination(max_msg_config) @@ -164,7 +164,7 @@ async def test_load_termination(component_factory: ComponentFactory): # Test StopMessageTermination stop_msg_config = TerminationConfig( termination_type=TerminationTypes.STOP_MESSAGE, - component_type=ComponentType.TERMINATION, + component_type=ComponentTypes.TERMINATION, version="1.0.0" ) termination = await component_factory.load_termination(stop_msg_config) @@ -174,13 +174,108 @@ async def test_load_termination(component_factory: ComponentFactory): text_mention_config = TerminationConfig( termination_type=TerminationTypes.TEXT_MENTION, text="DONE", - component_type=ComponentType.TERMINATION, + component_type=ComponentTypes.TERMINATION, version="1.0.0" ) termination = await component_factory.load_termination(text_mention_config) assert isinstance(termination, TextMentionTermination) assert termination._text == "DONE" + # Test AND combination + and_combo_config = TerminationConfig( + termination_type=TerminationTypes.COMBINATION, + operator="and", + conditions=[ + TerminationConfig( + termination_type=TerminationTypes.MAX_MESSAGES, + max_messages=5, + component_type=ComponentTypes.TERMINATION, + version="1.0.0" + ), + TerminationConfig( + termination_type=TerminationTypes.TEXT_MENTION, + text="DONE", + component_type=ComponentTypes.TERMINATION, + version="1.0.0" + ) + ], + component_type=ComponentTypes.TERMINATION, + version="1.0.0" + ) + termination = await component_factory.load_termination(and_combo_config) + assert termination is not None + + # Test OR combination + or_combo_config = TerminationConfig( + termination_type=TerminationTypes.COMBINATION, + operator="or", + conditions=[ + TerminationConfig( + termination_type=TerminationTypes.MAX_MESSAGES, + max_messages=5, + component_type=ComponentTypes.TERMINATION, + version="1.0.0" + ), + TerminationConfig( + termination_type=TerminationTypes.TEXT_MENTION, + text="DONE", + component_type=ComponentTypes.TERMINATION, + version="1.0.0" + ) + ], + component_type=ComponentTypes.TERMINATION, + version="1.0.0" + ) + termination = await component_factory.load_termination(or_combo_config) + assert termination is not None + + # Test invalid combinations + with pytest.raises(ValueError): + await component_factory.load_termination(TerminationConfig( + termination_type=TerminationTypes.COMBINATION, + conditions=[], # Empty conditions + component_type=ComponentTypes.TERMINATION, + version="1.0.0" + )) + + with pytest.raises(ValueError): + await component_factory.load_termination(TerminationConfig( + termination_type=TerminationTypes.COMBINATION, + operator="invalid", # type: ignore + conditions=[ + TerminationConfig( + termination_type=TerminationTypes.MAX_MESSAGES, + max_messages=5, + component_type=ComponentTypes.TERMINATION, + version="1.0.0" + ) + ], + component_type=ComponentTypes.TERMINATION, + version="1.0.0" + )) + + # Test missing operator + with pytest.raises(ValueError): + await component_factory.load_termination(TerminationConfig( + termination_type=TerminationTypes.COMBINATION, + conditions=[ + TerminationConfig( + termination_type=TerminationTypes.MAX_MESSAGES, + max_messages=5, + component_type=ComponentTypes.TERMINATION, + version="1.0.0" + ), + TerminationConfig( + termination_type=TerminationTypes.TEXT_MENTION, + text="DONE", + component_type=ComponentTypes.TERMINATION, + version="1.0.0" + ) + ], + component_type=ComponentTypes.TERMINATION, + version="1.0.0" + )) + @pytest.mark.asyncio async def test_load_team(component_factory: ComponentFactory, sample_team_config: TeamConfig, sample_model_config: ModelConfig): @@ -201,13 +296,13 @@ async def test_load_team(component_factory: ComponentFactory, sample_team_config system_message="You are another helpful assistant", model_client=sample_model_config, tools=sample_team_config.participants[0].tools, - component_type=ComponentType.AGENT, + component_type=ComponentTypes.AGENT, version="1.0.0" ) ], termination_condition=sample_team_config.termination_condition, model_client=sample_model_config, - component_type=ComponentType.TEAM, + component_type=ComponentTypes.TEAM, version="1.0.0" ) team = await component_factory.load_team(selector_team_config) @@ -223,7 +318,7 @@ async def test_invalid_configs(component_factory: ComponentFactory): name="test", agent_type="InvalidAgent", # type: ignore system_message="test", - component_type=ComponentType.AGENT, + component_type=ComponentTypes.AGENT, version="1.0.0" )) @@ -233,7 +328,7 @@ async def test_invalid_configs(component_factory: ComponentFactory): name="test", team_type="InvalidTeam", # type: ignore participants=[], - component_type=ComponentType.TEAM, + component_type=ComponentTypes.TEAM, version="1.0.0" )) @@ -241,6 +336,6 @@ async def test_invalid_configs(component_factory: ComponentFactory): with pytest.raises(ValueError): await component_factory.load_termination(TerminationConfig( termination_type="InvalidTermination", # type: ignore - component_type=ComponentType.TERMINATION, + component_type=ComponentTypes.TERMINATION, version="1.0.0" )) diff --git a/python/packages/autogen-studio/tests/test_db_manager.py b/python/packages/autogen-studio/tests/test_db_manager.py index ccc2aa5ddce..b77a891a797 100644 --- a/python/packages/autogen-studio/tests/test_db_manager.py +++ b/python/packages/autogen-studio/tests/test_db_manager.py @@ -1,15 +1,16 @@ import os +import asyncio import pytest from sqlmodel import Session, text, select from typing import Generator -from datetime import datetime from autogenstudio.database import DatabaseManager -from autogenstudio.datamodel import ( - Model, ModelConfig, Agent, AgentConfig, Tool, ToolConfig, - Team, TeamConfig, ModelTypes, AgentTypes, TeamTypes, ComponentType, - TerminationConfig, TerminationTypes, LinkTypes, ToolTypes +from autogenstudio.datamodel.types import ( + ModelConfig, AgentConfig, ToolConfig, + TeamConfig, ModelTypes, AgentTypes, TeamTypes, ComponentTypes, + TerminationConfig, TerminationTypes, ToolTypes ) +from autogenstudio.datamodel.db import Model, Tool, Agent, Team, LinkTypes @pytest.fixture @@ -18,13 +19,13 @@ def test_db() -> Generator[DatabaseManager, None, None]: db_path = "test.db" db = DatabaseManager(f"sqlite:///{db_path}") db.reset_db() - db.create_db_and_tables() + # Initialize database instead of create_db_and_tables + db.initialize_database(auto_upgrade=False) yield db + # Clean up + asyncio.run(db.close()) db.reset_db() try: - # Close database connections before removing file - db.engine.dispose() - # Remove the database file if os.path.exists(db_path): os.remove(db_path) except Exception as e: @@ -44,7 +45,7 @@ def sample_model(test_user: str) -> Model: config=ModelConfig( model="gpt-4", model_type=ModelTypes.OPENAI, - component_type=ComponentType.MODEL, + component_type=ComponentTypes.MODEL, version="1.0.0" ).model_dump() ) @@ -60,7 +61,7 @@ def sample_tool(test_user: str) -> Tool: description="A test tool", content="async def test_func(x: str) -> str:\n return f'Test {x}'", tool_type=ToolTypes.PYTHON_FUNCTION, - component_type=ComponentType.TOOL, + component_type=ComponentTypes.TOOL, version="1.0.0" ).model_dump() ) @@ -76,7 +77,7 @@ def sample_agent(test_user: str, sample_model: Model, sample_tool: Tool) -> Agen agent_type=AgentTypes.ASSISTANT, model_client=ModelConfig.model_validate(sample_model.config), tools=[ToolConfig.model_validate(sample_tool.config)], - component_type=ComponentType.AGENT, + component_type=ComponentTypes.AGENT, version="1.0.0" ).model_dump() ) @@ -92,11 +93,11 @@ def sample_team(test_user: str, sample_agent: Agent) -> Team: participants=[AgentConfig.model_validate(sample_agent.config)], termination_condition=TerminationConfig( termination_type=TerminationTypes.STOP_MESSAGE, - component_type=ComponentType.TERMINATION, + component_type=ComponentTypes.TERMINATION, version="1.0.0" ).model_dump(), team_type=TeamTypes.ROUND_ROBIN, - component_type=ComponentType.TEAM, + component_type=ComponentTypes.TEAM, version="1.0.0" ).model_dump() ) @@ -144,7 +145,7 @@ def test_multiple_links(self, test_db: DatabaseManager, sample_agent: Agent): config=ModelConfig( model="gpt-4", model_type=ModelTypes.OPENAI, - component_type=ComponentType.MODEL, + component_type=ComponentTypes.MODEL, version="1.0.0" ).model_dump() ) @@ -153,7 +154,7 @@ def test_multiple_links(self, test_db: DatabaseManager, sample_agent: Agent): config=ModelConfig( model="gpt-3.5", model_type=ModelTypes.OPENAI, - component_type=ComponentType.MODEL, + component_type=ComponentTypes.MODEL, version="1.0.0" ).model_dump() ) @@ -181,3 +182,59 @@ def test_multiple_links(self, test_db: DatabaseManager, sample_agent: Agent): model_names = [model.config["model"] for model in linked_models.data] assert "gpt-4" in model_names assert "gpt-3.5" in model_names + + def test_upsert_operations(self, test_db: DatabaseManager, sample_model: Model): + """Test upsert for both create and update scenarios""" + # Test Create + response = test_db.upsert(sample_model) + assert response.status is True + assert "Created Successfully" in response.message + + # Test Update + sample_model.config["model"] = "gpt-4-turbo" + response = test_db.upsert(sample_model) + assert response.status is True + assert "Updated Successfully" in response.message + + # Verify Update + result = test_db.get(Model, {"id": sample_model.id}) + assert result.status is True + assert result.data[0].config["model"] == "gpt-4-turbo" + + def test_delete_operations(self, test_db: DatabaseManager, sample_model: Model): + """Test delete with various filters""" + # First insert the model + test_db.upsert(sample_model) + + # Test deletion by id + response = test_db.delete(Model, {"id": sample_model.id}) + assert response.status is True + assert "Deleted Successfully" in response.message + + # Verify deletion + result = test_db.get(Model, {"id": sample_model.id}) + assert len(result.data) == 0 + + # Test deletion with non-existent id + response = test_db.delete(Model, {"id": 999999}) + assert "Row not found" in response.message + + def test_initialize_database_scenarios(self): + """Test different initialize_database parameters""" + db_path = "test_init.db" + db = DatabaseManager(f"sqlite:///{db_path}") + + try: + # Test basic initialization + response = db.initialize_database() + assert response.status is True + + # Test with auto_upgrade + response = db.initialize_database(auto_upgrade=True) + assert response.status is True + + finally: + asyncio.run(db.close()) + db.reset_db() + if os.path.exists(db_path): + os.remove(db_path) diff --git a/python/pyproject.toml b/python/pyproject.toml index a7fb3f89f42..e9b9753cfca 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -78,4 +78,6 @@ test = "python run_task_in_pkgs_if_exist.py test" check = ["fmt", "lint", "pyright", "mypy", "test"] -gen-proto = "python -m grpc_tools.protoc --python_out=./packages/autogen-core/src/autogen_core/application/protos --grpc_python_out=./packages/autogen-core/src/autogen_core/application/protos --mypy_out=./packages/autogen-core/src/autogen_core/application/protos --mypy_grpc_out=./packages/autogen-core/src/autogen_core/application/protos --proto_path ../protos/ agent_worker.proto --proto_path ../protos/ cloudevent.proto --proto_path ../protos/ agent_events.proto" +gen-proto = "python -m grpc_tools.protoc --python_out=./packages/autogen-core/src/autogen_core/application/protos --grpc_python_out=./packages/autogen-core/src/autogen_core/application/protos --mypy_out=./packages/autogen-core/src/autogen_core/application/protos --mypy_grpc_out=./packages/autogen-core/src/autogen_core/application/protos --proto_path ../protos/ agent_worker.proto --proto_path ../protos/ cloudevent.proto" + +gen-test-proto = "python -m grpc_tools.protoc --python_out=./packages/autogen-core/tests/protos --grpc_python_out=./packages/autogen-core/tests/protos --mypy_out=./packages/autogen-core/tests/protos --mypy_grpc_out=./packages/autogen-core/tests/protos --proto_path ./packages/autogen-core/tests/protos serialization_test.proto" \ No newline at end of file diff --git a/python/uv.lock b/python/uv.lock index 04d981b1385..c17a7a0a56b 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -7,7 +7,8 @@ resolution-markers = [ "python_full_version < '3.11'", "python_full_version == '3.11.*'", "python_full_version >= '3.12' and python_full_version < '3.12.4'", - "python_full_version >= '3.12.4'", + "python_full_version < '3.13'", + "python_full_version >= '3.13'", ] [manifest] @@ -346,6 +347,7 @@ source = { editable = "packages/autogen-core" } dependencies = [ { name = "aiohttp" }, { name = "asyncio-atexit" }, + { name = "grpcio" }, { name = "jsonref" }, { name = "openai" }, { name = "opentelemetry-api" }, @@ -407,13 +409,14 @@ dev = [ requires-dist = [ { name = "aiohttp" }, { name = "asyncio-atexit" }, + { name = "grpcio", specifier = "~=1.62.0" }, { name = "grpcio", marker = "extra == 'grpc'", specifier = "~=1.62.0" }, { name = "jsonref", specifier = "~=1.1.0" }, { name = "openai", specifier = ">=1.3" }, { name = "opentelemetry-api", specifier = "~=1.27.0" }, { name = "pillow" }, { name = "protobuf", specifier = "~=4.25.1" }, - { name = "pydantic", specifier = ">=2.0.0,<3.0.0" }, + { name = "pydantic", specifier = "<3.0.0,>=2.0.0" }, { name = "tiktoken" }, { name = "typing-extensions" }, ] @@ -562,7 +565,7 @@ requires-dist = [ { name = "pdfminer-six" }, { name = "playwright" }, { name = "puremagic" }, - { name = "pydantic", specifier = ">=2.0.0,<3.0.0" }, + { name = "pydantic", specifier = "<3.0.0,>=2.0.0" }, { name = "pydub" }, { name = "python-pptx" }, { name = "requests" }, @@ -582,9 +585,10 @@ dev = [ [[package]] name = "autogenstudio" -version = "0.4.0.dev35" +version = "0.4.0.dev38" source = { editable = "packages/autogen-studio" } dependencies = [ + { name = "aiofiles" }, { name = "alembic" }, { name = "autogen-agentchat" }, { name = "autogen-core" }, @@ -614,6 +618,7 @@ web = [ [package.metadata] requires-dist = [ + { name = "aiofiles" }, { name = "alembic" }, { name = "autogen-agentchat", editable = "packages/autogen-agentchat" }, { name = "autogen-core", editable = "packages/autogen-core" }, @@ -2582,6 +2587,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/07/37d67048786ae84e6612575e173d713c9a05d0ae495dde1e68d972207d98/mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2", size = 12589275 }, { url = "https://files.pythonhosted.org/packages/1f/17/b1018c6bb3e9f1ce3956722b3bf91bff86c1cefccca71cec05eae49d6d41/mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0", size = 13037783 }, { url = "https://files.pythonhosted.org/packages/cb/32/cd540755579e54a88099aee0287086d996f5a24281a673f78a0e14dba150/mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2", size = 9726197 }, + { url = "https://files.pythonhosted.org/packages/11/bb/ab4cfdc562cad80418f077d8be9b4491ee4fb257440da951b85cbb0a639e/mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7", size = 11069721 }, + { url = "https://files.pythonhosted.org/packages/59/3b/a393b1607cb749ea2c621def5ba8c58308ff05e30d9dbdc7c15028bca111/mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62", size = 10063996 }, + { url = "https://files.pythonhosted.org/packages/d1/1f/6b76be289a5a521bb1caedc1f08e76ff17ab59061007f201a8a18cc514d1/mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8", size = 12584043 }, + { url = "https://files.pythonhosted.org/packages/a6/83/5a85c9a5976c6f96e3a5a7591aa28b4a6ca3a07e9e5ba0cec090c8b596d6/mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7", size = 13036996 }, + { url = "https://files.pythonhosted.org/packages/b4/59/c39a6f752f1f893fccbcf1bdd2aca67c79c842402b5283563d006a67cf76/mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc", size = 9737709 }, { url = "https://files.pythonhosted.org/packages/3b/86/72ce7f57431d87a7ff17d442f521146a6585019eb8f4f31b7c02801f78ad/mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a", size = 2647043 }, ] @@ -3306,7 +3316,7 @@ name = "psycopg" version = "3.2.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, { name = "tzdata", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d1/ad/7ce016ae63e231575df0498d2395d15f005f05e32d3a2d439038e1bd0851/psycopg-3.2.3.tar.gz", hash = "sha256:a5764f67c27bec8bfac85764d23c534af2c27b893550377e37ce59c12aac47a2", size = 155550 } @@ -4320,7 +4330,7 @@ name = "sqlalchemy" version = "2.0.36" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, + { name = "greenlet", marker = "(python_full_version < '3.13' and platform_machine == 'AMD64') or (python_full_version < '3.13' and platform_machine == 'WIN32') or (python_full_version < '3.13' and platform_machine == 'aarch64') or (python_full_version < '3.13' and platform_machine == 'amd64') or (python_full_version < '3.13' and platform_machine == 'ppc64le') or (python_full_version < '3.13' and platform_machine == 'win32') or (python_full_version < '3.13' and platform_machine == 'x86_64')" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/65/9cbc9c4c3287bed2499e05033e207473504dc4df999ce49385fb1f8b058a/sqlalchemy-2.0.36.tar.gz", hash = "sha256:7f2767680b6d2398aea7082e45a774b2b0767b5c8d8ffb9c8b683088ea9b29c5", size = 9574485 } From a934721a1e82219a416ac01c987a527def06f7b1 Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Wed, 27 Nov 2024 09:32:21 -0800 Subject: [PATCH 12/16] simplify --- dotnet/samples/Hello/Hello.AppHost/Program.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dotnet/samples/Hello/Hello.AppHost/Program.cs b/dotnet/samples/Hello/Hello.AppHost/Program.cs index f8964a22b03..e550542a8b4 100644 --- a/dotnet/samples/Hello/Hello.AppHost/Program.cs +++ b/dotnet/samples/Hello/Hello.AppHost/Program.cs @@ -7,13 +7,14 @@ var backend = builder.AddProject("backend").WithExternalHttpEndpoints(); var client = builder.AddProject("HelloAgentsDotNET") .WithReference(backend) - .WithEnvironment("AGENT_HOST", $"{backend.GetEndpoint("https").Property(EndpointProperty.Url)}") + .WithEnvironment("AGENT_HOST", backend.GetEndpoint("https")) .WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true") .WaitFor(backend); #pragma warning disable ASPIREHOSTINGPYTHON001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +// xlang is over http for now - in prod use TLS between containers builder.AddPythonApp("HelloAgentsPython", "../HelloPythonAgent", "hello_python_agent.py", "../../../../python/.venv") .WithReference(backend) - .WithEnvironment("AGENT_HOST", $"{backend.GetEndpoint("http").Property(EndpointProperty.Url)}") + .WithEnvironment("AGENT_HOST", backend.GetEndpoint("http")) .WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true") .WithEnvironment("GRPC_DNS_RESOLVER", "native") .WithOtlpExporter() From 2fcfbfcf5b872d32ec56003c9c17be9952a43cde Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Wed, 27 Nov 2024 09:38:54 -0800 Subject: [PATCH 13/16] unneeded --- dotnet/AutoGen.sln | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dotnet/AutoGen.sln b/dotnet/AutoGen.sln index 2637d38294f..5b26e27165b 100644 --- a/dotnet/AutoGen.sln +++ b/dotnet/AutoGen.sln @@ -132,8 +132,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AutoGen.Extension EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AutoGen.Agents.Tests", "test\Microsoft.AutoGen.Agents.Tests\Microsoft.AutoGen.Agents.Tests.csproj", "{394FDAF8-74F9-4977-94A5-3371737EB774}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloPythonAgent.AppHost", "samples\Hello\HelloX-Lang\HelloPythonAgent\HelloPythonAgent.AppHost\HelloPythonAgent.AppHost.csproj", "{B461636A-B901-47C1-A378-7CC8025E72DD}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -344,10 +342,6 @@ Global {394FDAF8-74F9-4977-94A5-3371737EB774}.Debug|Any CPU.Build.0 = Debug|Any CPU {394FDAF8-74F9-4977-94A5-3371737EB774}.Release|Any CPU.ActiveCfg = Release|Any CPU {394FDAF8-74F9-4977-94A5-3371737EB774}.Release|Any CPU.Build.0 = Release|Any CPU - {B461636A-B901-47C1-A378-7CC8025E72DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B461636A-B901-47C1-A378-7CC8025E72DD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B461636A-B901-47C1-A378-7CC8025E72DD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B461636A-B901-47C1-A378-7CC8025E72DD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -408,7 +402,6 @@ Global {64EF61E7-00A6-4E5E-9808-62E10993A0E5} = {7EB336C2-7C0A-4BC8-80C6-A3173AB8DC45} {65059914-5527-4A00-9308-9FAF23D5E85A} = {18BF8DD7-0585-48BF-8F97-AD333080CE06} {394FDAF8-74F9-4977-94A5-3371737EB774} = {F823671B-3ECA-4AE6-86DA-25E920D3FE64} - {B461636A-B901-47C1-A378-7CC8025E72DD} = {7EB336C2-7C0A-4BC8-80C6-A3173AB8DC45} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {93384647-528D-46C8-922C-8DB36A382F0B} From 53440e3fe839c45d5f20432fa3cc74a54414ed29 Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Wed, 27 Nov 2024 11:53:10 -0800 Subject: [PATCH 14/16] interim state - fails --- dotnet/samples/Hello/Hello.AppHost/Program.cs | 2 +- .../autogen-core/samples/protos/__init__.py | 8 + .../samples/protos/agent_events_pb2.py | 49 +++++ .../samples/protos/agent_events_pb2.pyi | 197 ++++++++++++++++++ .../samples/protos/agent_events_pb2_grpc.py | 4 + .../samples/protos/agent_events_pb2_grpc.pyi | 17 ++ .../hello_python_agent}/hello_python_agent.py | 6 +- .../xlang/hello_python_agent}/user_input.py | 2 +- python/pyproject.toml | 4 +- 9 files changed, 285 insertions(+), 4 deletions(-) create mode 100644 python/packages/autogen-core/samples/protos/__init__.py create mode 100644 python/packages/autogen-core/samples/protos/agent_events_pb2.py create mode 100644 python/packages/autogen-core/samples/protos/agent_events_pb2.pyi create mode 100644 python/packages/autogen-core/samples/protos/agent_events_pb2_grpc.py create mode 100644 python/packages/autogen-core/samples/protos/agent_events_pb2_grpc.pyi rename {dotnet/samples/Hello/HelloPythonAgent => python/packages/autogen-core/samples/xlang/hello_python_agent}/hello_python_agent.py (89%) rename {dotnet/samples/Hello/HelloPythonAgent => python/packages/autogen-core/samples/xlang/hello_python_agent}/user_input.py (91%) diff --git a/dotnet/samples/Hello/Hello.AppHost/Program.cs b/dotnet/samples/Hello/Hello.AppHost/Program.cs index e550542a8b4..62af2b249a9 100644 --- a/dotnet/samples/Hello/Hello.AppHost/Program.cs +++ b/dotnet/samples/Hello/Hello.AppHost/Program.cs @@ -12,7 +12,7 @@ .WaitFor(backend); #pragma warning disable ASPIREHOSTINGPYTHON001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. // xlang is over http for now - in prod use TLS between containers -builder.AddPythonApp("HelloAgentsPython", "../HelloPythonAgent", "hello_python_agent.py", "../../../../python/.venv") +builder.AddPythonApp("HelloAgentsPython", "../../../../python/packages/autogen-core/samples/xlang/hello_python_agent", "hello_python_agent.py", "../../../../../.venv") .WithReference(backend) .WithEnvironment("AGENT_HOST", backend.GetEndpoint("http")) .WithEnvironment("STAY_ALIVE_ON_GOODBYE", "true") diff --git a/python/packages/autogen-core/samples/protos/__init__.py b/python/packages/autogen-core/samples/protos/__init__.py new file mode 100644 index 00000000000..b3ea671c3b9 --- /dev/null +++ b/python/packages/autogen-core/samples/protos/__init__.py @@ -0,0 +1,8 @@ +""" +The :mod:`autogen_core.worker.protos` module provides Google Protobuf classes for agent-worker communication +""" + +import os +import sys + +sys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) diff --git a/python/packages/autogen-core/samples/protos/agent_events_pb2.py b/python/packages/autogen-core/samples/protos/agent_events_pb2.py new file mode 100644 index 00000000000..e5e7f4d59a1 --- /dev/null +++ b/python/packages/autogen-core/samples/protos/agent_events_pb2.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: agent_events.proto +# Protobuf Python Version: 4.25.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x61gent_events.proto\x12\x06\x61gents\"2\n\x0bTextMessage\x12\x13\n\x0btextMessage\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"\x18\n\x05Input\x12\x0f\n\x07message\x18\x01 \x01(\t\"\x1f\n\x0eInputProcessed\x12\r\n\x05route\x18\x01 \x01(\t\"\x19\n\x06Output\x12\x0f\n\x07message\x18\x01 \x01(\t\"\x1e\n\rOutputWritten\x12\r\n\x05route\x18\x01 \x01(\t\"\x1a\n\x07IOError\x12\x0f\n\x07message\x18\x01 \x01(\t\"%\n\x12NewMessageReceived\x12\x0f\n\x07message\x18\x01 \x01(\t\"%\n\x11ResponseGenerated\x12\x10\n\x08response\x18\x01 \x01(\t\"\x1a\n\x07GoodBye\x12\x0f\n\x07message\x18\x01 \x01(\t\" \n\rMessageStored\x12\x0f\n\x07message\x18\x01 \x01(\t\";\n\x12\x43onversationClosed\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x14\n\x0cuser_message\x18\x02 \x01(\t\"\x1b\n\x08Shutdown\x12\x0f\n\x07message\x18\x01 \x01(\tB!\xaa\x02\x1eMicrosoft.AutoGen.Abstractionsb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agent_events_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + _globals['DESCRIPTOR']._options = None + _globals['DESCRIPTOR']._serialized_options = b'\252\002\036Microsoft.AutoGen.Abstractions' + _globals['_TEXTMESSAGE']._serialized_start=30 + _globals['_TEXTMESSAGE']._serialized_end=80 + _globals['_INPUT']._serialized_start=82 + _globals['_INPUT']._serialized_end=106 + _globals['_INPUTPROCESSED']._serialized_start=108 + _globals['_INPUTPROCESSED']._serialized_end=139 + _globals['_OUTPUT']._serialized_start=141 + _globals['_OUTPUT']._serialized_end=166 + _globals['_OUTPUTWRITTEN']._serialized_start=168 + _globals['_OUTPUTWRITTEN']._serialized_end=198 + _globals['_IOERROR']._serialized_start=200 + _globals['_IOERROR']._serialized_end=226 + _globals['_NEWMESSAGERECEIVED']._serialized_start=228 + _globals['_NEWMESSAGERECEIVED']._serialized_end=265 + _globals['_RESPONSEGENERATED']._serialized_start=267 + _globals['_RESPONSEGENERATED']._serialized_end=304 + _globals['_GOODBYE']._serialized_start=306 + _globals['_GOODBYE']._serialized_end=332 + _globals['_MESSAGESTORED']._serialized_start=334 + _globals['_MESSAGESTORED']._serialized_end=366 + _globals['_CONVERSATIONCLOSED']._serialized_start=368 + _globals['_CONVERSATIONCLOSED']._serialized_end=427 + _globals['_SHUTDOWN']._serialized_start=429 + _globals['_SHUTDOWN']._serialized_end=456 +# @@protoc_insertion_point(module_scope) diff --git a/python/packages/autogen-core/samples/protos/agent_events_pb2.pyi b/python/packages/autogen-core/samples/protos/agent_events_pb2.pyi new file mode 100644 index 00000000000..01cfbafee51 --- /dev/null +++ b/python/packages/autogen-core/samples/protos/agent_events_pb2.pyi @@ -0,0 +1,197 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class TextMessage(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TEXTMESSAGE_FIELD_NUMBER: builtins.int + SOURCE_FIELD_NUMBER: builtins.int + textMessage: builtins.str + source: builtins.str + def __init__( + self, + *, + textMessage: builtins.str = ..., + source: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["source", b"source", "textMessage", b"textMessage"]) -> None: ... + +global___TextMessage = TextMessage + +@typing.final +class Input(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + def __init__( + self, + *, + message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... + +global___Input = Input + +@typing.final +class InputProcessed(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ROUTE_FIELD_NUMBER: builtins.int + route: builtins.str + def __init__( + self, + *, + route: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["route", b"route"]) -> None: ... + +global___InputProcessed = InputProcessed + +@typing.final +class Output(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + def __init__( + self, + *, + message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... + +global___Output = Output + +@typing.final +class OutputWritten(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ROUTE_FIELD_NUMBER: builtins.int + route: builtins.str + def __init__( + self, + *, + route: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["route", b"route"]) -> None: ... + +global___OutputWritten = OutputWritten + +@typing.final +class IOError(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + def __init__( + self, + *, + message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... + +global___IOError = IOError + +@typing.final +class NewMessageReceived(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + def __init__( + self, + *, + message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... + +global___NewMessageReceived = NewMessageReceived + +@typing.final +class ResponseGenerated(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESPONSE_FIELD_NUMBER: builtins.int + response: builtins.str + def __init__( + self, + *, + response: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["response", b"response"]) -> None: ... + +global___ResponseGenerated = ResponseGenerated + +@typing.final +class GoodBye(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + def __init__( + self, + *, + message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... + +global___GoodBye = GoodBye + +@typing.final +class MessageStored(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + def __init__( + self, + *, + message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... + +global___MessageStored = MessageStored + +@typing.final +class ConversationClosed(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + USER_ID_FIELD_NUMBER: builtins.int + USER_MESSAGE_FIELD_NUMBER: builtins.int + user_id: builtins.str + user_message: builtins.str + def __init__( + self, + *, + user_id: builtins.str = ..., + user_message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["user_id", b"user_id", "user_message", b"user_message"]) -> None: ... + +global___ConversationClosed = ConversationClosed + +@typing.final +class Shutdown(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + def __init__( + self, + *, + message: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... + +global___Shutdown = Shutdown diff --git a/python/packages/autogen-core/samples/protos/agent_events_pb2_grpc.py b/python/packages/autogen-core/samples/protos/agent_events_pb2_grpc.py new file mode 100644 index 00000000000..2daafffebfc --- /dev/null +++ b/python/packages/autogen-core/samples/protos/agent_events_pb2_grpc.py @@ -0,0 +1,4 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + diff --git a/python/packages/autogen-core/samples/protos/agent_events_pb2_grpc.pyi b/python/packages/autogen-core/samples/protos/agent_events_pb2_grpc.pyi new file mode 100644 index 00000000000..a6a9cff9dfd --- /dev/null +++ b/python/packages/autogen-core/samples/protos/agent_events_pb2_grpc.pyi @@ -0,0 +1,17 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import abc +import collections.abc +import grpc +import grpc.aio +import typing + +_T = typing.TypeVar("_T") + +class _MaybeAsyncIterator(collections.abc.AsyncIterator[_T], collections.abc.Iterator[_T], metaclass=abc.ABCMeta): ... + +class _ServicerContext(grpc.ServicerContext, grpc.aio.ServicerContext): # type: ignore[misc, type-arg] + ... diff --git a/dotnet/samples/Hello/HelloPythonAgent/hello_python_agent.py b/python/packages/autogen-core/samples/xlang/hello_python_agent/hello_python_agent.py similarity index 89% rename from dotnet/samples/Hello/HelloPythonAgent/hello_python_agent.py rename to python/packages/autogen-core/samples/xlang/hello_python_agent/hello_python_agent.py index dc4522c8f5e..acff55851d7 100644 --- a/dotnet/samples/Hello/HelloPythonAgent/hello_python_agent.py +++ b/python/packages/autogen-core/samples/xlang/hello_python_agent/hello_python_agent.py @@ -1,9 +1,13 @@ import asyncio import logging import os +import sys +thisdir=os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.join(thisdir, "..", "..")) from autogen_core.application import WorkerAgentRuntime -from autogen_core.application.protos.agent_events_pb2 import NewMessageReceived +from protos.agent_events_pb2 import NewMessageReceived +#from protos.agents_events_pb2 import NewMessageReceived from autogen_core.base import AgentId, try_get_known_serializers_for_type from autogen_core.components import DefaultSubscription, DefaultTopicId diff --git a/dotnet/samples/Hello/HelloPythonAgent/user_input.py b/python/packages/autogen-core/samples/xlang/hello_python_agent/user_input.py similarity index 91% rename from dotnet/samples/Hello/HelloPythonAgent/user_input.py rename to python/packages/autogen-core/samples/xlang/hello_python_agent/user_input.py index 62d766ca05c..0da6c5355fc 100644 --- a/dotnet/samples/Hello/HelloPythonAgent/user_input.py +++ b/python/packages/autogen-core/samples/xlang/hello_python_agent/user_input.py @@ -2,7 +2,7 @@ import logging from typing import Union -from autogen_core.application.protos.agent_events_pb2 import ConversationClosed, Input, NewMessageReceived, Output +from protos.agent_events_pb2 import ConversationClosed, Input, NewMessageReceived, Output from autogen_core.base import MessageContext from autogen_core.components import DefaultTopicId, RoutedAgent, message_handler diff --git a/python/pyproject.toml b/python/pyproject.toml index e9b9753cfca..9f4d3773575 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -80,4 +80,6 @@ check = ["fmt", "lint", "pyright", "mypy", "test"] gen-proto = "python -m grpc_tools.protoc --python_out=./packages/autogen-core/src/autogen_core/application/protos --grpc_python_out=./packages/autogen-core/src/autogen_core/application/protos --mypy_out=./packages/autogen-core/src/autogen_core/application/protos --mypy_grpc_out=./packages/autogen-core/src/autogen_core/application/protos --proto_path ../protos/ agent_worker.proto --proto_path ../protos/ cloudevent.proto" -gen-test-proto = "python -m grpc_tools.protoc --python_out=./packages/autogen-core/tests/protos --grpc_python_out=./packages/autogen-core/tests/protos --mypy_out=./packages/autogen-core/tests/protos --mypy_grpc_out=./packages/autogen-core/tests/protos --proto_path ./packages/autogen-core/tests/protos serialization_test.proto" \ No newline at end of file +gen-test-proto = "python -m grpc_tools.protoc --python_out=./packages/autogen-core/tests/protos --grpc_python_out=./packages/autogen-core/tests/protos --mypy_out=./packages/autogen-core/tests/protos --mypy_grpc_out=./packages/autogen-core/tests/protos --proto_path ./packages/autogen-core/tests/protos serialization_test.proto" + +gen-proto-samples = "python -m grpc_tools.protoc --python_out=./packages/autogen-core/samples/protos --grpc_python_out=./packages/autogen-core/samples/protos --mypy_out=./packages/autogen-core/samples/protos --mypy_grpc_out=./packages/autogen-core/samples/protos --proto_path ../protos/ agent_events.proto" \ No newline at end of file From 172bf042bdd4aa63ff2904343ee7709ad8e15fb5 Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Wed, 27 Nov 2024 15:33:03 -0800 Subject: [PATCH 15/16] format --- .../samples/protos/agent_events_pb2.py | 61 ++++++++++--------- .../samples/protos/agent_events_pb2_grpc.py | 2 +- .../hello_python_agent/hello_python_agent.py | 8 ++- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/python/packages/autogen-core/samples/protos/agent_events_pb2.py b/python/packages/autogen-core/samples/protos/agent_events_pb2.py index e5e7f4d59a1..dfa96941b24 100644 --- a/python/packages/autogen-core/samples/protos/agent_events_pb2.py +++ b/python/packages/autogen-core/samples/protos/agent_events_pb2.py @@ -3,6 +3,7 @@ # source: agent_events.proto # Protobuf Python Version: 4.25.1 """Generated protocol buffer code.""" + from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database @@ -12,38 +13,38 @@ _sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x61gent_events.proto\x12\x06\x61gents\"2\n\x0bTextMessage\x12\x13\n\x0btextMessage\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\"\x18\n\x05Input\x12\x0f\n\x07message\x18\x01 \x01(\t\"\x1f\n\x0eInputProcessed\x12\r\n\x05route\x18\x01 \x01(\t\"\x19\n\x06Output\x12\x0f\n\x07message\x18\x01 \x01(\t\"\x1e\n\rOutputWritten\x12\r\n\x05route\x18\x01 \x01(\t\"\x1a\n\x07IOError\x12\x0f\n\x07message\x18\x01 \x01(\t\"%\n\x12NewMessageReceived\x12\x0f\n\x07message\x18\x01 \x01(\t\"%\n\x11ResponseGenerated\x12\x10\n\x08response\x18\x01 \x01(\t\"\x1a\n\x07GoodBye\x12\x0f\n\x07message\x18\x01 \x01(\t\" \n\rMessageStored\x12\x0f\n\x07message\x18\x01 \x01(\t\";\n\x12\x43onversationClosed\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x14\n\x0cuser_message\x18\x02 \x01(\t\"\x1b\n\x08Shutdown\x12\x0f\n\x07message\x18\x01 \x01(\tB!\xaa\x02\x1eMicrosoft.AutoGen.Abstractionsb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x12\x61gent_events.proto\x12\x06\x61gents"2\n\x0bTextMessage\x12\x13\n\x0btextMessage\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t"\x18\n\x05Input\x12\x0f\n\x07message\x18\x01 \x01(\t"\x1f\n\x0eInputProcessed\x12\r\n\x05route\x18\x01 \x01(\t"\x19\n\x06Output\x12\x0f\n\x07message\x18\x01 \x01(\t"\x1e\n\rOutputWritten\x12\r\n\x05route\x18\x01 \x01(\t"\x1a\n\x07IOError\x12\x0f\n\x07message\x18\x01 \x01(\t"%\n\x12NewMessageReceived\x12\x0f\n\x07message\x18\x01 \x01(\t"%\n\x11ResponseGenerated\x12\x10\n\x08response\x18\x01 \x01(\t"\x1a\n\x07GoodBye\x12\x0f\n\x07message\x18\x01 \x01(\t" \n\rMessageStored\x12\x0f\n\x07message\x18\x01 \x01(\t";\n\x12\x43onversationClosed\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x14\n\x0cuser_message\x18\x02 \x01(\t"\x1b\n\x08Shutdown\x12\x0f\n\x07message\x18\x01 \x01(\tB!\xaa\x02\x1eMicrosoft.AutoGen.Abstractionsb\x06proto3' +) _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agent_events_pb2', _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "agent_events_pb2", _globals) if _descriptor._USE_C_DESCRIPTORS == False: - _globals['DESCRIPTOR']._options = None - _globals['DESCRIPTOR']._serialized_options = b'\252\002\036Microsoft.AutoGen.Abstractions' - _globals['_TEXTMESSAGE']._serialized_start=30 - _globals['_TEXTMESSAGE']._serialized_end=80 - _globals['_INPUT']._serialized_start=82 - _globals['_INPUT']._serialized_end=106 - _globals['_INPUTPROCESSED']._serialized_start=108 - _globals['_INPUTPROCESSED']._serialized_end=139 - _globals['_OUTPUT']._serialized_start=141 - _globals['_OUTPUT']._serialized_end=166 - _globals['_OUTPUTWRITTEN']._serialized_start=168 - _globals['_OUTPUTWRITTEN']._serialized_end=198 - _globals['_IOERROR']._serialized_start=200 - _globals['_IOERROR']._serialized_end=226 - _globals['_NEWMESSAGERECEIVED']._serialized_start=228 - _globals['_NEWMESSAGERECEIVED']._serialized_end=265 - _globals['_RESPONSEGENERATED']._serialized_start=267 - _globals['_RESPONSEGENERATED']._serialized_end=304 - _globals['_GOODBYE']._serialized_start=306 - _globals['_GOODBYE']._serialized_end=332 - _globals['_MESSAGESTORED']._serialized_start=334 - _globals['_MESSAGESTORED']._serialized_end=366 - _globals['_CONVERSATIONCLOSED']._serialized_start=368 - _globals['_CONVERSATIONCLOSED']._serialized_end=427 - _globals['_SHUTDOWN']._serialized_start=429 - _globals['_SHUTDOWN']._serialized_end=456 + _globals["DESCRIPTOR"]._options = None + _globals["DESCRIPTOR"]._serialized_options = b"\252\002\036Microsoft.AutoGen.Abstractions" + _globals["_TEXTMESSAGE"]._serialized_start = 30 + _globals["_TEXTMESSAGE"]._serialized_end = 80 + _globals["_INPUT"]._serialized_start = 82 + _globals["_INPUT"]._serialized_end = 106 + _globals["_INPUTPROCESSED"]._serialized_start = 108 + _globals["_INPUTPROCESSED"]._serialized_end = 139 + _globals["_OUTPUT"]._serialized_start = 141 + _globals["_OUTPUT"]._serialized_end = 166 + _globals["_OUTPUTWRITTEN"]._serialized_start = 168 + _globals["_OUTPUTWRITTEN"]._serialized_end = 198 + _globals["_IOERROR"]._serialized_start = 200 + _globals["_IOERROR"]._serialized_end = 226 + _globals["_NEWMESSAGERECEIVED"]._serialized_start = 228 + _globals["_NEWMESSAGERECEIVED"]._serialized_end = 265 + _globals["_RESPONSEGENERATED"]._serialized_start = 267 + _globals["_RESPONSEGENERATED"]._serialized_end = 304 + _globals["_GOODBYE"]._serialized_start = 306 + _globals["_GOODBYE"]._serialized_end = 332 + _globals["_MESSAGESTORED"]._serialized_start = 334 + _globals["_MESSAGESTORED"]._serialized_end = 366 + _globals["_CONVERSATIONCLOSED"]._serialized_start = 368 + _globals["_CONVERSATIONCLOSED"]._serialized_end = 427 + _globals["_SHUTDOWN"]._serialized_start = 429 + _globals["_SHUTDOWN"]._serialized_end = 456 # @@protoc_insertion_point(module_scope) diff --git a/python/packages/autogen-core/samples/protos/agent_events_pb2_grpc.py b/python/packages/autogen-core/samples/protos/agent_events_pb2_grpc.py index 2daafffebfc..bf947056a2f 100644 --- a/python/packages/autogen-core/samples/protos/agent_events_pb2_grpc.py +++ b/python/packages/autogen-core/samples/protos/agent_events_pb2_grpc.py @@ -1,4 +1,4 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" -import grpc +import grpc diff --git a/python/packages/autogen-core/samples/xlang/hello_python_agent/hello_python_agent.py b/python/packages/autogen-core/samples/xlang/hello_python_agent/hello_python_agent.py index acff55851d7..18b1c8e1f3a 100644 --- a/python/packages/autogen-core/samples/xlang/hello_python_agent/hello_python_agent.py +++ b/python/packages/autogen-core/samples/xlang/hello_python_agent/hello_python_agent.py @@ -2,12 +2,14 @@ import logging import os import sys -thisdir=os.path.dirname(os.path.abspath(__file__)) + +thisdir = os.path.dirname(os.path.abspath(__file__)) sys.path.append(os.path.join(thisdir, "..", "..")) from autogen_core.application import WorkerAgentRuntime from protos.agent_events_pb2 import NewMessageReceived -#from protos.agents_events_pb2 import NewMessageReceived + +# from protos.agents_events_pb2 import NewMessageReceived from autogen_core.base import AgentId, try_get_known_serializers_for_type from autogen_core.components import DefaultSubscription, DefaultTopicId @@ -56,4 +58,4 @@ async def main() -> None: logging.basicConfig(level=logging.DEBUG) agnext_logger.setLevel(logging.DEBUG) agnext_logger.log(logging.DEBUG, "Starting worker") - asyncio.run(main()) \ No newline at end of file + asyncio.run(main()) From 9b76ce548edf31562ce9216ea0f97c9c1db5b3f5 Mon Sep 17 00:00:00 2001 From: Ryan Sweet Date: Wed, 27 Nov 2024 16:01:31 -0800 Subject: [PATCH 16/16] protobuf type --- .../samples/xlang/hello_python_agent/hello_python_agent.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/packages/autogen-core/samples/xlang/hello_python_agent/hello_python_agent.py b/python/packages/autogen-core/samples/xlang/hello_python_agent/hello_python_agent.py index 18b1c8e1f3a..e12c735aed9 100644 --- a/python/packages/autogen-core/samples/xlang/hello_python_agent/hello_python_agent.py +++ b/python/packages/autogen-core/samples/xlang/hello_python_agent/hello_python_agent.py @@ -10,7 +10,7 @@ from protos.agent_events_pb2 import NewMessageReceived # from protos.agents_events_pb2 import NewMessageReceived -from autogen_core.base import AgentId, try_get_known_serializers_for_type +from autogen_core.base import AgentId, try_get_known_serializers_for_type, PROTOBUF_DATA_CONTENT_TYPE from autogen_core.components import DefaultSubscription, DefaultTopicId # Add the local package directory to sys.path @@ -31,7 +31,7 @@ async def main() -> None: agentHost = agentHost[8:] agnext_logger.info("0") agnext_logger.info(agentHost) - runtime = WorkerAgentRuntime(host_address=agentHost) + runtime = WorkerAgentRuntime(host_address=agentHost, payload_serialization_format=PROTOBUF_DATA_CONTENT_TYPE) agnext_logger.info("1") runtime.start()