From 1edd28b2646d5141c5b042609c06d1cf64704cc7 Mon Sep 17 00:00:00 2001 From: David Jadczyk <35080988+DJ2695@users.noreply.github.com> Date: Tue, 2 Dec 2025 15:35:32 +0100 Subject: [PATCH 01/33] feat: pass agent run kwargs through to context providers (#1681) `_prepare_thread_and_messages` and `_notify_thread_of_new_messages` now are passing through the agent's `run` kwargs, sothat they can provide the context_provider with said kwargs #1679 Co-authored-by: David Jadczyk Co-authored-by: Chris <66376200+crickman@users.noreply.github.com> Co-authored-by: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> --- python/packages/core/agent_framework/_agents.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/python/packages/core/agent_framework/_agents.py b/python/packages/core/agent_framework/_agents.py index 7ded0af8d5..b000d4d41d 100644 --- a/python/packages/core/agent_framework/_agents.py +++ b/python/packages/core/agent_framework/_agents.py @@ -337,6 +337,7 @@ class BaseAgent(SerializationMixin): thread: AgentThread, input_messages: ChatMessage | Sequence[ChatMessage], response_messages: ChatMessage | Sequence[ChatMessage], + **kwargs: Any, ) -> None: """Notify the thread of new messages. @@ -346,13 +347,14 @@ class BaseAgent(SerializationMixin): thread: The thread to notify of new messages. input_messages: The input messages to notify about. response_messages: The response messages to notify about. + **kwargs: Any extra arguments to pass from the agent run. """ if isinstance(input_messages, ChatMessage) or len(input_messages) > 0: await thread.on_new_messages(input_messages) if isinstance(response_messages, ChatMessage) or len(response_messages) > 0: await thread.on_new_messages(response_messages) if thread.context_provider: - await thread.context_provider.invoked(input_messages, response_messages) + await thread.context_provider.invoked(input_messages, response_messages, **kwargs) @property def display_name(self) -> str: @@ -969,7 +971,7 @@ class ChatAgent(BaseAgent): """ input_messages = self._normalize_messages(messages) thread, run_chat_options, thread_messages = await self._prepare_thread_and_messages( - thread=thread, input_messages=input_messages + thread=thread, input_messages=input_messages, **kwargs ) agent_name = self._get_agent_name() # Resolve final tool list (runtime provided tools + local MCP server tools) @@ -1039,7 +1041,7 @@ class ChatAgent(BaseAgent): response = ChatResponse.from_chat_response_updates(response_updates, output_format_type=co.response_format) await self._update_thread_with_type_and_conversation_id(thread, response.conversation_id) - await self._notify_thread_of_new_messages(thread, input_messages, response.messages) + await self._notify_thread_of_new_messages(thread, input_messages, response.messages, **kwargs) @override def get_new_thread( @@ -1234,6 +1236,7 @@ class ChatAgent(BaseAgent): *, thread: AgentThread | None, input_messages: list[ChatMessage] | None = None, + **kwargs: Any, ) -> tuple[AgentThread, ChatOptions, list[ChatMessage]]: """Prepare the thread and messages for agent execution. @@ -1243,6 +1246,7 @@ class ChatAgent(BaseAgent): Keyword Args: thread: The conversation thread. input_messages: Messages to process. + **kwargs: Any extra arguments to pass from the agent run. Returns: A tuple containing: @@ -1263,7 +1267,7 @@ class ChatAgent(BaseAgent): context: Context | None = None if self.context_provider: async with self.context_provider: - context = await self.context_provider.invoking(input_messages or []) + context = await self.context_provider.invoking(input_messages or [], **kwargs) if context: if context.messages: thread_messages.extend(context.messages) From ceaea4676503236f3eac9bb34ee6fecba31ede0b Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Date: Tue, 2 Dec 2025 16:50:14 +0000 Subject: [PATCH 02/33] =?UTF-8?q?.NET:=20Change=20Anthropic=20Azure=20samp?= =?UTF-8?q?le=20to=20use=20Athropic.Foundry=20package=20+=20cachi=E2=80=A6?= =?UTF-8?q?=20(#2578)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change Anthropic Azure sample to use Athropic.Foundry package + caching token * Fix apikey null * Update dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Address copilot feedback --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dotnet/Directory.Packages.props | 4 +- .../Agent_With_Anthropic.csproj | 1 + .../Agent_With_Anthropic/Program.cs | 81 +++++++------------ 3 files changed, 30 insertions(+), 56 deletions(-) diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index 3c98835c61..5d4a80026e 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -11,8 +11,8 @@ - - + + diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Agent_With_Anthropic.csproj b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Agent_With_Anthropic.csproj index 002bd066fe..eb29d1d310 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Agent_With_Anthropic.csproj +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Agent_With_Anthropic.csproj @@ -11,6 +11,7 @@ + diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Program.cs index 531b56e1a1..df070c335b 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Program.cs +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_Anthropic/Program.cs @@ -2,10 +2,9 @@ // This sample shows how to create and use an AI agent with Anthropic as the backend. -using System.ClientModel; using System.Net.Http.Headers; using Anthropic; -using Anthropic.Core; +using Anthropic.Foundry; using Azure.Core; using Azure.Identity; using Microsoft.Agents.AI; @@ -15,8 +14,8 @@ var deploymentName = Environment.GetEnvironmentVariable("ANTHROPIC_DEPLOYMENT_NA // The resource is the subdomain name / first name coming before '.services.ai.azure.com' in the endpoint Uri // ie: https://(resource name).services.ai.azure.com/anthropic/v1/chat/completions -var resource = Environment.GetEnvironmentVariable("ANTHROPIC_RESOURCE"); -var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY"); +string? resource = Environment.GetEnvironmentVariable("ANTHROPIC_RESOURCE"); +string? apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY"); const string JokerInstructions = "You are good at telling jokes."; const string JokerName = "JokerAgent"; @@ -24,8 +23,8 @@ const string JokerName = "JokerAgent"; AnthropicClient? client = (resource is null) ? new AnthropicClient() { APIKey = apiKey ?? throw new InvalidOperationException("ANTHROPIC_API_KEY is required when no ANTHROPIC_RESOURCE is provided") } // If no resource is provided, use Anthropic public API : (apiKey is not null) - ? new AnthropicFoundryClient(resource, new ApiKeyCredential(apiKey)) // If an apiKey is provided, use Foundry with ApiKey authentication - : new AnthropicFoundryClient(resource, new AzureCliCredential()); // Otherwise, use Foundry with Azure Client authentication + ? new AnthropicFoundryClient(new AnthropicFoundryApiKeyCredentials(apiKey, resource)) // If an apiKey is provided, use Foundry with ApiKey authentication + : new AnthropicFoundryClient(new AnthropicAzureTokenCredential(new AzureCliCredential(), resource)); // Otherwise, use Foundry with Azure Client authentication AIAgent agent = client.CreateAIAgent(model: deploymentName, instructions: JokerInstructions, name: JokerName); @@ -35,67 +34,41 @@ Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.")); namespace Sample { /// - /// Provides methods for invoking the Azure hosted Anthropic api. + /// Provides methods for invoking the Azure hosted Anthropic models using types. /// - public class AnthropicFoundryClient : AnthropicClient + public sealed class AnthropicAzureTokenCredential : IAnthropicFoundryCredentials { private readonly TokenCredential _tokenCredential; - private readonly string _resourceName; + private readonly Lock _lock = new(); + private AccessToken? _cachedAccessToken; + + /// + public string ResourceName { get; } /// - /// Creates a new instance of the . + /// Creates a new instance of the . /// - /// The service resource subdomain name to use in the anthropic azure endpoint /// The credential provider. Use any specialization of to get your access token in supported environments. - /// Set of client option configurations - /// Resource is null - /// TokenCredential is null - /// - /// Any APIKey or Bearer token provided will be ignored in favor of the provided in the constructor - /// - public AnthropicFoundryClient(string resourceName, TokenCredential tokenCredential, Anthropic.Core.ClientOptions? options = null) : base(options ?? new()) + /// The service resource subdomain name to use in the anthropic azure endpoint + internal AnthropicAzureTokenCredential(TokenCredential tokenCredential, string resourceName) { - this._resourceName = resourceName ?? throw new ArgumentNullException(nameof(resourceName)); + this.ResourceName = resourceName ?? throw new ArgumentNullException(nameof(resourceName)); this._tokenCredential = tokenCredential ?? throw new ArgumentNullException(nameof(tokenCredential)); - this.BaseUrl = new Uri($"https://{this._resourceName}.services.ai.azure.com/anthropic", UriKind.Absolute); } - /// - /// Creates a new instance of the . - /// - /// The service resource subdomain name to use in the anthropic azure endpoint - /// The api key. - /// Set of client option configurations - /// Resource is null - /// Api key is null - /// - /// Any APIKey or Bearer token provided will be ignored in favor of the provided in the constructor - /// - public AnthropicFoundryClient(string resourceName, ApiKeyCredential apiKeyCredential, Anthropic.Core.ClientOptions? options = null) : - this(resourceName, apiKeyCredential is null - ? throw new ArgumentNullException(nameof(apiKeyCredential)) - : DelegatedTokenCredential.Create((_, _) => - { - apiKeyCredential.Deconstruct(out string dangerousCredential); - return new AccessToken(dangerousCredential, DateTimeOffset.MaxValue); - }), - options) - { } - - public override IAnthropicClient WithOptions(Func modifier) - => this; - - protected override ValueTask BeforeSend( - HttpRequest request, - HttpRequestMessage requestMessage, - CancellationToken cancellationToken - ) + /// + public void Apply(HttpRequestMessage requestMessage) { - var accessToken = this._tokenCredential.GetToken(new TokenRequestContext(scopes: ["https://ai.azure.com/.default"]), cancellationToken); + lock (this._lock) + { + // Add a 5-minute buffer to avoid using tokens that are about to expire + if (this._cachedAccessToken is null || this._cachedAccessToken.Value.ExpiresOn <= DateTimeOffset.Now.AddMinutes(5)) + { + this._cachedAccessToken = this._tokenCredential.GetToken(new TokenRequestContext(scopes: ["https://ai.azure.com/.default"]), CancellationToken.None); + } + } - requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken.Token); - - return default; + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", this._cachedAccessToken.Value.Token); } } } From b69f994840e36897cab211d8bec38b683d09b709 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Tue, 2 Dec 2025 18:00:40 +0100 Subject: [PATCH 03/33] .NET: Fix AG-UI forwardedProps JSON property name (#2543) * Fix AG-UI forwardedProps JSON property name The RunAgentInput.ForwardedProperties property was using the wrong JSON property name 'forwardedProperties' instead of 'forwardedProps' per the AG-UI protocol specification. This fix: - Changes JsonPropertyName from 'forwardedProperties' to 'forwardedProps' - Adds comprehensive integration tests for forwarded properties Fixes #2468 * Fix formatting --- .../Shared/RunAgentInput.cs | 2 +- .../ForwardedPropertiesTests.cs | 358 ++++++++++++++++++ 2 files changed, 359 insertions(+), 1 deletion(-) create mode 100644 dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs diff --git a/dotnet/src/Microsoft.Agents.AI.AGUI/Shared/RunAgentInput.cs b/dotnet/src/Microsoft.Agents.AI.AGUI/Shared/RunAgentInput.cs index a9396ff722..f64177146f 100644 --- a/dotnet/src/Microsoft.Agents.AI.AGUI/Shared/RunAgentInput.cs +++ b/dotnet/src/Microsoft.Agents.AI.AGUI/Shared/RunAgentInput.cs @@ -32,7 +32,7 @@ internal sealed class RunAgentInput [JsonPropertyName("context")] public AGUIContextItem[] Context { get; set; } = []; - [JsonPropertyName("forwardedProperties")] + [JsonPropertyName("forwardedProps")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public JsonElement ForwardedProperties { get; set; } } diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs new file mode 100644 index 0000000000..df8caea214 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs @@ -0,0 +1,358 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Net.Http; +using System.Net.ServerSentEvents; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests; + +public sealed class ForwardedPropertiesTests : IAsyncDisposable +{ + private WebApplication? _app; + private HttpClient? _client; + + [Fact] + public async Task ForwardedProps_AreParsedAndPassedToAgent_WhenProvidedInRequestAsync() + { + // Arrange + FakeForwardedPropsAgent fakeAgent = new(); + await this.SetupTestServerAsync(fakeAgent); + + // Create request JSON with forwardedProps (per AG-UI protocol spec) + const string RequestJson = """ + { + "threadId": "thread-123", + "runId": "run-456", + "messages": [{ "id": "msg-1", "role": "user", "content": "test forwarded props" }], + "forwardedProps": { "customProp": "customValue", "sessionId": "test-session-123" } + } + """; + + using StringContent content = new(RequestJson, Encoding.UTF8, "application/json"); + + // Act + HttpResponseMessage response = await this._client!.PostAsync(new Uri("/agent", UriKind.Relative), content); + + // Assert + response.IsSuccessStatusCode.Should().BeTrue(); + fakeAgent.ReceivedForwardedProperties.ValueKind.Should().Be(JsonValueKind.Object); + fakeAgent.ReceivedForwardedProperties.GetProperty("customProp").GetString().Should().Be("customValue"); + fakeAgent.ReceivedForwardedProperties.GetProperty("sessionId").GetString().Should().Be("test-session-123"); + } + + [Fact] + public async Task ForwardedProps_WithNestedObjects_AreCorrectlyParsedAsync() + { + // Arrange + FakeForwardedPropsAgent fakeAgent = new(); + await this.SetupTestServerAsync(fakeAgent); + + const string RequestJson = """ + { + "threadId": "thread-123", + "runId": "run-456", + "messages": [{ "id": "msg-1", "role": "user", "content": "test nested props" }], + "forwardedProps": { + "user": { "id": "user-1", "name": "Test User" }, + "metadata": { "version": "1.0", "feature": "test" } + } + } + """; + + using StringContent content = new(RequestJson, Encoding.UTF8, "application/json"); + + // Act + HttpResponseMessage response = await this._client!.PostAsync(new Uri("/agent", UriKind.Relative), content); + + // Assert + response.IsSuccessStatusCode.Should().BeTrue(); + fakeAgent.ReceivedForwardedProperties.ValueKind.Should().Be(JsonValueKind.Object); + + JsonElement user = fakeAgent.ReceivedForwardedProperties.GetProperty("user"); + user.GetProperty("id").GetString().Should().Be("user-1"); + user.GetProperty("name").GetString().Should().Be("Test User"); + + JsonElement metadata = fakeAgent.ReceivedForwardedProperties.GetProperty("metadata"); + metadata.GetProperty("version").GetString().Should().Be("1.0"); + metadata.GetProperty("feature").GetString().Should().Be("test"); + } + + [Fact] + public async Task ForwardedProps_WithArrays_AreCorrectlyParsedAsync() + { + // Arrange + FakeForwardedPropsAgent fakeAgent = new(); + await this.SetupTestServerAsync(fakeAgent); + + const string RequestJson = """ + { + "threadId": "thread-123", + "runId": "run-456", + "messages": [{ "id": "msg-1", "role": "user", "content": "test array props" }], + "forwardedProps": { + "tags": ["tag1", "tag2", "tag3"], + "scores": [1, 2, 3, 4, 5] + } + } + """; + + using StringContent content = new(RequestJson, Encoding.UTF8, "application/json"); + + // Act + HttpResponseMessage response = await this._client!.PostAsync(new Uri("/agent", UriKind.Relative), content); + + // Assert + response.IsSuccessStatusCode.Should().BeTrue(); + fakeAgent.ReceivedForwardedProperties.ValueKind.Should().Be(JsonValueKind.Object); + + JsonElement tags = fakeAgent.ReceivedForwardedProperties.GetProperty("tags"); + tags.GetArrayLength().Should().Be(3); + tags[0].GetString().Should().Be("tag1"); + + JsonElement scores = fakeAgent.ReceivedForwardedProperties.GetProperty("scores"); + scores.GetArrayLength().Should().Be(5); + scores[2].GetInt32().Should().Be(3); + } + + [Fact] + public async Task ForwardedProps_WhenEmpty_DoesNotCauseErrorsAsync() + { + // Arrange + FakeForwardedPropsAgent fakeAgent = new(); + await this.SetupTestServerAsync(fakeAgent); + + const string RequestJson = """ + { + "threadId": "thread-123", + "runId": "run-456", + "messages": [{ "id": "msg-1", "role": "user", "content": "test empty props" }], + "forwardedProps": {} + } + """; + + using StringContent content = new(RequestJson, Encoding.UTF8, "application/json"); + + // Act + HttpResponseMessage response = await this._client!.PostAsync(new Uri("/agent", UriKind.Relative), content); + + // Assert + response.IsSuccessStatusCode.Should().BeTrue(); + } + + [Fact] + public async Task ForwardedProps_WhenNotProvided_AgentStillWorksAsync() + { + // Arrange + FakeForwardedPropsAgent fakeAgent = new(); + await this.SetupTestServerAsync(fakeAgent); + + const string RequestJson = """ + { + "threadId": "thread-123", + "runId": "run-456", + "messages": [{ "id": "msg-1", "role": "user", "content": "test no props" }] + } + """; + + using StringContent content = new(RequestJson, Encoding.UTF8, "application/json"); + + // Act + HttpResponseMessage response = await this._client!.PostAsync(new Uri("/agent", UriKind.Relative), content); + + // Assert + response.IsSuccessStatusCode.Should().BeTrue(); + fakeAgent.ReceivedForwardedProperties.ValueKind.Should().Be(JsonValueKind.Undefined); + } + + [Fact] + public async Task ForwardedProps_ReturnsValidSSEResponse_WithTextDeltaEventsAsync() + { + // Arrange + FakeForwardedPropsAgent fakeAgent = new(); + await this.SetupTestServerAsync(fakeAgent); + + const string RequestJson = """ + { + "threadId": "thread-123", + "runId": "run-456", + "messages": [{ "id": "msg-1", "role": "user", "content": "test response" }], + "forwardedProps": { "customProp": "value" } + } + """; + + using StringContent content = new(RequestJson, Encoding.UTF8, "application/json"); + + // Act + HttpResponseMessage response = await this._client!.PostAsync(new Uri("/agent", UriKind.Relative), content); + response.EnsureSuccessStatusCode(); + + Stream stream = await response.Content.ReadAsStreamAsync(); + List> events = []; + await foreach (SseItem item in SseParser.Create(stream).EnumerateAsync()) + { + events.Add(item); + } + + // Assert + events.Should().NotBeEmpty(); + + // SSE events have EventType = "message" and the actual type is in the JSON data + // Should have run_started event + events.Should().Contain(e => e.Data != null && e.Data.Contains("\"type\":\"RUN_STARTED\"")); + + // Should have text_message_start event + events.Should().Contain(e => e.Data != null && e.Data.Contains("\"type\":\"TEXT_MESSAGE_START\"")); + + // Should have text_message_content event with the response text + events.Should().Contain(e => e.Data != null && e.Data.Contains("\"type\":\"TEXT_MESSAGE_CONTENT\"")); + + // Should have run_finished event + events.Should().Contain(e => e.Data != null && e.Data.Contains("\"type\":\"RUN_FINISHED\"")); + } + + [Fact] + public async Task ForwardedProps_WithMixedTypes_AreCorrectlyParsedAsync() + { + // Arrange + FakeForwardedPropsAgent fakeAgent = new(); + await this.SetupTestServerAsync(fakeAgent); + + const string RequestJson = """ + { + "threadId": "thread-123", + "runId": "run-456", + "messages": [{ "id": "msg-1", "role": "user", "content": "test mixed types" }], + "forwardedProps": { + "stringProp": "text", + "numberProp": 42, + "boolProp": true, + "nullProp": null, + "arrayProp": [1, "two", false], + "objectProp": { "nested": "value" } + } + } + """; + + using StringContent content = new(RequestJson, Encoding.UTF8, "application/json"); + + // Act + HttpResponseMessage response = await this._client!.PostAsync(new Uri("/agent", UriKind.Relative), content); + + // Assert + response.IsSuccessStatusCode.Should().BeTrue(); + fakeAgent.ReceivedForwardedProperties.ValueKind.Should().Be(JsonValueKind.Object); + + fakeAgent.ReceivedForwardedProperties.GetProperty("stringProp").GetString().Should().Be("text"); + fakeAgent.ReceivedForwardedProperties.GetProperty("numberProp").GetInt32().Should().Be(42); + fakeAgent.ReceivedForwardedProperties.GetProperty("boolProp").GetBoolean().Should().BeTrue(); + fakeAgent.ReceivedForwardedProperties.GetProperty("nullProp").ValueKind.Should().Be(JsonValueKind.Null); + fakeAgent.ReceivedForwardedProperties.GetProperty("arrayProp").GetArrayLength().Should().Be(3); + fakeAgent.ReceivedForwardedProperties.GetProperty("objectProp").GetProperty("nested").GetString().Should().Be("value"); + } + + private async Task SetupTestServerAsync(FakeForwardedPropsAgent fakeAgent) + { + WebApplicationBuilder builder = WebApplication.CreateBuilder(); + builder.Services.AddAGUI(); + builder.WebHost.UseTestServer(); + + this._app = builder.Build(); + + this._app.MapAGUI("/agent", fakeAgent); + + await this._app.StartAsync(); + + TestServer testServer = this._app.Services.GetRequiredService() as TestServer + ?? throw new InvalidOperationException("TestServer not found"); + + this._client = testServer.CreateClient(); + } + + public async ValueTask DisposeAsync() + { + this._client?.Dispose(); + if (this._app != null) + { + await this._app.DisposeAsync(); + } + } +} + +[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Instantiated in tests")] +internal sealed class FakeForwardedPropsAgent : AIAgent +{ + public FakeForwardedPropsAgent() + { + } + + public override string? Description => "Agent for forwarded properties testing"; + + public JsonElement ReceivedForwardedProperties { get; private set; } + + public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + { + return this.RunStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken); + } + + public override async IAsyncEnumerable RunStreamingAsync( + IEnumerable messages, + AgentThread? thread = null, + AgentRunOptions? options = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + // Extract forwarded properties from ChatOptions.AdditionalProperties (set by AG-UI hosting layer) + if (options is ChatClientAgentRunOptions { ChatOptions.AdditionalProperties: { } properties } && + properties.TryGetValue("ag_ui_forwarded_properties", out object? propsObj) && + propsObj is JsonElement forwardedProps) + { + this.ReceivedForwardedProperties = forwardedProps; + } + + // Always return a text response + string messageId = Guid.NewGuid().ToString("N"); + yield return new AgentRunResponseUpdate + { + MessageId = messageId, + Role = ChatRole.Assistant, + Contents = [new TextContent("Forwarded props processed")] + }; + + await Task.CompletedTask; + } + + public override AgentThread GetNewThread() => new FakeInMemoryAgentThread(); + + public override AgentThread DeserializeThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) + { + return new FakeInMemoryAgentThread(serializedThread, jsonSerializerOptions); + } + + private sealed class FakeInMemoryAgentThread : InMemoryAgentThread + { + public FakeInMemoryAgentThread() + : base() + { + } + + public FakeInMemoryAgentThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) + : base(serializedThread, jsonSerializerOptions) + { + } + } + + public override object? GetService(Type serviceType, object? serviceKey = null) => null; +} From e9ab514696dcc77a5e528ecf57afad57c287af45 Mon Sep 17 00:00:00 2001 From: David Wu Date: Tue, 2 Dec 2025 12:28:52 -0800 Subject: [PATCH 04/33] Python: Replace Eval SDK with AI Projects SDK in evaluation sample (#2540) * Replace Eval SDK with AI Projects SDK * Update per PR review suggestions --- .../evaluation/self_reflection/.env.example | 1 + .../evaluation/self_reflection/README.md | 15 +- .../self_reflection/self_reflection.py | 134 ++++++++++++++---- 3 files changed, 112 insertions(+), 38 deletions(-) diff --git a/python/samples/getting_started/evaluation/self_reflection/.env.example b/python/samples/getting_started/evaluation/self_reflection/.env.example index 9f6dc82564..413a62c0ff 100644 --- a/python/samples/getting_started/evaluation/self_reflection/.env.example +++ b/python/samples/getting_started/evaluation/self_reflection/.env.example @@ -1,2 +1,3 @@ AZURE_OPENAI_ENDPOINT="..." AZURE_OPENAI_API_KEY="..." +AZURE_AI_PROJECT_ENDPOINT="https://.services.ai.azure.com/api/projects//" diff --git a/python/samples/getting_started/evaluation/self_reflection/README.md b/python/samples/getting_started/evaluation/self_reflection/README.md index a6ab419b0d..c75aa62ce8 100644 --- a/python/samples/getting_started/evaluation/self_reflection/README.md +++ b/python/samples/getting_started/evaluation/self_reflection/README.md @@ -6,7 +6,7 @@ This sample demonstrates the self-reflection pattern using Agent Framework and A **What it demonstrates:** - Iterative self-reflection loop that automatically improves responses based on groundedness evaluation -- Batch processing of prompts from Parquet files with progress tracking +- Batch processing of prompts from JSONL files with progress tracking - Using `AzureOpenAIChatClient` with Azure CLI authentication - Comprehensive summary statistics and detailed result tracking @@ -18,14 +18,13 @@ This sample demonstrates the self-reflection pattern using Agent Framework and A ### Python Environment ```bash -pip install agent-framework-core azure-ai-evaluation pandas --pre +pip install agent-framework-core azure-ai-projects pandas --pre ``` ### Environment Variables ```bash # .env file -AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com/ -AZURE_OPENAI_API_KEY=your-api-key # Optional with Azure CLI +AZURE_AI_PROJECT_ENDPOINT=https://.services.ai.azure.com/api/projects// ``` ## Running the Sample @@ -35,15 +34,15 @@ AZURE_OPENAI_API_KEY=your-api-key # Optional with Azure CLI python self_reflection.py # With options -python self_reflection.py --input my_prompts.parquet \ - --output results.parquet \ +python self_reflection.py --input my_prompts.jsonl \ + --output results.jsonl \ --max-reflections 5 \ -n 10 ``` **CLI Options:** -- `--input`, `-i`: Input parquet file -- `--output`, `-o`: Output parquet file +- `--input`, `-i`: Input JSONL file +- `--output`, `-o`: Output JSONL file - `--agent-model`, `-m`: Agent model name (default: gpt-4.1) - `--judge-model`, `-e`: Evaluator model name (default: gpt-4.1) - `--max-reflections`: Max iterations (default: 3) diff --git a/python/samples/getting_started/evaluation/self_reflection/self_reflection.py b/python/samples/getting_started/evaluation/self_reflection/self_reflection.py index 76ed8d6e65..b5b1d1131a 100644 --- a/python/samples/getting_started/evaluation/self_reflection/self_reflection.py +++ b/python/samples/getting_started/evaluation/self_reflection/self_reflection.py @@ -5,13 +5,20 @@ import os import time import argparse import pandas as pd +import openai from typing import Any from dotenv import load_dotenv +from openai.types.eval_create_params import DataSourceConfigCustom +from openai.types.evals.create_eval_jsonl_run_data_source_param import ( + CreateEvalJSONLRunDataSourceParam, + SourceFileContent, + SourceFileContentContent, +) from agent_framework import ChatAgent, ChatMessage from agent_framework.azure import AzureOpenAIChatClient +from azure.ai.projects import AIProjectClient from azure.identity import AzureCliCredential -from azure.ai.evaluation import GroundednessEvaluator, AzureOpenAIModelConfiguration """ Self-Reflection LLM Runner @@ -41,30 +48,96 @@ DEFAULT_AGENT_MODEL = "gpt-4.1" DEFAULT_JUDGE_MODEL = "gpt-4.1" -def create_groundedness_evaluator(judge_model: str) -> GroundednessEvaluator: - """ - Create a groundedness evaluator. +def create_openai_client(): + endpoint = os.environ["AZURE_AI_PROJECT_ENDPOINT"] + credential = AzureCliCredential() + project_client = AIProjectClient(endpoint=endpoint, credential=credential) + return project_client.get_openai_client() - Args: - judge_model: Model deployment name for evaluation - Returns: - Configured GroundednessEvaluator - """ - judge_model_config = AzureOpenAIModelConfiguration( - azure_endpoint=os.environ.get("AZURE_OPENAI_ENDPOINT"), - api_key=os.environ.get("AZURE_OPENAI_API_KEY"), - api_version="2024-12-01-preview", - azure_deployment=judge_model, + +def create_eval(client: openai.OpenAI, judge_model: str) -> openai.types.EvalCreateResponse: + print("Creating Eval") + data_source_config = DataSourceConfigCustom({ + "type": "custom", + "item_schema": { + "type": "object", + "properties": { + "query": {"type": "string"}, + "response": {"type": "string"}, + "context": {"type": "string"}, + }, + "required": [], + }, + "include_sample_schema": True, + }) + + testing_criteria = [{ + "type": "azure_ai_evaluator", + "name": "groundedness", + "evaluator_name": "builtin.groundedness", + "data_mapping": {"query": "{{item.query}}", "response": "{{item.response}}", "context": "{{item.context}}"}, + "initialization_parameters": {"deployment_name": f"{judge_model}"}, + }] + + return client.evals.create( + name="Eval", + data_source_config=data_source_config, + testing_criteria=testing_criteria, # type: ignore ) - return GroundednessEvaluator(model_config=judge_model_config) + + +def run_eval( + client: openai.OpenAI, + eval_object: openai.types.EvalCreateResponse, + query: str, + response: str, + context: str, +): + eval_run_object = client.evals.runs.create( + eval_id=eval_object.id, + name="inline_data_run", + metadata={"team": "eval-exp", "scenario": "inline-data-v1"}, + data_source=CreateEvalJSONLRunDataSourceParam( + type="jsonl", + source=SourceFileContent( + type="file_content", + content=[ + SourceFileContentContent( + item={ + "query": query, + "context": context, + "response": response, + } + ), + ], + ), + ), + ) + + eval_run_response = client.evals.runs.retrieve(run_id=eval_run_object.id, eval_id=eval_object.id) + + MAX_RETRY = 10 + for _ in range(0, MAX_RETRY): + run = client.evals.runs.retrieve(run_id=eval_run_response.id, eval_id=eval_object.id) + if run.status == "failed": + print(f"Eval run failed. Run ID: {run.id}, Status: {run.status}, Error: {getattr(run, 'error', 'Unknown error')}") + continue + elif run.status == "completed": + output_items = list(client.evals.runs.output_items.list(run_id=run.id, eval_id=eval_object.id)) + return output_items + time.sleep(5) + + print("Eval result retrieval timeout.") + return None async def execute_query_with_self_reflection( *, + client: openai.OpenAI, agent: ChatAgent, + eval_object: openai.types.EvalCreateResponse, full_user_query: str, context: str, - evaluator: GroundednessEvaluator, max_self_reflections: int = 3, ) -> dict[str, Any]: """ @@ -108,17 +181,20 @@ async def execute_query_with_self_reflection( # Evaluate groundedness start_time_eval = time.time() - groundedness_res = evaluator( + eval_run_output_items = run_eval( + client=client, + eval_object=eval_object, query=full_user_query, response=agent_response, - context=context + context=context, ) + if eval_run_output_items is None: + print(f" ⚠️ Groundedness evaluation failed (timeout or error) for iteration {i+1}.") + continue + score = eval_run_output_items[0].results[0].score end_time_eval = time.time() total_groundedness_eval_time += (end_time_eval - start_time_eval) - feedback = groundedness_res['groundedness_reason'] - score = int(groundedness_res['groundedness']) - # Store score in structured format iteration_scores.append(score) @@ -144,11 +220,7 @@ async def execute_query_with_self_reflection( # Request improvement reflection_prompt = ( f"The groundedness score of your response is {score}/{max_score}. " - f"Explanation for score: [{feedback}]. " f"Reflect on your answer and improve it to get the maximum score of {max_score} " - f"considering the explanation. Now please provide an updated response, taking into " - f"account the feedback, but make your answer sound as if it was your first response. " - f"Don't refer to the feedback in your answer." ) messages.append(ChatMessage(role="user", text=reflection_prompt)) @@ -226,10 +298,11 @@ async def run_self_reflection_batch( # Configure clients print(f"Configuring Azure OpenAI client...") - - print(f"Creating groundedness evaluator with model: {judge_model}") - evaluator = create_groundedness_evaluator(judge_model) - + client = create_openai_client() + + # Create Eval + eval_object = create_eval(client=client, judge_model=judge_model) + # Process each prompt print(f"Max self-reflections: {max_self_reflections}\n") @@ -239,10 +312,11 @@ async def run_self_reflection_batch( try: result = await execute_query_with_self_reflection( + client=client, agent=agent, + eval_object=eval_object, full_user_query=row['full_prompt'], context=row['context_document'], - evaluator=evaluator, max_self_reflections=max_self_reflections, ) From 6232dd83055f4d148ff6ed0d762ed309c2670154 Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Date: Wed, 3 Dec 2025 13:39:47 +0000 Subject: [PATCH 05/33] .NET [Breaking] Simplify and Refactor ChatclientAgentOptions Ctor + Instructions (#1517) * Point AgentOptions.Instructions to ChatOptions * Update tests and checks * Update xml docs * Removal of agentOptions.Instructions in favor of chatOptions.Instructions * Instructions and tool check consistency * Instructions and tool check consistency * Address comment * Update .github/upgrades/prompts/SemanticKernelToAgentFramework.md Co-authored-by: westey <164392973+westey-m@users.noreply.github.com> * Address PR Comment * Update latest changes to comply with the PR proposal * Address feedback * Update dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs Co-authored-by: westey <164392973+westey-m@users.noreply.github.com> * Address instructions * Update declarative to use promptAgent.Instrucitons with chatOptions.Instructions --------- Co-authored-by: westey <164392973+westey-m@users.noreply.github.com> Co-authored-by: Mark Wallace <127216156+markwallace-microsoft@users.noreply.github.com> --- .../prompts/SemanticKernelToAgentFramework.md | 2 +- .../Program.cs | 2 +- .../Program.cs | 2 +- .../Program.cs | 2 +- .../Program.cs | 2 +- .../Program.cs | 2 +- .../Program.cs | 2 +- .../Agent_Step05_StructuredOutput/Program.cs | 10 +- .../Program.cs | 2 +- .../Program.cs | 3 +- .../Agent_Step16_ChatReduction/Program.cs | 2 +- .../Program.cs | 8 +- .../FoundryAgent_Hosted_MCP/Program.cs | 4 +- .../Agents/CustomAgentExecutors/Program.cs | 6 +- .../01_EdgeCondition/Program.cs | 6 +- .../ConditionalEdges/02_SwitchCase/Program.cs | 6 +- .../03_MultiSelection/Program.cs | 9 +- .../08_WriterCriticWorkflow/Program.cs | 22 +-- .../M365Agent/Agents/WeatherForecastAgent.cs | 2 +- .../AnthropicBetaServiceExtensions.cs | 10 +- .../AnthropicClientExtensions.cs | 10 +- .../PersistentAgentsClientExtensions.cs | 18 ++- .../AzureAIProjectChatClientExtensions.cs | 7 +- .../ChatClientPromptAgentFactory.cs | 1 - .../Extensions/PromptAgentExtensions.cs | 2 +- .../OpenAIAssistantClientExtensions.cs | 26 ++-- .../Extensions/OpenAIChatClientExtensions.cs | 4 +- .../OpenAIResponseClientExtensions.cs | 4 +- .../OpenAIChatClientAgent.cs | 2 +- .../OpenAIResponseClientAgent.cs | 2 +- .../ChatClient/ChatClientAgent.cs | 26 ++-- .../ChatClient/ChatClientAgentOptions.cs | 36 ----- .../AnthropicChatCompletionFixture.cs | 3 +- .../AIProjectClientCreateTests.cs | 38 +++--- .../AzureAIAgentsPersistentCreateTests.cs | 87 ++++++++---- .../AnthropicBetaServiceExtensionsTests.cs | 2 +- .../AnthropicClientExtensionsTests.cs | 2 +- .../PersistentAgentsClientExtensionsTests.cs | 12 +- ...AzureAIProjectChatClientExtensionsTests.cs | 15 +- .../AzureAIProjectChatClientTests.cs | 10 +- .../ChatClient/ChatClientAgentFactoryTests.cs | 2 +- .../OpenAIAssistantClientExtensionsTests.cs | 12 +- .../OpenAIChatClientExtensionsTests.cs | 2 +- .../OpenAIResponseClientExtensionsTests.cs | 2 +- .../ChatClient/ChatClientAgentOptionsTests.cs | 129 ++++++------------ .../ChatClient/ChatClientAgentTests.cs | 109 ++++++++------- .../ChatClientAgent_DeserializeThreadTests.cs | 4 +- .../ChatClientAgent_GetNewThreadTests.cs | 4 +- .../ChatClientBuilderExtensionsTests.cs | 8 +- .../ChatClient/ChatClientExtensionsTests.cs | 4 +- .../OpenAIAssistantClientExtensionsTests.cs | 66 ++++++--- .../OpenAIChatCompletionFixture.cs | 3 +- .../OpenAIResponseFixture.cs | 2 +- 53 files changed, 393 insertions(+), 365 deletions(-) diff --git a/.github/upgrades/prompts/SemanticKernelToAgentFramework.md b/.github/upgrades/prompts/SemanticKernelToAgentFramework.md index a121a5f446..1b28626ea8 100644 --- a/.github/upgrades/prompts/SemanticKernelToAgentFramework.md +++ b/.github/upgrades/prompts/SemanticKernelToAgentFramework.md @@ -1636,4 +1636,4 @@ The property mapping guide from a `AutoFunctionInvocationContext` to a `Function | Result | Use `return` from the delegate | | Terminate | Terminate | | CancellationToken | provided via argument to middleware delegate | -| Arguments | Arguments | \ No newline at end of file +| Arguments | Arguments | diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/Program.cs b/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/Program.cs index 9e4c27cebb..19e1fead1e 100644 --- a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step01_ChatHistoryMemory/Program.cs @@ -32,7 +32,7 @@ AIAgent agent = new AzureOpenAIClient( .GetChatClient(deploymentName) .CreateAIAgent(new ChatClientAgentOptions { - Instructions = "You are good at telling jokes.", + ChatOptions = new() { Instructions = "You are good at telling jokes." }, Name = "Joker", AIContextProviderFactory = (ctx) => new ChatHistoryMemoryProvider( vectorStore, diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/Program.cs b/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/Program.cs index feacead4dd..87f5842e2c 100644 --- a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step02_MemoryUsingMem0/Program.cs @@ -30,7 +30,7 @@ AIAgent agent = new AzureOpenAIClient( .GetChatClient(deploymentName) .CreateAIAgent(new ChatClientAgentOptions() { - Instructions = "You are a friendly travel assistant. Use known memories about the user when responding, and do not invent details.", + ChatOptions = new() { Instructions = "You are a friendly travel assistant. Use known memories about the user when responding, and do not invent details." }, AIContextProviderFactory = ctx => ctx.SerializedState.ValueKind is not JsonValueKind.Null and not JsonValueKind.Undefined // If each thread should have its own Mem0 scope, you can create a new id per thread here: // ? new Mem0Provider(mem0HttpClient, new Mem0ProviderScope() { ThreadId = Guid.NewGuid().ToString() }) diff --git a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step03_CustomMemory/Program.cs b/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step03_CustomMemory/Program.cs index ad59deb97f..4b9b1866a9 100644 --- a/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step03_CustomMemory/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithMemory/AgentWithMemory_Step03_CustomMemory/Program.cs @@ -33,7 +33,7 @@ ChatClient chatClient = new AzureOpenAIClient( // and its storage to that user id. AIAgent agent = chatClient.CreateAIAgent(new ChatClientAgentOptions() { - Instructions = "You are a friendly assistant. Always address the user by their name.", + ChatOptions = new() { Instructions = "You are a friendly assistant. Always address the user by their name." }, AIContextProviderFactory = ctx => new UserInfoMemory(chatClient.AsIChatClient(), ctx.SerializedState, ctx.JsonSerializerOptions) }); diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs b/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs index ec665325a7..a30e0371a0 100644 --- a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step01_BasicTextRAG/Program.cs @@ -62,7 +62,7 @@ AIAgent agent = azureOpenAIClient .GetChatClient(deploymentName) .CreateAIAgent(new ChatClientAgentOptions { - Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.", + ChatOptions = new() { Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available." }, AIContextProviderFactory = ctx => new TextSearchProvider(SearchAdapter, ctx.SerializedState, ctx.JsonSerializerOptions, textSearchOptions) }); diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs b/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs index 89ced52b69..89312f8597 100644 --- a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step02_CustomVectorStoreRAG/Program.cs @@ -71,7 +71,7 @@ AIAgent agent = azureOpenAIClient .GetChatClient(deploymentName) .CreateAIAgent(new ChatClientAgentOptions { - Instructions = "You are a helpful support specialist for the Microsoft Agent Framework. Answer questions using the provided context and cite the source document when available. Keep responses brief.", + ChatOptions = new() { Instructions = "You are a helpful support specialist for the Microsoft Agent Framework. Answer questions using the provided context and cite the source document when available. Keep responses brief." }, AIContextProviderFactory = ctx => new TextSearchProvider(SearchAdapter, ctx.SerializedState, ctx.JsonSerializerOptions, textSearchOptions) }); diff --git a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/Program.cs b/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/Program.cs index e2caedbb1c..5e7b2c4132 100644 --- a/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/Program.cs +++ b/dotnet/samples/GettingStarted/AgentWithRAG/AgentWithRAG_Step03_CustomRAGDataSource/Program.cs @@ -29,7 +29,7 @@ AIAgent agent = new AzureOpenAIClient( .GetChatClient(deploymentName) .CreateAIAgent(new ChatClientAgentOptions { - Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.", + ChatOptions = new() { Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available." }, AIContextProviderFactory = ctx => new TextSearchProvider(MockSearchAsync, ctx.SerializedState, ctx.JsonSerializerOptions, textSearchOptions) }); diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/Program.cs index b18d8e2d84..ef1849fe02 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step05_StructuredOutput/Program.cs @@ -22,7 +22,7 @@ ChatClient chatClient = new AzureOpenAIClient( .GetChatClient(deploymentName); // Create the ChatClientAgent with the specified name and instructions. -ChatClientAgent agent = chatClient.CreateAIAgent(new ChatClientAgentOptions(name: "HelpfulAssistant", instructions: "You are a helpful assistant.")); +ChatClientAgent agent = chatClient.CreateAIAgent(name: "HelpfulAssistant", instructions: "You are a helpful assistant."); // Set PersonInfo as the type parameter of RunAsync method to specify the expected structured output from the agent and invoke the agent with some unstructured input. AgentRunResponse response = await agent.RunAsync("Please provide information about John Smith, who is a 35-year-old software engineer."); @@ -34,12 +34,10 @@ Console.WriteLine($"Age: {response.Result.Age}"); Console.WriteLine($"Occupation: {response.Result.Occupation}"); // Create the ChatClientAgent with the specified name, instructions, and expected structured output the agent should produce. -ChatClientAgent agentWithPersonInfo = chatClient.CreateAIAgent(new ChatClientAgentOptions(name: "HelpfulAssistant", instructions: "You are a helpful assistant.") +ChatClientAgent agentWithPersonInfo = chatClient.CreateAIAgent(new ChatClientAgentOptions() { - ChatOptions = new() - { - ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema() - } + Name = "HelpfulAssistant", + ChatOptions = new() { Instructions = "You are a helpful assistant.", ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema() } }); // Invoke the agent with some unstructured input while streaming, to extract the structured information from. diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs index 8986734972..d1316b6c80 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs @@ -28,7 +28,7 @@ AIAgent agent = new AzureOpenAIClient( .GetChatClient(deploymentName) .CreateAIAgent(new ChatClientAgentOptions { - Instructions = "You are good at telling jokes.", + ChatOptions = new() { Instructions = "You are good at telling jokes." }, Name = "Joker", ChatMessageStoreFactory = ctx => { diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Program.cs index 894c034eb0..d1b75d2fe5 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step09_DependencyInjection/Program.cs @@ -18,8 +18,7 @@ var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); // Add agent options to the service collection. -builder.Services.AddSingleton( - new ChatClientAgentOptions(instructions: "You are good at telling jokes.", name: "Joker")); +builder.Services.AddSingleton(new ChatClientAgentOptions() { Name = "Joker", ChatOptions = new() { Instructions = "You are good at telling jokes." } }); // Add a chat client to the service collection. builder.Services.AddKeyedChatClient("AzureOpenAI", (sp) => new AzureOpenAIClient( diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step16_ChatReduction/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step16_ChatReduction/Program.cs index 590b5308d5..04704b5da0 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step16_ChatReduction/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step16_ChatReduction/Program.cs @@ -21,7 +21,7 @@ AIAgent agent = new AzureOpenAIClient( .GetChatClient(deploymentName) .CreateAIAgent(new ChatClientAgentOptions { - Instructions = "You are good at telling jokes.", + ChatOptions = new() { Instructions = "You are good at telling jokes." }, Name = "Joker", ChatMessageStoreFactory = ctx => new InMemoryChatMessageStore(new MessageCountingChatReducer(2), ctx.SerializedState, ctx.JsonSerializerOptions) }); diff --git a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs index 0edbed70e8..ac05565836 100644 --- a/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs +++ b/dotnet/samples/GettingStarted/FoundryAgents/FoundryAgents_Step05_StructuredOutput/Program.cs @@ -24,10 +24,12 @@ AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential( // Create ChatClientAgent directly ChatClientAgent agent = await aiProjectClient.CreateAIAgentAsync( model: deploymentName, - new ChatClientAgentOptions(name: AssistantName, instructions: AssistantInstructions) + new ChatClientAgentOptions() { + Name = AssistantName, ChatOptions = new() { + Instructions = AssistantInstructions, ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema() } }); @@ -44,10 +46,12 @@ Console.WriteLine($"Occupation: {response.Result.Occupation}"); // Create the ChatClientAgent with the specified name, instructions, and expected structured output the agent should produce. ChatClientAgent agentWithPersonInfo = aiProjectClient.CreateAIAgent( model: deploymentName, - new ChatClientAgentOptions(name: AssistantName, instructions: AssistantInstructions) + new ChatClientAgentOptions() { + Name = AssistantName, ChatOptions = new() { + Instructions = AssistantInstructions, ResponseFormat = Microsoft.Extensions.AI.ChatResponseFormat.ForJsonSchema() } }); diff --git a/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs b/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs index f824f09991..123d666f09 100644 --- a/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs +++ b/dotnet/samples/GettingStarted/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs @@ -34,9 +34,9 @@ AIAgent agent = await persistentAgentsClient.CreateAIAgentAsync( options: new() { Name = "MicrosoftLearnAgent", - Instructions = "You answer questions by searching the Microsoft Learn content only.", ChatOptions = new() { + Instructions = "You answer questions by searching the Microsoft Learn content only.", Tools = [mcpTool] }, }); @@ -67,9 +67,9 @@ AIAgent agentWithRequiredApproval = await persistentAgentsClient.CreateAIAgentAs options: new() { Name = "MicrosoftLearnAgentWithApproval", - Instructions = "You answer questions by searching the Microsoft Learn content only.", ChatOptions = new() { + Instructions = "You answer questions by searching the Microsoft Learn content only.", Tools = [mcpToolWithApproval] }, }); diff --git a/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs b/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs index 5d5369883c..91f58f460e 100644 --- a/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/Agents/CustomAgentExecutors/Program.cs @@ -118,10 +118,11 @@ internal sealed class SloganWriterExecutor : Executor /// The chat client to use for the AI agent. public SloganWriterExecutor(string id, IChatClient chatClient) : base(id) { - ChatClientAgentOptions agentOptions = new(instructions: "You are a professional slogan writer. You will be given a task to create a slogan.") + ChatClientAgentOptions agentOptions = new() { ChatOptions = new() { + Instructions = "You are a professional slogan writer. You will be given a task to create a slogan.", ResponseFormat = ChatResponseFormat.ForJsonSchema() } }; @@ -193,10 +194,11 @@ internal sealed class FeedbackExecutor : Executor /// The chat client to use for the AI agent. public FeedbackExecutor(string id, IChatClient chatClient) : base(id) { - ChatClientAgentOptions agentOptions = new(instructions: "You are a professional editor. You will be given a slogan and the task it is meant to accomplish.") + ChatClientAgentOptions agentOptions = new() { ChatOptions = new() { + Instructions = "You are a professional editor. You will be given a slogan and the task it is meant to accomplish.", ResponseFormat = ChatResponseFormat.ForJsonSchema() } }; diff --git a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Program.cs b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Program.cs index b6e3d4d513..0f762ea40d 100644 --- a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/01_EdgeCondition/Program.cs @@ -85,10 +85,11 @@ public static class Program /// /// A ChatClientAgent configured for spam detection private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) => - new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails.") + new(chatClient, new ChatClientAgentOptions() { ChatOptions = new() { + Instructions = "You are a spam detection assistant that identifies spam emails.", ResponseFormat = ChatResponseFormat.ForJsonSchema() } }); @@ -98,10 +99,11 @@ public static class Program /// /// A ChatClientAgent configured for email assistance private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) => - new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.") + new(chatClient, new ChatClientAgentOptions() { ChatOptions = new() { + Instructions = "You are an email assistant that helps users draft responses to emails with professionalism.", ResponseFormat = ChatResponseFormat.ForJsonSchema() } }); diff --git a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Program.cs b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Program.cs index 13f0a75bc2..ccda3fa19e 100644 --- a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/02_SwitchCase/Program.cs @@ -100,10 +100,11 @@ public static class Program /// /// A ChatClientAgent configured for spam detection private static ChatClientAgent GetSpamDetectionAgent(IChatClient chatClient) => - new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails. Be less confident in your assessments.") + new(chatClient, new ChatClientAgentOptions() { ChatOptions = new() { + Instructions = "You are a spam detection assistant that identifies spam emails. Be less confident in your assessments.", ResponseFormat = ChatResponseFormat.ForJsonSchema() } }); @@ -113,10 +114,11 @@ public static class Program /// /// A ChatClientAgent configured for email assistance private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) => - new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.") + new(chatClient, new ChatClientAgentOptions() { ChatOptions = new() { + Instructions = "You are an email assistant that helps users draft responses to emails with professionalism.", ResponseFormat = ChatResponseFormat.ForJsonSchema() } }); diff --git a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Program.cs b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Program.cs index 9d340cbae3..49faff39da 100644 --- a/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/ConditionalEdges/03_MultiSelection/Program.cs @@ -140,10 +140,11 @@ public static class Program /// /// A ChatClientAgent configured for email analysis private static ChatClientAgent GetEmailAnalysisAgent(IChatClient chatClient) => - new(chatClient, new ChatClientAgentOptions(instructions: "You are a spam detection assistant that identifies spam emails.") + new(chatClient, new ChatClientAgentOptions() { ChatOptions = new() { + Instructions = "You are a spam detection assistant that identifies spam emails.", ResponseFormat = ChatResponseFormat.ForJsonSchema() } }); @@ -153,10 +154,11 @@ public static class Program /// /// A ChatClientAgent configured for email assistance private static ChatClientAgent GetEmailAssistantAgent(IChatClient chatClient) => - new(chatClient, new ChatClientAgentOptions(instructions: "You are an email assistant that helps users draft responses to emails with professionalism.") + new(chatClient, new ChatClientAgentOptions() { ChatOptions = new() { + Instructions = "You are an email assistant that helps users draft responses to emails with professionalism.", ResponseFormat = ChatResponseFormat.ForJsonSchema() } }); @@ -166,10 +168,11 @@ public static class Program /// /// A ChatClientAgent configured for email summarization private static ChatClientAgent GetEmailSummaryAgent(IChatClient chatClient) => - new(chatClient, new ChatClientAgentOptions(instructions: "You are an assistant that helps users summarize emails.") + new(chatClient, new ChatClientAgentOptions() { ChatOptions = new() { + Instructions = "You are an assistant that helps users summarize emails.", ResponseFormat = ChatResponseFormat.ForJsonSchema() } }); diff --git a/dotnet/samples/GettingStarted/Workflows/_Foundational/08_WriterCriticWorkflow/Program.cs b/dotnet/samples/GettingStarted/Workflows/_Foundational/08_WriterCriticWorkflow/Program.cs index d9cc30f395..265a87b5f6 100644 --- a/dotnet/samples/GettingStarted/Workflows/_Foundational/08_WriterCriticWorkflow/Program.cs +++ b/dotnet/samples/GettingStarted/Workflows/_Foundational/08_WriterCriticWorkflow/Program.cs @@ -285,19 +285,19 @@ internal sealed class CriticExecutor : Executor this._agent = new ChatClientAgent(chatClient, new ChatClientAgentOptions { Name = "Critic", - Instructions = """ - You are a constructive critic. Review the content and provide specific feedback. - Always try to provide actionable suggestions for improvement and strive to identify improvement points. - Only approve if the content is high quality, clear, and meets the original requirements and you see no improvement points. - - Provide your decision as structured output with: - - approved: true if content is good, false if revisions needed - - feedback: specific improvements needed (empty if approved) - - Be concise but specific in your feedback. - """, ChatOptions = new() { + Instructions = """ + You are a constructive critic. Review the content and provide specific feedback. + Always try to provide actionable suggestions for improvement and strive to identify improvement points. + Only approve if the content is high quality, clear, and meets the original requirements and you see no improvement points. + + Provide your decision as structured output with: + - approved: true if content is good, false if revisions needed + - feedback: specific improvements needed (empty if approved) + + Be concise but specific in your feedback. + """, ResponseFormat = ChatResponseFormat.ForJsonSchema() } }); diff --git a/dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs b/dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs index e133170b36..740b959a7a 100644 --- a/dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs +++ b/dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs @@ -33,9 +33,9 @@ public class WeatherForecastAgent : DelegatingAIAgent new ChatClientAgentOptions() { Name = AgentName, - Instructions = AgentInstructions, ChatOptions = new ChatOptions() { + Instructions = AgentInstructions, Tools = [new ApprovalRequiredAIFunction(AIFunctionFactory.Create(GetWeather))], // We want the agent to return structured output in a known format // so that we can easily create adaptive cards from the response. diff --git a/dotnet/src/Microsoft.Agents.AI.Anthropic/AnthropicBetaServiceExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Anthropic/AnthropicBetaServiceExtensions.cs index 9bf698bded..6b4f872a63 100644 --- a/dotnet/src/Microsoft.Agents.AI.Anthropic/AnthropicBetaServiceExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Anthropic/AnthropicBetaServiceExtensions.cs @@ -45,14 +45,20 @@ public static class AnthropicBetaServiceExtensions { var options = new ChatClientAgentOptions { - Instructions = instructions, Name = name, Description = description, }; + if (!string.IsNullOrWhiteSpace(instructions)) + { + options.ChatOptions ??= new(); + options.ChatOptions.Instructions = instructions; + } + if (tools is { Count: > 0 }) { - options.ChatOptions = new ChatOptions { Tools = tools }; + options.ChatOptions ??= new(); + options.ChatOptions.Tools = tools; } var chatClient = betaService.AsIChatClient(model, defaultMaxTokens ?? DefaultMaxTokens); diff --git a/dotnet/src/Microsoft.Agents.AI.Anthropic/AnthropicClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Anthropic/AnthropicClientExtensions.cs index f43f4bd0ce..b4b8e2bc1e 100644 --- a/dotnet/src/Microsoft.Agents.AI.Anthropic/AnthropicClientExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Anthropic/AnthropicClientExtensions.cs @@ -45,14 +45,20 @@ public static class AnthropicClientExtensions { var options = new ChatClientAgentOptions { - Instructions = instructions, Name = name, Description = description, }; + if (!string.IsNullOrWhiteSpace(instructions)) + { + options.ChatOptions ??= new(); + options.ChatOptions.Instructions = instructions; + } + if (tools is { Count: > 0 }) { - options.ChatOptions = new ChatOptions { Tools = tools }; + options.ChatOptions ??= new(); + options.ChatOptions.Tools = tools; } var chatClient = client.AsIChatClient(model, defaultMaxTokens ?? DefaultMaxTokens); diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI.Persistent/PersistentAgentsClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI.Persistent/PersistentAgentsClientExtensions.cs index a05fca18ba..5ca1436587 100644 --- a/dotnet/src/Microsoft.Agents.AI.AzureAI.Persistent/PersistentAgentsClientExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.AzureAI.Persistent/PersistentAgentsClientExtensions.cs @@ -67,12 +67,17 @@ public static class PersistentAgentsClientExtensions chatClient = clientFactory(chatClient); } + if (!string.IsNullOrWhiteSpace(persistentAgentMetadata.Instructions) && chatOptions?.Instructions is null) + { + chatOptions ??= new ChatOptions(); + chatOptions.Instructions = persistentAgentMetadata.Instructions; + } + return new ChatClientAgent(chatClient, options: new() { Id = persistentAgentMetadata.Id, Name = persistentAgentMetadata.Name, Description = persistentAgentMetadata.Description, - Instructions = persistentAgentMetadata.Instructions, ChatOptions = chatOptions }, services: services); } @@ -207,12 +212,17 @@ public static class PersistentAgentsClientExtensions chatClient = clientFactory(chatClient); } + if (!string.IsNullOrWhiteSpace(persistentAgentMetadata.Instructions) && options.ChatOptions?.Instructions is null) + { + options.ChatOptions ??= new ChatOptions(); + options.ChatOptions.Instructions = persistentAgentMetadata.Instructions; + } + var agentOptions = new ChatClientAgentOptions() { Id = persistentAgentMetadata.Id, Name = options.Name ?? persistentAgentMetadata.Name, Description = options.Description ?? persistentAgentMetadata.Description, - Instructions = options.Instructions ?? persistentAgentMetadata.Instructions, ChatOptions = options.ChatOptions, AIContextProviderFactory = options.AIContextProviderFactory, ChatMessageStoreFactory = options.ChatMessageStoreFactory, @@ -453,7 +463,7 @@ public static class PersistentAgentsClientExtensions model: model, name: options.Name, description: options.Description, - instructions: options.Instructions, + instructions: options.ChatOptions?.Instructions, tools: toolDefinitionsAndResources.ToolDefinitions, toolResources: toolDefinitionsAndResources.ToolResources, temperature: null, @@ -513,7 +523,7 @@ public static class PersistentAgentsClientExtensions model: model, name: options.Name, description: options.Description, - instructions: options.Instructions, + instructions: options.ChatOptions?.Instructions, tools: toolDefinitionsAndResources.ToolDefinitions, toolResources: toolDefinitionsAndResources.ToolResources, temperature: null, diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs index 0ec5f593fd..dfbdad8e98 100644 --- a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs @@ -393,7 +393,7 @@ public static partial class AzureAIProjectChatClientExtensions PromptAgentDefinition agentDefinition = new(model) { - Instructions = options.Instructions, + Instructions = options.ChatOptions?.Instructions, Temperature = options.ChatOptions?.Temperature, TopP = options.ChatOptions?.TopP, TextOptions = new() { TextFormat = ToOpenAIResponseTextFormat(options.ChatOptions?.ResponseFormat, options.ChatOptions) } @@ -459,7 +459,7 @@ public static partial class AzureAIProjectChatClientExtensions PromptAgentDefinition agentDefinition = new(model) { - Instructions = options.Instructions, + Instructions = options.ChatOptions?.Instructions, Temperature = options.ChatOptions?.Temperature, TopP = options.ChatOptions?.TopP, TextOptions = new() { TextFormat = ToOpenAIResponseTextFormat(options.ChatOptions?.ResponseFormat, options.ChatOptions) } @@ -822,10 +822,9 @@ public static partial class AzureAIProjectChatClientExtensions if (agentDefinition is PromptAgentDefinition promptAgentDefinition) { agentOptions.ChatOptions ??= chatOptions?.Clone() ?? new(); - agentOptions.Instructions = promptAgentDefinition.Instructions; + agentOptions.ChatOptions.Instructions = promptAgentDefinition.Instructions; agentOptions.ChatOptions.Temperature = promptAgentDefinition.Temperature; agentOptions.ChatOptions.TopP = promptAgentDefinition.TopP; - agentOptions.ChatOptions.Instructions = promptAgentDefinition.Instructions; } if (agentTools is { Count: > 0 }) diff --git a/dotnet/src/Microsoft.Agents.AI.Declarative/ChatClient/ChatClientPromptAgentFactory.cs b/dotnet/src/Microsoft.Agents.AI.Declarative/ChatClient/ChatClientPromptAgentFactory.cs index 814eac70aa..a7918de051 100644 --- a/dotnet/src/Microsoft.Agents.AI.Declarative/ChatClient/ChatClientPromptAgentFactory.cs +++ b/dotnet/src/Microsoft.Agents.AI.Declarative/ChatClient/ChatClientPromptAgentFactory.cs @@ -38,7 +38,6 @@ public sealed class ChatClientPromptAgentFactory : PromptAgentFactory { Name = promptAgent.Name, Description = promptAgent.Description, - Instructions = promptAgent.Instructions?.ToTemplateString(), ChatOptions = promptAgent.GetChatOptions(this.Engine, this._functions), }; diff --git a/dotnet/src/Microsoft.Agents.AI.Declarative/Extensions/PromptAgentExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Declarative/Extensions/PromptAgentExtensions.cs index b85452119c..1597c0c54b 100644 --- a/dotnet/src/Microsoft.Agents.AI.Declarative/Extensions/PromptAgentExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Declarative/Extensions/PromptAgentExtensions.cs @@ -35,7 +35,7 @@ public static class PromptAgentExtensions return new ChatOptions() { - Instructions = promptAgent.ResponseInstructions?.ToTemplateString(), + Instructions = promptAgent.Instructions?.ToTemplateString(), Temperature = (float?)modelOptions?.Temperature?.Eval(engine), MaxOutputTokens = (int?)modelOptions?.MaxOutputTokens?.Eval(engine), TopP = (float?)modelOptions?.TopP?.Eval(engine), diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIAssistantClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIAssistantClientExtensions.cs index a36f6957b8..fb464fdc39 100644 --- a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIAssistantClientExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIAssistantClientExtensions.cs @@ -77,12 +77,17 @@ public static class OpenAIAssistantClientExtensions chatClient = clientFactory(chatClient); } + if (!string.IsNullOrWhiteSpace(assistantMetadata.Instructions) && chatOptions?.Instructions is null) + { + chatOptions ??= new ChatOptions(); + chatOptions.Instructions = assistantMetadata.Instructions; + } + return new ChatClientAgent(chatClient, options: new() { Id = assistantMetadata.Id, Name = assistantMetadata.Name, Description = assistantMetadata.Description, - Instructions = assistantMetadata.Instructions, ChatOptions = chatOptions }, services: services); } @@ -215,12 +220,17 @@ public static class OpenAIAssistantClientExtensions chatClient = clientFactory(chatClient); } + if (string.IsNullOrWhiteSpace(options.ChatOptions?.Instructions) && !string.IsNullOrWhiteSpace(assistantMetadata.Instructions)) + { + options.ChatOptions ??= new ChatOptions(); + options.ChatOptions.Instructions = assistantMetadata.Instructions; + } + var mergedOptions = new ChatClientAgentOptions() { Id = assistantMetadata.Id, Name = options.Name ?? assistantMetadata.Name, Description = options.Description ?? assistantMetadata.Description, - Instructions = options.Instructions ?? assistantMetadata.Instructions, ChatOptions = options.ChatOptions, AIContextProviderFactory = options.AIContextProviderFactory, ChatMessageStoreFactory = options.ChatMessageStoreFactory, @@ -339,10 +349,10 @@ public static class OpenAIAssistantClientExtensions { Name = name, Description = description, - Instructions = instructions, - ChatOptions = tools is null ? null : new ChatOptions() + ChatOptions = tools is null && string.IsNullOrWhiteSpace(instructions) ? null : new ChatOptions() { Tools = tools, + Instructions = instructions } }, clientFactory, @@ -377,7 +387,7 @@ public static class OpenAIAssistantClientExtensions { Name = options.Name, Description = options.Description, - Instructions = options.Instructions, + Instructions = options.ChatOptions?.Instructions, }; // Convert AITools to ToolDefinitions and ToolResources @@ -443,10 +453,10 @@ public static class OpenAIAssistantClientExtensions { Name = name, Description = description, - Instructions = instructions, - ChatOptions = tools is null ? null : new ChatOptions() + ChatOptions = tools is null && string.IsNullOrWhiteSpace(instructions) ? null : new ChatOptions() { Tools = tools, + Instructions = instructions, } }, clientFactory, @@ -484,7 +494,7 @@ public static class OpenAIAssistantClientExtensions { Name = options.Name, Description = options.Description, - Instructions = options.Instructions, + Instructions = options.ChatOptions?.Instructions, }; // Convert AITools to ToolDefinitions and ToolResources diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIChatClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIChatClientExtensions.cs index 36114d009c..b51679e42e 100644 --- a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIChatClientExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIChatClientExtensions.cs @@ -47,9 +47,9 @@ public static class OpenAIChatClientExtensions { Name = name, Description = description, - Instructions = instructions, - ChatOptions = tools is null ? null : new ChatOptions() + ChatOptions = tools is null && string.IsNullOrWhiteSpace(instructions) ? null : new ChatOptions() { + Instructions = instructions, Tools = tools, } }, diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIResponseClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIResponseClientExtensions.cs index a4375052ed..dd25d46047 100644 --- a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIResponseClientExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIResponseClientExtensions.cs @@ -50,9 +50,9 @@ public static class OpenAIResponseClientExtensions { Name = name, Description = description, - Instructions = instructions, - ChatOptions = tools is null ? null : new ChatOptions() + ChatOptions = tools is null && string.IsNullOrWhiteSpace(instructions) ? null : new ChatOptions() { + Instructions = instructions, Tools = tools, } }, diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/OpenAIChatClientAgent.cs b/dotnet/src/Microsoft.Agents.AI.OpenAI/OpenAIChatClientAgent.cs index b529e1151b..5870e2fdcc 100644 --- a/dotnet/src/Microsoft.Agents.AI.OpenAI/OpenAIChatClientAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.OpenAI/OpenAIChatClientAgent.cs @@ -32,7 +32,7 @@ public class OpenAIChatClientAgent : DelegatingAIAgent { Name = name, Description = description, - Instructions = instructions, + ChatOptions = new ChatOptions() { Instructions = instructions }, }, loggerFactory) { } diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/OpenAIResponseClientAgent.cs b/dotnet/src/Microsoft.Agents.AI.OpenAI/OpenAIResponseClientAgent.cs index 8c5603fb05..9d554e6a84 100644 --- a/dotnet/src/Microsoft.Agents.AI.OpenAI/OpenAIResponseClientAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.OpenAI/OpenAIResponseClientAgent.cs @@ -32,7 +32,7 @@ public class OpenAIResponseClientAgent : DelegatingAIAgent { Name = name, Description = description, - Instructions = instructions, + ChatOptions = new ChatOptions() { Instructions = instructions }, }, loggerFactory) { } diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs index 685dac8488..df7477241c 100644 --- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs @@ -59,13 +59,13 @@ public sealed partial class ChatClientAgent : AIAgent chatClient, new ChatClientAgentOptions { - Name = name, - Description = description, - Instructions = instructions, - ChatOptions = tools is null ? null : new ChatOptions + ChatOptions = (tools is null && string.IsNullOrWhiteSpace(instructions)) ? null : new ChatOptions { Tools = tools, - } + Instructions = instructions + }, + Name = name, + Description = description }, loggerFactory, services) @@ -141,7 +141,7 @@ public sealed partial class ChatClientAgent : AIAgent /// These instructions are typically provided to the AI model as system messages to establish /// the context and expected behavior for the agent's responses. /// - public string? Instructions => this._agentOptions?.Instructions; + public string? Instructions => this._agentOptions?.ChatOptions?.Instructions; /// /// Gets of the default used by the agent. @@ -492,7 +492,6 @@ public sealed partial class ChatClientAgent : AIAgent requestChatOptions.AllowMultipleToolCalls ??= this._agentOptions.ChatOptions.AllowMultipleToolCalls; requestChatOptions.ConversationId ??= this._agentOptions.ChatOptions.ConversationId; requestChatOptions.FrequencyPenalty ??= this._agentOptions.ChatOptions.FrequencyPenalty; - requestChatOptions.Instructions ??= this._agentOptions.ChatOptions.Instructions; requestChatOptions.MaxOutputTokens ??= this._agentOptions.ChatOptions.MaxOutputTokens; requestChatOptions.ModelId ??= this._agentOptions.ChatOptions.ModelId; requestChatOptions.PresencePenalty ??= this._agentOptions.ChatOptions.PresencePenalty; @@ -503,6 +502,13 @@ public sealed partial class ChatClientAgent : AIAgent requestChatOptions.TopK ??= this._agentOptions.ChatOptions.TopK; requestChatOptions.ToolMode ??= this._agentOptions.ChatOptions.ToolMode; + // Merge instructions by concatenating them if both are present. + requestChatOptions.Instructions = !string.IsNullOrWhiteSpace(requestChatOptions.Instructions) && !string.IsNullOrWhiteSpace(this.Instructions) + ? $"{this.Instructions}\n{requestChatOptions.Instructions}" + : (!string.IsNullOrWhiteSpace(requestChatOptions.Instructions) + ? requestChatOptions.Instructions + : this.Instructions); + // Merge only the additional properties from the agent if they are not already set in the request options. if (requestChatOptions.AdditionalProperties is not null && this._agentOptions.ChatOptions.AdditionalProperties is not null) { @@ -685,12 +691,6 @@ public sealed partial class ChatClientAgent : AIAgent """); } - if (!string.IsNullOrWhiteSpace(this.Instructions)) - { - chatOptions ??= new(); - chatOptions.Instructions = string.IsNullOrWhiteSpace(chatOptions.Instructions) ? this.Instructions : $"{this.Instructions}\n{chatOptions.Instructions}"; - } - // Only create or update ChatOptions if we have an id on the thread and we don't have the same one already in ChatOptions. if (!string.IsNullOrWhiteSpace(typedThread.ConversationId) && typedThread.ConversationId != chatOptions?.ConversationId) { diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentOptions.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentOptions.cs index f83e6912d5..019f4f42e4 100644 --- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentOptions.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. using System; -using System.Collections.Generic; using System.Text.Json; using Microsoft.Extensions.AI; @@ -17,35 +16,6 @@ namespace Microsoft.Agents.AI; /// public class ChatClientAgentOptions { - /// - /// Initializes a new instance of the class. - /// - public ChatClientAgentOptions() - { - } - - /// - /// Initializes a new instance of the class with the specified parameters. - /// - /// If is provided, a new instance is created - /// with the specified instructions and tools. - /// The instructions or guidelines for the chat client agent. Can be if not specified. - /// The name of the chat client agent. Can be if not specified. - /// The description of the chat client agent. Can be if not specified. - /// A list of instances available to the chat client agent. Can be if no - /// tools are specified. - public ChatClientAgentOptions(string? instructions, string? name = null, string? description = null, IList? tools = null) - { - this.Name = name; - this.Instructions = instructions; - this.Description = description; - - if (tools is not null) - { - (this.ChatOptions ??= new()).Tools = tools; - } - } - /// /// Gets or sets the agent id. /// @@ -56,11 +26,6 @@ public class ChatClientAgentOptions /// public string? Name { get; set; } - /// - /// Gets or sets the agent instructions. - /// - public string? Instructions { get; set; } - /// /// Gets or sets the agent description. /// @@ -106,7 +71,6 @@ public class ChatClientAgentOptions { Id = this.Id, Name = this.Name, - Instructions = this.Instructions, Description = this.Description, ChatOptions = this.ChatOptions?.Clone(), ChatMessageStoreFactory = this.ChatMessageStoreFactory, diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs index 76ca18d3de..72c0b14ae2 100644 --- a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicChatCompletionFixture.cs @@ -84,8 +84,7 @@ public class AnthropicChatCompletionFixture : IChatClientAgentFixture return Task.FromResult(new ChatClientAgent(chatClient, options: new() { Name = name, - Instructions = instructions, - ChatOptions = new() { Tools = aiTools } + ChatOptions = new() { Instructions = instructions, Tools = aiTools } })); } diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs index 4bb1c9cbfe..f626736418 100644 --- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs +++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs @@ -37,16 +37,20 @@ public class AIProjectClientCreateTests { "CreateWithChatClientAgentOptionsAsync" => await this._client.CreateAIAgentAsync( model: s_config.DeploymentName, - options: new ChatClientAgentOptions( - instructions: AgentInstructions, - name: AgentName, - description: AgentDescription)), + options: new ChatClientAgentOptions() + { + Name = AgentName, + Description = AgentDescription, + ChatOptions = new() { Instructions = AgentInstructions } + }), "CreateWithChatClientAgentOptionsSync" => this._client.CreateAIAgent( model: s_config.DeploymentName, - options: new ChatClientAgentOptions( - instructions: AgentInstructions, - name: AgentName, - description: AgentDescription)), + options: new ChatClientAgentOptions() + { + Name = AgentName, + Description = AgentDescription, + ChatOptions = new() { Instructions = AgentInstructions } + }), "CreateWithFoundryOptionsAsync" => await this._client.CreateAIAgentAsync( name: AgentName, creationOptions: new AgentVersionCreationOptions(new PromptAgentDefinition(s_config.DeploymentName) { Instructions = AgentInstructions }) { Description = AgentDescription }), @@ -239,16 +243,18 @@ public class AIProjectClientCreateTests { "CreateWithChatClientAgentOptionsAsync" => await this._client.CreateAIAgentAsync( model: s_config.DeploymentName, - options: new ChatClientAgentOptions( - name: AgentName, - instructions: AgentInstructions, - tools: [weatherFunction])), + options: new ChatClientAgentOptions() + { + Name = AgentName, + ChatOptions = new() { Instructions = AgentInstructions, Tools = [weatherFunction] } + }), "CreateWithChatClientAgentOptionsSync" => this._client.CreateAIAgent( s_config.DeploymentName, - options: new ChatClientAgentOptions( - name: AgentName, - instructions: AgentInstructions, - tools: [weatherFunction])), + options: new ChatClientAgentOptions() + { + Name = AgentName, + ChatOptions = new() { Instructions = AgentInstructions, Tools = [weatherFunction] } + }), _ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}") }; diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs index e3e9969a43..17fad1581f 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs @@ -34,16 +34,20 @@ public class AzureAIAgentsPersistentCreateTests { "CreateWithChatClientAgentOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync( s_config.DeploymentName, - options: new ChatClientAgentOptions( - instructions: AgentInstructions, - name: AgentName, - description: AgentDescription)), + options: new ChatClientAgentOptions() + { + ChatOptions = new() { Instructions = AgentInstructions }, + Name = AgentName, + Description = AgentDescription + }), "CreateWithChatClientAgentOptionsSync" => this._persistentAgentsClient.CreateAIAgent( s_config.DeploymentName, - options: new ChatClientAgentOptions( - instructions: AgentInstructions, - name: AgentName, - description: AgentDescription)), + options: new ChatClientAgentOptions() + { + ChatOptions = new() { Instructions = AgentInstructions }, + Name = AgentName, + Description = AgentDescription + }), "CreateWithFoundryOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync( s_config.DeploymentName, instructions: AgentInstructions, @@ -109,14 +113,24 @@ public class AzureAIAgentsPersistentCreateTests { "CreateWithChatClientAgentOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync( s_config.DeploymentName, - options: new ChatClientAgentOptions( - instructions: AgentInstructions, - tools: [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreMetadata.Value.Id)] }])), + options: new ChatClientAgentOptions() + { + ChatOptions = new() + { + Instructions = AgentInstructions, + Tools = [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreMetadata.Value.Id)] }] + } + }), "CreateWithChatClientAgentOptionsSync" => this._persistentAgentsClient.CreateAIAgent( s_config.DeploymentName, - options: new ChatClientAgentOptions( - instructions: AgentInstructions, - tools: [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreMetadata.Value.Id)] }])), + options: new ChatClientAgentOptions() + { + ChatOptions = new() + { + Instructions = AgentInstructions, + Tools = [new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreMetadata.Value.Id)] }] + } + }), "CreateWithFoundryOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync( s_config.DeploymentName, instructions: AgentInstructions, @@ -179,15 +193,24 @@ public class AzureAIAgentsPersistentCreateTests // Hosted tool path (tools supplied via ChatClientAgentOptions) "CreateWithChatClientAgentOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync( s_config.DeploymentName, - options: new ChatClientAgentOptions( - instructions: AgentInstructions, - tools: [new HostedCodeInterpreterTool() { Inputs = [new HostedFileContent(uploadedCodeFile.Id)] }])), + options: new ChatClientAgentOptions() + { + ChatOptions = new() + { + Instructions = AgentInstructions, + Tools = [new HostedCodeInterpreterTool() { Inputs = [new HostedFileContent(uploadedCodeFile.Id)] }] + } + }), "CreateWithChatClientAgentOptionsSync" => this._persistentAgentsClient.CreateAIAgent( s_config.DeploymentName, - options: new ChatClientAgentOptions( - instructions: AgentInstructions, - tools: [new HostedCodeInterpreterTool() { Inputs = [new HostedFileContent(uploadedCodeFile.Id)] }])), - // Foundry (definitions + resources provided directly) + options: new ChatClientAgentOptions() + { + ChatOptions = new() + { + Instructions = AgentInstructions, + Tools = [new HostedCodeInterpreterTool() { Inputs = [new HostedFileContent(uploadedCodeFile.Id)] }] + } + }), "CreateWithFoundryOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync( s_config.DeploymentName, instructions: AgentInstructions, @@ -232,14 +255,24 @@ public class AzureAIAgentsPersistentCreateTests { "CreateWithChatClientAgentOptionsAsync" => await this._persistentAgentsClient.CreateAIAgentAsync( s_config.DeploymentName, - options: new ChatClientAgentOptions( - instructions: AgentInstructions, - tools: [weatherFunction])), + options: new ChatClientAgentOptions() + { + ChatOptions = new() + { + Instructions = AgentInstructions, + Tools = [weatherFunction] + } + }), "CreateWithChatClientAgentOptionsSync" => this._persistentAgentsClient.CreateAIAgent( s_config.DeploymentName, - options: new ChatClientAgentOptions( - instructions: AgentInstructions, - tools: [weatherFunction])), + options: new ChatClientAgentOptions() + { + ChatOptions = new() + { + Instructions = AgentInstructions, + Tools = [weatherFunction] + } + }), _ => throw new InvalidOperationException($"Unknown create mechanism: {createMechanism}") }; diff --git a/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicBetaServiceExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicBetaServiceExtensionsTests.cs index af778c03c9..400bcf5456 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicBetaServiceExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicBetaServiceExtensionsTests.cs @@ -91,7 +91,7 @@ public sealed class AnthropicBetaServiceExtensionsTests { Name = "Test Agent", Description = "Test description", - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }; // Act diff --git a/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicClientExtensionsTests.cs index 7a9c34a508..c8bf4d6a5e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicClientExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicClientExtensionsTests.cs @@ -158,7 +158,7 @@ public sealed class AnthropicClientExtensionsTests { Name = "Test Agent", Description = "Test description", - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }; // Act diff --git a/dotnet/tests/Microsoft.Agents.AI.AzureAI.Persistent.UnitTests/Extensions/PersistentAgentsClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.AzureAI.Persistent.UnitTests/Extensions/PersistentAgentsClientExtensionsTests.cs index 8d35f7f0b7..b661a392be 100644 --- a/dotnet/tests/Microsoft.Agents.AI.AzureAI.Persistent.UnitTests/Extensions/PersistentAgentsClientExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.AzureAI.Persistent.UnitTests/Extensions/PersistentAgentsClientExtensionsTests.cs @@ -310,7 +310,7 @@ public sealed class PersistentAgentsClientExtensionsTests { Name = "Override Name", Description = "Override Description", - Instructions = "Override Instructions" + ChatOptions = new() { Instructions = "Override Instructions" } }; // Act @@ -337,7 +337,7 @@ public sealed class PersistentAgentsClientExtensionsTests { Name = "Override Name", Description = "Override Description", - Instructions = "Override Instructions" + ChatOptions = new() { Instructions = "Override Instructions" } }; // Act @@ -386,7 +386,7 @@ public sealed class PersistentAgentsClientExtensionsTests { Name = "Override Name", Description = "Override Description", - Instructions = "Override Instructions" + ChatOptions = new() { Instructions = "Override Instructions" } }; // Act @@ -413,7 +413,7 @@ public sealed class PersistentAgentsClientExtensionsTests { Name = "Override Name", Description = "Override Description", - Instructions = "Override Instructions" + ChatOptions = new() { Instructions = "Override Instructions" } }; // Act @@ -557,7 +557,7 @@ public sealed class PersistentAgentsClientExtensionsTests { Name = "Test Agent", Description = "Test description", - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }; // Act @@ -584,7 +584,7 @@ public sealed class PersistentAgentsClientExtensionsTests { Name = "Test Agent", Description = "Test description", - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }; // Act diff --git a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs index 33656a8486..1136d2b1f4 100644 --- a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs @@ -752,7 +752,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests var options = new ChatClientAgentOptions { Name = "test-agent", - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }; // Act @@ -775,7 +775,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests var options = new ChatClientAgentOptions { Name = "test-agent", - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }; TestChatClient? testChatClient = null; @@ -803,7 +803,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests var options = new ChatClientAgentOptions { Name = "test-agent", - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }; // Act @@ -826,7 +826,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests var options = new ChatClientAgentOptions { Name = "test-agent", - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }; TestChatClient? testChatClient = null; @@ -1575,8 +1575,8 @@ public sealed class AzureAIProjectChatClientExtensionsTests var options = new ChatClientAgentOptions { Name = "test-agent", - Instructions = "Custom instructions", - Description = "Custom description" + Description = "Custom description", + ChatOptions = new ChatOptions { Instructions = "Custom instructions" } }; // Act @@ -1610,8 +1610,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests var options = new ChatClientAgentOptions { Name = "test-agent", - Instructions = "Test", - ChatOptions = new ChatOptions { Tools = tools } + ChatOptions = new ChatOptions { Instructions = "Test", Tools = tools } }; // Act diff --git a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientTests.cs b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientTests.cs index 647beb4451..eee9f520b6 100644 --- a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientTests.cs @@ -49,8 +49,7 @@ public class AzureAIProjectChatClientTests new ChatClientAgentOptions { Name = "test-agent", - Instructions = "Test instructions", - ChatOptions = new() { ConversationId = "conv_12345" } + ChatOptions = new() { Instructions = "Test instructions", ConversationId = "conv_12345" } }); // Act @@ -99,7 +98,7 @@ public class AzureAIProjectChatClientTests new ChatClientAgentOptions { Name = "test-agent", - Instructions = "Test instructions", + ChatOptions = new() { Instructions = "Test instructions" }, }); // Act @@ -148,8 +147,7 @@ public class AzureAIProjectChatClientTests new ChatClientAgentOptions { Name = "test-agent", - Instructions = "Test instructions", - ChatOptions = new() { ConversationId = "conv_should_not_use_default" } + ChatOptions = new() { Instructions = "Test instructions", ConversationId = "conv_should_not_use_default" } }); // Act @@ -198,7 +196,7 @@ public class AzureAIProjectChatClientTests new ChatClientAgentOptions { Name = "test-agent", - Instructions = "Test instructions", + ChatOptions = new() { Instructions = "Test instructions" }, }); // Act diff --git a/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/ChatClient/ChatClientAgentFactoryTests.cs b/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/ChatClient/ChatClientAgentFactoryTests.cs index e790bd27f0..8590662000 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/ChatClient/ChatClientAgentFactoryTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/ChatClient/ChatClientAgentFactoryTests.cs @@ -70,7 +70,7 @@ public sealed class ChatClientAgentFactoryTests Assert.IsType(agent); var chatClientAgent = agent as ChatClientAgent; Assert.NotNull(chatClientAgent?.ChatOptions); - Assert.Equal("Provide detailed and accurate responses.", chatClientAgent?.ChatOptions?.Instructions); + Assert.Equal("You are a helpful assistant.", chatClientAgent?.ChatOptions?.Instructions); Assert.Equal(0.7F, chatClientAgent?.ChatOptions?.Temperature); Assert.Equal(0.7F, chatClientAgent?.ChatOptions?.FrequencyPenalty); Assert.Equal(1024, chatClientAgent?.ChatOptions?.MaxOutputTokens); diff --git a/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIAssistantClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIAssistantClientExtensionsTests.cs index c14582958b..26f855d59e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIAssistantClientExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIAssistantClientExtensionsTests.cs @@ -92,7 +92,7 @@ public sealed class OpenAIAssistantClientExtensionsTests { Name = "Test Agent", Description = "Test description", - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }; // Act @@ -223,7 +223,7 @@ public sealed class OpenAIAssistantClientExtensionsTests { Name = "Override Name", Description = "Override Description", - Instructions = "Override Instructions" + ChatOptions = new() { Instructions = "Override Instructions" } }; // Act @@ -250,7 +250,7 @@ public sealed class OpenAIAssistantClientExtensionsTests { Name = "Override Name", Description = "Override Description", - Instructions = "Override Instructions" + ChatOptions = new() { Instructions = "Override Instructions" } }; // Act @@ -299,7 +299,7 @@ public sealed class OpenAIAssistantClientExtensionsTests { Name = "Override Name", Description = "Override Description", - Instructions = "Override Instructions" + ChatOptions = new() { Instructions = "Override Instructions" } }; // Act @@ -326,7 +326,7 @@ public sealed class OpenAIAssistantClientExtensionsTests { Name = "Override Name", Description = "Override Description", - Instructions = "Override Instructions" + ChatOptions = new() { Instructions = "Override Instructions" } }; // Act @@ -498,7 +498,7 @@ public sealed class OpenAIAssistantClientExtensionsTests var options = new ChatClientAgentOptions { Name = "Test Agent", - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }; // Act diff --git a/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIChatClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIChatClientExtensionsTests.cs index 72ea0395e9..ef9f27b01a 100644 --- a/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIChatClientExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIChatClientExtensionsTests.cs @@ -130,7 +130,7 @@ public sealed class OpenAIChatClientExtensionsTests { Name = "Test Agent", Description = "Test description", - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }; // Act diff --git a/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIResponseClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIResponseClientExtensionsTests.cs index 11c42548da..f31d343157 100644 --- a/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIResponseClientExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIResponseClientExtensionsTests.cs @@ -208,7 +208,7 @@ public sealed class OpenAIResponseClientExtensionsTests var options = new ChatClientAgentOptions { Name = "Test Agent", - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }; // Act diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentOptionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentOptionsTests.cs index dc983ef202..58cf5f718f 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentOptionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentOptionsTests.cs @@ -19,7 +19,6 @@ public class ChatClientAgentOptionsTests // Assert Assert.Null(options.Name); - Assert.Null(options.Instructions); Assert.Null(options.Description); Assert.Null(options.ChatOptions); Assert.Null(options.ChatMessageStoreFactory); @@ -27,90 +26,44 @@ public class ChatClientAgentOptionsTests } [Fact] - public void ParameterizedConstructor_WithNullValues_SetsPropertiesCorrectly() + public void Constructor_WithNullValues_SetsPropertiesCorrectly() { // Act - var options = new ChatClientAgentOptions( - instructions: null, - name: null, - description: null, - tools: null); + var options = new ChatClientAgentOptions() { Name = null, Description = null, ChatOptions = new() { Tools = null, Instructions = null } }; // Assert Assert.Null(options.Name); - Assert.Null(options.Instructions); Assert.Null(options.Description); - Assert.Null(options.ChatOptions); Assert.Null(options.AIContextProviderFactory); + Assert.Null(options.ChatMessageStoreFactory); + Assert.NotNull(options.ChatOptions); + Assert.Null(options.ChatOptions.Instructions); + Assert.Null(options.ChatOptions.Tools); } [Fact] - public void ParameterizedConstructor_WithInstructionsOnly_SetsChatOptionsWithInstructions() - { - // Arrange - const string Instructions = "Test instructions"; - - // Act - var options = new ChatClientAgentOptions( - instructions: Instructions, - name: null, - description: null, - tools: null); - - // Assert - Assert.Null(options.Name); - Assert.Equal(Instructions, options.Instructions); - Assert.Null(options.Description); - Assert.Null(options.ChatOptions); - } - - [Fact] - public void ParameterizedConstructor_WithToolsOnly_SetsChatOptionsWithTools() + public void Constructor_WithToolsOnly_SetsChatOptionsWithTools() { // Arrange var tools = new List { AIFunctionFactory.Create(() => "test") }; // Act - var options = new ChatClientAgentOptions( - instructions: null, - name: null, - description: null, - tools: tools); + var options = new ChatClientAgentOptions() + { + Name = null, + Description = null, + ChatOptions = new() { Tools = tools } + }; // Assert Assert.Null(options.Name); - Assert.Null(options.Instructions); Assert.Null(options.Description); Assert.NotNull(options.ChatOptions); - Assert.Null(options.ChatOptions.Instructions); - Assert.Same(tools, options.ChatOptions.Tools); + AssertSameTools(tools, options.ChatOptions.Tools); } [Fact] - public void ParameterizedConstructor_WithInstructionsAndTools_SetsChatOptionsWithBoth() - { - // Arrange - const string Instructions = "Test instructions"; - var tools = new List { AIFunctionFactory.Create(() => "test") }; - - // Act - var options = new ChatClientAgentOptions( - instructions: Instructions, - name: null, - description: null, - tools: tools); - - // Assert - Assert.Null(options.Name); - Assert.Equal(Instructions, options.Instructions); - Assert.Null(options.Description); - Assert.NotNull(options.ChatOptions); - Assert.Null(options.ChatOptions.Instructions); - Assert.Same(tools, options.ChatOptions.Tools); - } - - [Fact] - public void ParameterizedConstructor_WithAllParameters_SetsAllPropertiesCorrectly() + public void Constructor_WithAllParameters_SetsAllPropertiesCorrectly() { // Arrange const string Instructions = "Test instructions"; @@ -119,38 +72,37 @@ public class ChatClientAgentOptionsTests var tools = new List { AIFunctionFactory.Create(() => "test") }; // Act - var options = new ChatClientAgentOptions( - instructions: Instructions, - name: Name, - description: Description, - tools: tools); + var options = new ChatClientAgentOptions() + { + Name = Name, + Description = Description, + ChatOptions = new() { Tools = tools, Instructions = Instructions } + }; // Assert Assert.Equal(Name, options.Name); - Assert.Equal(Instructions, options.Instructions); + Assert.Equal(Instructions, options.ChatOptions.Instructions); Assert.Equal(Description, options.Description); Assert.NotNull(options.ChatOptions); - Assert.Null(options.ChatOptions.Instructions); - Assert.Same(tools, options.ChatOptions.Tools); + AssertSameTools(tools, options.ChatOptions.Tools); } [Fact] - public void ParameterizedConstructor_WithNameAndDescriptionOnly_DoesNotCreateChatOptions() + public void Constructor_WithNameAndDescriptionOnly_DoesNotCreateChatOptions() { // Arrange const string Name = "Test name"; const string Description = "Test description"; // Act - var options = new ChatClientAgentOptions( - instructions: null, - name: Name, - description: Description, - tools: null); + var options = new ChatClientAgentOptions() + { + Name = Name, + Description = Description, + }; // Assert Assert.Equal(Name, options.Name); - Assert.Null(options.Instructions); Assert.Equal(Description, options.Description); Assert.Null(options.ChatOptions); } @@ -159,7 +111,6 @@ public class ChatClientAgentOptionsTests public void Clone_CreatesDeepCopyWithSameValues() { // Arrange - const string Instructions = "Test instructions"; const string Name = "Test name"; const string Description = "Test description"; var tools = new List { AIFunctionFactory.Create(() => "test") }; @@ -171,8 +122,11 @@ public class ChatClientAgentOptionsTests ChatClientAgentOptions.AIContextProviderFactoryContext ctx) => new Mock().Object; - var original = new ChatClientAgentOptions(Instructions, Name, Description, tools) + var original = new ChatClientAgentOptions() { + Name = Name, + Description = Description, + ChatOptions = new() { Tools = tools }, Id = "test-id", ChatMessageStoreFactory = ChatMessageStoreFactory, AIContextProviderFactory = AIContextProviderFactory @@ -185,7 +139,6 @@ public class ChatClientAgentOptionsTests Assert.NotSame(original, clone); Assert.Equal(original.Id, clone.Id); Assert.Equal(original.Name, clone.Name); - Assert.Equal(original.Instructions, clone.Instructions); Assert.Equal(original.Description, clone.Description); Assert.Same(original.ChatMessageStoreFactory, clone.ChatMessageStoreFactory); Assert.Same(original.AIContextProviderFactory, clone.AIContextProviderFactory); @@ -197,14 +150,13 @@ public class ChatClientAgentOptionsTests } [Fact] - public void Clone_WithNullChatOptions_ClonesCorrectly() + public void Clone_WithoutProvidingChatOptions_ClonesCorrectly() { // Arrange var original = new ChatClientAgentOptions { Id = "test-id", Name = "Test name", - Instructions = "Test instructions", Description = "Test description" }; @@ -215,10 +167,19 @@ public class ChatClientAgentOptionsTests Assert.NotSame(original, clone); Assert.Equal(original.Id, clone.Id); Assert.Equal(original.Name, clone.Name); - Assert.Equal(original.Instructions, clone.Instructions); Assert.Equal(original.Description, clone.Description); - Assert.Null(clone.ChatOptions); + Assert.Null(original.ChatOptions); Assert.Null(clone.ChatMessageStoreFactory); Assert.Null(clone.AIContextProviderFactory); } + + private static void AssertSameTools(IList? expected, IList? actual) + { + var index = 0; + foreach (var tool in expected ?? []) + { + Assert.Same(tool, actual?[index]); + index++; + } + } } diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs index 8b46d7b57c..6e9d952b57 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs @@ -31,7 +31,7 @@ public partial class ChatClientAgentTests Id = "test-agent-id", Name = "test name", Description = "test description", - Instructions = "test instructions", + ChatOptions = new() { Instructions = "test instructions" }, }); // Assert @@ -65,7 +65,7 @@ public partial class ChatClientAgentTests ChatClientAgent agent = new(mockService.Object, options: new() { - Instructions = "test instructions" + ChatOptions = new() { Instructions = "base instructions" }, }); // Act @@ -99,7 +99,7 @@ public partial class ChatClientAgentTests { // Arrange var chatClient = new Mock().Object; - ChatClientAgent agent = new(chatClient, options: new() { Instructions = "test instructions" }); + ChatClientAgent agent = new(chatClient, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); // Act & Assert await Assert.ThrowsAsync(() => agent.RunAsync((IReadOnlyCollection)null!)); @@ -120,7 +120,7 @@ public partial class ChatClientAgentTests It.Is(opts => opts.MaxOutputTokens == 100), It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")])); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" }); + ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); // Act await agent.RunAsync([new(ChatRole.User, "test")], options: new ChatClientAgentRunOptions(chatOptions)); @@ -181,7 +181,7 @@ public partial class ChatClientAgentTests capturedMessages.AddRange(msgs)) .ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")])); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions" }); + ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "base instructions" } }); var runOptions = new AgentRunOptions(); // Act @@ -212,7 +212,7 @@ public partial class ChatClientAgentTests It.IsAny(), It.IsAny())).ReturnsAsync(new ChatResponse(responseMessages)); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions", Name = authorName }); + ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" }, Name = authorName }); // Act var result = await agent.RunAsync([new(ChatRole.User, "test")]); @@ -239,7 +239,7 @@ public partial class ChatClientAgentTests capturedMessages.AddRange(msgs)) .ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")])); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" }); + ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); // Create a thread using the agent's GetNewThread method var thread = agent.GetNewThread(); @@ -270,7 +270,7 @@ public partial class ChatClientAgentTests capturedMessages.AddRange(msgs)) .ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")])); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = null }); + ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = null } }); // Act await agent.RunAsync([new(ChatRole.User, "test message")]); @@ -300,7 +300,7 @@ public partial class ChatClientAgentTests capturedMessages.AddRange(msgs)) .ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")])); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" }); + ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); // Act await agent.RunAsync([]); @@ -326,7 +326,7 @@ public partial class ChatClientAgentTests It.Is(opts => opts.ConversationId == "ConvId"), It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]) { ConversationId = "ConvId" }); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" }); + ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); ChatClientAgentThread thread = new() { ConversationId = "ConvId" }; @@ -346,7 +346,7 @@ public partial class ChatClientAgentTests var chatOptions = new ChatOptions { ConversationId = "ConvId" }; Mock mockService = new(); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" }); + ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); ChatClientAgentThread thread = new() { ConversationId = "ThreadId" }; @@ -369,7 +369,7 @@ public partial class ChatClientAgentTests It.Is(opts => opts.MaxOutputTokens == 100 && opts.ConversationId == "ConvId"), It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]) { ConversationId = "ConvId" }); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" }); + ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); ChatClientAgentThread thread = new() { ConversationId = "ConvId" }; @@ -394,7 +394,7 @@ public partial class ChatClientAgentTests It.IsAny(), It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")])); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" }); + ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); ChatClientAgentThread thread = new() { ConversationId = "ConvId" }; @@ -415,7 +415,7 @@ public partial class ChatClientAgentTests It.IsAny>(), It.IsAny(), It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]) { ConversationId = "ConvId" }); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" }); + ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); ChatClientAgentThread thread = new(); // Act @@ -442,7 +442,7 @@ public partial class ChatClientAgentTests mockFactory.Setup(f => f(It.IsAny())).Returns(new InMemoryChatMessageStore()); ChatClientAgent agent = new(mockService.Object, options: new() { - Instructions = "test instructions", + ChatOptions = new() { Instructions = "test instructions" }, ChatMessageStoreFactory = mockFactory.Object }); @@ -473,7 +473,7 @@ public partial class ChatClientAgentTests It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")])); ChatClientAgent agent = new(mockService.Object, options: new() { - Instructions = "test instructions", + ChatOptions = new() { Instructions = "test instructions" }, }); // Act @@ -508,7 +508,7 @@ public partial class ChatClientAgentTests ChatClientAgent agent = new(mockService.Object, options: new() { - Instructions = "test instructions", + ChatOptions = new() { Instructions = "test instructions" }, ChatMessageStoreFactory = mockFactory.Object }); @@ -539,7 +539,7 @@ public partial class ChatClientAgentTests mockFactory.Setup(f => f(It.IsAny())).Returns(new InMemoryChatMessageStore()); ChatClientAgent agent = new(mockService.Object, options: new() { - Instructions = "test instructions", + ChatOptions = new() { Instructions = "test instructions" }, ChatMessageStoreFactory = mockFactory.Object }); @@ -592,7 +592,7 @@ public partial class ChatClientAgentTests .Setup(p => p.InvokedAsync(It.IsAny(), It.IsAny())) .Returns(new ValueTask()); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions", AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Tools = [AIFunctionFactory.Create(() => { }, "base function")] } }); + ChatClientAgent agent = new(mockService.Object, options: new() { AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Instructions = "base instructions", Tools = [AIFunctionFactory.Create(() => { }, "base function")] } }); // Act var thread = agent.GetNewThread() as ChatClientAgentThread; @@ -654,7 +654,7 @@ public partial class ChatClientAgentTests .Setup(p => p.InvokedAsync(It.IsAny(), It.IsAny())) .Returns(new ValueTask()); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions", AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Tools = [AIFunctionFactory.Create(() => { }, "base function")] } }); + ChatClientAgent agent = new(mockService.Object, options: new() { AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Instructions = "base instructions", Tools = [AIFunctionFactory.Create(() => { }, "base function")] } }); // Act await Assert.ThrowsAsync(() => agent.RunAsync(requestMessages)); @@ -700,7 +700,7 @@ public partial class ChatClientAgentTests .Setup(p => p.InvokingAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new AIContext()); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions", AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Tools = [AIFunctionFactory.Create(() => { }, "base function")] } }); + ChatClientAgent agent = new(mockService.Object, options: new() { AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Instructions = "base instructions", Tools = [AIFunctionFactory.Create(() => { }, "base function")] } }); // Act await agent.RunAsync([new(ChatRole.User, "user message")]); @@ -907,7 +907,7 @@ public partial class ChatClientAgentTests { // Arrange var chatClient = new Mock().Object; - var metadata = new ChatClientAgentOptions { Instructions = "You are a helpful assistant" }; + var metadata = new ChatClientAgentOptions { ChatOptions = new() { Instructions = "You are a helpful assistant" } }; ChatClientAgent agent = new(chatClient, metadata); // Act & Assert @@ -936,7 +936,7 @@ public partial class ChatClientAgentTests { // Arrange var chatClient = new Mock().Object; - var metadata = new ChatClientAgentOptions { Instructions = null }; + var metadata = new ChatClientAgentOptions { ChatOptions = new() { Instructions = null } }; ChatClientAgent agent = new(chatClient, metadata); // Act & Assert @@ -967,10 +967,10 @@ public partial class ChatClientAgentTests } /// - /// Verify that ChatOptions property returns null when no params are provided that require a ChatOptions instance. + /// Verify that ChatOptions is created with instructions when instructions are provided and no tools are provided. /// [Fact] - public void ChatOptionsReturnsNullWhenConstructorToolsNotProvided() + public void ChatOptionsCreatedWithInstructionsEvenWhenConstructorToolsNotProvided() { // Arrange var chatClient = new Mock().Object; @@ -980,7 +980,8 @@ public partial class ChatClientAgentTests Assert.Equal("TestInstructions", agent.Instructions); Assert.Equal("TestName", agent.Name); Assert.Equal("TestDescription", agent.Description); - Assert.Null(agent.ChatOptions); + Assert.NotNull(agent.ChatOptions); + Assert.Equal("TestInstructions", agent.ChatOptions.Instructions); } #endregion @@ -1071,7 +1072,7 @@ public partial class ChatClientAgentTests public async Task ChatOptionsMergingUsesAgentOptionsWhenRequestHasNoneAsync() { // Arrange - var agentChatOptions = new ChatOptions { MaxOutputTokens = 100, Temperature = 0.7f }; + var agentChatOptions = new ChatOptions { MaxOutputTokens = 100, Temperature = 0.7f, Instructions = "test instructions" }; Mock mockService = new(); ChatOptions? capturedChatOptions = null; mockService.Setup( @@ -1085,7 +1086,6 @@ public partial class ChatClientAgentTests ChatClientAgent agent = new(mockService.Object, options: new() { - Instructions = "test instructions", ChatOptions = agentChatOptions }); var messages = new List { new(ChatRole.User, "test") }; @@ -1114,7 +1114,7 @@ public partial class ChatClientAgentTests capturedChatOptions = opts) .ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")])); - ChatClientAgent agent = new(mockService.Object, options: new("test instructions")); + ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "test instructions" } }); var messages = new List { new(ChatRole.User, "test") }; // Act @@ -1167,6 +1167,7 @@ public partial class ChatClientAgentTests // Arrange var agentChatOptions = new ChatOptions { + Instructions = "test instructions", MaxOutputTokens = 100, Temperature = 0.7f, TopP = 0.9f, @@ -1204,7 +1205,6 @@ public partial class ChatClientAgentTests ChatClientAgent agent = new(mockService.Object, options: new() { - Instructions = "test instructions", ChatOptions = agentChatOptions }); var messages = new List { new(ChatRole.User, "test") }; @@ -1263,6 +1263,7 @@ public partial class ChatClientAgentTests var agentChatOptions = new ChatOptions { + Instructions = "test instructions", Tools = [agentTool] }; var requestChatOptions = new ChatOptions @@ -1283,7 +1284,6 @@ public partial class ChatClientAgentTests ChatClientAgent agent = new(mockService.Object, options: new() { - Instructions = "test instructions", ChatOptions = agentChatOptions }); var messages = new List { new(ChatRole.User, "test") }; @@ -1312,6 +1312,7 @@ public partial class ChatClientAgentTests var agentChatOptions = new ChatOptions { + Instructions = "test instructions", Tools = [agentTool] }; var requestChatOptions = new ChatOptions @@ -1333,7 +1334,6 @@ public partial class ChatClientAgentTests ChatClientAgent agent = new(mockService.Object, options: new() { - Instructions = "test instructions", ChatOptions = agentChatOptions }); var messages = new List { new(ChatRole.User, "test") }; @@ -1360,6 +1360,7 @@ public partial class ChatClientAgentTests // Arrange var agentChatOptions = new ChatOptions { + Instructions = "test instructions", RawRepresentationFactory = _ => agentSetting }; var requestChatOptions = new ChatOptions @@ -1380,7 +1381,6 @@ public partial class ChatClientAgentTests ChatClientAgent agent = new(mockService.Object, options: new() { - Instructions = "test instructions", ChatOptions = agentChatOptions }); var messages = new List { new(ChatRole.User, "test") }; @@ -1436,7 +1436,7 @@ public partial class ChatClientAgentTests TopK = 50, PresencePenalty = 0.1f, FrequencyPenalty = 0.2f, - Instructions = "test instructions\nrequest instructions", + Instructions = "agent instructions\nrequest instructions", ModelId = "agent-model", Seed = 12345, ConversationId = "agent-conversation", @@ -1459,7 +1459,6 @@ public partial class ChatClientAgentTests ChatClientAgent agent = new(mockService.Object, options: new() { - Instructions = "test instructions", ChatOptions = agentChatOptions }); var messages = new List { new(ChatRole.User, "test") }; @@ -1509,7 +1508,7 @@ public partial class ChatClientAgentTests { Id = "test-agent-id", Name = "TestAgent", - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }); // Act @@ -1532,7 +1531,7 @@ public partial class ChatClientAgentTests var mockChatClient = new Mock(); var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions { - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }); // Act @@ -1556,7 +1555,7 @@ public partial class ChatClientAgentTests var mockChatClient = new Mock(); var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions { - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }); // Act @@ -1582,7 +1581,7 @@ public partial class ChatClientAgentTests var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions { - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }); // Act @@ -1606,7 +1605,7 @@ public partial class ChatClientAgentTests var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions { - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }); // Act @@ -1632,7 +1631,7 @@ public partial class ChatClientAgentTests var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions { - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }); // Act @@ -1661,7 +1660,7 @@ public partial class ChatClientAgentTests var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions { - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }); // Act @@ -1694,7 +1693,7 @@ public partial class ChatClientAgentTests { Id = "test-agent-id", Name = "TestAgent", - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }); // Act @@ -1721,7 +1720,7 @@ public partial class ChatClientAgentTests var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions { - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }); // Act @@ -1756,12 +1755,12 @@ public partial class ChatClientAgentTests var chatClientAgent1 = new ChatClientAgent(mockChatClient1.Object, new ChatClientAgentOptions { - Instructions = "Test instructions 1" + ChatOptions = new() { Instructions = "Test instructions 1" } }); var chatClientAgent2 = new ChatClientAgent(mockChatClient2.Object, new ChatClientAgentOptions { - Instructions = "Test instructions 2" + ChatOptions = new() { Instructions = "Test instructions 2" } }); // Act @@ -1796,7 +1795,7 @@ public partial class ChatClientAgentTests var mockChatClient = new Mock(); var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions { - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }); // Act @@ -1820,7 +1819,7 @@ public partial class ChatClientAgentTests var mockChatClient = new Mock(); var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions { - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }); // Act @@ -1845,7 +1844,7 @@ public partial class ChatClientAgentTests var mockChatClient = new Mock(); var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions { - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }); // Act - Request IChatClient with a service key (base.GetService will return null due to serviceKey) @@ -1870,7 +1869,7 @@ public partial class ChatClientAgentTests mockChatClient.Setup(c => c.GetService(typeof(string), "some-key")).Returns("test-result"); var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions { - Instructions = "Test instructions" + ChatOptions = new() { Instructions = "Test instructions" } }); // Act - Request string with a service key (base.GetService will return null due to serviceKey) @@ -1911,7 +1910,7 @@ public partial class ChatClientAgentTests ChatClientAgent agent = new(mockService.Object, options: new() { - Instructions = "test instructions" + ChatOptions = new() { Instructions = "test instructions" } }); // Act @@ -1958,7 +1957,7 @@ public partial class ChatClientAgentTests mockFactory.Setup(f => f(It.IsAny())).Returns(new InMemoryChatMessageStore()); ChatClientAgent agent = new(mockService.Object, options: new() { - Instructions = "test instructions", + ChatOptions = new() { Instructions = "test instructions" }, ChatMessageStoreFactory = mockFactory.Object }); @@ -1996,7 +1995,7 @@ public partial class ChatClientAgentTests mockFactory.Setup(f => f(It.IsAny())).Returns(new InMemoryChatMessageStore()); ChatClientAgent agent = new(mockService.Object, options: new() { - Instructions = "test instructions", + ChatOptions = new() { Instructions = "test instructions" }, ChatMessageStoreFactory = mockFactory.Object }); @@ -2049,7 +2048,7 @@ public partial class ChatClientAgentTests .Setup(p => p.InvokedAsync(It.IsAny(), It.IsAny())) .Returns(new ValueTask()); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions", AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Tools = [AIFunctionFactory.Create(() => { }, "base function")] } }); + ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "base instructions", Tools = [AIFunctionFactory.Create(() => { }, "base function")] }, AIContextProviderFactory = _ => mockProvider.Object }); // Act var thread = agent.GetNewThread() as ChatClientAgentThread; @@ -2112,7 +2111,7 @@ public partial class ChatClientAgentTests .Setup(p => p.InvokedAsync(It.IsAny(), It.IsAny())) .Returns(new ValueTask()); - ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions", AIContextProviderFactory = _ => mockProvider.Object, ChatOptions = new() { Tools = [AIFunctionFactory.Create(() => { }, "base function")] } }); + ChatClientAgent agent = new(mockService.Object, options: new() { ChatOptions = new() { Instructions = "base instructions", Tools = [AIFunctionFactory.Create(() => { }, "base function")] }, AIContextProviderFactory = _ => mockProvider.Object }); // Act await Assert.ThrowsAsync(async () => diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_DeserializeThreadTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_DeserializeThreadTests.cs index 1fd9a71b98..04eabf36af 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_DeserializeThreadTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_DeserializeThreadTests.cs @@ -20,7 +20,7 @@ public class ChatClientAgent_DeserializeThreadTests var factoryCalled = false; var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions { - Instructions = "Test instructions", + ChatOptions = new() { Instructions = "Test instructions" }, AIContextProviderFactory = _ => { factoryCalled = true; @@ -53,7 +53,7 @@ public class ChatClientAgent_DeserializeThreadTests var factoryCalled = false; var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions { - Instructions = "Test instructions", + ChatOptions = new() { Instructions = "Test instructions" }, ChatMessageStoreFactory = _ => { factoryCalled = true; diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_GetNewThreadTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_GetNewThreadTests.cs index 43e0bef8bc..628d738e72 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_GetNewThreadTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_GetNewThreadTests.cs @@ -19,7 +19,7 @@ public class ChatClientAgent_GetNewThreadTests var factoryCalled = false; var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions { - Instructions = "Test instructions", + ChatOptions = new() { Instructions = "Test instructions" }, AIContextProviderFactory = _ => { factoryCalled = true; @@ -46,7 +46,7 @@ public class ChatClientAgent_GetNewThreadTests var factoryCalled = false; var agent = new ChatClientAgent(mockChatClient.Object, new ChatClientAgentOptions { - Instructions = "Test instructions", + ChatOptions = new() { Instructions = "Test instructions" }, ChatMessageStoreFactory = _ => { factoryCalled = true; diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientBuilderExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientBuilderExtensionsTests.cs index 3877358644..3407f172a2 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientBuilderExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientBuilderExtensionsTests.cs @@ -90,7 +90,7 @@ public sealed class ChatClientBuilderExtensionsTests { Name = "AgentWithOptions", Description = "Desc", - Instructions = "Instr", + ChatOptions = new() { Instructions = "Instr" }, UseProvidedChatClientAsIs = true }; @@ -115,7 +115,7 @@ public sealed class ChatClientBuilderExtensionsTests var options = new ChatClientAgentOptions { Name = "ServiceAgent", - Instructions = "Service instructions" + ChatOptions = new() { Instructions = "Service instructions" } }; // Act @@ -148,7 +148,7 @@ public sealed class ChatClientBuilderExtensionsTests ChatClientBuilder builder = null!; // Act & Assert - Assert.Throws(() => builder.BuildAIAgent(options: new() { Instructions = "instructions" })); + Assert.Throws(() => builder.BuildAIAgent(options: new() { ChatOptions = new() { Instructions = "instructions" } })); } [Fact] @@ -166,7 +166,7 @@ public sealed class ChatClientBuilderExtensionsTests var agent = builder.BuildAIAgent( new ChatClientAgentOptions { - Instructions = "Middleware test", + ChatOptions = new() { Instructions = "Middleware test" }, UseProvidedChatClientAsIs = true } ); diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientExtensionsTests.cs index 182de0be5b..51beb6aa2e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientExtensionsTests.cs @@ -57,7 +57,7 @@ public sealed class ChatClientExtensionsTests { Name = "AgentWithOptions", Description = "Desc", - Instructions = "Instr", + ChatOptions = new() { Instructions = "Instr" }, UseProvidedChatClientAsIs = true }; @@ -89,6 +89,6 @@ public sealed class ChatClientExtensionsTests IChatClient chatClient = null!; // Act & Assert - Assert.Throws(() => chatClient.CreateAIAgent(options: new() { Instructions = "instructions" })); + Assert.Throws(() => chatClient.CreateAIAgent(options: new() { ChatOptions = new() { Instructions = "instructions" } })); } } diff --git a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantClientExtensionsTests.cs b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantClientExtensionsTests.cs index 0bd084951d..d187d85b78 100644 --- a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantClientExtensionsTests.cs +++ b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantClientExtensionsTests.cs @@ -37,14 +37,24 @@ public class OpenAIAssistantClientExtensionsTests { "CreateWithChatClientAgentOptionsAsync" => await this._assistantClient.CreateAIAgentAsync( model: s_config.ChatModelId!, - options: new ChatClientAgentOptions( - instructions: AgentInstructions, - tools: [weatherFunction])), + options: new ChatClientAgentOptions() + { + ChatOptions = new() + { + Instructions = AgentInstructions, + Tools = [weatherFunction] + } + }), "CreateWithChatClientAgentOptionsSync" => this._assistantClient.CreateAIAgent( model: s_config.ChatModelId!, - options: new ChatClientAgentOptions( - instructions: AgentInstructions, - tools: [weatherFunction])), + options: new ChatClientAgentOptions() + { + ChatOptions = new() + { + Instructions = AgentInstructions, + Tools = [weatherFunction] + } + }), "CreateWithParamsAsync" => await this._assistantClient.CreateAIAgentAsync( model: s_config.ChatModelId!, instructions: AgentInstructions, @@ -94,14 +104,24 @@ public class OpenAIAssistantClientExtensionsTests { "CreateWithChatClientAgentOptionsAsync" => await this._assistantClient.CreateAIAgentAsync( model: s_config.ChatModelId!, - options: new ChatClientAgentOptions( - instructions: Instructions, - tools: [codeInterpreterTool])), + options: new ChatClientAgentOptions() + { + ChatOptions = new() + { + Instructions = Instructions, + Tools = [codeInterpreterTool] + } + }), "CreateWithChatClientAgentOptionsSync" => this._assistantClient.CreateAIAgent( model: s_config.ChatModelId!, - options: new ChatClientAgentOptions( - instructions: Instructions, - tools: [codeInterpreterTool])), + options: new ChatClientAgentOptions() + { + ChatOptions = new() + { + Instructions = Instructions, + Tools = [codeInterpreterTool] + } + }), "CreateWithParamsAsync" => await this._assistantClient.CreateAIAgentAsync( model: s_config.ChatModelId!, instructions: Instructions, @@ -159,14 +179,24 @@ public class OpenAIAssistantClientExtensionsTests { "CreateWithChatClientAgentOptionsAsync" => await this._assistantClient.CreateAIAgentAsync( model: s_config.ChatModelId!, - options: new ChatClientAgentOptions( - instructions: Instructions, - tools: [fileSearchTool])), + options: new ChatClientAgentOptions() + { + ChatOptions = new() + { + Instructions = Instructions, + Tools = [fileSearchTool] + } + }), "CreateWithChatClientAgentOptionsSync" => this._assistantClient.CreateAIAgent( model: s_config.ChatModelId!, - options: new ChatClientAgentOptions( - instructions: Instructions, - tools: [fileSearchTool])), + options: new ChatClientAgentOptions() + { + ChatOptions = new() + { + Instructions = Instructions, + Tools = [fileSearchTool] + } + }), "CreateWithParamsAsync" => await this._assistantClient.CreateAIAgentAsync( model: s_config.ChatModelId!, instructions: Instructions, diff --git a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs index f98540d8cc..656d310ddf 100644 --- a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs +++ b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs @@ -47,8 +47,7 @@ public class OpenAIChatCompletionFixture : IChatClientAgentFixture return Task.FromResult(new ChatClientAgent(chatClient, options: new() { Name = name, - Instructions = instructions, - ChatOptions = new() { Tools = aiTools } + ChatOptions = new() { Instructions = instructions, Tools = aiTools } })); } diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs index fbb087a153..a58583fbca 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs @@ -73,9 +73,9 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture options: new() { Name = name, - Instructions = instructions, ChatOptions = new ChatOptions { + Instructions = instructions, Tools = aiTools, RawRepresentationFactory = new Func(_ => new ResponseCreationOptions() { StoredOutputEnabled = store }) }, From b5595f6f701f8b24831cd90a47ab3026f98065ea Mon Sep 17 00:00:00 2001 From: Farzad Sunavala <40604067+farzad528@users.noreply.github.com> Date: Wed, 3 Dec 2025 16:00:59 +0000 Subject: [PATCH 06/33] Python: Enable Agentic Mode to Use Existing Knowledge Bases Without index_name (#2464) * refactor KB for index creation logic * add user agent header for tracking * fix mypy issues --------- Co-authored-by: farzad528 --- .../_search_provider.py | 162 ++++++--- .../tests/test_search_provider.py | 327 ++++++++++-------- .../azure_ai_with_search_context_agentic.py | 79 +++-- 3 files changed, 347 insertions(+), 221 deletions(-) diff --git a/python/packages/azure-ai-search/agent_framework_azure_ai_search/_search_provider.py b/python/packages/azure-ai-search/agent_framework_azure_ai_search/_search_provider.py index 7e6ad8e621..a63ad1deb2 100644 --- a/python/packages/azure-ai-search/agent_framework_azure_ai_search/_search_provider.py +++ b/python/packages/azure-ai-search/agent_framework_azure_ai_search/_search_provider.py @@ -5,7 +5,7 @@ import sys from collections.abc import Awaitable, Callable, MutableSequence from typing import TYPE_CHECKING, Any, ClassVar, Literal -from agent_framework import ChatMessage, Context, ContextProvider, Role +from agent_framework import AGENT_FRAMEWORK_USER_AGENT, ChatMessage, Context, ContextProvider, Role from agent_framework._logging import get_logger from agent_framework._pydantic import AFBaseSettings from agent_framework.exceptions import ServiceInitializationError @@ -129,6 +129,8 @@ class AzureAISearchSettings(AFBaseSettings): Can be set via environment variable AZURE_SEARCH_ENDPOINT. index_name: Name of the search index. Can be set via environment variable AZURE_SEARCH_INDEX_NAME. + knowledge_base_name: Name of an existing Knowledge Base (for agentic mode). + Can be set via environment variable AZURE_SEARCH_KNOWLEDGE_BASE_NAME. api_key: API key for authentication (optional, use managed identity if not provided). Can be set via environment variable AZURE_SEARCH_API_KEY. env_file_path: If provided, the .env settings are read from this file path location. @@ -158,6 +160,7 @@ class AzureAISearchSettings(AFBaseSettings): endpoint: str | None = None index_name: str | None = None + knowledge_base_name: str | None = None api_key: SecretStr | None = None @@ -239,7 +242,6 @@ class AzureAISearchContextProvider(ContextProvider): embedding_function: Callable[[str], Awaitable[list[float]]] | None = None, context_prompt: str | None = None, # Agentic mode parameters (Knowledge Base) - azure_ai_project_endpoint: str | None = None, azure_openai_resource_url: str | None = None, model_deployment_name: str | None = None, model_name: str | None = None, @@ -277,22 +279,18 @@ class AzureAISearchContextProvider(ContextProvider): Required if vector_field_name is specified and no server-side vectorization. context_prompt: Custom prompt to prepend to retrieved context. Default: "Use the following context to answer the question:" - azure_ai_project_endpoint: Azure AI Foundry project endpoint URL. - This is NOT the same as azure_openai_resource_url - the project endpoint is used - for Azure AI Foundry services, while the OpenAI endpoint is used by the Knowledge - Base to call the model for query planning. Required for agentic mode. - Example: "https://myproject.services.ai.azure.com/api/projects/myproject" azure_openai_resource_url: Azure OpenAI resource URL for Knowledge Base model calls. - This is the OpenAI endpoint used by the Knowledge Base to call the LLM for - query planning and reasoning. This is separate from the project endpoint because - the Knowledge Base directly calls Azure OpenAI for its internal operations. - Required for agentic mode. Example: "https://myresource.openai.azure.com" + Required when using agentic mode with index_name (to auto-create Knowledge Base). + Not required when using an existing knowledge_base_name. + Example: "https://myresource.openai.azure.com" model_deployment_name: Model deployment name in Azure OpenAI for Knowledge Base. - This is the deployment name the Knowledge Base uses to call the LLM. - Required for agentic mode. + Required when using agentic mode with index_name (to auto-create Knowledge Base). + Not required when using an existing knowledge_base_name. model_name: The underlying model name (e.g., "gpt-4o", "gpt-4o-mini"). If not provided, defaults to model_deployment_name. Used for Knowledge Base configuration. - knowledge_base_name: Name for the Knowledge Base. Required for agentic mode. + knowledge_base_name: Name of an existing Knowledge Base to use. + Required for agentic mode if not providing index_name. + Supports KBs with any source type (web, blob, index, etc.). retrieval_instructions: Custom instructions for the Knowledge Base's retrieval planning. Only used in agentic mode. azure_openai_api_key: Azure OpenAI API key for Knowledge Base to call the model. @@ -340,6 +338,7 @@ class AzureAISearchContextProvider(ContextProvider): settings = AzureAISearchSettings( endpoint=endpoint, index_name=index_name, + knowledge_base_name=knowledge_base_name, api_key=api_key if isinstance(api_key, str) else None, env_file_path=env_file_path, env_file_encoding=env_file_encoding, @@ -353,11 +352,36 @@ class AzureAISearchContextProvider(ContextProvider): "Azure AI Search endpoint is required. Set via 'endpoint' parameter " "or 'AZURE_SEARCH_ENDPOINT' environment variable." ) - if not settings.index_name: - raise ServiceInitializationError( - "Azure AI Search index name is required. Set via 'index_name' parameter " - "or 'AZURE_SEARCH_INDEX_NAME' environment variable." - ) + + # Validate index_name and knowledge_base_name based on mode + # Note: settings.* contains the resolved value (explicit param OR env var) + if mode == "semantic": + # Semantic mode: always requires index_name + if not settings.index_name: + raise ServiceInitializationError( + "Azure AI Search index name is required for semantic mode. " + "Set via 'index_name' parameter or 'AZURE_SEARCH_INDEX_NAME' environment variable." + ) + elif mode == "agentic": + # Agentic mode: requires exactly ONE of index_name or knowledge_base_name + if settings.index_name and settings.knowledge_base_name: + raise ServiceInitializationError( + "For agentic mode, provide either 'index_name' OR 'knowledge_base_name', not both. " + "Use 'index_name' to auto-create a Knowledge Base, or 'knowledge_base_name' to use an existing one." + ) + if not settings.index_name and not settings.knowledge_base_name: + raise ServiceInitializationError( + "For agentic mode, provide either 'index_name' (to auto-create Knowledge Base) " + "or 'knowledge_base_name' (to use existing Knowledge Base). " + "Set via parameters or environment variables " + "AZURE_SEARCH_INDEX_NAME / AZURE_SEARCH_KNOWLEDGE_BASE_NAME." + ) + # If using index_name to create KB, model config is required + if settings.index_name and not model_deployment_name: + raise ServiceInitializationError( + "model_deployment_name is required for agentic mode when creating Knowledge Base from index. " + "This is the Azure OpenAI deployment used by the Knowledge Base for query planning." + ) # Determine the credential to use resolved_credential: AzureKeyCredential | AsyncTokenCredential @@ -389,14 +413,27 @@ class AzureAISearchContextProvider(ContextProvider): self.azure_openai_deployment_name = model_deployment_name # If model_name not provided, default to deployment name self.model_name = model_name or model_deployment_name - self.knowledge_base_name = knowledge_base_name + # Use resolved KB name (from explicit param or env var) + self.knowledge_base_name = settings.knowledge_base_name self.retrieval_instructions = retrieval_instructions self.azure_openai_api_key = azure_openai_api_key - self.azure_ai_project_endpoint = azure_ai_project_endpoint self.knowledge_base_output_mode = knowledge_base_output_mode self.retrieval_reasoning_effort = retrieval_reasoning_effort self.agentic_message_history_count = agentic_message_history_count + # Determine if using existing Knowledge Base or auto-creating from index + # Since validation ensures exactly one of index_name/knowledge_base_name for agentic mode: + # - knowledge_base_name provided: use existing KB + # - index_name provided: auto-create KB from index + self._use_existing_knowledge_base = False + if mode == "agentic": + if settings.knowledge_base_name: + # Use existing KB directly (supports any source type: web, blob, index, etc.) + self._use_existing_knowledge_base = True + else: + # Auto-generate KB name from index name + self.knowledge_base_name = f"{settings.index_name}-kb" + # Auto-discover vector field if not specified self._auto_discovered_vector_field = False self._use_vectorizable_query = False # Will be set to True if server-side vectorization detected @@ -415,22 +452,24 @@ class AzureAISearchContextProvider(ContextProvider): "Agentic retrieval requires azure-search-documents >= 11.7.0b1 with Knowledge Base support. " "Please upgrade: pip install azure-search-documents>=11.7.0b1" ) - if not self.azure_openai_resource_url: + # Only require OpenAI resource URL if NOT using existing KB + # (existing KB already has its model configuration) + # Note: model_deployment_name is already validated at initialization + if not self._use_existing_knowledge_base and not self.azure_openai_resource_url: raise ValueError( - "azure_openai_resource_url is required for agentic mode. " + "azure_openai_resource_url is required for agentic mode when creating Knowledge Base from index. " "This should be your Azure OpenAI endpoint (e.g., 'https://myresource.openai.azure.com')" ) - if not self.azure_openai_deployment_name: - raise ValueError("model_deployment_name is required for agentic mode") - if not knowledge_base_name: - raise ValueError("knowledge_base_name is required for agentic mode") - # Create search client for semantic mode - self._search_client = SearchClient( - endpoint=self.endpoint, - index_name=self.index_name, - credential=self.credential, - ) + # Create search client for semantic mode (only if index_name is available) + self._search_client: SearchClient | None = None + if self.index_name: + self._search_client = SearchClient( + endpoint=self.endpoint, + index_name=self.index_name, + credential=self.credential, + user_agent=AGENT_FRAMEWORK_USER_AGENT, + ) # Create index client and retrieval client for agentic mode (Knowledge Base) self._index_client: SearchIndexClient | None = None @@ -439,6 +478,7 @@ class AzureAISearchContextProvider(ContextProvider): self._index_client = SearchIndexClient( endpoint=self.endpoint, credential=self.credential, + user_agent=AGENT_FRAMEWORK_USER_AGENT, ) # Retrieval client will be created after Knowledge Base initialization @@ -574,10 +614,19 @@ class AzureAISearchContextProvider(ContextProvider): try: # Use existing index client or create temporary one if not self._index_client: - self._index_client = SearchIndexClient(endpoint=self.endpoint, credential=self.credential) + self._index_client = SearchIndexClient( + endpoint=self.endpoint, + credential=self.credential, + user_agent=AGENT_FRAMEWORK_USER_AGENT, + ) index_client = self._index_client - # Get index schema + # Get index schema (index_name is guaranteed to be set for semantic mode) + if not self.index_name: + logger.warning("Cannot auto-discover vector field: index_name is not set.") + self._auto_discovered_vector_field = True + return + index = await index_client.get_index(self.index_name) # Step 1: Find all vector fields @@ -694,7 +743,10 @@ class AzureAISearchContextProvider(ContextProvider): search_params["semantic_configuration_name"] = self.semantic_configuration_name search_params["query_caption"] = QueryCaptionType.EXTRACTIVE - # Execute search + # Execute search (search client is guaranteed to exist for semantic mode) + if not self._search_client: + raise RuntimeError("Search client is not initialized. This should not happen in semantic mode.") + results = await self._search_client.search(**search_params) # type: ignore[reportUnknownVariableType] # Format results with citations @@ -711,27 +763,48 @@ class AzureAISearchContextProvider(ContextProvider): return formatted_results async def _ensure_knowledge_base(self) -> None: - """Ensure Knowledge Base and knowledge source are created. + """Ensure Knowledge Base and knowledge source are created or use existing KB. This method is idempotent - it will only create resources if they don't exist. Note: Azure SDK uses KnowledgeAgent classes internally, but the feature is marketed as "Knowledge Bases" in Azure AI Search. """ - if self._knowledge_base_initialized or not self._index_client: + if self._knowledge_base_initialized: return - # Runtime validation for agentic mode parameters + # Runtime validation if not self.knowledge_base_name: raise ValueError("knowledge_base_name is required for agentic mode") - if not self.azure_openai_resource_url: - raise ValueError("azure_openai_resource_url is required for agentic mode") - if not self.azure_openai_deployment_name: - raise ValueError("model_deployment_name is required for agentic mode") knowledge_base_name = self.knowledge_base_name - # Step 1: Create or get knowledge source + # Path 1: Use existing Knowledge Base directly (no index needed) + # This supports KB with any source type (web, blob, index, etc.) + if self._use_existing_knowledge_base: + # Just create the retrieval client - KB already exists with its own sources + if _agentic_retrieval_available and self._retrieval_client is None: + self._retrieval_client = KnowledgeBaseRetrievalClient( + endpoint=self.endpoint, + knowledge_base_name=knowledge_base_name, + credential=self.credential, + user_agent=AGENT_FRAMEWORK_USER_AGENT, + ) + self._knowledge_base_initialized = True + return + + # Path 2: Auto-create Knowledge Base from search index + # Requires index_client and OpenAI configuration + if not self._index_client: + raise ValueError("Index client is required when creating Knowledge Base from index") + if not self.azure_openai_resource_url: + raise ValueError("azure_openai_resource_url is required when creating Knowledge Base from index") + if not self.azure_openai_deployment_name: + raise ValueError("model_deployment_name is required when creating Knowledge Base from index") + if not self.index_name: + raise ValueError("index_name is required when creating Knowledge Base from index") + + # Step 1: Create or get knowledge source from index knowledge_source_name = f"{self.index_name}-source" try: @@ -794,6 +867,7 @@ class AzureAISearchContextProvider(ContextProvider): endpoint=self.endpoint, knowledge_base_name=knowledge_base_name, credential=self.credential, + user_agent=AGENT_FRAMEWORK_USER_AGENT, ) async def _agentic_search(self, messages: list[ChatMessage]) -> list[str]: diff --git a/python/packages/azure-ai-search/tests/test_search_provider.py b/python/packages/azure-ai-search/tests/test_search_provider.py index 8d49c1532c..66ead79a6b 100644 --- a/python/packages/azure-ai-search/tests/test_search_provider.py +++ b/python/packages/azure-ai-search/tests/test_search_provider.py @@ -148,74 +148,105 @@ class TestSearchProviderInitialization: vector_field_name="embedding", ) - def test_init_agentic_mode_requires_azure_openai_resource_url(self) -> None: - """Test that agentic mode requires azure_openai_resource_url.""" - with pytest.raises(ValueError, match="azure_openai_resource_url"): + def test_init_agentic_mode_with_kb_only(self) -> None: + """Test agentic mode with existing knowledge_base_name (simplest path).""" + # Clear environment to ensure no env vars interfere + clean_env = {k: v for k, v in os.environ.items() if not k.startswith("AZURE_SEARCH_")} + with patch.dict(os.environ, clean_env, clear=True): + provider = AzureAISearchContextProvider( + endpoint="https://test.search.windows.net", + api_key="test-key", + mode="agentic", + knowledge_base_name="test-kb", + env_file_path="", # Disable .env file loading + ) + assert provider.mode == "agentic" + assert provider.knowledge_base_name == "test-kb" + assert provider._use_existing_knowledge_base is True + + def test_init_agentic_mode_with_index_requires_model(self) -> None: + """Test that agentic mode with index_name requires model_deployment_name.""" + # Clear environment to ensure no env vars interfere + clean_env = {k: v for k, v in os.environ.items() if not k.startswith("AZURE_SEARCH_")} + with ( + patch.dict(os.environ, clean_env, clear=True), + pytest.raises(ServiceInitializationError, match="model_deployment_name"), + ): AzureAISearchContextProvider( endpoint="https://test.search.windows.net", index_name="test-index", api_key="test-key", mode="agentic", + env_file_path="", # Disable .env file loading ) - def test_init_agentic_mode_requires_model_deployment_name(self) -> None: - """Test that agentic mode requires model_deployment_name.""" - with pytest.raises(ValueError, match="model_deployment_name"): - AzureAISearchContextProvider( + def test_init_agentic_mode_with_index_and_model(self) -> None: + """Test agentic mode with index_name (auto-create KB path).""" + # Clear environment to ensure no env vars interfere + clean_env = {k: v for k, v in os.environ.items() if not k.startswith("AZURE_SEARCH_")} + with patch.dict(os.environ, clean_env, clear=True): + provider = AzureAISearchContextProvider( endpoint="https://test.search.windows.net", index_name="test-index", api_key="test-key", mode="agentic", - azure_ai_project_endpoint="https://test.services.ai.azure.com", - azure_openai_resource_url="https://test.openai.azure.com", - ) - - def test_init_agentic_mode_requires_knowledge_base_name(self) -> None: - """Test that agentic mode requires knowledge_base_name.""" - with pytest.raises(ValueError, match="knowledge_base_name"): - AzureAISearchContextProvider( - endpoint="https://test.search.windows.net", - index_name="test-index", - api_key="test-key", - mode="agentic", - azure_ai_project_endpoint="https://test.services.ai.azure.com", model_deployment_name="gpt-4o", azure_openai_resource_url="https://test.openai.azure.com", + env_file_path="", # Disable .env file loading + ) + assert provider.mode == "agentic" + assert provider.index_name == "test-index" + assert provider.knowledge_base_name == "test-index-kb" # Auto-generated + assert provider._use_existing_knowledge_base is False + + def test_init_agentic_mode_rejects_both_index_and_kb(self) -> None: + """Test that agentic mode rejects both index_name AND knowledge_base_name.""" + # Clear environment to ensure no env vars interfere + clean_env = {k: v for k, v in os.environ.items() if not k.startswith("AZURE_SEARCH_")} + with ( + patch.dict(os.environ, clean_env, clear=True), + pytest.raises(ServiceInitializationError, match="either 'index_name' OR 'knowledge_base_name', not both"), + ): + AzureAISearchContextProvider( + endpoint="https://test.search.windows.net", + index_name="test-index", + api_key="test-key", + mode="agentic", + knowledge_base_name="test-kb", + model_deployment_name="gpt-4o", + azure_openai_resource_url="https://test.openai.azure.com", + env_file_path="", # Disable .env file loading ) - def test_init_agentic_mode_with_all_params(self) -> None: - """Test initialization with all agentic mode parameters.""" - provider = AzureAISearchContextProvider( - endpoint="https://test.search.windows.net", - index_name="test-index", - api_key="test-key", - mode="agentic", - azure_ai_project_endpoint="https://test.services.ai.azure.com", - model_deployment_name="my-gpt-4o-deployment", - model_name="gpt-4o", - knowledge_base_name="test-kb", - azure_openai_resource_url="https://test.openai.azure.com", - ) - assert provider.mode == "agentic" - assert provider.azure_ai_project_endpoint == "https://test.services.ai.azure.com" - assert provider.azure_openai_resource_url == "https://test.openai.azure.com" - assert provider.azure_openai_deployment_name == "my-gpt-4o-deployment" - assert provider.model_name == "gpt-4o" - assert provider.knowledge_base_name == "test-kb" + def test_init_agentic_mode_requires_index_or_kb(self) -> None: + """Test that agentic mode requires either index_name or knowledge_base_name.""" + # Clear environment to ensure no env vars interfere + clean_env = {k: v for k, v in os.environ.items() if not k.startswith("AZURE_SEARCH_")} + with ( + patch.dict(os.environ, clean_env, clear=True), + pytest.raises(ServiceInitializationError, match="provide either 'index_name'.*or 'knowledge_base_name'"), + ): + AzureAISearchContextProvider( + endpoint="https://test.search.windows.net", + api_key="test-key", + mode="agentic", + env_file_path="", # Disable .env file loading + ) def test_init_model_name_defaults_to_deployment_name(self) -> None: """Test that model_name defaults to deployment_name if not provided.""" - provider = AzureAISearchContextProvider( - endpoint="https://test.search.windows.net", - index_name="test-index", - api_key="test-key", - mode="agentic", - azure_ai_project_endpoint="https://test.services.ai.azure.com", - model_deployment_name="gpt-4o", - knowledge_base_name="test-kb", - azure_openai_resource_url="https://test.openai.azure.com", - ) - assert provider.model_name == "gpt-4o" + # Clear environment to ensure no env vars interfere + clean_env = {k: v for k, v in os.environ.items() if not k.startswith("AZURE_SEARCH_")} + with patch.dict(os.environ, clean_env, clear=True): + provider = AzureAISearchContextProvider( + endpoint="https://test.search.windows.net", + api_key="test-key", + mode="agentic", + knowledge_base_name="test-kb", + model_deployment_name="gpt-4o", + env_file_path="", # Disable .env file loading + ) + assert provider.model_name == "gpt-4o" def test_init_with_custom_context_prompt(self) -> None: """Test initialization with custom context prompt.""" @@ -335,7 +366,7 @@ class TestKnowledgeBaseSetup: async def test_ensure_knowledge_base_creates_when_not_exists( self, mock_search_class: MagicMock, mock_index_class: MagicMock ) -> None: - """Test that Knowledge Base is created when it doesn't exist.""" + """Test that Knowledge Base is created when it doesn't exist (index_name path).""" # Setup mocks mock_index_client = AsyncMock() mock_index_client.get_knowledge_source.side_effect = ResourceNotFoundError("Not found") @@ -347,57 +378,58 @@ class TestKnowledgeBaseSetup: mock_search_client = AsyncMock() mock_search_class.return_value = mock_search_client - provider = AzureAISearchContextProvider( - endpoint="https://test.search.windows.net", - index_name="test-index", - api_key="test-key", - mode="agentic", - azure_ai_project_endpoint="https://test.services.ai.azure.com", - model_deployment_name="gpt-4o", - model_name="gpt-4o", - knowledge_base_name="test-kb", - azure_openai_resource_url="https://test.openai.azure.com", - ) + # Clear environment to ensure no env vars interfere + clean_env = {k: v for k, v in os.environ.items() if not k.startswith("AZURE_SEARCH_")} + with patch.dict(os.environ, clean_env, clear=True): + # Use index_name path (auto-create KB) + provider = AzureAISearchContextProvider( + endpoint="https://test.search.windows.net", + index_name="test-index", + api_key="test-key", + mode="agentic", + model_deployment_name="gpt-4o", + azure_openai_resource_url="https://test.openai.azure.com", + env_file_path="", # Disable .env file loading + ) - await provider._ensure_knowledge_base() + await provider._ensure_knowledge_base() - # Verify knowledge source was created - mock_index_client.create_knowledge_source.assert_called_once() - # Verify Knowledge Base was created - mock_index_client.create_or_update_knowledge_base.assert_called_once() + # Verify knowledge source was created + mock_index_client.create_knowledge_source.assert_called_once() + # Verify Knowledge Base was created + mock_index_client.create_or_update_knowledge_base.assert_called_once() @pytest.mark.asyncio @patch("agent_framework_azure_ai_search._search_provider.SearchIndexClient") @patch("agent_framework_azure_ai_search._search_provider.SearchClient") - async def test_ensure_knowledge_base_skips_when_exists( + async def test_ensure_knowledge_base_skips_when_using_existing_kb( self, mock_search_class: MagicMock, mock_index_class: MagicMock ) -> None: - """Test that Knowledge Base setup is skipped when already exists.""" + """Test that KB setup is skipped when using existing knowledge_base_name.""" # Setup mocks mock_index_client = AsyncMock() - mock_index_client.get_knowledge_source.return_value = MagicMock() # Exists - mock_index_client.get_knowledge_base.return_value = MagicMock() # Exists mock_index_class.return_value = mock_index_client mock_search_client = AsyncMock() mock_search_class.return_value = mock_search_client - provider = AzureAISearchContextProvider( - endpoint="https://test.search.windows.net", - index_name="test-index", - api_key="test-key", - mode="agentic", - azure_ai_project_endpoint="https://test.services.ai.azure.com", - model_deployment_name="gpt-4o", - knowledge_base_name="test-kb", - azure_openai_resource_url="https://test.openai.azure.com", - ) + # Clear environment to ensure no env vars interfere + clean_env = {k: v for k, v in os.environ.items() if not k.startswith("AZURE_SEARCH_")} + with patch.dict(os.environ, clean_env, clear=True): + # Use knowledge_base_name path (existing KB) + provider = AzureAISearchContextProvider( + endpoint="https://test.search.windows.net", + api_key="test-key", + mode="agentic", + knowledge_base_name="test-kb", + env_file_path="", # Disable .env file loading + ) - await provider._ensure_knowledge_base() + await provider._ensure_knowledge_base() - # Verify nothing was created - mock_index_client.create_knowledge_source.assert_not_called() - mock_index_client.create_agent.assert_not_called() + # Verify nothing was created (using existing KB) + mock_index_client.create_knowledge_source.assert_not_called() + mock_index_client.create_or_update_knowledge_base.assert_not_called() class TestContextProviderLifecycle: @@ -437,21 +469,22 @@ class TestContextProviderLifecycle: mock_retrieval_client.close = AsyncMock() mock_retrieval_class.return_value = mock_retrieval_client - async with AzureAISearchContextProvider( - endpoint="https://test.search.windows.net", - index_name="test-index", - api_key="test-key", - mode="agentic", - azure_ai_project_endpoint="https://test.services.ai.azure.com", - model_deployment_name="gpt-4o", - knowledge_base_name="test-kb", - azure_openai_resource_url="https://test.openai.azure.com", - ) as provider: - # Simulate retrieval client being created - provider._retrieval_client = mock_retrieval_client + # Clear environment to ensure no env vars interfere + clean_env = {k: v for k, v in os.environ.items() if not k.startswith("AZURE_SEARCH_")} + with patch.dict(os.environ, clean_env, clear=True): + # Use knowledge_base_name path (existing KB) + async with AzureAISearchContextProvider( + endpoint="https://test.search.windows.net", + api_key="test-key", + mode="agentic", + knowledge_base_name="test-kb", + env_file_path="", # Disable .env file loading + ) as provider: + # Simulate retrieval client being created + provider._retrieval_client = mock_retrieval_client - # Verify cleanup was called - mock_retrieval_client.close.assert_called_once() + # Verify cleanup was called + mock_retrieval_client.close.assert_called_once() def test_string_api_key_conversion(self) -> None: """Test that string api_key is converted to AzureKeyCredential.""" @@ -579,9 +612,6 @@ class TestAgenticSearch: # Setup index client mock mock_index_client = AsyncMock() - mock_index_client.get_knowledge_source.side_effect = ResourceNotFoundError("Not found") - mock_index_client.create_knowledge_source = AsyncMock() - mock_index_client.create_or_update_knowledge_base = AsyncMock() mock_index_class.return_value = mock_index_client # Setup retrieval client mock with response @@ -603,22 +633,23 @@ class TestAgenticSearch: mock_retrieval_client.close = AsyncMock() mock_retrieval_class.return_value = mock_retrieval_client - provider = AzureAISearchContextProvider( - endpoint="https://test.search.windows.net", - index_name="test-index", - api_key="test-key", - mode="agentic", - azure_ai_project_endpoint="https://test.services.ai.azure.com", - model_deployment_name="gpt-4o", - knowledge_base_name="test-kb", - azure_openai_resource_url="https://test.openai.azure.com", - ) + # Clear environment to ensure no env vars interfere + clean_env = {k: v for k, v in os.environ.items() if not k.startswith("AZURE_SEARCH_")} + with patch.dict(os.environ, clean_env, clear=True): + # Use knowledge_base_name path (existing KB) + provider = AzureAISearchContextProvider( + endpoint="https://test.search.windows.net", + api_key="test-key", + mode="agentic", + knowledge_base_name="test-kb", + env_file_path="", # Disable .env file loading + ) - context = await provider.invoking(sample_messages) + context = await provider.invoking(sample_messages) - assert isinstance(context, Context) - # Should have at least the prompt message - assert len(context.messages) >= 1 + assert isinstance(context, Context) + # Should have at least the prompt message + assert len(context.messages) >= 1 @pytest.mark.asyncio @patch("agent_framework_azure_ai_search._search_provider.KnowledgeBaseRetrievalClient") @@ -637,9 +668,6 @@ class TestAgenticSearch: mock_search_class.return_value = mock_search_client mock_index_client = AsyncMock() - mock_index_client.get_knowledge_source.side_effect = ResourceNotFoundError("Not found") - mock_index_client.create_knowledge_source = AsyncMock() - mock_index_client.create_or_update_knowledge_base = AsyncMock() mock_index_class.return_value = mock_index_client # Empty response @@ -650,22 +678,23 @@ class TestAgenticSearch: mock_retrieval_client.close = AsyncMock() mock_retrieval_class.return_value = mock_retrieval_client - provider = AzureAISearchContextProvider( - endpoint="https://test.search.windows.net", - index_name="test-index", - api_key="test-key", - mode="agentic", - azure_ai_project_endpoint="https://test.services.ai.azure.com", - model_deployment_name="gpt-4o", - knowledge_base_name="test-kb", - azure_openai_resource_url="https://test.openai.azure.com", - ) + # Clear environment to ensure no env vars interfere + clean_env = {k: v for k, v in os.environ.items() if not k.startswith("AZURE_SEARCH_")} + with patch.dict(os.environ, clean_env, clear=True): + # Use knowledge_base_name path (existing KB) + provider = AzureAISearchContextProvider( + endpoint="https://test.search.windows.net", + api_key="test-key", + mode="agentic", + knowledge_base_name="test-kb", + env_file_path="", # Disable .env file loading + ) - context = await provider.invoking(sample_messages) + context = await provider.invoking(sample_messages) - assert isinstance(context, Context) - # Should have fallback message - assert len(context.messages) >= 1 + assert isinstance(context, Context) + # Should have fallback message + assert len(context.messages) >= 1 @pytest.mark.asyncio @patch("agent_framework_azure_ai_search._search_provider.KnowledgeBaseRetrievalClient") @@ -684,9 +713,6 @@ class TestAgenticSearch: mock_search_class.return_value = mock_search_client mock_index_client = AsyncMock() - mock_index_client.get_knowledge_source.side_effect = ResourceNotFoundError("Not found") - mock_index_client.create_knowledge_source = AsyncMock() - mock_index_client.create_or_update_knowledge_base = AsyncMock() mock_index_class.return_value = mock_index_client mock_retrieval_client = AsyncMock() @@ -706,22 +732,23 @@ class TestAgenticSearch: mock_retrieval_client.close = AsyncMock() mock_retrieval_class.return_value = mock_retrieval_client - provider = AzureAISearchContextProvider( - endpoint="https://test.search.windows.net", - index_name="test-index", - api_key="test-key", - mode="agentic", - azure_ai_project_endpoint="https://test.services.ai.azure.com", - model_deployment_name="gpt-4o", - knowledge_base_name="test-kb", - azure_openai_resource_url="https://test.openai.azure.com", - retrieval_reasoning_effort="medium", # Test medium reasoning - ) + # Clear environment to ensure no env vars interfere + clean_env = {k: v for k, v in os.environ.items() if not k.startswith("AZURE_SEARCH_")} + with patch.dict(os.environ, clean_env, clear=True): + # Use knowledge_base_name path (existing KB) + provider = AzureAISearchContextProvider( + endpoint="https://test.search.windows.net", + api_key="test-key", + mode="agentic", + knowledge_base_name="test-kb", + retrieval_reasoning_effort="medium", # Test medium reasoning + env_file_path="", # Disable .env file loading + ) - context = await provider.invoking(sample_messages) + context = await provider.invoking(sample_messages) - assert isinstance(context, Context) - assert len(context.messages) >= 1 + assert isinstance(context, Context) + assert len(context.messages) >= 1 class TestVectorFieldAutoDiscovery: diff --git a/python/samples/getting_started/context_providers/azure_ai_search/azure_ai_with_search_context_agentic.py b/python/samples/getting_started/context_providers/azure_ai_search/azure_ai_with_search_context_agentic.py index 7531f9977b..f8e1123b8f 100644 --- a/python/samples/getting_started/context_providers/azure_ai_search/azure_ai_with_search_context_agentic.py +++ b/python/samples/getting_started/context_providers/azure_ai_search/azure_ai_with_search_context_agentic.py @@ -25,18 +25,22 @@ This sample demonstrates how to use Azure AI Search with agentic mode for RAG For simple queries where speed is critical, use semantic mode instead (see azure_ai_with_search_context_semantic.py). Prerequisites: -1. An Azure AI Search service with a search index +1. An Azure AI Search service 2. An Azure AI Foundry project with a model deployment -3. An Azure OpenAI resource (for Knowledge Base model calls) -4. Set the following environment variables: +3. Either an existing Knowledge Base OR a search index (to auto-create a KB) + +Environment variables: - AZURE_SEARCH_ENDPOINT: Your Azure AI Search endpoint - - AZURE_SEARCH_API_KEY: (Optional) Your search API key - if not provided, uses DefaultAzureCredential for Entra ID - - AZURE_SEARCH_INDEX_NAME: Your search index name + - AZURE_SEARCH_API_KEY: (Optional) API key - if not provided, uses DefaultAzureCredential - AZURE_AI_PROJECT_ENDPOINT: Your Azure AI Foundry project endpoint - AZURE_AI_MODEL_DEPLOYMENT_NAME: Your model deployment name (e.g., "gpt-4o") + +For using an existing Knowledge Base (recommended): - AZURE_SEARCH_KNOWLEDGE_BASE_NAME: Your Knowledge Base name - - AZURE_OPENAI_RESOURCE_URL: Your Azure OpenAI resource URL (e.g., "https://myresource.openai.azure.com") - Note: This is different from AZURE_AI_PROJECT_ENDPOINT - Knowledge Base needs the OpenAI endpoint for model calls + +For auto-creating a Knowledge Base from an index: + - AZURE_SEARCH_INDEX_NAME: Your search index name + - AZURE_OPENAI_RESOURCE_URL: Azure OpenAI resource URL (e.g., "https://myresource.openai.azure.com") """ # Sample queries to demonstrate agentic RAG @@ -53,31 +57,52 @@ async def main() -> None: # Get configuration from environment search_endpoint = os.environ["AZURE_SEARCH_ENDPOINT"] search_key = os.environ.get("AZURE_SEARCH_API_KEY") - index_name = os.environ["AZURE_SEARCH_INDEX_NAME"] project_endpoint = os.environ["AZURE_AI_PROJECT_ENDPOINT"] model_deployment = os.environ.get("AZURE_AI_MODEL_DEPLOYMENT_NAME", "gpt-4o") - knowledge_base_name = os.environ["AZURE_SEARCH_KNOWLEDGE_BASE_NAME"] - azure_openai_resource_url = os.environ["AZURE_OPENAI_RESOURCE_URL"] + + # Agentic mode requires exactly ONE of: knowledge_base_name OR index_name + # Option 1: Use existing Knowledge Base (recommended) + knowledge_base_name = os.environ.get("AZURE_SEARCH_KNOWLEDGE_BASE_NAME") + # Option 2: Auto-create KB from index (requires azure_openai_resource_url) + index_name = os.environ.get("AZURE_SEARCH_INDEX_NAME") + azure_openai_resource_url = os.environ.get("AZURE_OPENAI_RESOURCE_URL") # Create Azure AI Search context provider with agentic mode (recommended for accuracy) print("Using AGENTIC mode (Knowledge Bases with query planning, recommended)\n") - print("ℹ️ This mode is slightly slower but provides more accurate results.\n") - search_provider = AzureAISearchContextProvider( - endpoint=search_endpoint, - index_name=index_name, - api_key=search_key, # Use api_key for API key auth, or credential for managed identity - credential=AzureCliCredential() if not search_key else None, - mode="agentic", # Advanced mode for multi-hop reasoning - # Agentic mode configuration - azure_ai_project_endpoint=project_endpoint, - azure_openai_resource_url=azure_openai_resource_url, - model_deployment_name=model_deployment, - knowledge_base_name=knowledge_base_name, - # Optional: Configure retrieval behavior - knowledge_base_output_mode="extractive_data", # or "answer_synthesis" - retrieval_reasoning_effort="minimal", # or "medium", "low" - top_k=3, # Note: In agentic mode, the server-side Knowledge Base determines final retrieval - ) + print("This mode is slightly slower but provides more accurate results.\n") + + # Configure based on whether using existing KB or auto-creating from index + if knowledge_base_name: + # Use existing Knowledge Base - simplest approach + search_provider = AzureAISearchContextProvider( + endpoint=search_endpoint, + api_key=search_key, + credential=AzureCliCredential() if not search_key else None, + mode="agentic", + knowledge_base_name=knowledge_base_name, + # Optional: Configure retrieval behavior + knowledge_base_output_mode="extractive_data", # or "answer_synthesis" + retrieval_reasoning_effort="minimal", # or "medium", "low" + ) + else: + # Auto-create Knowledge Base from index + if not index_name: + raise ValueError("Set AZURE_SEARCH_KNOWLEDGE_BASE_NAME or AZURE_SEARCH_INDEX_NAME") + if not azure_openai_resource_url: + raise ValueError("AZURE_OPENAI_RESOURCE_URL required when using index_name") + search_provider = AzureAISearchContextProvider( + endpoint=search_endpoint, + index_name=index_name, + api_key=search_key, + credential=AzureCliCredential() if not search_key else None, + mode="agentic", + azure_openai_resource_url=azure_openai_resource_url, + model_deployment_name=model_deployment, + # Optional: Configure retrieval behavior + knowledge_base_output_mode="extractive_data", # or "answer_synthesis" + retrieval_reasoning_effort="minimal", # or "medium", "low" + top_k=3, + ) # Create agent with search context provider async with ( From d57303cf538f078eda6b8afa651702acdecda416 Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Date: Wed, 3 Dec 2025 16:01:37 +0000 Subject: [PATCH 07/33] Bump Persistent V1 package with bugfixes (#2602) --- dotnet/Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index 5d4a80026e..ee5b244185 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -21,7 +21,7 @@ - + From 931197ba95aacce5b2fcf14727bdbc82e0051cfa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 16:55:04 +0000 Subject: [PATCH 08/33] Bump Aspire.Hosting.AppHost from 13.0.0 to 13.0.1 (#2493) --- updated-dependencies: - dependency-name: Aspire.Hosting.AppHost dependency-version: 13.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Chris <66376200+crickman@users.noreply.github.com> From a81279d96099cd6760a98dde602d48393d5e3493 Mon Sep 17 00:00:00 2001 From: Eduard van Valkenburg Date: Wed, 3 Dec 2025 19:19:17 +0100 Subject: [PATCH 09/33] Python: Pin meta core (#2597) * pin core dependency in the meta package * fix for mcp latest * fix mypy * test fix * test fix * fix tests for content parsing --- python/packages/core/agent_framework/_mcp.py | 141 +++++++++++++------ python/packages/core/tests/core/test_mcp.py | 84 +++-------- python/pyproject.toml | 2 +- python/uv.lock | 60 ++++---- 4 files changed, 154 insertions(+), 133 deletions(-) diff --git a/python/packages/core/agent_framework/_mcp.py b/python/packages/core/agent_framework/_mcp.py index 6e4c71fe49..b4caaea4f5 100644 --- a/python/packages/core/agent_framework/_mcp.py +++ b/python/packages/core/agent_framework/_mcp.py @@ -5,7 +5,7 @@ import logging import re import sys from abc import abstractmethod -from collections.abc import Collection +from collections.abc import Collection, Sequence from contextlib import AsyncExitStack, _AsyncGeneratorContextManager # type: ignore from datetime import timedelta from functools import partial @@ -22,7 +22,16 @@ from mcp.shared.session import RequestResponder from pydantic import BaseModel, Field, create_model from ._tools import AIFunction, HostedMCPSpecificApproval -from ._types import ChatMessage, Contents, DataContent, Role, TextContent, UriContent +from ._types import ( + ChatMessage, + Contents, + DataContent, + FunctionCallContent, + FunctionResultContent, + Role, + TextContent, + UriContent, +) from .exceptions import ToolException, ToolExecutionException if sys.version_info >= (3, 11): @@ -61,7 +70,7 @@ def _mcp_prompt_message_to_chat_message( """Convert a MCP container type to a Agent Framework type.""" return ChatMessage( role=Role(value=mcp_type.role), - contents=[_mcp_type_to_ai_content(mcp_type.content)], + contents=_mcp_type_to_ai_content(mcp_type.content), raw_representation=mcp_type, ) @@ -87,8 +96,7 @@ def _mcp_call_tool_result_to_ai_contents( A list of Agent Framework content items with metadata merged into additional_properties. """ - # Extract _meta field using getattr for compatibility - meta_data = getattr(mcp_type, "_meta", None) + meta_data = mcp_type.meta # Prepare merged metadata once if present merged_meta_props = None @@ -104,53 +112,104 @@ def _mcp_call_tool_result_to_ai_contents( # Convert each content item and merge metadata result_contents = [] for item in mcp_type.content: - content = _mcp_type_to_ai_content(item) + contents = _mcp_type_to_ai_content(item) if merged_meta_props: - existing_props = getattr(content, "additional_properties", None) or {} - # Merge with content-specific properties, letting content-specific props override - final_props = merged_meta_props.copy() - final_props.update(existing_props) - content.additional_properties = final_props - result_contents.append(content) - + for content in contents: + existing_props = getattr(content, "additional_properties", None) or {} + # Merge with content-specific properties, letting content-specific props override + final_props = merged_meta_props.copy() + final_props.update(existing_props) + content.additional_properties = final_props + result_contents.extend(contents) return result_contents def _mcp_type_to_ai_content( - mcp_type: types.ImageContent | types.TextContent | types.AudioContent | types.EmbeddedResource | types.ResourceLink, -) -> Contents: + mcp_type: types.ImageContent + | types.TextContent + | types.AudioContent + | types.EmbeddedResource + | types.ResourceLink + | types.ToolUseContent + | types.ToolResultContent + | Sequence[ + types.ImageContent + | types.TextContent + | types.AudioContent + | types.EmbeddedResource + | types.ResourceLink + | types.ToolUseContent + | types.ToolResultContent + ], +) -> list[Contents]: """Convert a MCP type to a Agent Framework type.""" - match mcp_type: - case types.TextContent(): - return TextContent(text=mcp_type.text, raw_representation=mcp_type) - case types.ImageContent() | types.AudioContent(): - return DataContent( - uri=mcp_type.data, - media_type=mcp_type.mimeType, - raw_representation=mcp_type, - ) - case types.ResourceLink(): - return UriContent( - uri=str(mcp_type.uri), - media_type=mcp_type.mimeType or "application/json", - raw_representation=mcp_type, - ) - case _: - match mcp_type.resource: - case types.TextResourceContents(): - return TextContent( - text=mcp_type.resource.text, + mcp_types = mcp_type if isinstance(mcp_type, Sequence) else [mcp_type] + return_types: list[Contents] = [] + for mcp_type in mcp_types: + match mcp_type: + case types.TextContent(): + return_types.append(TextContent(text=mcp_type.text, raw_representation=mcp_type)) + case types.ImageContent() | types.AudioContent(): + return_types.append( + DataContent( + uri=mcp_type.data, + media_type=mcp_type.mimeType, raw_representation=mcp_type, - additional_properties=(mcp_type.annotations.model_dump() if mcp_type.annotations else None), ) - case types.BlobResourceContents(): - return DataContent( - uri=mcp_type.resource.blob, - media_type=mcp_type.resource.mimeType, + ) + case types.ResourceLink(): + return_types.append( + UriContent( + uri=str(mcp_type.uri), + media_type=mcp_type.mimeType or "application/json", raw_representation=mcp_type, - additional_properties=(mcp_type.annotations.model_dump() if mcp_type.annotations else None), ) + ) + case types.ToolUseContent(): + return_types.append( + FunctionCallContent( + call_id=mcp_type.id, + name=mcp_type.name, + arguments=mcp_type.input, + raw_representation=mcp_type, + ) + ) + case types.ToolResultContent(): + return_types.append( + FunctionResultContent( + call_id=mcp_type.toolUseId, + result=_mcp_type_to_ai_content(mcp_type.content) + if mcp_type.content + else mcp_type.structuredContent, + exception=Exception() if mcp_type.isError else None, + raw_representation=mcp_type, + ) + ) + case types.EmbeddedResource(): + match mcp_type.resource: + case types.TextResourceContents(): + return_types.append( + TextContent( + text=mcp_type.resource.text, + raw_representation=mcp_type, + additional_properties=( + mcp_type.annotations.model_dump() if mcp_type.annotations else None + ), + ) + ) + case types.BlobResourceContents(): + return_types.append( + DataContent( + uri=mcp_type.resource.blob, + media_type=mcp_type.resource.mimeType, + raw_representation=mcp_type, + additional_properties=( + mcp_type.annotations.model_dump() if mcp_type.annotations else None + ), + ) + ) + return return_types def _ai_content_to_mcp_types( diff --git a/python/packages/core/tests/core/test_mcp.py b/python/packages/core/tests/core/test_mcp.py index b73136fd6b..264ff1929b 100644 --- a/python/packages/core/tests/core/test_mcp.py +++ b/python/packages/core/tests/core/test_mcp.py @@ -91,9 +91,10 @@ def test_mcp_call_tool_result_to_ai_contents(): def test_mcp_call_tool_result_with_meta_error(): """Test conversion from MCP tool result with _meta field containing isError=True.""" # Create a mock CallToolResult with _meta field containing error information - mcp_result = types.CallToolResult(content=[types.TextContent(type="text", text="Error occurred")]) - # Simulate _meta field with isError=True - mcp_result._meta = {"isError": True, "errorCode": "TOOL_ERROR", "errorMessage": "Tool execution failed"} + mcp_result = types.CallToolResult( + content=[types.TextContent(type="text", text="Error occurred")], + _meta={"isError": True, "errorCode": "TOOL_ERROR", "errorMessage": "Tool execution failed"}, + ) ai_contents = _mcp_call_tool_result_to_ai_contents(mcp_result) @@ -115,15 +116,16 @@ def test_mcp_call_tool_result_with_meta_arbitrary_data(): MCP server chooses to provide. This test uses example metadata to verify that whatever is provided gets preserved in additional_properties. """ - mcp_result = types.CallToolResult(content=[types.TextContent(type="text", text="Success result")]) - # Example _meta field - different MCP servers may provide completely different structures - mcp_result._meta = { - "serverVersion": "2.1.0", - "executionId": "exec_abc123", - "metrics": {"responseTime": 1.25, "memoryUsed": "64MB"}, - "source": "example-mcp-server", - "customField": "arbitrary_value", - } + mcp_result = types.CallToolResult( + content=[types.TextContent(type="text", text="Success result")], + _meta={ + "serverVersion": "2.1.0", + "executionId": "exec_abc123", + "metrics": {"responseTime": 1.25, "memoryUsed": "64MB"}, + "source": "example-mcp-server", + "customField": "arbitrary_value", + }, + ) ai_contents = _mcp_call_tool_result_to_ai_contents(mcp_result) @@ -145,8 +147,7 @@ def test_mcp_call_tool_result_with_meta_merging_existing_properties(): """Test that _meta data merges correctly with existing additional_properties.""" # Create content with existing additional_properties text_content = types.TextContent(type="text", text="Test content") - mcp_result = types.CallToolResult(content=[text_content]) - mcp_result._meta = {"newField": "newValue", "isError": False} + mcp_result = types.CallToolResult(content=[text_content], _meta={"newField": "newValue", "isError": False}) ai_contents = _mcp_call_tool_result_to_ai_contents(mcp_result) @@ -159,30 +160,6 @@ def test_mcp_call_tool_result_with_meta_merging_existing_properties(): assert content.additional_properties["isError"] is False -def test_mcp_call_tool_result_with_meta_object_attributes(): - """Test conversion when _meta is an object with attributes rather than a dict.""" - - class MetaObject: - def __init__(self): - self.isError = True - self.requestId = "req-12345" - self.executionTime = 2.5 - - mcp_result = types.CallToolResult(content=[types.TextContent(type="text", text="Object meta test")]) - mcp_result._meta = MetaObject() - - ai_contents = _mcp_call_tool_result_to_ai_contents(mcp_result) - - assert len(ai_contents) == 1 - content = ai_contents[0] - - # Check that object attributes are extracted correctly - assert content.additional_properties is not None - assert content.additional_properties["isError"] is True - assert content.additional_properties["requestId"] == "req-12345" - assert content.additional_properties["executionTime"] == 2.5 - - def test_mcp_call_tool_result_with_meta_none(): """Test that missing _meta field is handled gracefully.""" mcp_result = types.CallToolResult(content=[types.TextContent(type="text", text="No meta test")]) @@ -200,21 +177,6 @@ def test_mcp_call_tool_result_with_meta_none(): assert props is None or props == {} -def test_mcp_call_tool_result_with_meta_non_dict_value(): - """Test conversion when _meta contains a non-dict value.""" - mcp_result = types.CallToolResult(content=[types.TextContent(type="text", text="Non-dict meta test")]) - mcp_result._meta = "simple string meta" - - ai_contents = _mcp_call_tool_result_to_ai_contents(mcp_result) - - assert len(ai_contents) == 1 - content = ai_contents[0] - - # Non-dict _meta should be stored under '_meta' key - assert content.additional_properties is not None - assert content.additional_properties["_meta"] == "simple string meta" - - def test_mcp_call_tool_result_regression_successful_workflow(): """Regression test to ensure existing successful workflows remain unchanged.""" # Test the original successful workflow still works @@ -247,7 +209,7 @@ def test_mcp_call_tool_result_regression_successful_workflow(): def test_mcp_content_types_to_ai_content_text(): """Test conversion of MCP text content to AI content.""" mcp_content = types.TextContent(type="text", text="Sample text") - ai_content = _mcp_type_to_ai_content(mcp_content) + ai_content = _mcp_type_to_ai_content(mcp_content)[0] assert isinstance(ai_content, TextContent) assert ai_content.text == "Sample text" @@ -257,7 +219,7 @@ def test_mcp_content_types_to_ai_content_text(): def test_mcp_content_types_to_ai_content_image(): """Test conversion of MCP image content to AI content.""" mcp_content = types.ImageContent(type="image", data="data:image/jpeg;base64,abc", mimeType="image/jpeg") - ai_content = _mcp_type_to_ai_content(mcp_content) + ai_content = _mcp_type_to_ai_content(mcp_content)[0] assert isinstance(ai_content, DataContent) assert ai_content.uri == "data:image/jpeg;base64,abc" @@ -268,7 +230,7 @@ def test_mcp_content_types_to_ai_content_image(): def test_mcp_content_types_to_ai_content_audio(): """Test conversion of MCP audio content to AI content.""" mcp_content = types.AudioContent(type="audio", data="data:audio/wav;base64,def", mimeType="audio/wav") - ai_content = _mcp_type_to_ai_content(mcp_content) + ai_content = _mcp_type_to_ai_content(mcp_content)[0] assert isinstance(ai_content, DataContent) assert ai_content.uri == "data:audio/wav;base64,def" @@ -284,7 +246,7 @@ def test_mcp_content_types_to_ai_content_resource_link(): name="test_resource", mimeType="application/json", ) - ai_content = _mcp_type_to_ai_content(mcp_content) + ai_content = _mcp_type_to_ai_content(mcp_content)[0] assert isinstance(ai_content, UriContent) assert ai_content.uri == "https://example.com/resource" @@ -300,7 +262,7 @@ def test_mcp_content_types_to_ai_content_embedded_resource_text(): text="Embedded text content", ) mcp_content = types.EmbeddedResource(type="resource", resource=text_resource) - ai_content = _mcp_type_to_ai_content(mcp_content) + ai_content = _mcp_type_to_ai_content(mcp_content)[0] assert isinstance(ai_content, TextContent) assert ai_content.text == "Embedded text content" @@ -316,7 +278,7 @@ def test_mcp_content_types_to_ai_content_embedded_resource_blob(): blob="data:application/octet-stream;base64,dGVzdCBkYXRh", ) mcp_content = types.EmbeddedResource(type="resource", resource=blob_resource) - ai_content = _mcp_type_to_ai_content(mcp_content) + ai_content = _mcp_type_to_ai_content(mcp_content)[0] assert isinstance(ai_content, DataContent) assert ai_content.uri == "data:application/octet-stream;base64,dGVzdCBkYXRh" @@ -650,9 +612,9 @@ async def test_mcp_tool_call_tool_with_meta_integration(): # Create a CallToolResult with _meta field tool_result = types.CallToolResult( - content=[types.TextContent(type="text", text="Tool executed with metadata")] + content=[types.TextContent(type="text", text="Tool executed with metadata")], + _meta={"executionTime": 1.5, "cost": {"usd": 0.002}, "isError": False, "toolVersion": "1.2.3"}, ) - tool_result._meta = {"executionTime": 1.5, "cost": {"usd": 0.002}, "isError": False, "toolVersion": "1.2.3"} self.session.call_tool = AsyncMock(return_value=tool_result) diff --git a/python/pyproject.toml b/python/pyproject.toml index 3547801c71..0fc9c382a5 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "agent-framework-core[all]", + "agent-framework-core[all]==1.0.0b251120", ] [dependency-groups] diff --git a/python/uv.lock b/python/uv.lock index 6308986fc2..b262b669eb 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -1327,7 +1327,7 @@ name = "clr-loader" version = "0.2.8" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, + { name = "cffi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e8/88/9e0a80d59b28d394aad5d736bd47e5aa5883cf1d3674b313ba93e2a353e4/clr_loader-0.2.8.tar.gz", hash = "sha256:b4cd3a2ee5f0489885ef07ffd87eb38b2cee24ca65dcacea97b34e7b59913814", size = 61502, upload-time = "2025-10-20T21:03:16.548Z" } wheels = [ @@ -1824,7 +1824,7 @@ wheels = [ [[package]] name = "fastapi" -version = "0.123.0" +version = "0.123.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -1832,9 +1832,9 @@ dependencies = [ { name = "starlette", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "typing-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/c7/d3956d7c2da2b66188eacc8db0919635b28313a30334dd78cba4c366caf0/fastapi-0.123.0.tar.gz", hash = "sha256:1410678b3c44418245eec85088b15140d894074b86e66061017e2b492c09b138", size = 347702, upload-time = "2025-11-30T14:49:17.848Z" } +sdist = { url = "https://files.pythonhosted.org/packages/78/b8/c6e916565af8a8e1c8f5a4736b31a6995adb51dbd4cbefc8b022e753ecb9/fastapi-0.123.5.tar.gz", hash = "sha256:54bbb660ca231d3985474498b51c621ddcf8888d9a4c1ecb10aa40ec217e4965", size = 352030, upload-time = "2025-12-02T21:08:38.532Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/17/62c82beab6536ea72576f90b84a3dbe6bcceb88d3d46afc4d05c376f0231/fastapi-0.123.0-py3-none-any.whl", hash = "sha256:cb56e69e874afa897bd3416c8a3dbfdae1730d0a308d4c63303f3f4b44136ae4", size = 110865, upload-time = "2025-11-30T14:49:16.164Z" }, + { url = "https://files.pythonhosted.org/packages/c2/dc/faa52fe784892bb057934248ded02705d26ca3aca562876e61c239947036/fastapi-0.123.5-py3-none-any.whl", hash = "sha256:a9c708e47c0fa424139cddb8601d0f92d3111b77843c22e9c8d0164d65fe3c97", size = 111316, upload-time = "2025-12-02T21:08:36.191Z" }, ] [[package]] @@ -3282,7 +3282,7 @@ wheels = [ [[package]] name = "mcp" -version = "1.22.0" +version = "1.23.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, @@ -3300,9 +3300,9 @@ dependencies = [ { name = "typing-inspection", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, { name = "uvicorn", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/a2/c5ec0ab38b35ade2ae49a90fada718fbc76811dc5aa1760414c6aaa6b08a/mcp-1.22.0.tar.gz", hash = "sha256:769b9ac90ed42134375b19e777a2858ca300f95f2e800982b3e2be62dfc0ba01", size = 471788, upload-time = "2025-11-20T20:11:28.095Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/42/10c0c09ca27aceacd8c428956cfabdd67e3d328fe55c4abc16589285d294/mcp-1.23.1.tar.gz", hash = "sha256:7403e053e8e2283b1e6ae631423cb54736933fea70b32422152e6064556cd298", size = 596519, upload-time = "2025-12-02T18:41:12.807Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/bb/711099f9c6bb52770f56e56401cdfb10da5b67029f701e0df29362df4c8e/mcp-1.22.0-py3-none-any.whl", hash = "sha256:bed758e24df1ed6846989c909ba4e3df339a27b4f30f1b8b627862a4bade4e98", size = 175489, upload-time = "2025-11-20T20:11:26.542Z" }, + { url = "https://files.pythonhosted.org/packages/9f/9e/26e1d2d2c6afe15dfba5ca6799eeeea7656dce625c22766e4c57305e9cc2/mcp-1.23.1-py3-none-any.whl", hash = "sha256:3ce897fcc20a41bd50b4c58d3aa88085f11f505dcc0eaed48930012d34c731d8", size = 231433, upload-time = "2025-12-02T18:41:11.195Z" }, ] [package.optional-dependencies] @@ -4452,8 +4452,8 @@ name = "powerfx" version = "0.0.33" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, - { name = "pythonnet", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, + { name = "cffi", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, + { name = "pythonnet", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5e/41/8f95f72f4f3b7ea54357c449bf5bd94813b6321dec31db9ffcbf578e2fa3/powerfx-0.0.33.tar.gz", hash = "sha256:85e8330bef8a7a207c3e010aa232df0ae38825e94d590c73daf3a3f44115cb09", size = 3236647, upload-time = "2025-11-20T19:31:09.414Z" } wheels = [ @@ -5122,7 +5122,7 @@ name = "pythonnet" version = "3.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "clr-loader", marker = "(python_full_version < '3.14' and sys_platform == 'darwin') or (python_full_version < '3.14' and sys_platform == 'linux') or (python_full_version < '3.14' and sys_platform == 'win32')" }, + { name = "clr-loader", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9a/d6/1afd75edd932306ae9bd2c2d961d603dc2b52fcec51b04afea464f1f6646/pythonnet-3.0.5.tar.gz", hash = "sha256:48e43ca463941b3608b32b4e236db92d8d40db4c58a75ace902985f76dac21cf", size = 239212, upload-time = "2024-12-13T08:30:44.393Z" } wheels = [ @@ -6457,28 +6457,28 @@ wheels = [ [[package]] name = "uv" -version = "0.9.14" +version = "0.9.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/93/77/36977111f1a7a6625ba432959958d284124d5cda678b703b77f1e1c8e8b8/uv-0.9.14.tar.gz", hash = "sha256:e62ae030bb607abe9c2d6d2569c696804fa668a3b176d7cce20cfa1c66012855", size = 3766833, upload-time = "2025-12-01T17:22:51.155Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/b8/63e4ad24d7ef24ef1de10cb2db9ff0f74b2cceb4bd71c4b3909297d40967/uv-0.9.15.tar.gz", hash = "sha256:241a57d8ce90273d0ad8460897e1b2250bd4aa6bafe72fd8be07fbc3a7662f3d", size = 3788825, upload-time = "2025-12-03T01:33:06.404Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/f8/05876ea28ef1edd4a4dbcd5c9e44daa7d056bad25b0f114305a49457baa7/uv-0.9.14-py3-none-linux_armv6l.whl", hash = "sha256:876d0cf2a92113e1237ef71a7dc21e2cc82ab0664f98004d61abeb05c944ffd2", size = 20844416, upload-time = "2025-12-01T17:22:42.25Z" }, - { url = "https://files.pythonhosted.org/packages/67/c0/c88a40306b2d437704e25f19890da1f6f9b42cbe1695de0373e3ca1258d8/uv-0.9.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e14853fb7781251f75cbb200fa2a81f2ac087a7f0647ee8699689198d6496f05", size = 19981109, upload-time = "2025-12-01T17:22:07.422Z" }, - { url = "https://files.pythonhosted.org/packages/f0/65/6ba20daba11fc88d41cb03fe903d8440618f6033fba511f34c7bd9df02ad/uv-0.9.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:dd90bc5e364a2fdc89499de9c1cffe9036b0318e54644b5664a9c395bb21bb29", size = 18469837, upload-time = "2025-12-01T17:22:19.014Z" }, - { url = "https://files.pythonhosted.org/packages/e9/a1/245dfacce0e2755b82b00dc5fbaea4a690e3fb7046a779c1fd719896f04b/uv-0.9.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:c086218fe1f3f88281d2f881bbeb5ada062eb4ea5d28292f352e45de38aa125a", size = 20347846, upload-time = "2025-12-01T17:22:35.144Z" }, - { url = "https://files.pythonhosted.org/packages/03/0d/9314fd85e8ab574c9433b014d49fe233cd8e0ae38274cc5716a9f8291f5e/uv-0.9.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6dc4d37a593e2843df96a32be4cdab682e7abb15552c967277ac29fe8e556cdb", size = 20441070, upload-time = "2025-12-01T17:22:46.793Z" }, - { url = "https://files.pythonhosted.org/packages/b2/e9/1eab4b0e3b7eb6823a927a86bf34e3e0086c6321d794da4fafc1e168373c/uv-0.9.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7770890958273fe5f6222857be0981e06808f531a2d28cc8da5907b3036fa7dd", size = 21636744, upload-time = "2025-12-01T17:22:28.272Z" }, - { url = "https://files.pythonhosted.org/packages/4f/ca/3a25e8bce4402d410bdbe5dc327eb9cf1e441f29cde73a7838816b23a14b/uv-0.9.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2a1724160ab2429317ab7d340f95d34c93a4830fef7f2d952795754837fb2e2c", size = 23033527, upload-time = "2025-12-01T17:22:30.643Z" }, - { url = "https://files.pythonhosted.org/packages/39/44/c3e3ac7e80de643aa34fc70661f668a121fb48cc515e0a263daaf24e92cb/uv-0.9.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:180496428c669244e6af4b4b05f3c450d7976367b4907312d609890a2ee03be5", size = 22666761, upload-time = "2025-12-01T17:22:13.771Z" }, - { url = "https://files.pythonhosted.org/packages/b9/c7/14eddd397d6333673b1dc15f4f13548afae191b3dbf5a40d25bbf12c2789/uv-0.9.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:144bad91b4c4efd7104e219beab4a238ccf560a87323128f0d6471b85c08915e", size = 21653308, upload-time = "2025-12-01T17:22:21.358Z" }, - { url = "https://files.pythonhosted.org/packages/38/9e/0ddb21e94fc7fd67547e74aa0cbb042d57f52fe283f3d517d1a8c9e5df66/uv-0.9.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b5a27f528af437d9cd7bd85905095f166d0c37bdf3404a8a900948068e03d6b", size = 21690920, upload-time = "2025-12-01T17:22:32.777Z" }, - { url = "https://files.pythonhosted.org/packages/17/35/44a7aeafc1cc9b1ec55ab433bed0211c34ca77f230853735c6c8d8683783/uv-0.9.14-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:cbf18113f0e07898af804f6f4a9ef521eb181865a94b7d162431dcae5b55f8fa", size = 20467749, upload-time = "2025-12-01T17:22:11.23Z" }, - { url = "https://files.pythonhosted.org/packages/dd/f8/6b087904c897f2e96c69c9386fdefbd6c5fdeecab6624c5e972a0e31dd91/uv-0.9.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:18231f386e3f153e9560f535bd224b618f4990c4f417504f915fe95fc5513448", size = 21513786, upload-time = "2025-12-01T17:22:25.953Z" }, - { url = "https://files.pythonhosted.org/packages/a1/4b/1959897d40affc078eca5812db6bdef0a331e594e8907d336db2e90d0252/uv-0.9.14-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:e2cd3e885e4c30048f9c2c526bd340f6e082ca5fb6bf4516c90671a114746fc3", size = 20406081, upload-time = "2025-12-01T17:22:23.66Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ce/e7e27f7891e38c98f5c83b3c8068c6265f5dc96c12924f2a0fc31b4eb7ac/uv-0.9.14-py3-none-musllinux_1_1_i686.whl", hash = "sha256:da227183ab9860832533e7f152a83d0d749f8d0156348b68f48773d42f690ff1", size = 20965537, upload-time = "2025-12-01T17:22:16.582Z" }, - { url = "https://files.pythonhosted.org/packages/71/44/b9cdb4137338b33a419ff4aff70ac00df4a5a68e1b9bd21a59f96caf6c6f/uv-0.9.14-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:70a55f189b2d9ec035194c927f2c0b4f746b251e329a5dc8391ab6a41fe14e1a", size = 21919764, upload-time = "2025-12-01T17:22:37.369Z" }, - { url = "https://files.pythonhosted.org/packages/66/57/2e294c6d758b48883434ad979e089cfe5ec87584ec7ffee005be359f6035/uv-0.9.14-py3-none-win32.whl", hash = "sha256:06923d5ee88b50dabb364c4fcc2a0de84e079b6a2fb6cc6ca318e74e979affed", size = 19742562, upload-time = "2025-12-01T17:22:49.106Z" }, - { url = "https://files.pythonhosted.org/packages/f0/2f/81d551db61228adb062ff29dec7634d82091e38f579d56ed27db40bd300e/uv-0.9.14-py3-none-win_amd64.whl", hash = "sha256:c0f18fd246726cdc194357aca50fd13153d719daecd765049f0ff4c2262143d3", size = 21655524, upload-time = "2025-12-01T17:22:39.643Z" }, - { url = "https://files.pythonhosted.org/packages/ef/91/deb722a8ddb076018aee02ab3bffcdda6f10b7ca96f72aeca06b5efaccec/uv-0.9.14-py3-none-win_arm64.whl", hash = "sha256:d974fcbec84aa7eb4ee1cc7e650a5b8973895a03f6d6f0c61b488e1d1b8179ea", size = 20121260, upload-time = "2025-12-01T17:22:44.502Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e7/5c7fcf7a49884273c18a54b68ffd5f05775cb0e59aeeb2801f2b6d31787b/uv-0.9.15-py3-none-linux_armv6l.whl", hash = "sha256:4ccf2aa7f2e0fcb553dccc8badceb2fc533000e5baf144fd982bb9be88b304b8", size = 20936584, upload-time = "2025-12-03T01:32:58.983Z" }, + { url = "https://files.pythonhosted.org/packages/30/a8/5c376318f57dd4fc58bb01db870f9e564a689d479ddc0440731933286740/uv-0.9.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:944cd6d500974f9693994ec990c5b263a185e66cbc1cbd230f319445f8330e4e", size = 20000398, upload-time = "2025-12-03T01:33:04.518Z" }, + { url = "https://files.pythonhosted.org/packages/ca/c7/6af7e5dc21902eb936a54c037650f070cea015d742b3a49e82f53e6f32c4/uv-0.9.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ec4716fee5db65cfc78016d07f6fddb9b1fa29a0471fe67fe6676e2befee3215", size = 18440619, upload-time = "2025-12-03T01:32:56.388Z" }, + { url = "https://files.pythonhosted.org/packages/88/73/8364801f678ba58d1a31ec9c8e0bfc407b48c0c57e0e3618e8fbf6b285f6/uv-0.9.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:2b9ad2581a90b7b2ed3f5a3b2657c3cf84119bdf101e1a1c49a2f38577eecb1b", size = 20326432, upload-time = "2025-12-03T01:32:53.805Z" }, + { url = "https://files.pythonhosted.org/packages/89/ed/9c13d81005b00dcc759f7ea4b640b1efff8ecebbf852df90c2f237373ed5/uv-0.9.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d6a837537294e283ddbb18c494dbc085afca3e29d2176059140b7b4e94e485fd", size = 20531552, upload-time = "2025-12-03T01:33:18.894Z" }, + { url = "https://files.pythonhosted.org/packages/b1/24/99c26056300c83f0d542272c02465937965191739d44bf8654d09d2d296f/uv-0.9.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:214013314564347ad629a5cfcd28b962038fc23c72155d7a798313f6b9f64f81", size = 21428020, upload-time = "2025-12-03T01:33:10.373Z" }, + { url = "https://files.pythonhosted.org/packages/a2/4a/15dd32d695eae71cb4c09af6e9cbde703674824653c9f2963b068108b344/uv-0.9.15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b32c1ac11ea80ab4cc6ea61029880e1319041d2b3783f2478c8eadfc9a9c9d5a", size = 23061719, upload-time = "2025-12-03T01:32:48.914Z" }, + { url = "https://files.pythonhosted.org/packages/51/02/83c179d6a5cfee0c437dd1d73b515557c6b2b7ab19fb9421420c13b10bc8/uv-0.9.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64d99dfe8af75fed7fd6b7f6a8a81ba26ac88e046c5cb366184c2b53b023b070", size = 22609336, upload-time = "2025-12-03T01:32:41.645Z" }, + { url = "https://files.pythonhosted.org/packages/a3/77/c620febe2662ab1897c2ef7c95a5424517efc456b77a1f75f6da81b4e542/uv-0.9.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5375c95a70fc76104fc178771cd4a8db1dab55345c7162c3d2d47ca2993c4bb", size = 21663700, upload-time = "2025-12-03T01:32:36.335Z" }, + { url = "https://files.pythonhosted.org/packages/70/1b/4273d02565a4e86f238e9fee23e6c5c3bb7b5c237c63228e1b72451db224/uv-0.9.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9c1cefdd6e878baa32ff7a7d1ef463690750ff45d899fbb6bd6705e8329b00a", size = 21714799, upload-time = "2025-12-03T01:33:01.785Z" }, + { url = "https://files.pythonhosted.org/packages/61/ae/29787af8124d821c1a88bb66612c24ff6180309d35348a4915214c5078d3/uv-0.9.15-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:b6d342a4610e7dbc3450c8000313ee7693905ddee12a65a34fdbcd8418a852a5", size = 20454218, upload-time = "2025-12-03T01:33:14.113Z" }, + { url = "https://files.pythonhosted.org/packages/56/fe/ab906a1e530f0fbb7042e82057d7c08ef46131deb75d5fef2133de6725c5/uv-0.9.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:dea4fd678d48b9790234de43b2e5e4a445f9541fd361db7dc1a2cac861c9717a", size = 21549370, upload-time = "2025-12-03T01:32:46.766Z" }, + { url = "https://files.pythonhosted.org/packages/19/11/20ea6dc5ca56f2ad9a8f1b5674f84c38a45a28ccf880aa7c1abb226355a6/uv-0.9.15-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:bae65f4748cacc20ea7f93b94a9ba82dd5505a98cf61e33d75e2669c6aefbfc5", size = 20502715, upload-time = "2025-12-03T01:32:51.454Z" }, + { url = "https://files.pythonhosted.org/packages/37/38/ed98b5f48be2aeb63dc8853bc777b739c18277d0b950196b4d46f7b4d74a/uv-0.9.15-py3-none-musllinux_1_1_i686.whl", hash = "sha256:5473c1095a697b7c7996a312232f78e81cf71e5afc30c889e46a486fe298d853", size = 20890379, upload-time = "2025-12-03T01:32:28.035Z" }, + { url = "https://files.pythonhosted.org/packages/dd/50/a97468ed93b80a50ea97a717662be4b841e7149bc68197ded087bcdd9e36/uv-0.9.15-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:a0ef2e2bcf807aebd70e3a87bde618d0a01f07f627f5da0b0b457d7be2124843", size = 21921653, upload-time = "2025-12-03T01:32:31.016Z" }, + { url = "https://files.pythonhosted.org/packages/94/e8/ad85878d4e789c40b75c9f670eba52c7e5e63393f31e1c1a7246849595a2/uv-0.9.15-py3-none-win32.whl", hash = "sha256:5efa39d9085f918d17869e43471ccd4526372e71d945b8d7cd3c8867ce6eab33", size = 19762699, upload-time = "2025-12-03T01:32:39.104Z" }, + { url = "https://files.pythonhosted.org/packages/bc/37/e15a46f880b2c230f7d6934f046825ebe547f90008984c861e84e2ef34c4/uv-0.9.15-py3-none-win_amd64.whl", hash = "sha256:e46f36c80353fb406d4c0fb2cfe1953b4a01bfb3fc5b88dd4f763641b8899f1a", size = 21758441, upload-time = "2025-12-03T01:32:33.722Z" }, + { url = "https://files.pythonhosted.org/packages/9a/fe/af3595c25dfaa85daff11ed0c8ca0fc1d088a426c65ef93d3751490673fd/uv-0.9.15-py3-none-win_arm64.whl", hash = "sha256:8f14456f357ebbf3f494ae5af41e9b306ba66ecc81cb2304095b38d99a1f7d28", size = 20203340, upload-time = "2025-12-03T01:32:44.184Z" }, ] [[package]] From 3cfd34836f8f60ce7ebe98f04172bd2c96fe6b07 Mon Sep 17 00:00:00 2001 From: mdxw73 <57395659+mdxw73@users.noreply.github.com> Date: Wed, 3 Dec 2025 19:54:05 +0000 Subject: [PATCH 10/33] Python: Update _provider.py (#2548) * Update _provider.py * Update test_mem0_context_provider.py * Update test_mem0_context_provider.py Ruff linting fixes * Update test_mem0_context_provider.py --------- Co-authored-by: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> --- .../mem0/agent_framework_mem0/_provider.py | 4 +++ .../mem0/tests/test_mem0_context_provider.py | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/python/packages/mem0/agent_framework_mem0/_provider.py b/python/packages/mem0/agent_framework_mem0/_provider.py index 3c962d58c2..6d726f3a0f 100644 --- a/python/packages/mem0/agent_framework_mem0/_provider.py +++ b/python/packages/mem0/agent_framework_mem0/_provider.py @@ -146,6 +146,10 @@ class Mem0Provider(ContextProvider): messages_list = [messages] if isinstance(messages, ChatMessage) else list(messages) input_text = "\n".join(msg.text for msg in messages_list if msg and msg.text and msg.text.strip()) + # Validate input text is not empty before searching (possible for function approval responses) + if not input_text.strip(): + return Context(messages=None) + search_response: MemorySearchResponse_v1_1 | MemorySearchResponse_v2 = await self.mem0_client.search( # type: ignore[misc] query=input_text, user_id=self.user_id, diff --git a/python/packages/mem0/tests/test_mem0_context_provider.py b/python/packages/mem0/tests/test_mem0_context_provider.py index 324a655bf3..4c1be141dc 100644 --- a/python/packages/mem0/tests/test_mem0_context_provider.py +++ b/python/packages/mem0/tests/test_mem0_context_provider.py @@ -406,6 +406,32 @@ class TestMem0ProviderModelInvoking: assert isinstance(context, Context) assert not context.messages + async def test_model_invoking_function_approval_response_returns_none_instructions( + self, mock_mem0_client: AsyncMock + ) -> None: + """Test invoking with function approval response content messages returns context with None instructions.""" + from agent_framework import FunctionApprovalResponseContent, FunctionCallContent + + provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) + function_call = FunctionCallContent(call_id="1", name="test_func", arguments='{"arg1": "value1"}') + message = ChatMessage( + role=Role.USER, + contents=[ + FunctionApprovalResponseContent( + id="approval_1", + function_call=function_call, + approved=True, + ) + ], + ) + + mock_mem0_client.search.return_value = [] + + context = await provider.invoking(message) + + assert isinstance(context, Context) + assert not context.messages + async def test_model_invoking_filters_empty_message_text(self, mock_mem0_client: AsyncMock) -> None: """Test that empty message text is filtered out from query.""" provider = Mem0Provider(user_id="user123", mem0_client=mock_mem0_client) From 6835161f2d4cadec865f467a0d1a2a3d94432797 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 20:06:21 +0000 Subject: [PATCH 11/33] .NET: Fix flaky vector store integration tests - race condition on indexing (#2586) * Initial plan * Add vector store indexing wait helpers to fix flaky tests Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Use proper VectorStoreStatus struct comparison in Azure tests Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Remove unused System.ClientModel import Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Remove excessive comments and unnecessary status variable per code review Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> --- .../AzureAIAgentsPersistentCreateTests.cs | 42 ++++++++++++++++++ .../OpenAIAssistantClientExtensionsTests.cs | 43 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs index 17fad1581f..a10cc11d79 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Diagnostics; using System.IO; using System.Threading.Tasks; using AgentConformance.IntegrationTests.Support; @@ -108,6 +109,9 @@ public class AzureAIAgentsPersistentCreateTests ); var vectorStoreMetadata = await this._persistentAgentsClient.VectorStores.CreateVectorStoreAsync([uploadedAgentFile.Id], name: "WordCodeLookup_VectorStore"); + // Wait for vector store indexing to complete before using it + await this.WaitForVectorStoreReadyAsync(this._persistentAgentsClient, vectorStoreMetadata.Value.Id); + // Act. var agent = createMechanism switch { @@ -292,4 +296,42 @@ public class AzureAIAgentsPersistentCreateTests await this._persistentAgentsClient.Administration.DeleteAgentAsync(agent.Id); } } + + /// + /// Waits for a vector store to complete indexing by polling its status. + /// + /// The persistent agents client. + /// The ID of the vector store. + /// Maximum time to wait in seconds (default: 30). + /// A task that completes when the vector store is ready or throws on timeout/failure. + private async Task WaitForVectorStoreReadyAsync( + PersistentAgentsClient client, + string vectorStoreId, + int maxWaitSeconds = 30) + { + Stopwatch sw = Stopwatch.StartNew(); + while (sw.Elapsed.TotalSeconds < maxWaitSeconds) + { + PersistentAgentsVectorStore vectorStore = await client.VectorStores.GetVectorStoreAsync(vectorStoreId); + + if (vectorStore.Status == VectorStoreStatus.Completed) + { + if (vectorStore.FileCounts.Failed > 0) + { + throw new InvalidOperationException("Vector store indexing failed for some files"); + } + + return; + } + + if (vectorStore.Status == VectorStoreStatus.Expired) + { + throw new InvalidOperationException("Vector store has expired"); + } + + await Task.Delay(1000); + } + + throw new TimeoutException($"Vector store did not complete indexing within {maxWaitSeconds}s"); + } } diff --git a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantClientExtensionsTests.cs b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantClientExtensionsTests.cs index d187d85b78..f1373bd98b 100644 --- a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantClientExtensionsTests.cs +++ b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantClientExtensionsTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Diagnostics; using System.IO; using System.Threading.Tasks; using AgentConformance.IntegrationTests.Support; @@ -173,6 +174,9 @@ public class OpenAIAssistantClientExtensionsTests }); string vectorStoreId = vectorStoreCreate.Value.Id; + // Wait for vector store indexing to complete before using it + await WaitForVectorStoreReadyAsync(vectorStoreClient, vectorStoreId); + var fileSearchTool = new HostedFileSearchTool() { Inputs = [new HostedVectorStoreContent(vectorStoreId)] }; var agent = createMechanism switch @@ -219,4 +223,43 @@ public class OpenAIAssistantClientExtensionsTests File.Delete(searchFilePath); } } + + /// + /// Waits for a vector store to complete indexing by polling its status. + /// + /// The vector store client. + /// The ID of the vector store. + /// Maximum time to wait in seconds (default: 30). + /// A task that completes when the vector store is ready or throws on timeout/failure. + private static async Task WaitForVectorStoreReadyAsync( + VectorStoreClient client, + string vectorStoreId, + int maxWaitSeconds = 30) + { + Stopwatch sw = Stopwatch.StartNew(); + while (sw.Elapsed.TotalSeconds < maxWaitSeconds) + { + VectorStore vectorStore = await client.GetVectorStoreAsync(vectorStoreId); + VectorStoreStatus status = vectorStore.Status; + + if (status == VectorStoreStatus.Completed) + { + if (vectorStore.FileCounts.Failed > 0) + { + throw new InvalidOperationException("Vector store indexing failed for some files"); + } + + return; + } + + if (status == VectorStoreStatus.Expired) + { + throw new InvalidOperationException("Vector store has expired"); + } + + await Task.Delay(1000); + } + + throw new TimeoutException($"Vector store did not complete indexing within {maxWaitSeconds}s"); + } } From 411ee7a60f64488417158f375af3113a718d85f4 Mon Sep 17 00:00:00 2001 From: Victor Dibia Date: Wed, 3 Dec 2025 12:15:51 -0800 Subject: [PATCH 12/33] Python: DevUI fixes : Add multimodal input support for workflows and refactor chat input (#2593) * show app version in devui .NET: Python: Improved Versioning for DevUI Fixes #2059 * feat: Add multimodal input support for workflows and refactor chat input This PR adds support for multimodal content (images, files) in workflow inputs and refactors the chat input into a reusable component. ## Multimodal Workflow Support - Add `isChatMessageSchema()` to detect ChatMessage input schemas - Update `RunWorkflowButton` to use `ChatMessageInput` for ChatMessage workflows - Wrap multimodal content in OpenAI message format for backend processing - Add `_is_openai_multimodal_format()` to detect OpenAI ResponseInputParam - Update `_parse_workflow_input()` to route multimodal input through existing `_convert_input_to_chat_message()` converter ## Reusable ChatMessageInput Component - Extract chat input logic from agent-view into `ChatMessageInput` component - Support file upload, drag & drop, paste handling, and attachments - Add `useDragDrop` hook for parent-level drag handling with full-area drop zones - Refactor agent-view to use the new shared component ## Other Improvements - Add `isStreaming` prop to executor nodes for animation control - Clean up unused imports and state variables in agent-view - Add tests for multimodal workflow input handling Fixes workflow input not receiving images when using AgentExecutor nodes. * add self loop edge, fix #2470 * fix test --- .../agent_framework_devui/_conversations.py | 109 ++- .../devui/agent_framework_devui/_executor.py | 33 +- .../devui/agent_framework_devui/_mapper.py | 48 +- .../devui/agent_framework_devui/_server.py | 155 ++- .../agent_framework_devui/ui/assets/index.css | 2 +- .../agent_framework_devui/ui/assets/index.js | 278 +++--- python/packages/devui/frontend/src/App.tsx | 1 + .../components/features/agent/agent-view.tsx | 639 ++++--------- .../workflow/checkpoint-info-modal.tsx | 395 ++++++++ .../features/workflow/execution-timeline.tsx | 173 +++- .../features/workflow/executor-node.tsx | 4 +- .../features/workflow/hil-input-modal.tsx | 150 --- .../features/workflow/hil-timeline-item.tsx | 153 +++ .../src/components/features/workflow/index.ts | 4 +- .../features/workflow/run-workflow-button.tsx | 427 +++++++++ .../workflow/schema-form-renderer.tsx | 4 +- .../features/workflow/self-loop-edge.tsx | 56 ++ .../features/workflow/workflow-flow.tsx | 71 +- .../features/workflow/workflow-input-form.tsx | 171 ++-- .../workflow/workflow-session-manager.tsx | 12 +- .../features/workflow/workflow-view.tsx | 894 ++++++++---------- .../src/components/layout/about-modal.tsx | 55 -- .../src/components/layout/app-header.tsx | 16 +- .../frontend/src/components/layout/index.ts | 1 - .../src/components/layout/settings-modal.tsx | 60 +- .../src/components/ui/chat-message-input.tsx | 471 +++++++++ .../devui/frontend/src/hooks/index.ts | 3 + .../devui/frontend/src/hooks/use-drag-drop.ts | 98 ++ .../src/hooks/useCancellableRequest.ts | 70 ++ python/packages/devui/frontend/src/index.css | 37 + .../devui/frontend/src/services/api.ts | 48 +- .../devui/frontend/src/stores/devuiStore.ts | 5 +- .../devui/frontend/src/types/index.ts | 37 +- .../devui/frontend/src/types/openai.ts | 2 +- .../frontend/src/utils/workflow-utils.ts | 86 +- .../devui/tests/test_conversations.py | 4 +- python/packages/devui/tests/test_mapper.py | 55 +- .../devui/tests/test_multimodal_workflow.py | 154 +++ 38 files changed, 3488 insertions(+), 1493 deletions(-) create mode 100644 python/packages/devui/frontend/src/components/features/workflow/checkpoint-info-modal.tsx delete mode 100644 python/packages/devui/frontend/src/components/features/workflow/hil-input-modal.tsx create mode 100644 python/packages/devui/frontend/src/components/features/workflow/hil-timeline-item.tsx create mode 100644 python/packages/devui/frontend/src/components/features/workflow/run-workflow-button.tsx create mode 100644 python/packages/devui/frontend/src/components/features/workflow/self-loop-edge.tsx delete mode 100644 python/packages/devui/frontend/src/components/layout/about-modal.tsx create mode 100644 python/packages/devui/frontend/src/components/ui/chat-message-input.tsx create mode 100644 python/packages/devui/frontend/src/hooks/index.ts create mode 100644 python/packages/devui/frontend/src/hooks/use-drag-drop.ts create mode 100644 python/packages/devui/frontend/src/hooks/useCancellableRequest.ts create mode 100644 python/packages/devui/tests/test_multimodal_workflow.py diff --git a/python/packages/devui/agent_framework_devui/_conversations.py b/python/packages/devui/agent_framework_devui/_conversations.py index 9762b55d0e..512b92f647 100644 --- a/python/packages/devui/agent_framework_devui/_conversations.py +++ b/python/packages/devui/agent_framework_devui/_conversations.py @@ -134,9 +134,12 @@ class ConversationStore(ABC): pass @abstractmethod - def get_item(self, conversation_id: str, item_id: str) -> ConversationItem | None: + async def get_item(self, conversation_id: str, item_id: str) -> ConversationItem | None: """Get a specific conversation item by ID. + Supports checkpoint items - will load full checkpoint state from storage. + For checkpoints, the full state is included in metadata.full_checkpoint. + Args: conversation_id: Conversation ID item_id: Item ID @@ -162,7 +165,7 @@ class ConversationStore(ABC): pass @abstractmethod - def list_conversations_by_metadata(self, metadata_filter: dict[str, str]) -> list[Conversation]: + async def list_conversations_by_metadata(self, metadata_filter: dict[str, str]) -> list[Conversation]: """Filter conversations by metadata (e.g., agent_id). Args: @@ -444,7 +447,15 @@ class InMemoryConversationStore(ConversationStore): # Get all checkpoints for this conversation checkpoints = await checkpoint_storage.list_checkpoints() for checkpoint in checkpoints: - # Create a conversation item for each checkpoint + # Create a conversation item for each checkpoint with summary metadata + # Full checkpoint state is NOT included here (too large for list view) + # Use get_item() to retrieve full checkpoint details + # Calculate approximate size of checkpoint + import json + + checkpoint_json = json.dumps(checkpoint.to_dict()) + checkpoint_size = len(checkpoint_json.encode("utf-8")) + checkpoint_item = { "id": f"checkpoint_{checkpoint.checkpoint_id}", "type": "checkpoint", @@ -452,6 +463,15 @@ class InMemoryConversationStore(ConversationStore): "workflow_id": checkpoint.workflow_id, "timestamp": checkpoint.timestamp, "status": "completed", + "metadata": { + # Summary metrics for list view + "iteration_count": checkpoint.iteration_count, + "pending_hil_count": len(checkpoint.pending_request_info_events), + "has_pending_hil": len(checkpoint.pending_request_info_events) > 0, + "message_count": sum(len(msgs) for msgs in checkpoint.messages.values()), + "size_bytes": checkpoint_size, + "version": checkpoint.version, + }, } items.append(cast(ConversationItem, checkpoint_item)) @@ -472,24 +492,91 @@ class InMemoryConversationStore(ConversationStore): return paginated_items, has_more - def get_item(self, conversation_id: str, item_id: str) -> ConversationItem | None: - """Get a specific conversation item by ID.""" - # Use the item index for O(1) lookup + async def get_item(self, conversation_id: str, item_id: str) -> ConversationItem | None: + """Get a specific conversation item by ID. + + Supports checkpoint items - will load full checkpoint state from storage. + For checkpoints, the full state is included in metadata.full_checkpoint. + """ + # First check item index for messages, function calls, etc. (O(1) lookup) conv_items = self._item_index.get(conversation_id, {}) - return conv_items.get(item_id) + item = conv_items.get(item_id) + if item: + return item + + # If not found and ID is a checkpoint, load from checkpoint storage + if item_id.startswith("checkpoint_"): + checkpoint_id = item_id[len("checkpoint_") :] # Remove "checkpoint_" prefix + conv_data = self._conversations.get(conversation_id) + if not conv_data: + return None + + checkpoint_storage = conv_data.get("checkpoint_storage") + if not checkpoint_storage: + return None + + # Load full checkpoint from storage + checkpoint = await checkpoint_storage.load_checkpoint(checkpoint_id) + if not checkpoint: + return None + + # Calculate size of checkpoint + import json + + checkpoint_json = json.dumps(checkpoint.to_dict()) + checkpoint_size = len(checkpoint_json.encode("utf-8")) + + # Build checkpoint item with FULL state in metadata + checkpoint_item = { + "id": item_id, + "type": "checkpoint", + "checkpoint_id": checkpoint.checkpoint_id, + "workflow_id": checkpoint.workflow_id, + "timestamp": checkpoint.timestamp, + "status": "completed", + "metadata": { + # Summary metrics (same as list view) + "iteration_count": checkpoint.iteration_count, + "pending_hil_count": len(checkpoint.pending_request_info_events), + "has_pending_hil": len(checkpoint.pending_request_info_events) > 0, + "message_count": sum(len(msgs) for msgs in checkpoint.messages.values()), + "size_bytes": checkpoint_size, + "version": checkpoint.version, + # 🔥 FULL checkpoint state (lazy loaded) + "full_checkpoint": checkpoint.to_dict(), + }, + } + + return cast(ConversationItem, checkpoint_item) + + return None def get_thread(self, conversation_id: str) -> AgentThread | None: """Get AgentThread for execution - CRITICAL for agent.run_stream().""" conv_data = self._conversations.get(conversation_id) return conv_data["thread"] if conv_data else None - def list_conversations_by_metadata(self, metadata_filter: dict[str, str]) -> list[Conversation]: + async def list_conversations_by_metadata(self, metadata_filter: dict[str, str]) -> list[Conversation]: """Filter conversations by metadata (e.g., agent_id).""" results = [] for conv_data in self._conversations.values(): - conv_meta = conv_data.get("metadata", {}) + conv_meta = conv_data.get("metadata", {}).copy() # Copy to avoid mutating original + # Check if all filter items match if all(conv_meta.get(k) == v for k, v in metadata_filter.items()): + # Enrich workflow sessions with checkpoint summary + if conv_meta.get("type") == "workflow_session": + checkpoint_storage = conv_data.get("checkpoint_storage") + if checkpoint_storage: + checkpoints = await checkpoint_storage.list_checkpoints() + latest = checkpoints[0] if checkpoints else None + conv_meta["checkpoint_summary"] = { + "count": len(checkpoints), + "latest_iteration": latest.iteration_count if latest else 0, + "has_pending_hil": len(latest.pending_request_info_events) > 0 if latest else False, + "pending_hil_count": len(latest.pending_request_info_events) if latest else 0, + } + results.append( Conversation( id=conv_data["id"], @@ -498,6 +585,10 @@ class InMemoryConversationStore(ConversationStore): metadata=conv_meta, ) ) + + # Sort by created_at descending (most recent first) + results.sort(key=lambda c: c.created_at, reverse=True) + return results diff --git a/python/packages/devui/agent_framework_devui/_executor.py b/python/packages/devui/agent_framework_devui/_executor.py index dc4e8091b8..99379f6bf9 100644 --- a/python/packages/devui/agent_framework_devui/_executor.py +++ b/python/packages/devui/agent_framework_devui/_executor.py @@ -722,6 +722,20 @@ class AgentFrameworkExecutor: return json.dumps(input_data) return str(input_data) + def _is_openai_multimodal_format(self, input_data: Any) -> bool: + """Check if input is OpenAI ResponseInputParam format (list with message items). + + Args: + input_data: Input data to check + + Returns: + True if input is OpenAI multimodal format + """ + if not isinstance(input_data, list) or not input_data: + return False + first_item = input_data[0] + return isinstance(first_item, dict) and first_item.get("type") == "message" + async def _parse_workflow_input(self, workflow: Any, raw_input: Any) -> Any: """Parse input based on workflow's expected input type. @@ -733,9 +747,26 @@ class AgentFrameworkExecutor: Parsed input appropriate for the workflow """ try: - # Handle structured input + # Handle JSON string input (from frontend api.ts JSON.stringify) + if isinstance(raw_input, str): + try: + parsed = json.loads(raw_input) + raw_input = parsed + except (json.JSONDecodeError, TypeError): + # Plain text string, continue with string handling + pass + + # Check for OpenAI multimodal format (list with type: "message") + # This handles ChatMessage inputs with images, files, etc. + if self._is_openai_multimodal_format(raw_input): + logger.debug("Detected OpenAI multimodal format, converting to ChatMessage") + return self._convert_input_to_chat_message(raw_input) + + # Handle structured input (dict) if isinstance(raw_input, dict): return self._parse_structured_workflow_input(workflow, raw_input) + + # Handle string input return self._parse_raw_workflow_input(workflow, str(raw_input)) except Exception as e: diff --git a/python/packages/devui/agent_framework_devui/_mapper.py b/python/packages/devui/agent_framework_devui/_mapper.py index fd32e3f623..9d9e0e5ccd 100644 --- a/python/packages/devui/agent_framework_devui/_mapper.py +++ b/python/packages/devui/agent_framework_devui/_mapper.py @@ -885,6 +885,10 @@ class MessageMapper: context[f"exec_item_{executor_id}"] = item_id context["output_index"] = context.get("output_index", -1) + 1 + # Track current executor for routing Magentic agent events + # This allows MagenticAgentDeltaEvent to route to the executor's item + context["current_executor_id"] = executor_id + # Create ExecutorActionItem with proper type executor_item = ExecutorActionItem( type="executor_action", @@ -908,6 +912,10 @@ class MessageMapper: executor_id = getattr(event, "executor_id", "unknown") item_id = context.get(f"exec_item_{executor_id}", f"exec_{executor_id}_unknown") + # Clear current executor tracking when executor completes + if context.get("current_executor_id") == executor_id: + context.pop("current_executor_id", None) + # Create ExecutorActionItem with completed status # ExecutorCompletedEvent uses 'data' field, not 'result' executor_item = ExecutorActionItem( @@ -1059,6 +1067,30 @@ class MessageMapper: text = getattr(event, "text", None) if text: + # Check if we're inside an executor - route to executor's item + # This prevents duplicate timeline entries (executor + inner agent) + current_executor_id = context.get("current_executor_id") + executor_item_key = f"exec_item_{current_executor_id}" if current_executor_id else None + + if executor_item_key and executor_item_key in context: + # Route delta to the executor's item instead of creating a new message item + item_id = context[executor_item_key] + + # Emit text delta event routed to the executor's item + return [ + ResponseTextDeltaEvent( + type="response.output_text.delta", + output_index=context.get("output_index", 0), + content_index=0, + item_id=item_id, + delta=text, + logprobs=[], + sequence_number=self._next_sequence(context), + ) + ] + + # Fallback: No executor context - create separate message item (original behavior) + # This handles cases where MagenticAgentDeltaEvent is emitted outside an executor events = [] # Track Magentic agent messages separately from regular messages @@ -1181,7 +1213,21 @@ class MessageMapper: agent_id = getattr(event, "agent_id", "unknown_agent") message = getattr(event, "message", None) - # Track Magentic agent messages + # Check if we're inside an executor - if so, deltas were already routed there + # We don't need to emit a separate message completion event + current_executor_id = context.get("current_executor_id") + executor_item_key = f"exec_item_{current_executor_id}" if current_executor_id else None + + if executor_item_key and executor_item_key in context: + # Deltas were routed to executor item - no separate message item to complete + # The executor's output_item.done will mark completion + logger.debug( + f"MagenticAgentMessageEvent from {agent_id} - " + f"deltas routed to executor {current_executor_id}, skipping" + ) + return [] + + # Fallback: Handle case where we created a separate message item (no executor context) magentic_key = f"magentic_message_{agent_id}" # Check if we were streaming for this agent diff --git a/python/packages/devui/agent_framework_devui/_server.py b/python/packages/devui/agent_framework_devui/_server.py index 988d45c2f9..284164cefb 100644 --- a/python/packages/devui/agent_framework_devui/_server.py +++ b/python/packages/devui/agent_framework_devui/_server.py @@ -2,14 +2,17 @@ """FastAPI server implementation.""" +import asyncio +import importlib.metadata import inspect import json import logging import os import secrets +import uuid from collections.abc import AsyncGenerator, Awaitable, Callable from contextlib import asynccontextmanager -from typing import Any +from typing import Any, cast from fastapi import FastAPI, HTTPException, Request from fastapi.middleware.cors import CORSMiddleware @@ -26,6 +29,12 @@ from .models._discovery_models import Deployment, DeploymentConfig, DiscoveryRes logger = logging.getLogger(__name__) +# Get package version +try: + __version__ = importlib.metadata.version("agent-framework-devui") +except importlib.metadata.PackageNotFoundError: + __version__ = "0.0.0" # Fallback for development mode + # No AuthMiddleware class needed - we'll use the decorator pattern instead @@ -70,6 +79,7 @@ class DevServer: self.deployment_manager = DeploymentManager() self._app: FastAPI | None = None self._pending_entities: list[Any] | None = None + self._running_tasks: dict[str, asyncio.Task[Any]] = {} # Track running response tasks for cancellation def _is_dev_mode(self) -> bool: """Check if running in developer mode. @@ -293,7 +303,7 @@ class DevServer: app = FastAPI( title="Agent Framework Server", description="OpenAI-compatible API server for Agent Framework and other AI frameworks", - version="1.0.0", + version=__version__, lifespan=lifespan, ) @@ -388,8 +398,6 @@ class DevServer: """Get server metadata and configuration.""" import os - from . import __version__ - # Ensure executors are initialized to check capabilities openai_executor = await self._ensure_openai_executor() @@ -731,13 +739,18 @@ class DevServer: # Execute request if request.stream: + # Generate response ID for tracking + response_id = f"resp_{uuid.uuid4().hex[:8]}" + logger.info(f"[CANCELLATION] Creating response {response_id} for entity {entity_id}") + return StreamingResponse( - self._stream_execution(executor, request), + self._stream_with_cancellation(executor, request, response_id), media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "Connection": "keep-alive", "Access-Control-Allow-Origin": "*", + "X-Response-ID": response_id, # Include ID for debugging/tracking }, ) return await executor.execute_sync(request) @@ -747,6 +760,30 @@ class DevServer: error = OpenAIError.create(error_msg) return JSONResponse(status_code=500, content=error.to_dict()) + @app.post("/v1/responses/{response_id}/cancel") + async def cancel_response(response_id: str) -> dict[str, Any]: + """Cancel a running response execution. + + This endpoint allows explicit cancellation of a running stream. + Note: Cancellation also happens automatically when the client disconnects. + """ + logger.info(f"[CANCELLATION] Cancel request received for {response_id}") + + if task := self._running_tasks.get(response_id): + if not task.done(): + logger.info(f"[CANCELLATION] Cancelling task for {response_id}") + task.cancel() + # Wait briefly for cancellation to propagate + try: # noqa: SIM105 + await asyncio.wait_for(task, timeout=0.5) + except (asyncio.CancelledError, asyncio.TimeoutError): + pass + return {"status": "cancelled", "response_id": response_id} + logger.warning(f"[CANCELLATION] Task already completed for {response_id}") + return {"status": "already_completed", "response_id": response_id} + logger.warning(f"[CANCELLATION] No task found for {response_id}") + return {"status": "not_found", "response_id": response_id} + # ======================================== # OpenAI Conversations API (Standard) # ======================================== @@ -862,7 +899,7 @@ class DevServer: filters["type"] = type # Apply filters - conversations = executor.conversation_store.list_conversations_by_metadata(filters) + conversations = await executor.conversation_store.list_conversations_by_metadata(filters) return { "object": "list", @@ -973,13 +1010,19 @@ class DevServer: @app.get("/v1/conversations/{conversation_id}/items/{item_id}") async def retrieve_conversation_item(conversation_id: str, item_id: str) -> dict[str, Any]: - """Get specific conversation item - OpenAI standard.""" + """Get specific conversation item - OpenAI standard. + + Supports checkpoint items - returns full checkpoint state in metadata.full_checkpoint. + """ try: executor = await self._ensure_executor() - item = executor.conversation_store.get_item(conversation_id, item_id) + item = await executor.conversation_store.get_item(conversation_id, item_id) if not item: raise HTTPException(status_code=404, detail="Item not found") - result: dict[str, Any] = item.model_dump() + # Handle both Pydantic models and dicts + result: dict[str, Any] = ( + item.model_dump() if hasattr(item, "model_dump") else cast(dict[str, Any], item) + ) return result except HTTPException: raise @@ -1139,6 +1182,100 @@ class DevServer: } yield f"data: {json.dumps(error_event)}\n\n" + async def _stream_with_cancellation( + self, executor: AgentFrameworkExecutor, request: AgentFrameworkRequest, response_id: str + ) -> AsyncGenerator[str, None]: + """Stream execution with automatic cancellation on client disconnect. + + This wrapper adds cancellation support to the execution stream: + 1. Tracks the execution as an asyncio Task + 2. Detects client disconnection via GeneratorExit + 3. Cancels the task when client disconnects + 4. Propagates CancelledError through the execution chain + + Args: + executor: Agent Framework executor instance + request: Request to execute + response_id: Unique ID for this response/execution + + Yields: + SSE-formatted event strings from the original stream + """ + task = None + + async def execution_wrapper() -> AsyncGenerator[str, None]: + """Inner wrapper to handle the actual execution.""" + try: + logger.debug(f"[CANCELLATION] Starting execution for {response_id}") + + async for chunk in self._stream_execution(executor, request): + # Check if we're being cancelled + current_task = asyncio.current_task() + if current_task and current_task.cancelled(): + logger.info(f"[CANCELLATION] Detected cancellation, breaking stream for {response_id}") + break + yield chunk + + except asyncio.CancelledError: + logger.info(f"[CANCELLATION] Execution cancelled via CancelledError for {response_id}") + # Emit cancellation event to client (if still connected) + cancelled_event = { + "type": "response.cancelled", + "response_id": response_id, + "message": "Execution cancelled by user", + } + yield f"data: {json.dumps(cancelled_event)}\n\n" + raise + except Exception as e: + logger.error(f"[CANCELLATION] Error in cancellable execution for {response_id}: {e}") + raise + + try: + # Get or create the current task and track it + task = asyncio.current_task() + if task: + self._running_tasks[response_id] = task + logger.debug(f"[CANCELLATION] Tracking task {task.get_name()} for response {response_id}") + else: + logger.warning(f"[CANCELLATION] No current task found to track for {response_id}") + + # Stream the execution + async for chunk in execution_wrapper(): + yield chunk + + logger.debug(f"[CANCELLATION] Stream completed normally for {response_id}") + + except GeneratorExit: + # Client disconnected - this is raised when the generator is closed + logger.info(f"[CANCELLATION] Client disconnected, initiating cancellation for {response_id}") + + if task and not task.done(): + logger.info(f"[CANCELLATION] Cancelling task for disconnected client {response_id}") + task.cancel() + # Give it a moment to cancel gracefully + # Note: We should NOT use asyncio.shield here as it prevents cancellation + try: + await asyncio.wait_for(task, timeout=1.0) + except (asyncio.CancelledError, asyncio.TimeoutError): + logger.debug(f"[CANCELLATION] Task cancelled successfully for {response_id}") + except Exception as e: + logger.warning(f"[CANCELLATION] Error during task cancellation for {response_id}: {e}") + raise # Re-raise GeneratorExit to properly close the generator + + except asyncio.CancelledError: + logger.info(f"[CANCELLATION] Stream cancelled for {response_id}") + raise + + except Exception as e: + logger.error(f"[CANCELLATION] Unexpected error in stream for {response_id}: {e}") + raise + + finally: + # Clean up tracking + if response_id in self._running_tasks: + self._running_tasks.pop(response_id) + logger.debug(f"[CANCELLATION] Cleaned up task tracking for {response_id}") + def _mount_ui(self, app: FastAPI) -> None: """Mount the UI as static files.""" from pathlib import Path diff --git a/python/packages/devui/agent_framework_devui/ui/assets/index.css b/python/packages/devui/agent_framework_devui/ui/assets/index.css index 488784de54..52fcb2ff6e 100644 --- a/python/packages/devui/agent_framework_devui/ui/assets/index.css +++ b/python/packages/devui/agent_framework_devui/ui/assets/index.css @@ -1 +1 @@ -/*! tailwindcss v4.1.12 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-red-950:oklch(25.8% .092 26.042);--color-orange-50:oklch(98% .016 73.684);--color-orange-100:oklch(95.4% .038 75.164);--color-orange-200:oklch(90.1% .076 70.697);--color-orange-400:oklch(75% .183 55.934);--color-orange-500:oklch(70.5% .213 47.604);--color-orange-600:oklch(64.6% .222 41.116);--color-orange-800:oklch(47% .157 37.304);--color-orange-900:oklch(40.8% .123 38.172);--color-orange-950:oklch(26.6% .079 36.259);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-amber-950:oklch(27.9% .077 45.635);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-600:oklch(68.1% .162 75.834);--color-yellow-700:oklch(55.4% .135 66.442);--color-green-50:oklch(98.2% .018 155.826);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .084 155.995);--color-green-300:oklch(87.1% .15 154.449);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-green-800:oklch(44.8% .119 151.328);--color-green-900:oklch(39.3% .095 152.535);--color-green-950:oklch(26.6% .065 152.934);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-200:oklch(90.5% .093 164.15);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-700:oklch(50.8% .118 165.612);--color-emerald-800:oklch(43.2% .095 166.913);--color-blue-50:oklch(97% .014 254.604);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-blue-800:oklch(42.4% .199 265.638);--color-blue-900:oklch(37.9% .146 265.522);--color-blue-950:oklch(28.2% .091 267.935);--color-purple-50:oklch(97.7% .014 308.299);--color-purple-100:oklch(94.6% .033 307.174);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-purple-600:oklch(55.8% .288 302.321);--color-purple-900:oklch(38.1% .176 304.987);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-lg:32rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--container-5xl:64rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-widest:.1em;--leading-tight:1.25;--leading-relaxed:1.625;--drop-shadow-lg:0 4px 4px #00000026;--ease-out:cubic-bezier(0,0,.2,1);--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--animate-bounce:bounce 1s infinite;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{border-color:var(--border);outline-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){*{outline-color:color-mix(in oklab,var(--ring)50%,transparent)}}body{background-color:var(--background);color:var(--foreground)}}@layer components;@layer utilities{.\@container\/card-header{container:card-header/inline-size}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing)*0)}.inset-2{inset:calc(var(--spacing)*2)}.inset-y-0{inset-block:calc(var(--spacing)*0)}.top-1{top:calc(var(--spacing)*1)}.top-2{top:calc(var(--spacing)*2)}.top-4{top:calc(var(--spacing)*4)}.-right-2{right:calc(var(--spacing)*-2)}.right-0{right:calc(var(--spacing)*0)}.right-1{right:calc(var(--spacing)*1)}.right-2{right:calc(var(--spacing)*2)}.right-4{right:calc(var(--spacing)*4)}.bottom-0{bottom:calc(var(--spacing)*0)}.bottom-24{bottom:calc(var(--spacing)*24)}.-left-2{left:calc(var(--spacing)*-2)}.left-0{left:calc(var(--spacing)*0)}.left-1\/2{left:50%}.left-2{left:calc(var(--spacing)*2)}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.col-start-2{grid-column-start:2}.row-span-2{grid-row:span 2/span 2}.row-start-1{grid-row-start:1}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.container\!{width:100%!important}@media (min-width:40rem){.container\!{max-width:40rem!important}}@media (min-width:48rem){.container\!{max-width:48rem!important}}@media (min-width:64rem){.container\!{max-width:64rem!important}}@media (min-width:80rem){.container\!{max-width:80rem!important}}@media (min-width:96rem){.container\!{max-width:96rem!important}}.-mx-1{margin-inline:calc(var(--spacing)*-1)}.mx-4{margin-inline:calc(var(--spacing)*4)}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing)*1)}.my-2{margin-block:calc(var(--spacing)*2)}.my-3{margin-block:calc(var(--spacing)*3)}.my-4{margin-block:calc(var(--spacing)*4)}.mt-0{margin-top:calc(var(--spacing)*0)}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-12{margin-top:calc(var(--spacing)*12)}.mr-1{margin-right:calc(var(--spacing)*1)}.mr-2{margin-right:calc(var(--spacing)*2)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.ml-0{margin-left:calc(var(--spacing)*0)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-1\.5{margin-left:calc(var(--spacing)*1.5)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-3{margin-left:calc(var(--spacing)*3)}.ml-4{margin-left:calc(var(--spacing)*4)}.ml-5{margin-left:calc(var(--spacing)*5)}.ml-auto{margin-left:auto}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-3{-webkit-line-clamp:3;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.field-sizing-content{field-sizing:content}.size-2{width:calc(var(--spacing)*2);height:calc(var(--spacing)*2)}.size-3\.5{width:calc(var(--spacing)*3.5);height:calc(var(--spacing)*3.5)}.size-4{width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.size-9{width:calc(var(--spacing)*9);height:calc(var(--spacing)*9)}.\!h-2{height:calc(var(--spacing)*2)!important}.h-0{height:calc(var(--spacing)*0)}.h-0\.5{height:calc(var(--spacing)*.5)}.h-1{height:calc(var(--spacing)*1)}.h-2{height:calc(var(--spacing)*2)}.h-2\.5{height:calc(var(--spacing)*2.5)}.h-3{height:calc(var(--spacing)*3)}.h-3\.5{height:calc(var(--spacing)*3.5)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-8{height:calc(var(--spacing)*8)}.h-9{height:calc(var(--spacing)*9)}.h-10{height:calc(var(--spacing)*10)}.h-12{height:calc(var(--spacing)*12)}.h-14{height:calc(var(--spacing)*14)}.h-16{height:calc(var(--spacing)*16)}.h-32{height:calc(var(--spacing)*32)}.h-96{height:calc(var(--spacing)*96)}.h-\[1\.2rem\]{height:1.2rem}.h-\[1px\]{height:1px}.h-\[500px\]{height:500px}.h-\[calc\(100vh-3\.5rem\)\]{height:calc(100vh - 3.5rem)}.h-\[calc\(100vh-3\.7rem\)\]{height:calc(100vh - 3.7rem)}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-\(--radix-dropdown-menu-content-available-height\){max-height:var(--radix-dropdown-menu-content-available-height)}.max-h-\(--radix-select-content-available-height\){max-height:var(--radix-select-content-available-height)}.max-h-20{max-height:calc(var(--spacing)*20)}.max-h-32{max-height:calc(var(--spacing)*32)}.max-h-40{max-height:calc(var(--spacing)*40)}.max-h-48{max-height:calc(var(--spacing)*48)}.max-h-60{max-height:calc(var(--spacing)*60)}.max-h-64{max-height:calc(var(--spacing)*64)}.max-h-\[80vh\]{max-height:80vh}.max-h-\[85vh\]{max-height:85vh}.max-h-\[90vh\]{max-height:90vh}.max-h-\[200px\]{max-height:200px}.max-h-none{max-height:none}.max-h-screen{max-height:100vh}.\!min-h-0{min-height:calc(var(--spacing)*0)!important}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-16{min-height:calc(var(--spacing)*16)}.min-h-\[36px\]{min-height:36px}.min-h-\[40px\]{min-height:40px}.min-h-\[50vh\]{min-height:50vh}.min-h-\[400px\]{min-height:400px}.min-h-screen{min-height:100vh}.\!w-2{width:calc(var(--spacing)*2)!important}.w-1{width:calc(var(--spacing)*1)}.w-2{width:calc(var(--spacing)*2)}.w-2\.5{width:calc(var(--spacing)*2.5)}.w-3{width:calc(var(--spacing)*3)}.w-3\.5{width:calc(var(--spacing)*3.5)}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-8{width:calc(var(--spacing)*8)}.w-9{width:calc(var(--spacing)*9)}.w-10{width:calc(var(--spacing)*10)}.w-12{width:calc(var(--spacing)*12)}.w-16{width:calc(var(--spacing)*16)}.w-56{width:calc(var(--spacing)*56)}.w-64{width:calc(var(--spacing)*64)}.w-80{width:calc(var(--spacing)*80)}.w-96{width:calc(var(--spacing)*96)}.w-\[1\.2rem\]{width:1.2rem}.w-\[1px\]{width:1px}.w-\[200px\]{width:200px}.w-\[600px\]{width:600px}.w-\[800px\]{width:800px}.w-fit{width:fit-content}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-7xl{max-width:var(--container-7xl)}.max-w-\[80\%\]{max-width:80%}.max-w-\[90vw\]{max-width:90vw}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-none{max-width:none}.\!min-w-0{min-width:calc(var(--spacing)*0)!important}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-\[8rem\]{min-width:8rem}.min-w-\[300px\]{min-width:300px}.min-w-\[400px\]{min-width:400px}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.min-w-full{min-width:100%}.flex-1{flex:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.origin-\(--radix-dropdown-menu-content-transform-origin\){transform-origin:var(--radix-dropdown-menu-content-transform-origin)}.origin-\(--radix-select-content-transform-origin\){transform-origin:var(--radix-select-content-transform-origin)}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-4{--tw-translate-x:calc(var(--spacing)*4);translate:var(--tw-translate-x)var(--tw-translate-y)}.scale-0{--tw-scale-x:0%;--tw-scale-y:0%;--tw-scale-z:0%;scale:var(--tw-scale-x)var(--tw-scale-y)}.scale-75{--tw-scale-x:75%;--tw-scale-y:75%;--tw-scale-z:75%;scale:var(--tw-scale-x)var(--tw-scale-y)}.scale-100{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x)var(--tw-scale-y)}.rotate-0{rotate:none}.rotate-90{rotate:90deg}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-bounce{animation:var(--animate-bounce)}.animate-in{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-col-resize{cursor:col-resize}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.resize{resize:both}.resize-none{resize:none}.scroll-my-1{scroll-margin-block:calc(var(--spacing)*1)}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-\[auto_auto_1fr_auto\]{grid-template-columns:auto auto 1fr auto}.grid-rows-\[auto_auto\]{grid-template-rows:auto auto}.flex-col{flex-direction:column}.flex-row-reverse{flex-direction:row-reverse}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0{gap:calc(var(--spacing)*0)}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-6{gap:calc(var(--spacing)*6)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*1)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*2)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-x-reverse)))}.self-start{align-self:flex-start}.justify-self-end{justify-self:flex-end}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.\!rounded-full{border-radius:3.40282e38px!important}.rounded{border-radius:.25rem}.rounded-\[4px\]{border-radius:4px}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-none{border-radius:0}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-l-none{border-top-left-radius:0;border-bottom-left-radius:0}.rounded-r-none{border-top-right-radius:0;border-bottom-right-radius:0}.\!border{border-style:var(--tw-border-style)!important;border-width:1px!important}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-0{border-left-style:var(--tw-border-style);border-left-width:0}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.\!border-gray-600{border-color:var(--color-gray-600)!important}.border-\[\#643FB2\]{border-color:#643fb2}.border-\[\#643FB2\]\/20{border-color:#643fb233}.border-\[\#643FB2\]\/30{border-color:#643fb24d}.border-amber-200{border-color:var(--color-amber-200)}.border-blue-200{border-color:var(--color-blue-200)}.border-blue-300{border-color:var(--color-blue-300)}.border-blue-400{border-color:var(--color-blue-400)}.border-blue-500{border-color:var(--color-blue-500)}.border-border,.border-border\/50{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-border\/50{border-color:color-mix(in oklab,var(--border)50%,transparent)}}.border-current\/30{border-color:currentColor}@supports (color:color-mix(in lab,red,red)){.border-current\/30{border-color:color-mix(in oklab,currentcolor 30%,transparent)}}.border-destructive\/30{border-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.border-destructive\/30{border-color:color-mix(in oklab,var(--destructive)30%,transparent)}}.border-foreground\/5{border-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.border-foreground\/5{border-color:color-mix(in oklab,var(--foreground)5%,transparent)}}.border-foreground\/10{border-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.border-foreground\/10{border-color:color-mix(in oklab,var(--foreground)10%,transparent)}}.border-foreground\/20{border-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.border-foreground\/20{border-color:color-mix(in oklab,var(--foreground)20%,transparent)}}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-gray-400{border-color:var(--color-gray-400)}.border-gray-500\/20{border-color:#6a728233}@supports (color:color-mix(in lab,red,red)){.border-gray-500\/20{border-color:color-mix(in oklab,var(--color-gray-500)20%,transparent)}}.border-green-200{border-color:var(--color-green-200)}.border-green-500{border-color:var(--color-green-500)}.border-green-500\/20{border-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.border-green-500\/20{border-color:color-mix(in oklab,var(--color-green-500)20%,transparent)}}.border-green-500\/40{border-color:#00c75866}@supports (color:color-mix(in lab,red,red)){.border-green-500\/40{border-color:color-mix(in oklab,var(--color-green-500)40%,transparent)}}.border-green-600\/20{border-color:#00a54433}@supports (color:color-mix(in lab,red,red)){.border-green-600\/20{border-color:color-mix(in oklab,var(--color-green-600)20%,transparent)}}.border-input{border-color:var(--input)}.border-muted{border-color:var(--muted)}.border-orange-200{border-color:var(--color-orange-200)}.border-orange-500{border-color:var(--color-orange-500)}.border-orange-500\/20{border-color:#fe6e0033}@supports (color:color-mix(in lab,red,red)){.border-orange-500\/20{border-color:color-mix(in oklab,var(--color-orange-500)20%,transparent)}}.border-orange-600\/20{border-color:#f0510033}@supports (color:color-mix(in lab,red,red)){.border-orange-600\/20{border-color:color-mix(in oklab,var(--color-orange-600)20%,transparent)}}.border-primary,.border-primary\/20{border-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.border-primary\/20{border-color:color-mix(in oklab,var(--primary)20%,transparent)}}.border-red-200{border-color:var(--color-red-200)}.border-red-500{border-color:var(--color-red-500)}.border-red-500\/20{border-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.border-red-500\/20{border-color:color-mix(in oklab,var(--color-red-500)20%,transparent)}}.border-transparent{border-color:#0000}.border-yellow-200{border-color:var(--color-yellow-200)}.border-t-transparent{border-top-color:#0000}.border-l-transparent{border-left-color:#0000}.bg-\[\#643FB2\]{background-color:#643fb2}.bg-\[\#643FB2\]\/10{background-color:#643fb21a}.bg-accent\/10{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.bg-accent\/10{background-color:color-mix(in oklab,var(--accent)10%,transparent)}}.bg-amber-50{background-color:var(--color-amber-50)}.bg-background{background-color:var(--background)}.bg-black{background-color:var(--color-black)}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black)50%,transparent)}}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black)60%,transparent)}}.bg-blue-50{background-color:var(--color-blue-50)}.bg-blue-50\/80{background-color:#eff6ffcc}@supports (color:color-mix(in lab,red,red)){.bg-blue-50\/80{background-color:color-mix(in oklab,var(--color-blue-50)80%,transparent)}}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-500{background-color:var(--color-blue-500)}.bg-blue-500\/5{background-color:#3080ff0d}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/5{background-color:color-mix(in oklab,var(--color-blue-500)5%,transparent)}}.bg-blue-600{background-color:var(--color-blue-600)}.bg-border{background-color:var(--border)}.bg-card{background-color:var(--card)}.bg-current{background-color:currentColor}.bg-destructive,.bg-destructive\/10{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.bg-destructive\/10{background-color:color-mix(in oklab,var(--destructive)10%,transparent)}}.bg-foreground\/5{background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.bg-foreground\/5{background-color:color-mix(in oklab,var(--foreground)5%,transparent)}}.bg-foreground\/10{background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.bg-foreground\/10{background-color:color-mix(in oklab,var(--foreground)10%,transparent)}}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-200{background-color:var(--color-gray-200)}.bg-gray-400{background-color:var(--color-gray-400)}.bg-gray-500\/10{background-color:#6a72821a}@supports (color:color-mix(in lab,red,red)){.bg-gray-500\/10{background-color:color-mix(in oklab,var(--color-gray-500)10%,transparent)}}.bg-gray-900\/90{background-color:#101828e6}@supports (color:color-mix(in lab,red,red)){.bg-gray-900\/90{background-color:color-mix(in oklab,var(--color-gray-900)90%,transparent)}}.bg-green-50{background-color:var(--color-green-50)}.bg-green-100{background-color:var(--color-green-100)}.bg-green-500{background-color:var(--color-green-500)}.bg-green-500\/5{background-color:#00c7580d}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/5{background-color:color-mix(in oklab,var(--color-green-500)5%,transparent)}}.bg-green-500\/10{background-color:#00c7581a}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/10{background-color:color-mix(in oklab,var(--color-green-500)10%,transparent)}}.bg-muted,.bg-muted\/30{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\/30{background-color:color-mix(in oklab,var(--muted)30%,transparent)}}.bg-muted\/50{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\/50{background-color:color-mix(in oklab,var(--muted)50%,transparent)}}.bg-orange-50{background-color:var(--color-orange-50)}.bg-orange-100{background-color:var(--color-orange-100)}.bg-orange-500{background-color:var(--color-orange-500)}.bg-orange-500\/10{background-color:#fe6e001a}@supports (color:color-mix(in lab,red,red)){.bg-orange-500\/10{background-color:color-mix(in oklab,var(--color-orange-500)10%,transparent)}}.bg-popover{background-color:var(--popover)}.bg-primary,.bg-primary\/10{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/10{background-color:color-mix(in oklab,var(--primary)10%,transparent)}}.bg-primary\/30{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/30{background-color:color-mix(in oklab,var(--primary)30%,transparent)}}.bg-primary\/40{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/40{background-color:color-mix(in oklab,var(--primary)40%,transparent)}}.bg-purple-50{background-color:var(--color-purple-50)}.bg-purple-100{background-color:var(--color-purple-100)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-500{background-color:var(--color-red-500)}.bg-red-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.bg-secondary{background-color:var(--secondary)}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-white\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab,red,red)){.bg-white\/90{background-color:color-mix(in oklab,var(--color-white)90%,transparent)}}.bg-yellow-100{background-color:var(--color-yellow-100)}.fill-current{fill:currentColor}.object-cover{object-fit:cover}.p-0{padding:calc(var(--spacing)*0)}.p-1{padding:calc(var(--spacing)*1)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-6{padding:calc(var(--spacing)*6)}.p-8{padding:calc(var(--spacing)*8)}.p-\[1px\]{padding:1px}.px-1{padding-inline:calc(var(--spacing)*1)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-6{padding-inline:calc(var(--spacing)*6)}.px-8{padding-inline:calc(var(--spacing)*8)}.py-0{padding-block:calc(var(--spacing)*0)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-2\.5{padding-block:calc(var(--spacing)*2.5)}.py-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.py-6{padding-block:calc(var(--spacing)*6)}.py-8{padding-block:calc(var(--spacing)*8)}.pt-0{padding-top:calc(var(--spacing)*0)}.pt-1{padding-top:calc(var(--spacing)*1)}.pt-2{padding-top:calc(var(--spacing)*2)}.pt-3{padding-top:calc(var(--spacing)*3)}.pt-4{padding-top:calc(var(--spacing)*4)}.pt-6{padding-top:calc(var(--spacing)*6)}.pt-8{padding-top:calc(var(--spacing)*8)}.pr-2{padding-right:calc(var(--spacing)*2)}.pr-4{padding-right:calc(var(--spacing)*4)}.pr-8{padding-right:calc(var(--spacing)*8)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-3{padding-bottom:calc(var(--spacing)*3)}.pb-4{padding-bottom:calc(var(--spacing)*4)}.pb-6{padding-bottom:calc(var(--spacing)*6)}.pl-2{padding-left:calc(var(--spacing)*2)}.pl-3{padding-left:calc(var(--spacing)*3)}.pl-4{padding-left:calc(var(--spacing)*4)}.pl-5{padding-left:calc(var(--spacing)*5)}.pl-8{padding-left:calc(var(--spacing)*8)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[\#643FB2\]{color:#643fb2}.text-amber-500{color:var(--color-amber-500)}.text-amber-600{color:var(--color-amber-600)}.text-amber-700{color:var(--color-amber-700)}.text-amber-800{color:var(--color-amber-800)}.text-amber-900{color:var(--color-amber-900)}.text-blue-500{color:var(--color-blue-500)}.text-blue-600{color:var(--color-blue-600)}.text-blue-700{color:var(--color-blue-700)}.text-blue-800{color:var(--color-blue-800)}.text-blue-900{color:var(--color-blue-900)}.text-card-foreground{color:var(--card-foreground)}.text-current{color:currentColor}.text-destructive,.text-destructive\/70{color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.text-destructive\/70{color:color-mix(in oklab,var(--destructive)70%,transparent)}}.text-destructive\/90{color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.text-destructive\/90{color:color-mix(in oklab,var(--destructive)90%,transparent)}}.text-foreground{color:var(--foreground)}.text-gray-300{color:var(--color-gray-300)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-900{color:var(--color-gray-900)}.text-green-500{color:var(--color-green-500)}.text-green-600{color:var(--color-green-600)}.text-green-700{color:var(--color-green-700)}.text-green-800{color:var(--color-green-800)}.text-green-900{color:var(--color-green-900)}.text-muted-foreground,.text-muted-foreground\/60{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\/60{color:color-mix(in oklab,var(--muted-foreground)60%,transparent)}}.text-muted-foreground\/80{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\/80{color:color-mix(in oklab,var(--muted-foreground)80%,transparent)}}.text-orange-500{color:var(--color-orange-500)}.text-orange-600{color:var(--color-orange-600)}.text-orange-800{color:var(--color-orange-800)}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-purple-500{color:var(--color-purple-500)}.text-purple-600{color:var(--color-purple-600)}.text-red-500{color:var(--color-red-500)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-red-800{color:var(--color-red-800)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-white{color:var(--color-white)}.text-yellow-600{color:var(--color-yellow-600)}.text-yellow-700{color:var(--color-yellow-700)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.opacity-100{opacity:1}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-0{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(0px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[\#643FB2\]\/20{--tw-shadow-color:#643fb233}@supports (color:color-mix(in lab,red,red)){.shadow-\[\#643FB2\]\/20{--tw-shadow-color:color-mix(in oklab,oklab(47.4316% .069152 -.159147/.2) var(--tw-shadow-alpha),transparent)}}.shadow-green-500\/20{--tw-shadow-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.shadow-green-500\/20{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-green-500)20%,transparent)var(--tw-shadow-alpha),transparent)}}.shadow-orange-500\/20{--tw-shadow-color:#fe6e0033}@supports (color:color-mix(in lab,red,red)){.shadow-orange-500\/20{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-orange-500)20%,transparent)var(--tw-shadow-alpha),transparent)}}.shadow-primary\/25{--tw-shadow-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.shadow-primary\/25{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--primary)25%,transparent)var(--tw-shadow-alpha),transparent)}}.shadow-red-500\/20{--tw-shadow-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.shadow-red-500\/20{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-red-500)20%,transparent)var(--tw-shadow-alpha),transparent)}}.ring-blue-500{--tw-ring-color:var(--color-blue-500)}.ring-offset-2{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.ring-offset-background{--tw-ring-offset-color:var(--background)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.drop-shadow-lg{--tw-drop-shadow-size:drop-shadow(0 4px 4px var(--tw-drop-shadow-color,#00000026));--tw-drop-shadow:drop-shadow(var(--drop-shadow-lg));filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,visibility,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[color\,box-shadow\]{transition-property:color,box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-none{transition-property:none}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.\[animation-delay\:-0\.3s\]{animation-delay:-.3s}.\[animation-delay\:-0\.15s\]{animation-delay:-.15s}.fade-in{--tw-enter-opacity:0}.paused{animation-play-state:paused}.running{animation-play-state:running}.slide-in-from-bottom-2{--tw-enter-translate-y:calc(2*var(--spacing))}.group-open\:rotate-90:is(:where(.group):is([open],:popover-open,:open) *){rotate:90deg}.group-open\:rotate-180:is(:where(.group):is([open],:popover-open,:open) *){rotate:180deg}@media (hover:hover){.group-hover\:bg-primary:is(:where(.group):hover *){background-color:var(--primary)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.group-hover\:shadow-md:is(:where(.group):hover *){--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.group-hover\:shadow-primary\/20:is(:where(.group):hover *){--tw-shadow-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.group-hover\:shadow-primary\/20:is(:where(.group):hover *){--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--primary)20%,transparent)var(--tw-shadow-alpha),transparent)}}}.group-data-\[disabled\=true\]\:pointer-events-none:is(:where(.group)[data-disabled=true] *){pointer-events:none}.group-data-\[disabled\=true\]\:opacity-50:is(:where(.group)[data-disabled=true] *){opacity:.5}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-50:is(:where(.peer):disabled~*){opacity:.5}.selection\:bg-primary ::selection{background-color:var(--primary)}.selection\:bg-primary::selection{background-color:var(--primary)}.selection\:text-primary-foreground ::selection{color:var(--primary-foreground)}.selection\:text-primary-foreground::selection{color:var(--primary-foreground)}.file\:inline-flex::file-selector-button{display:inline-flex}.file\:h-7::file-selector-button{height:calc(var(--spacing)*7)}.file\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\:bg-transparent::file-selector-button{background-color:#0000}.file\:text-sm::file-selector-button{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.file\:font-medium::file-selector-button{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.file\:text-foreground::file-selector-button{color:var(--foreground)}.placeholder\:text-muted-foreground::placeholder{color:var(--muted-foreground)}.first\:mt-0:first-child{margin-top:calc(var(--spacing)*0)}.last\:border-r-0:last-child{border-right-style:var(--tw-border-style);border-right-width:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media (hover:hover){.hover\:border-gray-300:hover{border-color:var(--color-gray-300)}.hover\:border-muted-foreground\/30:hover{border-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.hover\:border-muted-foreground\/30:hover{border-color:color-mix(in oklab,var(--muted-foreground)30%,transparent)}}.hover\:bg-accent:hover,.hover\:bg-accent\/50:hover{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-accent\/50:hover{background-color:color-mix(in oklab,var(--accent)50%,transparent)}}.hover\:bg-amber-100:hover{background-color:var(--color-amber-100)}.hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.hover\:bg-destructive\/80:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/80:hover{background-color:color-mix(in oklab,var(--destructive)80%,transparent)}}.hover\:bg-destructive\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab,var(--destructive)90%,transparent)}}.hover\:bg-muted:hover,.hover\:bg-muted\/50:hover{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-muted\/50:hover{background-color:color-mix(in oklab,var(--muted)50%,transparent)}}.hover\:bg-primary\/20:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/20:hover{background-color:color-mix(in oklab,var(--primary)20%,transparent)}}.hover\:bg-primary\/80:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/80:hover{background-color:color-mix(in oklab,var(--primary)80%,transparent)}}.hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--primary)90%,transparent)}}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-secondary\/80:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab,var(--secondary)80%,transparent)}}.hover\:bg-white:hover{background-color:var(--color-white)}.hover\:text-accent-foreground:hover{color:var(--accent-foreground)}.hover\:text-destructive\/80:hover{color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:text-destructive\/80:hover{color:color-mix(in oklab,var(--destructive)80%,transparent)}}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:text-red-600:hover{color:var(--color-red-600)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-70:hover{opacity:.7}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.focus\:bg-accent:focus{background-color:var(--accent)}.focus\:text-accent-foreground:focus{color:var(--accent-foreground)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-ring:focus{--tw-ring-color:var(--ring)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:border-ring:focus-visible{border-color:var(--ring)}.focus-visible\:ring-1:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-\[3px\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(3px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.focus-visible\:ring-ring:focus-visible,.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:color-mix(in oklab,var(--ring)50%,transparent)}}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color:var(--background)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has([data-slot=card-action]){grid-template-columns:1fr auto}.has-\[\>svg\]\:px-2\.5:has(>svg){padding-inline:calc(var(--spacing)*2.5)}.has-\[\>svg\]\:px-3:has(>svg){padding-inline:calc(var(--spacing)*3)}.has-\[\>svg\]\:px-4:has(>svg){padding-inline:calc(var(--spacing)*4)}.aria-invalid\:border-destructive[aria-invalid=true]{border-color:var(--destructive)}.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[inset\]\:pl-8[data-inset]{padding-left:calc(var(--spacing)*8)}.data-\[placeholder\]\:text-muted-foreground[data-placeholder]{color:var(--muted-foreground)}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:calc(2*var(--spacing)*-1)}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x:calc(var(--spacing)*-1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:calc(2*var(--spacing))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y:calc(var(--spacing)*-1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:calc(2*var(--spacing))}.data-\[size\=default\]\:h-9[data-size=default]{height:calc(var(--spacing)*9)}.data-\[size\=sm\]\:h-8[data-size=sm]{height:calc(var(--spacing)*8)}:is(.\*\:data-\[slot\=select-value\]\:line-clamp-1>*)[data-slot=select-value]{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}:is(.\*\:data-\[slot\=select-value\]\:flex>*)[data-slot=select-value]{display:flex}:is(.\*\:data-\[slot\=select-value\]\:items-center>*)[data-slot=select-value]{align-items:center}:is(.\*\:data-\[slot\=select-value\]\:gap-2>*)[data-slot=select-value]{gap:calc(var(--spacing)*2)}.data-\[state\=active\]\:bg-background[data-state=active]{background-color:var(--background)}.data-\[state\=active\]\:text-foreground[data-state=active]{color:var(--foreground)}.data-\[state\=active\]\:shadow[data-state=active]{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.data-\[state\=checked\]\:translate-x-4[data-state=checked]{--tw-translate-x:calc(var(--spacing)*4);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[state\=checked\]\:border-primary[data-state=checked]{border-color:var(--primary)}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:var(--primary)}.data-\[state\=checked\]\:text-primary-foreground[data-state=checked]{color:var(--primary-foreground)}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.data-\[state\=open\]\:animate-in[data-state=open]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:var(--accent)}.data-\[state\=open\]\:text-accent-foreground[data-state=open]{color:var(--accent-foreground)}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity:0}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:var(--input)}.data-\[variant\=destructive\]\:text-destructive[data-variant=destructive]{color:var(--destructive)}.data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:color-mix(in oklab,var(--destructive)10%,transparent)}}.data-\[variant\=destructive\]\:focus\:text-destructive[data-variant=destructive]:focus{color:var(--destructive)}@media (min-width:40rem){.sm\:col-span-2{grid-column:span 2/span 2}.sm\:w-64{width:calc(var(--spacing)*64)}.sm\:max-w-lg{max-width:var(--container-lg)}.sm\:flex-none{flex:none}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}}@media (min-width:48rem){.md\:col-span-2{grid-column:span 2/span 2}.md\:col-start-2{grid-column-start:2}.md\:inline{display:inline}.md\:max-w-2xl{max-width:var(--container-2xl)}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:gap-6{gap:calc(var(--spacing)*6)}.md\:gap-8{gap:calc(var(--spacing)*8)}.md\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}}@media (min-width:64rem){.lg\:col-span-3{grid-column:span 3/span 3}.lg\:max-w-4xl{max-width:var(--container-4xl)}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:items-center{align-items:center}.lg\:justify-between{justify-content:space-between}}@media (min-width:80rem){.xl\:col-span-2{grid-column:span 2/span 2}.xl\:col-span-4{grid-column:span 4/span 4}.xl\:max-w-5xl{max-width:var(--container-5xl)}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}.dark\:scale-0:is(.dark *){--tw-scale-x:0%;--tw-scale-y:0%;--tw-scale-z:0%;scale:var(--tw-scale-x)var(--tw-scale-y)}.dark\:scale-100:is(.dark *){--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x)var(--tw-scale-y)}.dark\:-rotate-90:is(.dark *){rotate:-90deg}.dark\:rotate-0:is(.dark *){rotate:none}.dark\:\!border-gray-500:is(.dark *){border-color:var(--color-gray-500)!important}.dark\:\!border-gray-600:is(.dark *){border-color:var(--color-gray-600)!important}.dark\:border-\[\#8B5CF6\]:is(.dark *){border-color:#8b5cf6}.dark\:border-\[\#8B5CF6\]\/20:is(.dark *){border-color:#8b5cf633}.dark\:border-\[\#8B5CF6\]\/30:is(.dark *){border-color:#8b5cf64d}.dark\:border-amber-800:is(.dark *){border-color:var(--color-amber-800)}.dark\:border-amber-900:is(.dark *){border-color:var(--color-amber-900)}.dark\:border-blue-400:is(.dark *){border-color:var(--color-blue-400)}.dark\:border-blue-500:is(.dark *){border-color:var(--color-blue-500)}.dark\:border-blue-700:is(.dark *){border-color:var(--color-blue-700)}.dark\:border-blue-800:is(.dark *){border-color:var(--color-blue-800)}.dark\:border-gray-500:is(.dark *){border-color:var(--color-gray-500)}.dark\:border-gray-600:is(.dark *){border-color:var(--color-gray-600)}.dark\:border-gray-700:is(.dark *){border-color:var(--color-gray-700)}.dark\:border-green-400:is(.dark *){border-color:var(--color-green-400)}.dark\:border-green-800:is(.dark *){border-color:var(--color-green-800)}.dark\:border-input:is(.dark *){border-color:var(--input)}.dark\:border-orange-400:is(.dark *){border-color:var(--color-orange-400)}.dark\:border-orange-800:is(.dark *){border-color:var(--color-orange-800)}.dark\:border-red-400:is(.dark *){border-color:var(--color-red-400)}.dark\:border-red-800:is(.dark *){border-color:var(--color-red-800)}.dark\:\!bg-gray-800\/90:is(.dark *){background-color:#1e2939e6!important}@supports (color:color-mix(in lab,red,red)){.dark\:\!bg-gray-800\/90:is(.dark *){background-color:color-mix(in oklab,var(--color-gray-800)90%,transparent)!important}}.dark\:bg-\[\#8B5CF6\]:is(.dark *){background-color:#8b5cf6}.dark\:bg-\[\#8B5CF6\]\/10:is(.dark *){background-color:#8b5cf61a}.dark\:bg-amber-950\/20:is(.dark *){background-color:#46190133}@supports (color:color-mix(in lab,red,red)){.dark\:bg-amber-950\/20:is(.dark *){background-color:color-mix(in oklab,var(--color-amber-950)20%,transparent)}}.dark\:bg-amber-950\/50:is(.dark *){background-color:#46190180}@supports (color:color-mix(in lab,red,red)){.dark\:bg-amber-950\/50:is(.dark *){background-color:color-mix(in oklab,var(--color-amber-950)50%,transparent)}}.dark\:bg-blue-500\/10:is(.dark *){background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-500\/10:is(.dark *){background-color:color-mix(in oklab,var(--color-blue-500)10%,transparent)}}.dark\:bg-blue-900:is(.dark *){background-color:var(--color-blue-900)}.dark\:bg-blue-900\/20:is(.dark *){background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-900\/20:is(.dark *){background-color:color-mix(in oklab,var(--color-blue-900)20%,transparent)}}.dark\:bg-blue-950\/20:is(.dark *){background-color:#16245633}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-950\/20:is(.dark *){background-color:color-mix(in oklab,var(--color-blue-950)20%,transparent)}}.dark\:bg-blue-950\/30:is(.dark *){background-color:#1624564d}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-950\/30:is(.dark *){background-color:color-mix(in oklab,var(--color-blue-950)30%,transparent)}}.dark\:bg-blue-950\/40:is(.dark *){background-color:#16245666}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-950\/40:is(.dark *){background-color:color-mix(in oklab,var(--color-blue-950)40%,transparent)}}.dark\:bg-blue-950\/50:is(.dark *){background-color:#16245680}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-950\/50:is(.dark *){background-color:color-mix(in oklab,var(--color-blue-950)50%,transparent)}}.dark\:bg-card:is(.dark *){background-color:var(--card)}.dark\:bg-destructive\/60:is(.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-destructive\/60:is(.dark *){background-color:color-mix(in oklab,var(--destructive)60%,transparent)}}.dark\:bg-foreground\/10:is(.dark *){background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-foreground\/10:is(.dark *){background-color:color-mix(in oklab,var(--foreground)10%,transparent)}}.dark\:bg-gray-500:is(.dark *){background-color:var(--color-gray-500)}.dark\:bg-gray-800:is(.dark *){background-color:var(--color-gray-800)}.dark\:bg-gray-800\/90:is(.dark *){background-color:#1e2939e6}@supports (color:color-mix(in lab,red,red)){.dark\:bg-gray-800\/90:is(.dark *){background-color:color-mix(in oklab,var(--color-gray-800)90%,transparent)}}.dark\:bg-gray-900:is(.dark *){background-color:var(--color-gray-900)}.dark\:bg-green-400:is(.dark *){background-color:var(--color-green-400)}.dark\:bg-green-500\/10:is(.dark *){background-color:#00c7581a}@supports (color:color-mix(in lab,red,red)){.dark\:bg-green-500\/10:is(.dark *){background-color:color-mix(in oklab,var(--color-green-500)10%,transparent)}}.dark\:bg-green-900:is(.dark *){background-color:var(--color-green-900)}.dark\:bg-green-950:is(.dark *){background-color:var(--color-green-950)}.dark\:bg-green-950\/20:is(.dark *){background-color:#032e1533}@supports (color:color-mix(in lab,red,red)){.dark\:bg-green-950\/20:is(.dark *){background-color:color-mix(in oklab,var(--color-green-950)20%,transparent)}}.dark\:bg-green-950\/50:is(.dark *){background-color:#032e1580}@supports (color:color-mix(in lab,red,red)){.dark\:bg-green-950\/50:is(.dark *){background-color:color-mix(in oklab,var(--color-green-950)50%,transparent)}}.dark\:bg-input\/30:is(.dark *){background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-input\/30:is(.dark *){background-color:color-mix(in oklab,var(--input)30%,transparent)}}.dark\:bg-orange-400:is(.dark *){background-color:var(--color-orange-400)}.dark\:bg-orange-900:is(.dark *){background-color:var(--color-orange-900)}.dark\:bg-orange-950:is(.dark *){background-color:var(--color-orange-950)}.dark\:bg-orange-950\/50:is(.dark *){background-color:#44130680}@supports (color:color-mix(in lab,red,red)){.dark\:bg-orange-950\/50:is(.dark *){background-color:color-mix(in oklab,var(--color-orange-950)50%,transparent)}}.dark\:bg-purple-900:is(.dark *){background-color:var(--color-purple-900)}.dark\:bg-red-400:is(.dark *){background-color:var(--color-red-400)}.dark\:bg-red-900:is(.dark *){background-color:var(--color-red-900)}.dark\:bg-red-950:is(.dark *){background-color:var(--color-red-950)}.dark\:bg-red-950\/20:is(.dark *){background-color:#46080933}@supports (color:color-mix(in lab,red,red)){.dark\:bg-red-950\/20:is(.dark *){background-color:color-mix(in oklab,var(--color-red-950)20%,transparent)}}.dark\:text-\[\#8B5CF6\]:is(.dark *){color:#8b5cf6}.dark\:text-amber-100:is(.dark *){color:var(--color-amber-100)}.dark\:text-amber-200:is(.dark *){color:var(--color-amber-200)}.dark\:text-amber-300:is(.dark *){color:var(--color-amber-300)}.dark\:text-amber-400:is(.dark *){color:var(--color-amber-400)}.dark\:text-amber-500:is(.dark *){color:var(--color-amber-500)}.dark\:text-blue-100:is(.dark *){color:var(--color-blue-100)}.dark\:text-blue-200:is(.dark *){color:var(--color-blue-200)}.dark\:text-blue-300:is(.dark *){color:var(--color-blue-300)}.dark\:text-blue-400:is(.dark *){color:var(--color-blue-400)}.dark\:text-blue-500:is(.dark *){color:var(--color-blue-500)}.dark\:text-gray-100:is(.dark *){color:var(--color-gray-100)}.dark\:text-gray-300:is(.dark *){color:var(--color-gray-300)}.dark\:text-gray-400:is(.dark *){color:var(--color-gray-400)}.dark\:text-green-100:is(.dark *){color:var(--color-green-100)}.dark\:text-green-200:is(.dark *){color:var(--color-green-200)}.dark\:text-green-300:is(.dark *){color:var(--color-green-300)}.dark\:text-green-400:is(.dark *){color:var(--color-green-400)}.dark\:text-orange-200:is(.dark *){color:var(--color-orange-200)}.dark\:text-orange-400:is(.dark *){color:var(--color-orange-400)}.dark\:text-purple-400:is(.dark *){color:var(--color-purple-400)}.dark\:text-red-200:is(.dark *){color:var(--color-red-200)}.dark\:text-red-400:is(.dark *){color:var(--color-red-400)}.dark\:text-yellow-400:is(.dark *){color:var(--color-yellow-400)}.dark\:opacity-30:is(.dark *){opacity:.3}@media (hover:hover){.dark\:hover\:border-gray-600:is(.dark *):hover{border-color:var(--color-gray-600)}.dark\:hover\:bg-accent\/50:is(.dark *):hover{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-accent\/50:is(.dark *):hover{background-color:color-mix(in oklab,var(--accent)50%,transparent)}}.dark\:hover\:bg-amber-950\/30:is(.dark *):hover{background-color:#4619014d}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-amber-950\/30:is(.dark *):hover{background-color:color-mix(in oklab,var(--color-amber-950)30%,transparent)}}.dark\:hover\:bg-gray-800:is(.dark *):hover{background-color:var(--color-gray-800)}.dark\:hover\:bg-input\/50:is(.dark *):hover{background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-input\/50:is(.dark *):hover{background-color:color-mix(in oklab,var(--input)50%,transparent)}}.dark\:hover\:bg-red-900\/20:is(.dark *):hover{background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-red-900\/20:is(.dark *):hover{background-color:color-mix(in oklab,var(--color-red-900)20%,transparent)}}}.dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible{--tw-ring-color:color-mix(in oklab,var(--destructive)40%,transparent)}}.dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid=true]{--tw-ring-color:color-mix(in oklab,var(--destructive)40%,transparent)}}.dark\:data-\[state\=checked\]\:bg-primary:is(.dark *)[data-state=checked]{background-color:var(--primary)}.dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is(.dark *)[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is(.dark *)[data-variant=destructive]:focus{background-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.\[\&_p\]\:leading-relaxed p{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 svg:not([class*=size-]){width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.\[\&_svg\:not\(\[class\*\=\'text-\'\]\)\]\:text-muted-foreground svg:not([class*=text-]){color:var(--muted-foreground)}.\[\.border-b\]\:pb-6.border-b{padding-bottom:calc(var(--spacing)*6)}.\[\.border-t\]\:pt-6.border-t{padding-top:calc(var(--spacing)*6)}:is(.\*\:\[span\]\:last\:flex>*):is(span):last-child{display:flex}:is(.\*\:\[span\]\:last\:items-center>*):is(span):last-child{align-items:center}:is(.\*\:\[span\]\:last\:gap-2>*):is(span):last-child{gap:calc(var(--spacing)*2)}:is(.data-\[variant\=destructive\]\:\*\:\[svg\]\:\!text-destructive[data-variant=destructive]>*):is(svg){color:var(--destructive)!important}.\[\&\>svg\]\:absolute>svg{position:absolute}.\[\&\>svg\]\:top-4>svg{top:calc(var(--spacing)*4)}.\[\&\>svg\]\:left-4>svg{left:calc(var(--spacing)*4)}.\[\&\>svg\]\:text-foreground>svg{color:var(--foreground)}.\[\&\>svg\+div\]\:translate-y-\[-3px\]>svg+div{--tw-translate-y:-3px;translate:var(--tw-translate-x)var(--tw-translate-y)}.\[\&\>svg\~\*\]\:pl-7>svg~*{padding-left:calc(var(--spacing)*7)}}@property --tw-animation-delay{syntax:"*";inherits:false;initial-value:0s}@property --tw-animation-direction{syntax:"*";inherits:false;initial-value:normal}@property --tw-animation-duration{syntax:"*";inherits:false}@property --tw-animation-fill-mode{syntax:"*";inherits:false;initial-value:none}@property --tw-animation-iteration-count{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-translate-y{syntax:"*";inherits:false;initial-value:0}:root{--radius:.625rem;--background:oklch(100% 0 0);--foreground:oklch(14.5% 0 0);--card:oklch(100% 0 0);--card-foreground:oklch(14.5% 0 0);--popover:oklch(100% 0 0);--popover-foreground:oklch(14.5% 0 0);--primary:oklch(48% .18 290);--primary-foreground:oklch(98.5% 0 0);--secondary:oklch(97% 0 0);--secondary-foreground:oklch(20.5% 0 0);--muted:oklch(97% 0 0);--muted-foreground:oklch(55.6% 0 0);--accent:oklch(97% 0 0);--accent-foreground:oklch(20.5% 0 0);--destructive:oklch(57.7% .245 27.325);--border:oklch(92.2% 0 0);--input:oklch(92.2% 0 0);--ring:oklch(70.8% 0 0);--chart-1:oklch(64.6% .222 41.116);--chart-2:oklch(60% .118 184.704);--chart-3:oklch(39.8% .07 227.392);--chart-4:oklch(82.8% .189 84.429);--chart-5:oklch(76.9% .188 70.08);--sidebar:oklch(98.5% 0 0);--sidebar-foreground:oklch(14.5% 0 0);--sidebar-primary:oklch(20.5% 0 0);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(97% 0 0);--sidebar-accent-foreground:oklch(20.5% 0 0);--sidebar-border:oklch(92.2% 0 0);--sidebar-ring:oklch(70.8% 0 0)}.dark{--background:oklch(14.5% 0 0);--foreground:oklch(98.5% 0 0);--card:oklch(20.5% 0 0);--card-foreground:oklch(98.5% 0 0);--popover:oklch(20.5% 0 0);--popover-foreground:oklch(98.5% 0 0);--primary:oklch(62% .2 290);--primary-foreground:oklch(98.5% 0 0);--secondary:oklch(26.9% 0 0);--secondary-foreground:oklch(98.5% 0 0);--muted:oklch(26.9% 0 0);--muted-foreground:oklch(70.8% 0 0);--accent:oklch(26.9% 0 0);--accent-foreground:oklch(98.5% 0 0);--destructive:oklch(70.4% .191 22.216);--border:oklch(100% 0 0/.1);--input:oklch(100% 0 0/.15);--ring:oklch(55.6% 0 0);--chart-1:oklch(48.8% .243 264.376);--chart-2:oklch(69.6% .17 162.48);--chart-3:oklch(76.9% .188 70.08);--chart-4:oklch(62.7% .265 303.9);--chart-5:oklch(64.5% .246 16.439);--sidebar:oklch(20.5% 0 0);--sidebar-foreground:oklch(98.5% 0 0);--sidebar-primary:oklch(48.8% .243 264.376);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(26.9% 0 0);--sidebar-accent-foreground:oklch(98.5% 0 0);--sidebar-border:oklch(100% 0 0/.1);--sidebar-ring:oklch(55.6% 0 0)}.workflow-chat-view .border-green-200{border-color:var(--color-emerald-200)}.workflow-chat-view .bg-green-50{background-color:var(--color-emerald-50)}.workflow-chat-view .bg-green-100{background-color:var(--color-emerald-100)}.workflow-chat-view .text-green-600{color:var(--color-emerald-600)}.workflow-chat-view .text-green-700{color:var(--color-emerald-700)}.workflow-chat-view .text-green-800{color:var(--color-emerald-800)}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0)scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1))rotate(var(--tw-enter-rotate,0));filter:blur(var(--tw-enter-blur,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0)scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1))rotate(var(--tw-exit-rotate,0));filter:blur(var(--tw-exit-blur,0))}}.react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #777;--xy-background-pattern-lines-color-default: #777;--xy-background-pattern-cross-color-default: #777;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))} +/*! tailwindcss v4.1.12 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-red-950:oklch(25.8% .092 26.042);--color-orange-50:oklch(98% .016 73.684);--color-orange-100:oklch(95.4% .038 75.164);--color-orange-200:oklch(90.1% .076 70.697);--color-orange-300:oklch(83.7% .128 66.29);--color-orange-400:oklch(75% .183 55.934);--color-orange-500:oklch(70.5% .213 47.604);--color-orange-600:oklch(64.6% .222 41.116);--color-orange-700:oklch(55.3% .195 38.402);--color-orange-800:oklch(47% .157 37.304);--color-orange-900:oklch(40.8% .123 38.172);--color-orange-950:oklch(26.6% .079 36.259);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-amber-950:oklch(27.9% .077 45.635);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-600:oklch(68.1% .162 75.834);--color-yellow-700:oklch(55.4% .135 66.442);--color-green-50:oklch(98.2% .018 155.826);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .084 155.995);--color-green-300:oklch(87.1% .15 154.449);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-green-800:oklch(44.8% .119 151.328);--color-green-900:oklch(39.3% .095 152.535);--color-green-950:oklch(26.6% .065 152.934);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-200:oklch(90.5% .093 164.15);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-700:oklch(50.8% .118 165.612);--color-emerald-800:oklch(43.2% .095 166.913);--color-blue-50:oklch(97% .014 254.604);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-blue-800:oklch(42.4% .199 265.638);--color-blue-900:oklch(37.9% .146 265.522);--color-blue-950:oklch(28.2% .091 267.935);--color-purple-50:oklch(97.7% .014 308.299);--color-purple-100:oklch(94.6% .033 307.174);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-purple-600:oklch(55.8% .288 302.321);--color-purple-900:oklch(38.1% .176 304.987);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-lg:32rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--container-5xl:64rem;--container-6xl:72rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wide:.025em;--tracking-wider:.05em;--tracking-widest:.1em;--leading-tight:1.25;--leading-relaxed:1.625;--drop-shadow-lg:0 4px 4px #00000026;--ease-out:cubic-bezier(0,0,.2,1);--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--animate-bounce:bounce 1s infinite;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{border-color:var(--border);outline-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){*{outline-color:color-mix(in oklab,var(--ring)50%,transparent)}}body{background-color:var(--background);color:var(--foreground)}}@layer components;@layer utilities{.\@container\/card-header{container:card-header/inline-size}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing)*0)}.inset-2{inset:calc(var(--spacing)*2)}.inset-y-0{inset-block:calc(var(--spacing)*0)}.top-1{top:calc(var(--spacing)*1)}.top-2{top:calc(var(--spacing)*2)}.top-4{top:calc(var(--spacing)*4)}.top-\[30px\]{top:30px}.-right-2{right:calc(var(--spacing)*-2)}.right-0{right:calc(var(--spacing)*0)}.right-1{right:calc(var(--spacing)*1)}.right-2{right:calc(var(--spacing)*2)}.right-4{right:calc(var(--spacing)*4)}.bottom-0{bottom:calc(var(--spacing)*0)}.bottom-24{bottom:calc(var(--spacing)*24)}.-left-2{left:calc(var(--spacing)*-2)}.left-0{left:calc(var(--spacing)*0)}.left-1\/2{left:50%}.left-2{left:calc(var(--spacing)*2)}.left-\[18px\]{left:18px}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.col-start-2{grid-column-start:2}.row-span-2{grid-row:span 2/span 2}.row-start-1{grid-row-start:1}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.container\!{width:100%!important}@media (min-width:40rem){.container\!{max-width:40rem!important}}@media (min-width:48rem){.container\!{max-width:48rem!important}}@media (min-width:64rem){.container\!{max-width:64rem!important}}@media (min-width:80rem){.container\!{max-width:80rem!important}}@media (min-width:96rem){.container\!{max-width:96rem!important}}.m-2{margin:calc(var(--spacing)*2)}.-mx-1{margin-inline:calc(var(--spacing)*-1)}.mx-4{margin-inline:calc(var(--spacing)*4)}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing)*1)}.my-2{margin-block:calc(var(--spacing)*2)}.my-3{margin-block:calc(var(--spacing)*3)}.my-4{margin-block:calc(var(--spacing)*4)}.mt-0{margin-top:calc(var(--spacing)*0)}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-12{margin-top:calc(var(--spacing)*12)}.mr-1{margin-right:calc(var(--spacing)*1)}.mr-2{margin-right:calc(var(--spacing)*2)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.ml-0{margin-left:calc(var(--spacing)*0)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-3{margin-left:calc(var(--spacing)*3)}.ml-4{margin-left:calc(var(--spacing)*4)}.ml-5{margin-left:calc(var(--spacing)*5)}.ml-6{margin-left:calc(var(--spacing)*6)}.ml-auto{margin-left:auto}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-3{-webkit-line-clamp:3;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.field-sizing-content{field-sizing:content}.size-2{width:calc(var(--spacing)*2);height:calc(var(--spacing)*2)}.size-3\.5{width:calc(var(--spacing)*3.5);height:calc(var(--spacing)*3.5)}.size-4{width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.size-9{width:calc(var(--spacing)*9);height:calc(var(--spacing)*9)}.\!h-2{height:calc(var(--spacing)*2)!important}.h-0{height:calc(var(--spacing)*0)}.h-0\.5{height:calc(var(--spacing)*.5)}.h-1{height:calc(var(--spacing)*1)}.h-2{height:calc(var(--spacing)*2)}.h-2\.5{height:calc(var(--spacing)*2.5)}.h-3{height:calc(var(--spacing)*3)}.h-3\.5{height:calc(var(--spacing)*3.5)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-8{height:calc(var(--spacing)*8)}.h-9{height:calc(var(--spacing)*9)}.h-10{height:calc(var(--spacing)*10)}.h-12{height:calc(var(--spacing)*12)}.h-14{height:calc(var(--spacing)*14)}.h-16{height:calc(var(--spacing)*16)}.h-32{height:calc(var(--spacing)*32)}.h-96{height:calc(var(--spacing)*96)}.h-\[1\.2rem\]{height:1.2rem}.h-\[1px\]{height:1px}.h-\[85vh\]{height:85vh}.h-\[500px\]{height:500px}.h-\[calc\(100\%\+8px\)\]{height:calc(100% + 8px)}.h-\[calc\(100vh-3\.5rem\)\]{height:calc(100vh - 3.5rem)}.h-\[calc\(100vh-3\.7rem\)\]{height:calc(100vh - 3.7rem)}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-\(--radix-dropdown-menu-content-available-height\){max-height:var(--radix-dropdown-menu-content-available-height)}.max-h-\(--radix-select-content-available-height\){max-height:var(--radix-select-content-available-height)}.max-h-20{max-height:calc(var(--spacing)*20)}.max-h-32{max-height:calc(var(--spacing)*32)}.max-h-40{max-height:calc(var(--spacing)*40)}.max-h-48{max-height:calc(var(--spacing)*48)}.max-h-60{max-height:calc(var(--spacing)*60)}.max-h-64{max-height:calc(var(--spacing)*64)}.max-h-\[85vh\]{max-height:85vh}.max-h-\[90vh\]{max-height:90vh}.max-h-\[200px\]{max-height:200px}.max-h-\[400px\]{max-height:400px}.max-h-none{max-height:none}.max-h-screen{max-height:100vh}.\!min-h-0{min-height:calc(var(--spacing)*0)!important}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-16{min-height:calc(var(--spacing)*16)}.min-h-\[36px\]{min-height:36px}.min-h-\[40px\]{min-height:40px}.min-h-\[50vh\]{min-height:50vh}.min-h-\[400px\]{min-height:400px}.min-h-screen{min-height:100vh}.\!w-2{width:calc(var(--spacing)*2)!important}.w-1{width:calc(var(--spacing)*1)}.w-2{width:calc(var(--spacing)*2)}.w-2\.5{width:calc(var(--spacing)*2.5)}.w-3{width:calc(var(--spacing)*3)}.w-3\.5{width:calc(var(--spacing)*3.5)}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-8{width:calc(var(--spacing)*8)}.w-9{width:calc(var(--spacing)*9)}.w-10{width:calc(var(--spacing)*10)}.w-12{width:calc(var(--spacing)*12)}.w-16{width:calc(var(--spacing)*16)}.w-56{width:calc(var(--spacing)*56)}.w-64{width:calc(var(--spacing)*64)}.w-80{width:calc(var(--spacing)*80)}.w-\[1\.2rem\]{width:1.2rem}.w-\[1px\]{width:1px}.w-\[28rem\]{width:28rem}.w-\[90vw\]{width:90vw}.w-\[600px\]{width:600px}.w-\[800px\]{width:800px}.w-fit{width:fit-content}.w-full{width:100%}.w-px{width:1px}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-7xl{max-width:var(--container-7xl)}.max-w-\[80\%\]{max-width:80%}.max-w-\[90vw\]{max-width:90vw}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-none{max-width:none}.\!min-w-0{min-width:calc(var(--spacing)*0)!important}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-\[8rem\]{min-width:8rem}.min-w-\[300px\]{min-width:300px}.min-w-\[400px\]{min-width:400px}.min-w-\[800px\]{min-width:800px}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.min-w-full{min-width:100%}.flex-1{flex:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.origin-\(--radix-dropdown-menu-content-transform-origin\){transform-origin:var(--radix-dropdown-menu-content-transform-origin)}.origin-\(--radix-select-content-transform-origin\){transform-origin:var(--radix-select-content-transform-origin)}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-4{--tw-translate-x:calc(var(--spacing)*4);translate:var(--tw-translate-x)var(--tw-translate-y)}.scale-0{--tw-scale-x:0%;--tw-scale-y:0%;--tw-scale-z:0%;scale:var(--tw-scale-x)var(--tw-scale-y)}.scale-75{--tw-scale-x:75%;--tw-scale-y:75%;--tw-scale-z:75%;scale:var(--tw-scale-x)var(--tw-scale-y)}.scale-100{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x)var(--tw-scale-y)}.rotate-0{rotate:none}.rotate-90{rotate:90deg}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-bounce{animation:var(--animate-bounce)}.animate-in{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-col-resize{cursor:col-resize}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.resize{resize:both}.resize-none{resize:none}.scroll-my-1{scroll-margin-block:calc(var(--spacing)*1)}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\[auto_auto_1fr_auto\]{grid-template-columns:auto auto 1fr auto}.grid-rows-\[auto_auto\]{grid-template-rows:auto auto}.flex-col{flex-direction:column}.flex-row-reverse{flex-direction:row-reverse}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0{gap:calc(var(--spacing)*0)}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-6{gap:calc(var(--spacing)*6)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*1)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*2)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-x-reverse)))}.self-start{align-self:flex-start}.justify-self-end{justify-self:flex-end}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.\!rounded-full{border-radius:3.40282e38px!important}.rounded{border-radius:.25rem}.rounded-\[4px\]{border-radius:4px}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-none{border-radius:0}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-l-none{border-top-left-radius:0;border-bottom-left-radius:0}.rounded-r-none{border-top-right-radius:0;border-bottom-right-radius:0}.\!border{border-style:var(--tw-border-style)!important;border-width:1px!important}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-0{border-left-style:var(--tw-border-style);border-left-width:0}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.\!border-gray-600{border-color:var(--color-gray-600)!important}.border-\[\#643FB2\]{border-color:#643fb2}.border-\[\#643FB2\]\/20{border-color:#643fb233}.border-\[\#643FB2\]\/30{border-color:#643fb24d}.border-amber-200{border-color:var(--color-amber-200)}.border-blue-200{border-color:var(--color-blue-200)}.border-blue-300{border-color:var(--color-blue-300)}.border-blue-400{border-color:var(--color-blue-400)}.border-blue-500{border-color:var(--color-blue-500)}.border-border,.border-border\/50{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-border\/50{border-color:color-mix(in oklab,var(--border)50%,transparent)}}.border-current\/30{border-color:currentColor}@supports (color:color-mix(in lab,red,red)){.border-current\/30{border-color:color-mix(in oklab,currentcolor 30%,transparent)}}.border-destructive\/30{border-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.border-destructive\/30{border-color:color-mix(in oklab,var(--destructive)30%,transparent)}}.border-foreground\/5{border-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.border-foreground\/5{border-color:color-mix(in oklab,var(--foreground)5%,transparent)}}.border-foreground\/10{border-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.border-foreground\/10{border-color:color-mix(in oklab,var(--foreground)10%,transparent)}}.border-foreground\/20{border-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.border-foreground\/20{border-color:color-mix(in oklab,var(--foreground)20%,transparent)}}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-gray-400{border-color:var(--color-gray-400)}.border-gray-500\/20{border-color:#6a728233}@supports (color:color-mix(in lab,red,red)){.border-gray-500\/20{border-color:color-mix(in oklab,var(--color-gray-500)20%,transparent)}}.border-green-200{border-color:var(--color-green-200)}.border-green-500{border-color:var(--color-green-500)}.border-green-500\/20{border-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.border-green-500\/20{border-color:color-mix(in oklab,var(--color-green-500)20%,transparent)}}.border-green-500\/40{border-color:#00c75866}@supports (color:color-mix(in lab,red,red)){.border-green-500\/40{border-color:color-mix(in oklab,var(--color-green-500)40%,transparent)}}.border-green-600\/20{border-color:#00a54433}@supports (color:color-mix(in lab,red,red)){.border-green-600\/20{border-color:color-mix(in oklab,var(--color-green-600)20%,transparent)}}.border-input{border-color:var(--input)}.border-muted{border-color:var(--muted)}.border-orange-200{border-color:var(--color-orange-200)}.border-orange-300{border-color:var(--color-orange-300)}.border-orange-500{border-color:var(--color-orange-500)}.border-orange-500\/20{border-color:#fe6e0033}@supports (color:color-mix(in lab,red,red)){.border-orange-500\/20{border-color:color-mix(in oklab,var(--color-orange-500)20%,transparent)}}.border-orange-500\/40{border-color:#fe6e0066}@supports (color:color-mix(in lab,red,red)){.border-orange-500\/40{border-color:color-mix(in oklab,var(--color-orange-500)40%,transparent)}}.border-orange-600\/20{border-color:#f0510033}@supports (color:color-mix(in lab,red,red)){.border-orange-600\/20{border-color:color-mix(in oklab,var(--color-orange-600)20%,transparent)}}.border-primary,.border-primary\/20{border-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.border-primary\/20{border-color:color-mix(in oklab,var(--primary)20%,transparent)}}.border-red-200{border-color:var(--color-red-200)}.border-red-500{border-color:var(--color-red-500)}.border-red-500\/20{border-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.border-red-500\/20{border-color:color-mix(in oklab,var(--color-red-500)20%,transparent)}}.border-transparent{border-color:#0000}.border-yellow-200{border-color:var(--color-yellow-200)}.border-t-transparent{border-top-color:#0000}.border-l-transparent{border-left-color:#0000}.bg-\[\#643FB2\]{background-color:#643fb2}.bg-\[\#643FB2\]\/10{background-color:#643fb21a}.bg-accent\/10{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.bg-accent\/10{background-color:color-mix(in oklab,var(--accent)10%,transparent)}}.bg-amber-50{background-color:var(--color-amber-50)}.bg-background,.bg-background\/50{background-color:var(--background)}@supports (color:color-mix(in lab,red,red)){.bg-background\/50{background-color:color-mix(in oklab,var(--background)50%,transparent)}}.bg-black{background-color:var(--color-black)}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black)50%,transparent)}}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black)60%,transparent)}}.bg-blue-50{background-color:var(--color-blue-50)}.bg-blue-50\/80{background-color:#eff6ffcc}@supports (color:color-mix(in lab,red,red)){.bg-blue-50\/80{background-color:color-mix(in oklab,var(--color-blue-50)80%,transparent)}}.bg-blue-50\/95{background-color:#eff6fff2}@supports (color:color-mix(in lab,red,red)){.bg-blue-50\/95{background-color:color-mix(in oklab,var(--color-blue-50)95%,transparent)}}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-500{background-color:var(--color-blue-500)}.bg-blue-500\/5{background-color:#3080ff0d}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/5{background-color:color-mix(in oklab,var(--color-blue-500)5%,transparent)}}.bg-blue-500\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/10{background-color:color-mix(in oklab,var(--color-blue-500)10%,transparent)}}.bg-blue-600{background-color:var(--color-blue-600)}.bg-border{background-color:var(--border)}.bg-card{background-color:var(--card)}.bg-current{background-color:currentColor}.bg-destructive,.bg-destructive\/10{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.bg-destructive\/10{background-color:color-mix(in oklab,var(--destructive)10%,transparent)}}.bg-foreground\/5{background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.bg-foreground\/5{background-color:color-mix(in oklab,var(--foreground)5%,transparent)}}.bg-foreground\/10{background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.bg-foreground\/10{background-color:color-mix(in oklab,var(--foreground)10%,transparent)}}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-200{background-color:var(--color-gray-200)}.bg-gray-400{background-color:var(--color-gray-400)}.bg-gray-500\/10{background-color:#6a72821a}@supports (color:color-mix(in lab,red,red)){.bg-gray-500\/10{background-color:color-mix(in oklab,var(--color-gray-500)10%,transparent)}}.bg-gray-900\/90{background-color:#101828e6}@supports (color:color-mix(in lab,red,red)){.bg-gray-900\/90{background-color:color-mix(in oklab,var(--color-gray-900)90%,transparent)}}.bg-green-50{background-color:var(--color-green-50)}.bg-green-100{background-color:var(--color-green-100)}.bg-green-500{background-color:var(--color-green-500)}.bg-green-500\/5{background-color:#00c7580d}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/5{background-color:color-mix(in oklab,var(--color-green-500)5%,transparent)}}.bg-green-500\/10{background-color:#00c7581a}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/10{background-color:color-mix(in oklab,var(--color-green-500)10%,transparent)}}.bg-muted{background-color:var(--muted)}.bg-muted-foreground\/30{background-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.bg-muted-foreground\/30{background-color:color-mix(in oklab,var(--muted-foreground)30%,transparent)}}.bg-muted\/30{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\/30{background-color:color-mix(in oklab,var(--muted)30%,transparent)}}.bg-muted\/50{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\/50{background-color:color-mix(in oklab,var(--muted)50%,transparent)}}.bg-orange-50{background-color:var(--color-orange-50)}.bg-orange-50\/50{background-color:#fff7ed80}@supports (color:color-mix(in lab,red,red)){.bg-orange-50\/50{background-color:color-mix(in oklab,var(--color-orange-50)50%,transparent)}}.bg-orange-100{background-color:var(--color-orange-100)}.bg-orange-100\/50{background-color:#ffedd580}@supports (color:color-mix(in lab,red,red)){.bg-orange-100\/50{background-color:color-mix(in oklab,var(--color-orange-100)50%,transparent)}}.bg-orange-500{background-color:var(--color-orange-500)}.bg-orange-500\/5{background-color:#fe6e000d}@supports (color:color-mix(in lab,red,red)){.bg-orange-500\/5{background-color:color-mix(in oklab,var(--color-orange-500)5%,transparent)}}.bg-orange-500\/10{background-color:#fe6e001a}@supports (color:color-mix(in lab,red,red)){.bg-orange-500\/10{background-color:color-mix(in oklab,var(--color-orange-500)10%,transparent)}}.bg-popover{background-color:var(--popover)}.bg-primary,.bg-primary\/10{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/10{background-color:color-mix(in oklab,var(--primary)10%,transparent)}}.bg-primary\/30{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/30{background-color:color-mix(in oklab,var(--primary)30%,transparent)}}.bg-primary\/40{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/40{background-color:color-mix(in oklab,var(--primary)40%,transparent)}}.bg-purple-50{background-color:var(--color-purple-50)}.bg-purple-100{background-color:var(--color-purple-100)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-500{background-color:var(--color-red-500)}.bg-red-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.bg-secondary{background-color:var(--secondary)}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-white\/60{background-color:#fff9}@supports (color:color-mix(in lab,red,red)){.bg-white\/60{background-color:color-mix(in oklab,var(--color-white)60%,transparent)}}.bg-white\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab,red,red)){.bg-white\/90{background-color:color-mix(in oklab,var(--color-white)90%,transparent)}}.bg-yellow-100{background-color:var(--color-yellow-100)}.fill-current{fill:currentColor}.object-cover{object-fit:cover}.p-0{padding:calc(var(--spacing)*0)}.p-1{padding:calc(var(--spacing)*1)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-6{padding:calc(var(--spacing)*6)}.p-8{padding:calc(var(--spacing)*8)}.p-\[1px\]{padding:1px}.px-1{padding-inline:calc(var(--spacing)*1)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-6{padding-inline:calc(var(--spacing)*6)}.px-8{padding-inline:calc(var(--spacing)*8)}.py-0{padding-block:calc(var(--spacing)*0)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-2\.5{padding-block:calc(var(--spacing)*2.5)}.py-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.py-6{padding-block:calc(var(--spacing)*6)}.py-8{padding-block:calc(var(--spacing)*8)}.pt-0{padding-top:calc(var(--spacing)*0)}.pt-1{padding-top:calc(var(--spacing)*1)}.pt-2{padding-top:calc(var(--spacing)*2)}.pt-3{padding-top:calc(var(--spacing)*3)}.pt-4{padding-top:calc(var(--spacing)*4)}.pt-6{padding-top:calc(var(--spacing)*6)}.pt-8{padding-top:calc(var(--spacing)*8)}.pr-2{padding-right:calc(var(--spacing)*2)}.pr-4{padding-right:calc(var(--spacing)*4)}.pr-8{padding-right:calc(var(--spacing)*8)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-3{padding-bottom:calc(var(--spacing)*3)}.pb-4{padding-bottom:calc(var(--spacing)*4)}.pb-6{padding-bottom:calc(var(--spacing)*6)}.pl-2{padding-left:calc(var(--spacing)*2)}.pl-3{padding-left:calc(var(--spacing)*3)}.pl-4{padding-left:calc(var(--spacing)*4)}.pl-5{padding-left:calc(var(--spacing)*5)}.pl-8{padding-left:calc(var(--spacing)*8)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[\#643FB2\]{color:#643fb2}.text-amber-500{color:var(--color-amber-500)}.text-amber-600{color:var(--color-amber-600)}.text-amber-700{color:var(--color-amber-700)}.text-amber-800{color:var(--color-amber-800)}.text-amber-900{color:var(--color-amber-900)}.text-blue-500{color:var(--color-blue-500)}.text-blue-500\/80{color:#3080ffcc}@supports (color:color-mix(in lab,red,red)){.text-blue-500\/80{color:color-mix(in oklab,var(--color-blue-500)80%,transparent)}}.text-blue-600{color:var(--color-blue-600)}.text-blue-700{color:var(--color-blue-700)}.text-blue-800{color:var(--color-blue-800)}.text-blue-900{color:var(--color-blue-900)}.text-card-foreground{color:var(--card-foreground)}.text-current{color:currentColor}.text-destructive,.text-destructive\/70{color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.text-destructive\/70{color:color-mix(in oklab,var(--destructive)70%,transparent)}}.text-destructive\/90{color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.text-destructive\/90{color:color-mix(in oklab,var(--destructive)90%,transparent)}}.text-foreground{color:var(--foreground)}.text-gray-300{color:var(--color-gray-300)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-900{color:var(--color-gray-900)}.text-green-500{color:var(--color-green-500)}.text-green-600{color:var(--color-green-600)}.text-green-700{color:var(--color-green-700)}.text-green-800{color:var(--color-green-800)}.text-green-900{color:var(--color-green-900)}.text-muted-foreground,.text-muted-foreground\/60{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\/60{color:color-mix(in oklab,var(--muted-foreground)60%,transparent)}}.text-muted-foreground\/70{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\/70{color:color-mix(in oklab,var(--muted-foreground)70%,transparent)}}.text-muted-foreground\/80{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\/80{color:color-mix(in oklab,var(--muted-foreground)80%,transparent)}}.text-orange-500{color:var(--color-orange-500)}.text-orange-600{color:var(--color-orange-600)}.text-orange-700{color:var(--color-orange-700)}.text-orange-800{color:var(--color-orange-800)}.text-orange-900{color:var(--color-orange-900)}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-purple-500{color:var(--color-purple-500)}.text-purple-600{color:var(--color-purple-600)}.text-red-500{color:var(--color-red-500)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-red-800{color:var(--color-red-800)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-white{color:var(--color-white)}.text-yellow-600{color:var(--color-yellow-600)}.text-yellow-700{color:var(--color-yellow-700)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.opacity-100{opacity:1}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xs{--tw-shadow:0 1px 2px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-0{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(0px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[\#643FB2\]\/20{--tw-shadow-color:#643fb233}@supports (color:color-mix(in lab,red,red)){.shadow-\[\#643FB2\]\/20{--tw-shadow-color:color-mix(in oklab,oklab(47.4316% .069152 -.159147/.2) var(--tw-shadow-alpha),transparent)}}.shadow-green-500\/20{--tw-shadow-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.shadow-green-500\/20{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-green-500)20%,transparent)var(--tw-shadow-alpha),transparent)}}.shadow-orange-500\/20{--tw-shadow-color:#fe6e0033}@supports (color:color-mix(in lab,red,red)){.shadow-orange-500\/20{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-orange-500)20%,transparent)var(--tw-shadow-alpha),transparent)}}.shadow-primary\/25{--tw-shadow-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.shadow-primary\/25{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--primary)25%,transparent)var(--tw-shadow-alpha),transparent)}}.shadow-red-500\/20{--tw-shadow-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.shadow-red-500\/20{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-red-500)20%,transparent)var(--tw-shadow-alpha),transparent)}}.ring-blue-500{--tw-ring-color:var(--color-blue-500)}.ring-blue-500\/20{--tw-ring-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.ring-blue-500\/20{--tw-ring-color:color-mix(in oklab,var(--color-blue-500)20%,transparent)}}.ring-offset-2{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.ring-offset-background{--tw-ring-offset-color:var(--background)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.drop-shadow-lg{--tw-drop-shadow-size:drop-shadow(0 4px 4px var(--tw-drop-shadow-color,#00000026));--tw-drop-shadow:drop-shadow(var(--drop-shadow-lg));filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,visibility,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[color\,box-shadow\]{transition-property:color,box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-none{transition-property:none}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.\[animation-delay\:-0\.3s\]{animation-delay:-.3s}.\[animation-delay\:-0\.15s\]{animation-delay:-.15s}.fade-in{--tw-enter-opacity:0}.running{animation-play-state:running}.slide-in-from-bottom-2{--tw-enter-translate-y:calc(2*var(--spacing))}.group-open\:rotate-90:is(:where(.group):is([open],:popover-open,:open) *){rotate:90deg}.group-open\:rotate-180:is(:where(.group):is([open],:popover-open,:open) *){rotate:180deg}@media (hover:hover){.group-hover\:bg-primary:is(:where(.group):hover *){background-color:var(--primary)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.group-hover\:shadow-md:is(:where(.group):hover *){--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.group-hover\:shadow-primary\/20:is(:where(.group):hover *){--tw-shadow-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.group-hover\:shadow-primary\/20:is(:where(.group):hover *){--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--primary)20%,transparent)var(--tw-shadow-alpha),transparent)}}}.group-data-\[disabled\=true\]\:pointer-events-none:is(:where(.group)[data-disabled=true] *){pointer-events:none}.group-data-\[disabled\=true\]\:opacity-50:is(:where(.group)[data-disabled=true] *){opacity:.5}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-50:is(:where(.peer):disabled~*){opacity:.5}.selection\:bg-primary ::selection{background-color:var(--primary)}.selection\:bg-primary::selection{background-color:var(--primary)}.selection\:text-primary-foreground ::selection{color:var(--primary-foreground)}.selection\:text-primary-foreground::selection{color:var(--primary-foreground)}.file\:inline-flex::file-selector-button{display:inline-flex}.file\:h-7::file-selector-button{height:calc(var(--spacing)*7)}.file\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\:bg-transparent::file-selector-button{background-color:#0000}.file\:text-sm::file-selector-button{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.file\:font-medium::file-selector-button{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.file\:text-foreground::file-selector-button{color:var(--foreground)}.placeholder\:text-muted-foreground::placeholder{color:var(--muted-foreground)}.first\:mt-0:first-child{margin-top:calc(var(--spacing)*0)}.last\:border-r-0:last-child{border-right-style:var(--tw-border-style);border-right-width:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media (hover:hover){.hover\:border-gray-300:hover{border-color:var(--color-gray-300)}.hover\:border-muted-foreground\/30:hover{border-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.hover\:border-muted-foreground\/30:hover{border-color:color-mix(in oklab,var(--muted-foreground)30%,transparent)}}.hover\:bg-accent:hover,.hover\:bg-accent\/50:hover{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-accent\/50:hover{background-color:color-mix(in oklab,var(--accent)50%,transparent)}}.hover\:bg-amber-100:hover{background-color:var(--color-amber-100)}.hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.hover\:bg-destructive\/80:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/80:hover{background-color:color-mix(in oklab,var(--destructive)80%,transparent)}}.hover\:bg-destructive\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab,var(--destructive)90%,transparent)}}.hover\:bg-muted:hover,.hover\:bg-muted\/50:hover{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-muted\/50:hover{background-color:color-mix(in oklab,var(--muted)50%,transparent)}}.hover\:bg-orange-100:hover{background-color:var(--color-orange-100)}.hover\:bg-primary\/20:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/20:hover{background-color:color-mix(in oklab,var(--primary)20%,transparent)}}.hover\:bg-primary\/80:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/80:hover{background-color:color-mix(in oklab,var(--primary)80%,transparent)}}.hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--primary)90%,transparent)}}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-secondary\/80:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab,var(--secondary)80%,transparent)}}.hover\:bg-white:hover{background-color:var(--color-white)}.hover\:text-accent-foreground:hover{color:var(--accent-foreground)}.hover\:text-destructive\/80:hover{color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\:text-destructive\/80:hover{color:color-mix(in oklab,var(--destructive)80%,transparent)}}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:text-orange-900:hover{color:var(--color-orange-900)}.hover\:text-primary:hover{color:var(--primary)}.hover\:text-red-600:hover{color:var(--color-red-600)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-70:hover{opacity:.7}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.focus\:bg-accent:focus{background-color:var(--accent)}.focus\:text-accent-foreground:focus{color:var(--accent-foreground)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-ring:focus{--tw-ring-color:var(--ring)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:border-ring:focus-visible{border-color:var(--ring)}.focus-visible\:ring-1:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-\[3px\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(3px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.focus-visible\:ring-ring:focus-visible,.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:color-mix(in oklab,var(--ring)50%,transparent)}}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color:var(--background)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has([data-slot=card-action]){grid-template-columns:1fr auto}.has-\[\>svg\]\:px-2\.5:has(>svg){padding-inline:calc(var(--spacing)*2.5)}.has-\[\>svg\]\:px-3:has(>svg){padding-inline:calc(var(--spacing)*3)}.has-\[\>svg\]\:px-4:has(>svg){padding-inline:calc(var(--spacing)*4)}.aria-invalid\:border-destructive[aria-invalid=true]{border-color:var(--destructive)}.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[inset\]\:pl-8[data-inset]{padding-left:calc(var(--spacing)*8)}.data-\[placeholder\]\:text-muted-foreground[data-placeholder]{color:var(--muted-foreground)}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:calc(2*var(--spacing)*-1)}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x:calc(var(--spacing)*-1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:calc(2*var(--spacing))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y:calc(var(--spacing)*-1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:calc(2*var(--spacing))}.data-\[size\=default\]\:h-9[data-size=default]{height:calc(var(--spacing)*9)}.data-\[size\=sm\]\:h-8[data-size=sm]{height:calc(var(--spacing)*8)}:is(.\*\:data-\[slot\=select-value\]\:line-clamp-1>*)[data-slot=select-value]{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}:is(.\*\:data-\[slot\=select-value\]\:flex>*)[data-slot=select-value]{display:flex}:is(.\*\:data-\[slot\=select-value\]\:items-center>*)[data-slot=select-value]{align-items:center}:is(.\*\:data-\[slot\=select-value\]\:gap-2>*)[data-slot=select-value]{gap:calc(var(--spacing)*2)}.data-\[state\=active\]\:bg-background[data-state=active]{background-color:var(--background)}.data-\[state\=active\]\:text-foreground[data-state=active]{color:var(--foreground)}.data-\[state\=active\]\:shadow[data-state=active]{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.data-\[state\=checked\]\:translate-x-4[data-state=checked]{--tw-translate-x:calc(var(--spacing)*4);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[state\=checked\]\:border-primary[data-state=checked]{border-color:var(--primary)}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:var(--primary)}.data-\[state\=checked\]\:text-primary-foreground[data-state=checked]{color:var(--primary-foreground)}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.data-\[state\=open\]\:animate-in[data-state=open]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:var(--accent)}.data-\[state\=open\]\:text-accent-foreground[data-state=open]{color:var(--accent-foreground)}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity:0}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:var(--input)}.data-\[variant\=destructive\]\:text-destructive[data-variant=destructive]{color:var(--destructive)}.data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:color-mix(in oklab,var(--destructive)10%,transparent)}}.data-\[variant\=destructive\]\:focus\:text-destructive[data-variant=destructive]:focus{color:var(--destructive)}@media (min-width:40rem){.sm\:col-span-2{grid-column:span 2/span 2}.sm\:w-64{width:calc(var(--spacing)*64)}.sm\:max-w-lg{max-width:var(--container-lg)}.sm\:flex-none{flex:none}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}}@media (min-width:48rem){.md\:col-span-2{grid-column:span 2/span 2}.md\:col-start-2{grid-column-start:2}.md\:inline{display:inline}.md\:max-w-2xl{max-width:var(--container-2xl)}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:gap-8{gap:calc(var(--spacing)*8)}.md\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}}@media (min-width:64rem){.lg\:col-span-3{grid-column:span 3/span 3}.lg\:max-w-4xl{max-width:var(--container-4xl)}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:items-center{align-items:center}.lg\:justify-between{justify-content:space-between}}@media (min-width:80rem){.xl\:col-span-2{grid-column:span 2/span 2}.xl\:col-span-4{grid-column:span 4/span 4}.xl\:max-w-5xl{max-width:var(--container-5xl)}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}.dark\:scale-0:is(.dark *){--tw-scale-x:0%;--tw-scale-y:0%;--tw-scale-z:0%;scale:var(--tw-scale-x)var(--tw-scale-y)}.dark\:scale-100:is(.dark *){--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%;scale:var(--tw-scale-x)var(--tw-scale-y)}.dark\:-rotate-90:is(.dark *){rotate:-90deg}.dark\:rotate-0:is(.dark *){rotate:none}.dark\:\!border-gray-500:is(.dark *){border-color:var(--color-gray-500)!important}.dark\:\!border-gray-600:is(.dark *){border-color:var(--color-gray-600)!important}.dark\:border-\[\#8B5CF6\]:is(.dark *){border-color:#8b5cf6}.dark\:border-\[\#8B5CF6\]\/20:is(.dark *){border-color:#8b5cf633}.dark\:border-\[\#8B5CF6\]\/30:is(.dark *){border-color:#8b5cf64d}.dark\:border-amber-800:is(.dark *){border-color:var(--color-amber-800)}.dark\:border-amber-900:is(.dark *){border-color:var(--color-amber-900)}.dark\:border-blue-400:is(.dark *){border-color:var(--color-blue-400)}.dark\:border-blue-500:is(.dark *){border-color:var(--color-blue-500)}.dark\:border-blue-700:is(.dark *){border-color:var(--color-blue-700)}.dark\:border-blue-800:is(.dark *){border-color:var(--color-blue-800)}.dark\:border-gray-500:is(.dark *){border-color:var(--color-gray-500)}.dark\:border-gray-600:is(.dark *){border-color:var(--color-gray-600)}.dark\:border-gray-700:is(.dark *){border-color:var(--color-gray-700)}.dark\:border-green-400:is(.dark *){border-color:var(--color-green-400)}.dark\:border-green-800:is(.dark *){border-color:var(--color-green-800)}.dark\:border-input:is(.dark *){border-color:var(--input)}.dark\:border-orange-400:is(.dark *){border-color:var(--color-orange-400)}.dark\:border-orange-700:is(.dark *){border-color:var(--color-orange-700)}.dark\:border-orange-800:is(.dark *){border-color:var(--color-orange-800)}.dark\:border-red-400:is(.dark *){border-color:var(--color-red-400)}.dark\:border-red-800:is(.dark *){border-color:var(--color-red-800)}.dark\:\!bg-gray-800\/90:is(.dark *){background-color:#1e2939e6!important}@supports (color:color-mix(in lab,red,red)){.dark\:\!bg-gray-800\/90:is(.dark *){background-color:color-mix(in oklab,var(--color-gray-800)90%,transparent)!important}}.dark\:bg-\[\#8B5CF6\]:is(.dark *){background-color:#8b5cf6}.dark\:bg-\[\#8B5CF6\]\/10:is(.dark *){background-color:#8b5cf61a}.dark\:bg-amber-950\/20:is(.dark *){background-color:#46190133}@supports (color:color-mix(in lab,red,red)){.dark\:bg-amber-950\/20:is(.dark *){background-color:color-mix(in oklab,var(--color-amber-950)20%,transparent)}}.dark\:bg-amber-950\/50:is(.dark *){background-color:#46190180}@supports (color:color-mix(in lab,red,red)){.dark\:bg-amber-950\/50:is(.dark *){background-color:color-mix(in oklab,var(--color-amber-950)50%,transparent)}}.dark\:bg-blue-500\/10:is(.dark *){background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-500\/10:is(.dark *){background-color:color-mix(in oklab,var(--color-blue-500)10%,transparent)}}.dark\:bg-blue-900:is(.dark *){background-color:var(--color-blue-900)}.dark\:bg-blue-900\/20:is(.dark *){background-color:#1c398e33}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-900\/20:is(.dark *){background-color:color-mix(in oklab,var(--color-blue-900)20%,transparent)}}.dark\:bg-blue-950\/20:is(.dark *){background-color:#16245633}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-950\/20:is(.dark *){background-color:color-mix(in oklab,var(--color-blue-950)20%,transparent)}}.dark\:bg-blue-950\/40:is(.dark *){background-color:#16245666}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-950\/40:is(.dark *){background-color:color-mix(in oklab,var(--color-blue-950)40%,transparent)}}.dark\:bg-blue-950\/50:is(.dark *){background-color:#16245680}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-950\/50:is(.dark *){background-color:color-mix(in oklab,var(--color-blue-950)50%,transparent)}}.dark\:bg-blue-950\/95:is(.dark *){background-color:#162456f2}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-950\/95:is(.dark *){background-color:color-mix(in oklab,var(--color-blue-950)95%,transparent)}}.dark\:bg-card:is(.dark *){background-color:var(--card)}.dark\:bg-destructive\/60:is(.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-destructive\/60:is(.dark *){background-color:color-mix(in oklab,var(--destructive)60%,transparent)}}.dark\:bg-foreground\/10:is(.dark *){background-color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-foreground\/10:is(.dark *){background-color:color-mix(in oklab,var(--foreground)10%,transparent)}}.dark\:bg-gray-500:is(.dark *){background-color:var(--color-gray-500)}.dark\:bg-gray-800:is(.dark *){background-color:var(--color-gray-800)}.dark\:bg-gray-800\/90:is(.dark *){background-color:#1e2939e6}@supports (color:color-mix(in lab,red,red)){.dark\:bg-gray-800\/90:is(.dark *){background-color:color-mix(in oklab,var(--color-gray-800)90%,transparent)}}.dark\:bg-gray-900:is(.dark *){background-color:var(--color-gray-900)}.dark\:bg-gray-900\/30:is(.dark *){background-color:#1018284d}@supports (color:color-mix(in lab,red,red)){.dark\:bg-gray-900\/30:is(.dark *){background-color:color-mix(in oklab,var(--color-gray-900)30%,transparent)}}.dark\:bg-green-400:is(.dark *){background-color:var(--color-green-400)}.dark\:bg-green-500\/10:is(.dark *){background-color:#00c7581a}@supports (color:color-mix(in lab,red,red)){.dark\:bg-green-500\/10:is(.dark *){background-color:color-mix(in oklab,var(--color-green-500)10%,transparent)}}.dark\:bg-green-900:is(.dark *){background-color:var(--color-green-900)}.dark\:bg-green-950:is(.dark *){background-color:var(--color-green-950)}.dark\:bg-green-950\/20:is(.dark *){background-color:#032e1533}@supports (color:color-mix(in lab,red,red)){.dark\:bg-green-950\/20:is(.dark *){background-color:color-mix(in oklab,var(--color-green-950)20%,transparent)}}.dark\:bg-green-950\/50:is(.dark *){background-color:#032e1580}@supports (color:color-mix(in lab,red,red)){.dark\:bg-green-950\/50:is(.dark *){background-color:color-mix(in oklab,var(--color-green-950)50%,transparent)}}.dark\:bg-input\/30:is(.dark *){background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-input\/30:is(.dark *){background-color:color-mix(in oklab,var(--input)30%,transparent)}}.dark\:bg-orange-400:is(.dark *){background-color:var(--color-orange-400)}.dark\:bg-orange-500\/10:is(.dark *){background-color:#fe6e001a}@supports (color:color-mix(in lab,red,red)){.dark\:bg-orange-500\/10:is(.dark *){background-color:color-mix(in oklab,var(--color-orange-500)10%,transparent)}}.dark\:bg-orange-900:is(.dark *){background-color:var(--color-orange-900)}.dark\:bg-orange-950:is(.dark *){background-color:var(--color-orange-950)}.dark\:bg-orange-950\/20:is(.dark *){background-color:#44130633}@supports (color:color-mix(in lab,red,red)){.dark\:bg-orange-950\/20:is(.dark *){background-color:color-mix(in oklab,var(--color-orange-950)20%,transparent)}}.dark\:bg-orange-950\/30:is(.dark *){background-color:#4413064d}@supports (color:color-mix(in lab,red,red)){.dark\:bg-orange-950\/30:is(.dark *){background-color:color-mix(in oklab,var(--color-orange-950)30%,transparent)}}.dark\:bg-orange-950\/50:is(.dark *){background-color:#44130680}@supports (color:color-mix(in lab,red,red)){.dark\:bg-orange-950\/50:is(.dark *){background-color:color-mix(in oklab,var(--color-orange-950)50%,transparent)}}.dark\:bg-purple-900:is(.dark *){background-color:var(--color-purple-900)}.dark\:bg-red-400:is(.dark *){background-color:var(--color-red-400)}.dark\:bg-red-900:is(.dark *){background-color:var(--color-red-900)}.dark\:bg-red-950:is(.dark *){background-color:var(--color-red-950)}.dark\:bg-red-950\/20:is(.dark *){background-color:#46080933}@supports (color:color-mix(in lab,red,red)){.dark\:bg-red-950\/20:is(.dark *){background-color:color-mix(in oklab,var(--color-red-950)20%,transparent)}}.dark\:text-\[\#8B5CF6\]:is(.dark *){color:#8b5cf6}.dark\:text-amber-100:is(.dark *){color:var(--color-amber-100)}.dark\:text-amber-200:is(.dark *){color:var(--color-amber-200)}.dark\:text-amber-300:is(.dark *){color:var(--color-amber-300)}.dark\:text-amber-400:is(.dark *){color:var(--color-amber-400)}.dark\:text-amber-500:is(.dark *){color:var(--color-amber-500)}.dark\:text-blue-100:is(.dark *){color:var(--color-blue-100)}.dark\:text-blue-200:is(.dark *){color:var(--color-blue-200)}.dark\:text-blue-300:is(.dark *){color:var(--color-blue-300)}.dark\:text-blue-400:is(.dark *){color:var(--color-blue-400)}.dark\:text-blue-400\/70:is(.dark *){color:#54a2ffb3}@supports (color:color-mix(in lab,red,red)){.dark\:text-blue-400\/70:is(.dark *){color:color-mix(in oklab,var(--color-blue-400)70%,transparent)}}.dark\:text-blue-500:is(.dark *){color:var(--color-blue-500)}.dark\:text-gray-100:is(.dark *){color:var(--color-gray-100)}.dark\:text-gray-300:is(.dark *){color:var(--color-gray-300)}.dark\:text-gray-400:is(.dark *){color:var(--color-gray-400)}.dark\:text-green-100:is(.dark *){color:var(--color-green-100)}.dark\:text-green-200:is(.dark *){color:var(--color-green-200)}.dark\:text-green-300:is(.dark *){color:var(--color-green-300)}.dark\:text-green-400:is(.dark *){color:var(--color-green-400)}.dark\:text-orange-100:is(.dark *){color:var(--color-orange-100)}.dark\:text-orange-200:is(.dark *){color:var(--color-orange-200)}.dark\:text-orange-300:is(.dark *){color:var(--color-orange-300)}.dark\:text-orange-400:is(.dark *){color:var(--color-orange-400)}.dark\:text-purple-400:is(.dark *){color:var(--color-purple-400)}.dark\:text-red-200:is(.dark *){color:var(--color-red-200)}.dark\:text-red-400:is(.dark *){color:var(--color-red-400)}.dark\:text-yellow-400:is(.dark *){color:var(--color-yellow-400)}.dark\:opacity-30:is(.dark *){opacity:.3}@media (hover:hover){.dark\:hover\:border-gray-600:is(.dark *):hover{border-color:var(--color-gray-600)}.dark\:hover\:bg-accent\/50:is(.dark *):hover{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-accent\/50:is(.dark *):hover{background-color:color-mix(in oklab,var(--accent)50%,transparent)}}.dark\:hover\:bg-amber-950\/30:is(.dark *):hover{background-color:#4619014d}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-amber-950\/30:is(.dark *):hover{background-color:color-mix(in oklab,var(--color-amber-950)30%,transparent)}}.dark\:hover\:bg-gray-800:is(.dark *):hover{background-color:var(--color-gray-800)}.dark\:hover\:bg-input\/50:is(.dark *):hover{background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-input\/50:is(.dark *):hover{background-color:color-mix(in oklab,var(--input)50%,transparent)}}.dark\:hover\:bg-orange-950\/40:is(.dark *):hover{background-color:#44130666}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-orange-950\/40:is(.dark *):hover{background-color:color-mix(in oklab,var(--color-orange-950)40%,transparent)}}.dark\:hover\:bg-red-900\/20:is(.dark *):hover{background-color:#82181a33}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-red-900\/20:is(.dark *):hover{background-color:color-mix(in oklab,var(--color-red-900)20%,transparent)}}.dark\:hover\:text-orange-200:is(.dark *):hover{color:var(--color-orange-200)}}.dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible{--tw-ring-color:color-mix(in oklab,var(--destructive)40%,transparent)}}.dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid=true]{--tw-ring-color:color-mix(in oklab,var(--destructive)40%,transparent)}}.dark\:data-\[state\=checked\]\:bg-primary:is(.dark *)[data-state=checked]{background-color:var(--primary)}.dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is(.dark *)[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is(.dark *)[data-variant=destructive]:focus{background-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.\[\&_p\]\:leading-relaxed p{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 svg:not([class*=size-]){width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.\[\&_svg\:not\(\[class\*\=\'text-\'\]\)\]\:text-muted-foreground svg:not([class*=text-]){color:var(--muted-foreground)}.\[\.border-b\]\:pb-6.border-b{padding-bottom:calc(var(--spacing)*6)}.\[\.border-t\]\:pt-6.border-t{padding-top:calc(var(--spacing)*6)}:is(.\*\:\[span\]\:last\:flex>*):is(span):last-child{display:flex}:is(.\*\:\[span\]\:last\:items-center>*):is(span):last-child{align-items:center}:is(.\*\:\[span\]\:last\:gap-2>*):is(span):last-child{gap:calc(var(--spacing)*2)}:is(.data-\[variant\=destructive\]\:\*\:\[svg\]\:\!text-destructive[data-variant=destructive]>*):is(svg){color:var(--destructive)!important}.\[\&\>svg\]\:absolute>svg{position:absolute}.\[\&\>svg\]\:top-4>svg{top:calc(var(--spacing)*4)}.\[\&\>svg\]\:left-4>svg{left:calc(var(--spacing)*4)}.\[\&\>svg\]\:text-foreground>svg{color:var(--foreground)}.\[\&\>svg\+div\]\:translate-y-\[-3px\]>svg+div{--tw-translate-y:-3px;translate:var(--tw-translate-x)var(--tw-translate-y)}.\[\&\>svg\~\*\]\:pl-7>svg~*{padding-left:calc(var(--spacing)*7)}}@property --tw-animation-delay{syntax:"*";inherits:false;initial-value:0s}@property --tw-animation-direction{syntax:"*";inherits:false;initial-value:normal}@property --tw-animation-duration{syntax:"*";inherits:false}@property --tw-animation-fill-mode{syntax:"*";inherits:false;initial-value:none}@property --tw-animation-iteration-count{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-translate-y{syntax:"*";inherits:false;initial-value:0}:root{--radius:.625rem;--background:oklch(100% 0 0);--foreground:oklch(14.5% 0 0);--card:oklch(100% 0 0);--card-foreground:oklch(14.5% 0 0);--popover:oklch(100% 0 0);--popover-foreground:oklch(14.5% 0 0);--primary:oklch(48% .18 290);--primary-foreground:oklch(98.5% 0 0);--secondary:oklch(97% 0 0);--secondary-foreground:oklch(20.5% 0 0);--muted:oklch(97% 0 0);--muted-foreground:oklch(55.6% 0 0);--accent:oklch(97% 0 0);--accent-foreground:oklch(20.5% 0 0);--destructive:oklch(57.7% .245 27.325);--border:oklch(92.2% 0 0);--input:oklch(92.2% 0 0);--ring:oklch(70.8% 0 0);--chart-1:oklch(64.6% .222 41.116);--chart-2:oklch(60% .118 184.704);--chart-3:oklch(39.8% .07 227.392);--chart-4:oklch(82.8% .189 84.429);--chart-5:oklch(76.9% .188 70.08);--sidebar:oklch(98.5% 0 0);--sidebar-foreground:oklch(14.5% 0 0);--sidebar-primary:oklch(20.5% 0 0);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(97% 0 0);--sidebar-accent-foreground:oklch(20.5% 0 0);--sidebar-border:oklch(92.2% 0 0);--sidebar-ring:oklch(70.8% 0 0)}.dark{--background:oklch(14.5% 0 0);--foreground:oklch(98.5% 0 0);--card:oklch(20.5% 0 0);--card-foreground:oklch(98.5% 0 0);--popover:oklch(20.5% 0 0);--popover-foreground:oklch(98.5% 0 0);--primary:oklch(62% .2 290);--primary-foreground:oklch(98.5% 0 0);--secondary:oklch(26.9% 0 0);--secondary-foreground:oklch(98.5% 0 0);--muted:oklch(26.9% 0 0);--muted-foreground:oklch(70.8% 0 0);--accent:oklch(26.9% 0 0);--accent-foreground:oklch(98.5% 0 0);--destructive:oklch(70.4% .191 22.216);--border:oklch(100% 0 0/.1);--input:oklch(100% 0 0/.15);--ring:oklch(55.6% 0 0);--chart-1:oklch(48.8% .243 264.376);--chart-2:oklch(69.6% .17 162.48);--chart-3:oklch(76.9% .188 70.08);--chart-4:oklch(62.7% .265 303.9);--chart-5:oklch(64.5% .246 16.439);--sidebar:oklch(20.5% 0 0);--sidebar-foreground:oklch(98.5% 0 0);--sidebar-primary:oklch(48.8% .243 264.376);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(26.9% 0 0);--sidebar-accent-foreground:oklch(98.5% 0 0);--sidebar-border:oklch(100% 0 0/.1);--sidebar-ring:oklch(55.6% 0 0)}.workflow-chat-view .border-green-200{border-color:var(--color-emerald-200)}.workflow-chat-view .bg-green-50{background-color:var(--color-emerald-50)}.workflow-chat-view .bg-green-100{background-color:var(--color-emerald-100)}.workflow-chat-view .text-green-600{color:var(--color-emerald-600)}.workflow-chat-view .text-green-700{color:var(--color-emerald-700)}.workflow-chat-view .text-green-800{color:var(--color-emerald-800)}.highlight-attention{animation:1s ease-out highlight-flash}@keyframes highlight-flash{0%{background-color:#fb923c4d;transform:scale(1.02)}to{background-color:#0000;transform:scale(1)}}.hil-waiting-glow{animation:2s infinite pulse-glow;box-shadow:0 0 #fb923c66,inset 0 0 0 1px #fb923c33}@keyframes pulse-glow{0%,to{box-shadow:0 0 #fb923c66,inset 0 0 0 1px #fb923c33}50%{box-shadow:0 0 20px 5px #fb923c33,inset 0 0 0 2px #fb923c4d}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0)scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1))rotate(var(--tw-enter-rotate,0));filter:blur(var(--tw-enter-blur,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0)scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1))rotate(var(--tw-exit-rotate,0));filter:blur(var(--tw-exit-blur,0))}}.react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #777;--xy-background-pattern-lines-color-default: #777;--xy-background-pattern-cross-color-default: #777;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))} diff --git a/python/packages/devui/agent_framework_devui/ui/assets/index.js b/python/packages/devui/agent_framework_devui/ui/assets/index.js index 7aa23415c4..203ffd294e 100644 --- a/python/packages/devui/agent_framework_devui/ui/assets/index.js +++ b/python/packages/devui/agent_framework_devui/ui/assets/index.js @@ -1,4 +1,4 @@ -function pE(e,n){for(var s=0;so[l]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const n=document.createElement("link").relList;if(n&&n.supports&&n.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))o(l);new MutationObserver(l=>{for(const c of l)if(c.type==="childList")for(const d of c.addedNodes)d.tagName==="LINK"&&d.rel==="modulepreload"&&o(d)}).observe(document,{childList:!0,subtree:!0});function s(l){const c={};return l.integrity&&(c.integrity=l.integrity),l.referrerPolicy&&(c.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?c.credentials="include":l.crossOrigin==="anonymous"?c.credentials="omit":c.credentials="same-origin",c}function o(l){if(l.ep)return;l.ep=!0;const c=s(l);fetch(l.href,c)}})();function dp(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var Pm={exports:{}},Ii={};/** +function yE(e,n){for(var r=0;ra[l]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const n=document.createElement("link").relList;if(n&&n.supports&&n.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))a(l);new MutationObserver(l=>{for(const c of l)if(c.type==="childList")for(const d of c.addedNodes)d.tagName==="LINK"&&d.rel==="modulepreload"&&a(d)}).observe(document,{childList:!0,subtree:!0});function r(l){const c={};return l.integrity&&(c.integrity=l.integrity),l.referrerPolicy&&(c.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?c.credentials="include":l.crossOrigin==="anonymous"?c.credentials="omit":c.credentials="same-origin",c}function a(l){if(l.ep)return;l.ep=!0;const c=r(l);fetch(l.href,c)}})();function yp(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var Gm={exports:{}},Pi={};/** * @license React * react-jsx-runtime.production.js * @@ -6,7 +6,7 @@ function pE(e,n){for(var s=0;s>>1,T=A[$];if(0>>1;$l(Z,B))rel(de,Z)?(A[$]=de,A[re]=B,$=re):(A[$]=Z,A[W]=B,$=W);else if(rel(de,B))A[$]=de,A[re]=B,$=re;else break e}}return I}function l(A,I){var B=A.sortIndex-I.sortIndex;return B!==0?B:A.id-I.id}if(e.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var c=performance;e.unstable_now=function(){return c.now()}}else{var d=Date,f=d.now();e.unstable_now=function(){return d.now()-f}}var m=[],p=[],g=1,v=null,x=3,b=!1,S=!1,N=!1,_=!1,E=typeof setTimeout=="function"?setTimeout:null,M=typeof clearTimeout=="function"?clearTimeout:null,j=typeof setImmediate<"u"?setImmediate:null;function k(A){for(var I=s(p);I!==null;){if(I.callback===null)o(p);else if(I.startTime<=A)o(p),I.sortIndex=I.expirationTime,n(m,I);else break;I=s(p)}}function R(A){if(N=!1,k(A),!S)if(s(m)!==null)S=!0,D||(D=!0,G());else{var I=s(p);I!==null&&V(R,I.startTime-A)}}var D=!1,z=-1,H=5,U=-1;function F(){return _?!0:!(e.unstable_now()-UA&&F());){var $=v.callback;if(typeof $=="function"){v.callback=null,x=v.priorityLevel;var T=$(v.expirationTime<=A);if(A=e.unstable_now(),typeof T=="function"){v.callback=T,k(A),I=!0;break t}v===s(m)&&o(m),k(A)}else o(m);v=s(m)}if(v!==null)I=!0;else{var P=s(p);P!==null&&V(R,P.startTime-A),I=!1}}break e}finally{v=null,x=B,b=!1}I=void 0}}finally{I?G():D=!1}}}var G;if(typeof j=="function")G=function(){j(K)};else if(typeof MessageChannel<"u"){var ne=new MessageChannel,L=ne.port2;ne.port1.onmessage=K,G=function(){L.postMessage(null)}}else G=function(){E(K,0)};function V(A,I){z=E(function(){A(e.unstable_now())},I)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(A){A.callback=null},e.unstable_forceFrameRate=function(A){0>A||125$?(A.sortIndex=B,n(p,A),s(m)===null&&A===s(p)&&(N?(M(z),z=-1):N=!0,V(R,B-$))):(A.sortIndex=T,n(m,A),S||b||(S=!0,D||(D=!0,G()))),A},e.unstable_shouldYield=F,e.unstable_wrapCallback=function(A){var I=x;return function(){var B=x;x=I;try{return A.apply(this,arguments)}finally{x=B}}}})(qm)),qm}var Wy;function bE(){return Wy||(Wy=1,Vm.exports=vE()),Vm.exports}var Fm={exports:{}},Yt={};/** + */var ev;function NE(){return ev||(ev=1,(function(e){function n(k,L){var I=k.length;k.push(L);e:for(;0>>1,C=k[H];if(0>>1;H<$;){var Y=2*(H+1)-1,V=k[Y],K=Y+1,fe=k[K];if(0>l(V,I))Kl(fe,V)?(k[H]=fe,k[K]=I,H=K):(k[H]=V,k[Y]=I,H=Y);else if(Kl(fe,I))k[H]=fe,k[K]=I,H=K;else break e}}return L}function l(k,L){var I=k.sortIndex-L.sortIndex;return I!==0?I:k.id-L.id}if(e.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var c=performance;e.unstable_now=function(){return c.now()}}else{var d=Date,f=d.now();e.unstable_now=function(){return d.now()-f}}var m=[],h=[],g=1,y=null,x=3,b=!1,S=!1,N=!1,j=!1,_=typeof setTimeout=="function"?setTimeout:null,M=typeof clearTimeout=="function"?clearTimeout:null,E=typeof setImmediate<"u"?setImmediate:null;function T(k){for(var L=r(h);L!==null;){if(L.callback===null)a(h);else if(L.startTime<=k)a(h),L.sortIndex=L.expirationTime,n(m,L);else break;L=r(h)}}function R(k){if(N=!1,T(k),!S)if(r(m)!==null)S=!0,D||(D=!0,G());else{var L=r(h);L!==null&&U(R,L.startTime-k)}}var D=!1,O=-1,P=5,q=-1;function Q(){return j?!0:!(e.unstable_now()-qk&&Q());){var H=y.callback;if(typeof H=="function"){y.callback=null,x=y.priorityLevel;var C=H(y.expirationTime<=k);if(k=e.unstable_now(),typeof C=="function"){y.callback=C,T(k),L=!0;break t}y===r(m)&&a(m),T(k)}else a(m);y=r(m)}if(y!==null)L=!0;else{var $=r(h);$!==null&&U(R,$.startTime-k),L=!1}}break e}finally{y=null,x=I,b=!1}L=void 0}}finally{L?G():D=!1}}}var G;if(typeof E=="function")G=function(){E(ee)};else if(typeof MessageChannel<"u"){var W=new MessageChannel,B=W.port2;W.port1.onmessage=ee,G=function(){B.postMessage(null)}}else G=function(){_(ee,0)};function U(k,L){O=_(function(){k(e.unstable_now())},L)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(k){k.callback=null},e.unstable_forceFrameRate=function(k){0>k||125H?(k.sortIndex=I,n(h,k),r(m)===null&&k===r(h)&&(N?(M(O),O=-1):N=!0,U(R,I-H))):(k.sortIndex=C,n(m,k),S||b||(S=!0,D||(D=!0,G()))),k},e.unstable_shouldYield=Q,e.unstable_wrapCallback=function(k){var L=x;return function(){var I=x;x=L;try{return k.apply(this,arguments)}finally{x=I}}}})(Km)),Km}var tv;function SE(){return tv||(tv=1,Wm.exports=NE()),Wm.exports}var Qm={exports:{}},Wt={};/** * @license React * react-dom.production.js * @@ -30,7 +30,7 @@ function pE(e,n){for(var s=0;s"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(n){console.error(n)}}return e(),Fm.exports=wE(),Fm.exports}/** + */var nv;function jE(){if(nv)return Wt;nv=1;var e=pl();function n(m){var h="https://react.dev/errors/"+m;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(n){console.error(n)}}return e(),Qm.exports=jE(),Qm.exports}/** * @license React * react-dom-client.production.js * @@ -38,414 +38,414 @@ function pE(e,n){for(var s=0;sT||(t.current=$[T],$[T]=null,T--)}function Z(t,r){T++,$[T]=t.current,t.current=r}var re=P(null),de=P(null),pe=P(null),J=P(null);function ce(t,r){switch(Z(pe,r),Z(de,t),Z(re,null),r.nodeType){case 9:case 11:t=(t=r.documentElement)&&(t=t.namespaceURI)?vy(t):0;break;default:if(t=r.tagName,r=r.namespaceURI)r=vy(r),t=by(r,t);else switch(t){case"svg":t=1;break;case"math":t=2;break;default:t=0}}W(re),Z(re,t)}function be(){W(re),W(de),W(pe)}function _e(t){t.memoizedState!==null&&Z(J,t);var r=re.current,i=by(r,t.type);r!==i&&(Z(de,t),Z(re,i))}function je(t){de.current===t&&(W(re),W(de)),J.current===t&&(W(J),Ti._currentValue=B)}var Ne=Object.prototype.hasOwnProperty,De=e.unstable_scheduleCallback,te=e.unstable_cancelCallback,Ce=e.unstable_shouldYield,Fe=e.unstable_requestPaint,Ve=e.unstable_now,Ht=e.unstable_getCurrentPriorityLevel,Xt=e.unstable_ImmediatePriority,ht=e.unstable_UserBlockingPriority,Je=e.unstable_NormalPriority,ft=e.unstable_LowPriority,wn=e.unstable_IdlePriority,le=e.log,ie=e.unstable_setDisableYieldValue,ue=null,ge=null;function ye(t){if(typeof le=="function"&&ie(t),ge&&typeof ge.setStrictMode=="function")try{ge.setStrictMode(ue,t)}catch{}}var se=Math.clz32?Math.clz32:Pe,ve=Math.log,Ee=Math.LN2;function Pe(t){return t>>>=0,t===0?32:31-(ve(t)/Ee|0)|0}var wt=256,Se=4194304;function Le(t){var r=t&42;if(r!==0)return r;switch(t&-t){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t&4194048;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function we(t,r,i){var u=t.pendingLanes;if(u===0)return 0;var h=0,y=t.suspendedLanes,C=t.pingedLanes;t=t.warmLanes;var O=u&134217727;return O!==0?(u=O&~y,u!==0?h=Le(u):(C&=O,C!==0?h=Le(C):i||(i=O&~t,i!==0&&(h=Le(i))))):(O=u&~y,O!==0?h=Le(O):C!==0?h=Le(C):i||(i=u&~t,i!==0&&(h=Le(i)))),h===0?0:r!==0&&r!==h&&(r&y)===0&&(y=h&-h,i=r&-r,y>=i||y===32&&(i&4194048)!==0)?r:h}function Ye(t,r){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&r)===0}function at(t,r){switch(t){case 1:case 2:case 4:case 8:case 64:return r+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return r+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Dt(){var t=wt;return wt<<=1,(wt&4194048)===0&&(wt=256),t}function qn(){var t=Se;return Se<<=1,(Se&62914560)===0&&(Se=4194304),t}function Ra(t){for(var r=[],i=0;31>i;i++)r.push(t);return r}function Ms(t,r){t.pendingLanes|=r,r!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function Ad(t,r,i,u,h,y){var C=t.pendingLanes;t.pendingLanes=i,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=i,t.entangledLanes&=i,t.errorRecoveryDisabledLanes&=i,t.shellSuspendCounter=0;var O=t.entanglements,q=t.expirationTimes,ee=t.hiddenUpdates;for(i=C&~i;0C||(t.current=H[C],H[C]=null,C--)}function V(t,s){C++,H[C]=t.current,t.current=s}var K=$(null),fe=$(null),ue=$(null),te=$(null);function ie(t,s){switch(V(ue,s),V(fe,t),V(K,null),s.nodeType){case 9:case 11:t=(t=s.documentElement)&&(t=t.namespaceURI)?jy(t):0;break;default:if(t=s.tagName,s=s.namespaceURI)s=jy(s),t=_y(s,t);else switch(t){case"svg":t=1;break;case"math":t=2;break;default:t=0}}Y(K),V(K,t)}function xe(){Y(K),Y(fe),Y(ue)}function ve(t){t.memoizedState!==null&&V(te,t);var s=K.current,i=_y(s,t.type);s!==i&&(V(fe,t),V(K,i))}function be(t){fe.current===t&&(Y(K),Y(fe)),te.current===t&&(Y(te),zi._currentValue=I)}var ne=Object.prototype.hasOwnProperty,he=e.unstable_scheduleCallback,X=e.unstable_cancelCallback,pe=e.unstable_shouldYield,Ne=e.unstable_requestPaint,ye=e.unstable_now,Oe=e.unstable_getCurrentPriorityLevel,Se=e.unstable_ImmediatePriority,Ie=e.unstable_UserBlockingPriority,Xe=e.unstable_NormalPriority,He=e.unstable_LowPriority,Re=e.unstable_IdlePriority,Ve=e.log,_e=e.unstable_setDisableYieldValue,$e=null,Fe=null;function Nt(t){if(typeof Ve=="function"&&_e(t),Fe&&typeof Fe.setStrictMode=="function")try{Fe.setStrictMode($e,t)}catch{}}var yt=Math.clz32?Math.clz32:ge,hs=Math.log,wo=Math.LN2;function ge(t){return t>>>=0,t===0?32:31-(hs(t)/wo|0)|0}var Me=256,Be=4194304;function Et(t){var s=t&42;if(s!==0)return s;switch(t&-t){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t&4194048;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function Rn(t,s,i){var u=t.pendingLanes;if(u===0)return 0;var p=0,v=t.suspendedLanes,A=t.pingedLanes;t=t.warmLanes;var z=u&134217727;return z!==0?(u=z&~v,u!==0?p=Et(u):(A&=z,A!==0?p=Et(A):i||(i=z&~t,i!==0&&(p=Et(i))))):(z=u&~v,z!==0?p=Et(z):A!==0?p=Et(A):i||(i=u&~t,i!==0&&(p=Et(i)))),p===0?0:s!==0&&s!==p&&(s&v)===0&&(v=p&-p,i=s&-s,v>=i||v===32&&(i&4194048)!==0)?s:p}function Le(t,s){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&s)===0}function we(t,s){switch(t){case 1:case 2:case 4:case 8:case 64:return s+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return s+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function lt(){var t=Me;return Me<<=1,(Me&4194048)===0&&(Me=256),t}function at(){var t=Be;return Be<<=1,(Be&62914560)===0&&(Be=4194304),t}function At(t){for(var s=[],i=0;31>i;i++)s.push(t);return s}function en(t,s){t.pendingLanes|=s,s!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function Dn(t,s,i,u,p,v){var A=t.pendingLanes;t.pendingLanes=i,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=i,t.entangledLanes&=i,t.errorRecoveryDisabledLanes&=i,t.shellSuspendCounter=0;var z=t.entanglements,F=t.expirationTimes,re=t.hiddenUpdates;for(i=A&~i;0)":-1h||q[u]!==ee[h]){var fe=` -`+q[u].replace(" at new "," at ");return t.displayName&&fe.includes("")&&(fe=fe.replace("",t.displayName)),fe}while(1<=u&&0<=h);break}}}finally{Pa=!1,Error.prepareStackTrace=i}return(i=t?t.displayName||t.name:"")?mr(i):""}function zd(t){switch(t.tag){case 26:case 27:case 5:return mr(t.type);case 16:return mr("Lazy");case 13:return mr("Suspense");case 19:return mr("SuspenseList");case 0:case 15:return $a(t.type,!1);case 11:return $a(t.type.render,!1);case 1:return $a(t.type,!0);case 31:return mr("Activity");default:return""}}function Dl(t){try{var r="";do r+=zd(t),t=t.return;while(t);return r}catch(i){return` +`+Va+t+zl}var qa=!1;function Fa(t,s){if(!t||qa)return"";qa=!0;var i=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var u={DetermineComponentFrameRoot:function(){try{if(s){var me=function(){throw Error()};if(Object.defineProperty(me.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(me,[])}catch(ae){var oe=ae}Reflect.construct(t,[],me)}else{try{me.call()}catch(ae){oe=ae}t.call(me.prototype)}}else{try{throw Error()}catch(ae){oe=ae}(me=t())&&typeof me.catch=="function"&&me.catch(function(){})}}catch(ae){if(ae&&oe&&typeof ae.stack=="string")return[ae.stack,oe.stack]}return[null,null]}};u.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var p=Object.getOwnPropertyDescriptor(u.DetermineComponentFrameRoot,"name");p&&p.configurable&&Object.defineProperty(u.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var v=u.DetermineComponentFrameRoot(),A=v[0],z=v[1];if(A&&z){var F=A.split(` +`),re=z.split(` +`);for(p=u=0;up||F[u]!==re[p]){var le=` +`+F[u].replace(" at new "," at ");return t.displayName&&le.includes("")&&(le=le.replace("",t.displayName)),le}while(1<=u&&0<=p);break}}}finally{qa=!1,Error.prepareStackTrace=i}return(i=t?t.displayName||t.name:"")?ys(i):""}function Ud(t){switch(t.tag){case 26:case 27:case 5:return ys(t.type);case 16:return ys("Lazy");case 13:return ys("Suspense");case 19:return ys("SuspenseList");case 0:case 15:return Fa(t.type,!1);case 11:return Fa(t.type.render,!1);case 1:return Fa(t.type,!0);case 31:return ys("Activity");default:return""}}function Il(t){try{var s="";do s+=Ud(t),t=t.return;while(t);return s}catch(i){return` Error generating stack: `+i.message+` -`+i.stack}}function en(t){switch(typeof t){case"bigint":case"boolean":case"number":case"string":case"undefined":return t;case"object":return t;default:return""}}function Ol(t){var r=t.type;return(t=t.nodeName)&&t.toLowerCase()==="input"&&(r==="checkbox"||r==="radio")}function Id(t){var r=Ol(t)?"checked":"value",i=Object.getOwnPropertyDescriptor(t.constructor.prototype,r),u=""+t[r];if(!t.hasOwnProperty(r)&&typeof i<"u"&&typeof i.get=="function"&&typeof i.set=="function"){var h=i.get,y=i.set;return Object.defineProperty(t,r,{configurable:!0,get:function(){return h.call(this)},set:function(C){u=""+C,y.call(this,C)}}),Object.defineProperty(t,r,{enumerable:i.enumerable}),{getValue:function(){return u},setValue:function(C){u=""+C},stopTracking:function(){t._valueTracker=null,delete t[r]}}}}function bo(t){t._valueTracker||(t._valueTracker=Id(t))}function Ua(t){if(!t)return!1;var r=t._valueTracker;if(!r)return!0;var i=r.getValue(),u="";return t&&(u=Ol(t)?t.checked?"true":"false":t.value),t=u,t!==i?(r.setValue(t),!0):!1}function wo(t){if(t=t||(typeof document<"u"?document:void 0),typeof t>"u")return null;try{return t.activeElement||t.body}catch{return t.body}}var Ld=/[\n"\\]/g;function tn(t){return t.replace(Ld,function(r){return"\\"+r.charCodeAt(0).toString(16)+" "})}function Rs(t,r,i,u,h,y,C,O){t.name="",C!=null&&typeof C!="function"&&typeof C!="symbol"&&typeof C!="boolean"?t.type=C:t.removeAttribute("type"),r!=null?C==="number"?(r===0&&t.value===""||t.value!=r)&&(t.value=""+en(r)):t.value!==""+en(r)&&(t.value=""+en(r)):C!=="submit"&&C!=="reset"||t.removeAttribute("value"),r!=null?Va(t,C,en(r)):i!=null?Va(t,C,en(i)):u!=null&&t.removeAttribute("value"),h==null&&y!=null&&(t.defaultChecked=!!y),h!=null&&(t.checked=h&&typeof h!="function"&&typeof h!="symbol"),O!=null&&typeof O!="function"&&typeof O!="symbol"&&typeof O!="boolean"?t.name=""+en(O):t.removeAttribute("name")}function zl(t,r,i,u,h,y,C,O){if(y!=null&&typeof y!="function"&&typeof y!="symbol"&&typeof y!="boolean"&&(t.type=y),r!=null||i!=null){if(!(y!=="submit"&&y!=="reset"||r!=null))return;i=i!=null?""+en(i):"",r=r!=null?""+en(r):i,O||r===t.value||(t.value=r),t.defaultValue=r}u=u??h,u=typeof u!="function"&&typeof u!="symbol"&&!!u,t.checked=O?t.checked:!!u,t.defaultChecked=!!u,C!=null&&typeof C!="function"&&typeof C!="symbol"&&typeof C!="boolean"&&(t.name=C)}function Va(t,r,i){r==="number"&&wo(t.ownerDocument)===t||t.defaultValue===""+i||(t.defaultValue=""+i)}function hr(t,r,i,u){if(t=t.options,r){r={};for(var h=0;h"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Ud=!1;if(pr)try{var Fa={};Object.defineProperty(Fa,"passive",{get:function(){Ud=!0}}),window.addEventListener("test",Fa,Fa),window.removeEventListener("test",Fa,Fa)}catch{Ud=!1}var Vr=null,Vd=null,Ll=null;function Sg(){if(Ll)return Ll;var t,r=Vd,i=r.length,u,h="value"in Vr?Vr.value:Vr.textContent,y=h.length;for(t=0;t=Xa),Ag=" ",Mg=!1;function Tg(t,r){switch(t){case"keyup":return B_.indexOf(r.keyCode)!==-1;case"keydown":return r.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Rg(t){return t=t.detail,typeof t=="object"&&"data"in t?t.data:null}var jo=!1;function $_(t,r){switch(t){case"compositionend":return Rg(r);case"keypress":return r.which!==32?null:(Mg=!0,Ag);case"textInput":return t=r.data,t===Ag&&Mg?null:t;default:return null}}function U_(t,r){if(jo)return t==="compositionend"||!Xd&&Tg(t,r)?(t=Sg(),Ll=Vd=Vr=null,jo=!1,t):null;switch(t){case"paste":return null;case"keypress":if(!(r.ctrlKey||r.altKey||r.metaKey)||r.ctrlKey&&r.altKey){if(r.char&&1=r)return{node:i,offset:r-t};t=u}e:{for(;i;){if(i.nextSibling){i=i.nextSibling;break e}i=i.parentNode}i=void 0}i=Pg(i)}}function Ug(t,r){return t&&r?t===r?!0:t&&t.nodeType===3?!1:r&&r.nodeType===3?Ug(t,r.parentNode):"contains"in t?t.contains(r):t.compareDocumentPosition?!!(t.compareDocumentPosition(r)&16):!1:!1}function Vg(t){t=t!=null&&t.ownerDocument!=null&&t.ownerDocument.defaultView!=null?t.ownerDocument.defaultView:window;for(var r=wo(t.document);r instanceof t.HTMLIFrameElement;){try{var i=typeof r.contentWindow.location.href=="string"}catch{i=!1}if(i)t=r.contentWindow;else break;r=wo(t.document)}return r}function Kd(t){var r=t&&t.nodeName&&t.nodeName.toLowerCase();return r&&(r==="input"&&(t.type==="text"||t.type==="search"||t.type==="tel"||t.type==="url"||t.type==="password")||r==="textarea"||t.contentEditable==="true")}var W_=pr&&"documentMode"in document&&11>=document.documentMode,Eo=null,Qd=null,Qa=null,Jd=!1;function qg(t,r,i){var u=i.window===i?i.document:i.nodeType===9?i:i.ownerDocument;Jd||Eo==null||Eo!==wo(u)||(u=Eo,"selectionStart"in u&&Kd(u)?u={start:u.selectionStart,end:u.selectionEnd}:(u=(u.ownerDocument&&u.ownerDocument.defaultView||window).getSelection(),u={anchorNode:u.anchorNode,anchorOffset:u.anchorOffset,focusNode:u.focusNode,focusOffset:u.focusOffset}),Qa&&Ka(Qa,u)||(Qa=u,u=Cc(Qd,"onSelect"),0>=C,h-=C,xr=1<<32-se(r)+h|i<y?y:8;var C=A.T,O={};A.T=O,Bf(t,!1,r,i);try{var q=h(),ee=A.S;if(ee!==null&&ee(O,q),q!==null&&typeof q=="object"&&typeof q.then=="function"){var fe=oj(q,u);mi(t,r,fe,mn(t))}else mi(t,r,u,mn(t))}catch(xe){mi(t,r,{then:function(){},status:"rejected",reason:xe},mn())}finally{I.p=y,A.T=C}}function uj(){}function Lf(t,r,i,u){if(t.tag!==5)throw Error(o(476));var h=Fx(t).queue;qx(t,h,r,B,i===null?uj:function(){return Yx(t),i(u)})}function Fx(t){var r=t.memoizedState;if(r!==null)return r;r={memoizedState:B,baseState:B,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:wr,lastRenderedState:B},next:null};var i={};return r.next={memoizedState:i,baseState:i,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:wr,lastRenderedState:i},next:null},t.memoizedState=r,t=t.alternate,t!==null&&(t.memoizedState=r),r}function Yx(t){var r=Fx(t).next.queue;mi(t,r,{},mn())}function Hf(){return Ft(Ti)}function Gx(){return kt().memoizedState}function Xx(){return kt().memoizedState}function dj(t){for(var r=t.return;r!==null;){switch(r.tag){case 24:case 3:var i=mn();t=Yr(i);var u=Gr(r,t,i);u!==null&&(hn(u,r,i),ii(u,r,i)),r={cache:hf()},t.payload=r;return}r=r.return}}function fj(t,r,i){var u=mn();i={lane:u,revertLane:0,action:i,hasEagerState:!1,eagerState:null,next:null},ic(t)?Wx(r,i):(i=rf(t,r,i,u),i!==null&&(hn(i,t,u),Kx(i,r,u)))}function Zx(t,r,i){var u=mn();mi(t,r,i,u)}function mi(t,r,i,u){var h={lane:u,revertLane:0,action:i,hasEagerState:!1,eagerState:null,next:null};if(ic(t))Wx(r,h);else{var y=t.alternate;if(t.lanes===0&&(y===null||y.lanes===0)&&(y=r.lastRenderedReducer,y!==null))try{var C=r.lastRenderedState,O=y(C,i);if(h.hasEagerState=!0,h.eagerState=O,ln(O,C))return ql(t,r,h,0),pt===null&&Vl(),!1}catch{}finally{}if(i=rf(t,r,h,u),i!==null)return hn(i,t,u),Kx(i,r,u),!0}return!1}function Bf(t,r,i,u){if(u={lane:2,revertLane:xm(),action:u,hasEagerState:!1,eagerState:null,next:null},ic(t)){if(r)throw Error(o(479))}else r=rf(t,i,u,2),r!==null&&hn(r,t,2)}function ic(t){var r=t.alternate;return t===Ge||r!==null&&r===Ge}function Wx(t,r){Io=tc=!0;var i=t.pending;i===null?r.next=r:(r.next=i.next,i.next=r),t.pending=r}function Kx(t,r,i){if((i&4194048)!==0){var u=r.lanes;u&=t.pendingLanes,i|=u,r.lanes=i,Da(t,i)}}var lc={readContext:Ft,use:rc,useCallback:_t,useContext:_t,useEffect:_t,useImperativeHandle:_t,useLayoutEffect:_t,useInsertionEffect:_t,useMemo:_t,useReducer:_t,useRef:_t,useState:_t,useDebugValue:_t,useDeferredValue:_t,useTransition:_t,useSyncExternalStore:_t,useId:_t,useHostTransitionStatus:_t,useFormState:_t,useActionState:_t,useOptimistic:_t,useMemoCache:_t,useCacheRefresh:_t},Qx={readContext:Ft,use:rc,useCallback:function(t,r){return rn().memoizedState=[t,r===void 0?null:r],t},useContext:Ft,useEffect:zx,useImperativeHandle:function(t,r,i){i=i!=null?i.concat([t]):null,ac(4194308,4,Bx.bind(null,r,t),i)},useLayoutEffect:function(t,r){return ac(4194308,4,t,r)},useInsertionEffect:function(t,r){ac(4,2,t,r)},useMemo:function(t,r){var i=rn();r=r===void 0?null:r;var u=t();if(qs){ye(!0);try{t()}finally{ye(!1)}}return i.memoizedState=[u,r],u},useReducer:function(t,r,i){var u=rn();if(i!==void 0){var h=i(r);if(qs){ye(!0);try{i(r)}finally{ye(!1)}}}else h=r;return u.memoizedState=u.baseState=h,t={pending:null,lanes:0,dispatch:null,lastRenderedReducer:t,lastRenderedState:h},u.queue=t,t=t.dispatch=fj.bind(null,Ge,t),[u.memoizedState,t]},useRef:function(t){var r=rn();return t={current:t},r.memoizedState=t},useState:function(t){t=Df(t);var r=t.queue,i=Zx.bind(null,Ge,r);return r.dispatch=i,[t.memoizedState,i]},useDebugValue:zf,useDeferredValue:function(t,r){var i=rn();return If(i,t,r)},useTransition:function(){var t=Df(!1);return t=qx.bind(null,Ge,t.queue,!0,!1),rn().memoizedState=t,[!1,t]},useSyncExternalStore:function(t,r,i){var u=Ge,h=rn();if(ot){if(i===void 0)throw Error(o(407));i=i()}else{if(i=r(),pt===null)throw Error(o(349));(tt&124)!==0||vx(u,r,i)}h.memoizedState=i;var y={value:i,getSnapshot:r};return h.queue=y,zx(wx.bind(null,u,y,t),[t]),u.flags|=2048,Ho(9,oc(),bx.bind(null,u,y,i,r),null),i},useId:function(){var t=rn(),r=pt.identifierPrefix;if(ot){var i=yr,u=xr;i=(u&~(1<<32-se(u)-1)).toString(32)+i,r="«"+r+"R"+i,i=nc++,0Be?(Lt=ze,ze=null):Lt=ze.sibling;var st=oe(X,ze,Q[Be],he);if(st===null){ze===null&&(ze=Lt);break}t&&ze&&st.alternate===null&&r(X,ze),Y=y(st,Y,Be),Ze===null?Ae=st:Ze.sibling=st,Ze=st,ze=Lt}if(Be===Q.length)return i(X,ze),ot&&Hs(X,Be),Ae;if(ze===null){for(;BeBe?(Lt=ze,ze=null):Lt=ze.sibling;var us=oe(X,ze,st.value,he);if(us===null){ze===null&&(ze=Lt);break}t&&ze&&us.alternate===null&&r(X,ze),Y=y(us,Y,Be),Ze===null?Ae=us:Ze.sibling=us,Ze=us,ze=Lt}if(st.done)return i(X,ze),ot&&Hs(X,Be),Ae;if(ze===null){for(;!st.done;Be++,st=Q.next())st=xe(X,st.value,he),st!==null&&(Y=y(st,Y,Be),Ze===null?Ae=st:Ze.sibling=st,Ze=st);return ot&&Hs(X,Be),Ae}for(ze=u(ze);!st.done;Be++,st=Q.next())st=ae(ze,X,Be,st.value,he),st!==null&&(t&&st.alternate!==null&&ze.delete(st.key===null?Be:st.key),Y=y(st,Y,Be),Ze===null?Ae=st:Ze.sibling=st,Ze=st);return t&&ze.forEach(function(hE){return r(X,hE)}),ot&&Hs(X,Be),Ae}function dt(X,Y,Q,he){if(typeof Q=="object"&&Q!==null&&Q.type===S&&Q.key===null&&(Q=Q.props.children),typeof Q=="object"&&Q!==null){switch(Q.$$typeof){case x:e:{for(var Ae=Q.key;Y!==null;){if(Y.key===Ae){if(Ae=Q.type,Ae===S){if(Y.tag===7){i(X,Y.sibling),he=h(Y,Q.props.children),he.return=X,X=he;break e}}else if(Y.elementType===Ae||typeof Ae=="object"&&Ae!==null&&Ae.$$typeof===H&&e0(Ae)===Y.type){i(X,Y.sibling),he=h(Y,Q.props),pi(he,Q),he.return=X,X=he;break e}i(X,Y);break}else r(X,Y);Y=Y.sibling}Q.type===S?(he=Is(Q.props.children,X.mode,he,Q.key),he.return=X,X=he):(he=Yl(Q.type,Q.key,Q.props,null,X.mode,he),pi(he,Q),he.return=X,X=he)}return C(X);case b:e:{for(Ae=Q.key;Y!==null;){if(Y.key===Ae)if(Y.tag===4&&Y.stateNode.containerInfo===Q.containerInfo&&Y.stateNode.implementation===Q.implementation){i(X,Y.sibling),he=h(Y,Q.children||[]),he.return=X,X=he;break e}else{i(X,Y);break}else r(X,Y);Y=Y.sibling}he=af(Q,X.mode,he),he.return=X,X=he}return C(X);case H:return Ae=Q._init,Q=Ae(Q._payload),dt(X,Y,Q,he)}if(V(Q))return $e(X,Y,Q,he);if(G(Q)){if(Ae=G(Q),typeof Ae!="function")throw Error(o(150));return Q=Ae.call(Q),He(X,Y,Q,he)}if(typeof Q.then=="function")return dt(X,Y,cc(Q),he);if(Q.$$typeof===j)return dt(X,Y,Wl(X,Q),he);uc(X,Q)}return typeof Q=="string"&&Q!==""||typeof Q=="number"||typeof Q=="bigint"?(Q=""+Q,Y!==null&&Y.tag===6?(i(X,Y.sibling),he=h(Y,Q),he.return=X,X=he):(i(X,Y),he=of(Q,X.mode,he),he.return=X,X=he),C(X)):i(X,Y)}return function(X,Y,Q,he){try{hi=0;var Ae=dt(X,Y,Q,he);return Bo=null,Ae}catch(ze){if(ze===oi||ze===Ql)throw ze;var Ze=cn(29,ze,null,X.mode);return Ze.lanes=he,Ze.return=X,Ze}finally{}}}var Po=t0(!0),n0=t0(!1),En=P(null),Gn=null;function Zr(t){var r=t.alternate;Z(Tt,Tt.current&1),Z(En,t),Gn===null&&(r===null||zo.current!==null||r.memoizedState!==null)&&(Gn=t)}function r0(t){if(t.tag===22){if(Z(Tt,Tt.current),Z(En,t),Gn===null){var r=t.alternate;r!==null&&r.memoizedState!==null&&(Gn=t)}}else Wr()}function Wr(){Z(Tt,Tt.current),Z(En,En.current)}function Nr(t){W(En),Gn===t&&(Gn=null),W(Tt)}var Tt=P(0);function dc(t){for(var r=t;r!==null;){if(r.tag===13){var i=r.memoizedState;if(i!==null&&(i=i.dehydrated,i===null||i.data==="$?"||Am(i)))return r}else if(r.tag===19&&r.memoizedProps.revealOrder!==void 0){if((r.flags&128)!==0)return r}else if(r.child!==null){r.child.return=r,r=r.child;continue}if(r===t)break;for(;r.sibling===null;){if(r.return===null||r.return===t)return null;r=r.return}r.sibling.return=r.return,r=r.sibling}return null}function Pf(t,r,i,u){r=t.memoizedState,i=i(u,r),i=i==null?r:g({},r,i),t.memoizedState=i,t.lanes===0&&(t.updateQueue.baseState=i)}var $f={enqueueSetState:function(t,r,i){t=t._reactInternals;var u=mn(),h=Yr(u);h.payload=r,i!=null&&(h.callback=i),r=Gr(t,h,u),r!==null&&(hn(r,t,u),ii(r,t,u))},enqueueReplaceState:function(t,r,i){t=t._reactInternals;var u=mn(),h=Yr(u);h.tag=1,h.payload=r,i!=null&&(h.callback=i),r=Gr(t,h,u),r!==null&&(hn(r,t,u),ii(r,t,u))},enqueueForceUpdate:function(t,r){t=t._reactInternals;var i=mn(),u=Yr(i);u.tag=2,r!=null&&(u.callback=r),r=Gr(t,u,i),r!==null&&(hn(r,t,i),ii(r,t,i))}};function s0(t,r,i,u,h,y,C){return t=t.stateNode,typeof t.shouldComponentUpdate=="function"?t.shouldComponentUpdate(u,y,C):r.prototype&&r.prototype.isPureReactComponent?!Ka(i,u)||!Ka(h,y):!0}function o0(t,r,i,u){t=r.state,typeof r.componentWillReceiveProps=="function"&&r.componentWillReceiveProps(i,u),typeof r.UNSAFE_componentWillReceiveProps=="function"&&r.UNSAFE_componentWillReceiveProps(i,u),r.state!==t&&$f.enqueueReplaceState(r,r.state,null)}function Fs(t,r){var i=r;if("ref"in r){i={};for(var u in r)u!=="ref"&&(i[u]=r[u])}if(t=t.defaultProps){i===r&&(i=g({},i));for(var h in t)i[h]===void 0&&(i[h]=t[h])}return i}var fc=typeof reportError=="function"?reportError:function(t){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var r=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof t=="object"&&t!==null&&typeof t.message=="string"?String(t.message):String(t),error:t});if(!window.dispatchEvent(r))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",t);return}console.error(t)};function a0(t){fc(t)}function i0(t){console.error(t)}function l0(t){fc(t)}function mc(t,r){try{var i=t.onUncaughtError;i(r.value,{componentStack:r.stack})}catch(u){setTimeout(function(){throw u})}}function c0(t,r,i){try{var u=t.onCaughtError;u(i.value,{componentStack:i.stack,errorBoundary:r.tag===1?r.stateNode:null})}catch(h){setTimeout(function(){throw h})}}function Uf(t,r,i){return i=Yr(i),i.tag=3,i.payload={element:null},i.callback=function(){mc(t,r)},i}function u0(t){return t=Yr(t),t.tag=3,t}function d0(t,r,i,u){var h=i.type.getDerivedStateFromError;if(typeof h=="function"){var y=u.value;t.payload=function(){return h(y)},t.callback=function(){c0(r,i,u)}}var C=i.stateNode;C!==null&&typeof C.componentDidCatch=="function"&&(t.callback=function(){c0(r,i,u),typeof h!="function"&&(ns===null?ns=new Set([this]):ns.add(this));var O=u.stack;this.componentDidCatch(u.value,{componentStack:O!==null?O:""})})}function hj(t,r,i,u,h){if(i.flags|=32768,u!==null&&typeof u=="object"&&typeof u.then=="function"){if(r=i.alternate,r!==null&&ni(r,i,h,!0),i=En.current,i!==null){switch(i.tag){case 13:return Gn===null?fm():i.alternate===null&&St===0&&(St=3),i.flags&=-257,i.flags|=65536,i.lanes=h,u===xf?i.flags|=16384:(r=i.updateQueue,r===null?i.updateQueue=new Set([u]):r.add(u),hm(t,u,h)),!1;case 22:return i.flags|=65536,u===xf?i.flags|=16384:(r=i.updateQueue,r===null?(r={transitions:null,markerInstances:null,retryQueue:new Set([u])},i.updateQueue=r):(i=r.retryQueue,i===null?r.retryQueue=new Set([u]):i.add(u)),hm(t,u,h)),!1}throw Error(o(435,i.tag))}return hm(t,u,h),fm(),!1}if(ot)return r=En.current,r!==null?((r.flags&65536)===0&&(r.flags|=256),r.flags|=65536,r.lanes=h,u!==uf&&(t=Error(o(422),{cause:u}),ti(Nn(t,i)))):(u!==uf&&(r=Error(o(423),{cause:u}),ti(Nn(r,i))),t=t.current.alternate,t.flags|=65536,h&=-h,t.lanes|=h,u=Nn(u,i),h=Uf(t.stateNode,u,h),bf(t,h),St!==4&&(St=2)),!1;var y=Error(o(520),{cause:u});if(y=Nn(y,i),Ni===null?Ni=[y]:Ni.push(y),St!==4&&(St=2),r===null)return!0;u=Nn(u,i),i=r;do{switch(i.tag){case 3:return i.flags|=65536,t=h&-h,i.lanes|=t,t=Uf(i.stateNode,u,t),bf(i,t),!1;case 1:if(r=i.type,y=i.stateNode,(i.flags&128)===0&&(typeof r.getDerivedStateFromError=="function"||y!==null&&typeof y.componentDidCatch=="function"&&(ns===null||!ns.has(y))))return i.flags|=65536,h&=-h,i.lanes|=h,h=u0(h),d0(h,t,i,u),bf(i,h),!1}i=i.return}while(i!==null);return!1}var f0=Error(o(461)),zt=!1;function Bt(t,r,i,u){r.child=t===null?n0(r,null,i,u):Po(r,t.child,i,u)}function m0(t,r,i,u,h){i=i.render;var y=r.ref;if("ref"in u){var C={};for(var O in u)O!=="ref"&&(C[O]=u[O])}else C=u;return Us(r),u=jf(t,r,i,C,y,h),O=Ef(),t!==null&&!zt?(Cf(t,r,h),Sr(t,r,h)):(ot&&O&&lf(r),r.flags|=1,Bt(t,r,u,h),r.child)}function h0(t,r,i,u,h){if(t===null){var y=i.type;return typeof y=="function"&&!sf(y)&&y.defaultProps===void 0&&i.compare===null?(r.tag=15,r.type=y,p0(t,r,y,u,h)):(t=Yl(i.type,null,u,r,r.mode,h),t.ref=r.ref,t.return=r,r.child=t)}if(y=t.child,!Wf(t,h)){var C=y.memoizedProps;if(i=i.compare,i=i!==null?i:Ka,i(C,u)&&t.ref===r.ref)return Sr(t,r,h)}return r.flags|=1,t=gr(y,u),t.ref=r.ref,t.return=r,r.child=t}function p0(t,r,i,u,h){if(t!==null){var y=t.memoizedProps;if(Ka(y,u)&&t.ref===r.ref)if(zt=!1,r.pendingProps=u=y,Wf(t,h))(t.flags&131072)!==0&&(zt=!0);else return r.lanes=t.lanes,Sr(t,r,h)}return Vf(t,r,i,u,h)}function g0(t,r,i){var u=r.pendingProps,h=u.children,y=t!==null?t.memoizedState:null;if(u.mode==="hidden"){if((r.flags&128)!==0){if(u=y!==null?y.baseLanes|i:i,t!==null){for(h=r.child=t.child,y=0;h!==null;)y=y|h.lanes|h.childLanes,h=h.sibling;r.childLanes=y&~u}else r.childLanes=0,r.child=null;return x0(t,r,u,i)}if((i&536870912)!==0)r.memoizedState={baseLanes:0,cachePool:null},t!==null&&Kl(r,y!==null?y.cachePool:null),y!==null?px(r,y):Nf(),r0(r);else return r.lanes=r.childLanes=536870912,x0(t,r,y!==null?y.baseLanes|i:i,i)}else y!==null?(Kl(r,y.cachePool),px(r,y),Wr(),r.memoizedState=null):(t!==null&&Kl(r,null),Nf(),Wr());return Bt(t,r,h,i),r.child}function x0(t,r,i,u){var h=gf();return h=h===null?null:{parent:Mt._currentValue,pool:h},r.memoizedState={baseLanes:i,cachePool:h},t!==null&&Kl(r,null),Nf(),r0(r),t!==null&&ni(t,r,u,!0),null}function hc(t,r){var i=r.ref;if(i===null)t!==null&&t.ref!==null&&(r.flags|=4194816);else{if(typeof i!="function"&&typeof i!="object")throw Error(o(284));(t===null||t.ref!==i)&&(r.flags|=4194816)}}function Vf(t,r,i,u,h){return Us(r),i=jf(t,r,i,u,void 0,h),u=Ef(),t!==null&&!zt?(Cf(t,r,h),Sr(t,r,h)):(ot&&u&&lf(r),r.flags|=1,Bt(t,r,i,h),r.child)}function y0(t,r,i,u,h,y){return Us(r),r.updateQueue=null,i=xx(r,u,i,h),gx(t),u=Ef(),t!==null&&!zt?(Cf(t,r,y),Sr(t,r,y)):(ot&&u&&lf(r),r.flags|=1,Bt(t,r,i,y),r.child)}function v0(t,r,i,u,h){if(Us(r),r.stateNode===null){var y=Mo,C=i.contextType;typeof C=="object"&&C!==null&&(y=Ft(C)),y=new i(u,y),r.memoizedState=y.state!==null&&y.state!==void 0?y.state:null,y.updater=$f,r.stateNode=y,y._reactInternals=r,y=r.stateNode,y.props=u,y.state=r.memoizedState,y.refs={},yf(r),C=i.contextType,y.context=typeof C=="object"&&C!==null?Ft(C):Mo,y.state=r.memoizedState,C=i.getDerivedStateFromProps,typeof C=="function"&&(Pf(r,i,C,u),y.state=r.memoizedState),typeof i.getDerivedStateFromProps=="function"||typeof y.getSnapshotBeforeUpdate=="function"||typeof y.UNSAFE_componentWillMount!="function"&&typeof y.componentWillMount!="function"||(C=y.state,typeof y.componentWillMount=="function"&&y.componentWillMount(),typeof y.UNSAFE_componentWillMount=="function"&&y.UNSAFE_componentWillMount(),C!==y.state&&$f.enqueueReplaceState(y,y.state,null),ci(r,u,y,h),li(),y.state=r.memoizedState),typeof y.componentDidMount=="function"&&(r.flags|=4194308),u=!0}else if(t===null){y=r.stateNode;var O=r.memoizedProps,q=Fs(i,O);y.props=q;var ee=y.context,fe=i.contextType;C=Mo,typeof fe=="object"&&fe!==null&&(C=Ft(fe));var xe=i.getDerivedStateFromProps;fe=typeof xe=="function"||typeof y.getSnapshotBeforeUpdate=="function",O=r.pendingProps!==O,fe||typeof y.UNSAFE_componentWillReceiveProps!="function"&&typeof y.componentWillReceiveProps!="function"||(O||ee!==C)&&o0(r,y,u,C),Fr=!1;var oe=r.memoizedState;y.state=oe,ci(r,u,y,h),li(),ee=r.memoizedState,O||oe!==ee||Fr?(typeof xe=="function"&&(Pf(r,i,xe,u),ee=r.memoizedState),(q=Fr||s0(r,i,q,u,oe,ee,C))?(fe||typeof y.UNSAFE_componentWillMount!="function"&&typeof y.componentWillMount!="function"||(typeof y.componentWillMount=="function"&&y.componentWillMount(),typeof y.UNSAFE_componentWillMount=="function"&&y.UNSAFE_componentWillMount()),typeof y.componentDidMount=="function"&&(r.flags|=4194308)):(typeof y.componentDidMount=="function"&&(r.flags|=4194308),r.memoizedProps=u,r.memoizedState=ee),y.props=u,y.state=ee,y.context=C,u=q):(typeof y.componentDidMount=="function"&&(r.flags|=4194308),u=!1)}else{y=r.stateNode,vf(t,r),C=r.memoizedProps,fe=Fs(i,C),y.props=fe,xe=r.pendingProps,oe=y.context,ee=i.contextType,q=Mo,typeof ee=="object"&&ee!==null&&(q=Ft(ee)),O=i.getDerivedStateFromProps,(ee=typeof O=="function"||typeof y.getSnapshotBeforeUpdate=="function")||typeof y.UNSAFE_componentWillReceiveProps!="function"&&typeof y.componentWillReceiveProps!="function"||(C!==xe||oe!==q)&&o0(r,y,u,q),Fr=!1,oe=r.memoizedState,y.state=oe,ci(r,u,y,h),li();var ae=r.memoizedState;C!==xe||oe!==ae||Fr||t!==null&&t.dependencies!==null&&Zl(t.dependencies)?(typeof O=="function"&&(Pf(r,i,O,u),ae=r.memoizedState),(fe=Fr||s0(r,i,fe,u,oe,ae,q)||t!==null&&t.dependencies!==null&&Zl(t.dependencies))?(ee||typeof y.UNSAFE_componentWillUpdate!="function"&&typeof y.componentWillUpdate!="function"||(typeof y.componentWillUpdate=="function"&&y.componentWillUpdate(u,ae,q),typeof y.UNSAFE_componentWillUpdate=="function"&&y.UNSAFE_componentWillUpdate(u,ae,q)),typeof y.componentDidUpdate=="function"&&(r.flags|=4),typeof y.getSnapshotBeforeUpdate=="function"&&(r.flags|=1024)):(typeof y.componentDidUpdate!="function"||C===t.memoizedProps&&oe===t.memoizedState||(r.flags|=4),typeof y.getSnapshotBeforeUpdate!="function"||C===t.memoizedProps&&oe===t.memoizedState||(r.flags|=1024),r.memoizedProps=u,r.memoizedState=ae),y.props=u,y.state=ae,y.context=q,u=fe):(typeof y.componentDidUpdate!="function"||C===t.memoizedProps&&oe===t.memoizedState||(r.flags|=4),typeof y.getSnapshotBeforeUpdate!="function"||C===t.memoizedProps&&oe===t.memoizedState||(r.flags|=1024),u=!1)}return y=u,hc(t,r),u=(r.flags&128)!==0,y||u?(y=r.stateNode,i=u&&typeof i.getDerivedStateFromError!="function"?null:y.render(),r.flags|=1,t!==null&&u?(r.child=Po(r,t.child,null,h),r.child=Po(r,null,i,h)):Bt(t,r,i,h),r.memoizedState=y.state,t=r.child):t=Sr(t,r,h),t}function b0(t,r,i,u){return ei(),r.flags|=256,Bt(t,r,i,u),r.child}var qf={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function Ff(t){return{baseLanes:t,cachePool:ix()}}function Yf(t,r,i){return t=t!==null?t.childLanes&~i:0,r&&(t|=Cn),t}function w0(t,r,i){var u=r.pendingProps,h=!1,y=(r.flags&128)!==0,C;if((C=y)||(C=t!==null&&t.memoizedState===null?!1:(Tt.current&2)!==0),C&&(h=!0,r.flags&=-129),C=(r.flags&32)!==0,r.flags&=-33,t===null){if(ot){if(h?Zr(r):Wr(),ot){var O=Nt,q;if(q=O){e:{for(q=O,O=Yn;q.nodeType!==8;){if(!O){O=null;break e}if(q=zn(q.nextSibling),q===null){O=null;break e}}O=q}O!==null?(r.memoizedState={dehydrated:O,treeContext:Ls!==null?{id:xr,overflow:yr}:null,retryLane:536870912,hydrationErrors:null},q=cn(18,null,null,0),q.stateNode=O,q.return=r,r.child=q,Zt=r,Nt=null,q=!0):q=!1}q||Ps(r)}if(O=r.memoizedState,O!==null&&(O=O.dehydrated,O!==null))return Am(O)?r.lanes=32:r.lanes=536870912,null;Nr(r)}return O=u.children,u=u.fallback,h?(Wr(),h=r.mode,O=pc({mode:"hidden",children:O},h),u=Is(u,h,i,null),O.return=r,u.return=r,O.sibling=u,r.child=O,h=r.child,h.memoizedState=Ff(i),h.childLanes=Yf(t,C,i),r.memoizedState=qf,u):(Zr(r),Gf(r,O))}if(q=t.memoizedState,q!==null&&(O=q.dehydrated,O!==null)){if(y)r.flags&256?(Zr(r),r.flags&=-257,r=Xf(t,r,i)):r.memoizedState!==null?(Wr(),r.child=t.child,r.flags|=128,r=null):(Wr(),h=u.fallback,O=r.mode,u=pc({mode:"visible",children:u.children},O),h=Is(h,O,i,null),h.flags|=2,u.return=r,h.return=r,u.sibling=h,r.child=u,Po(r,t.child,null,i),u=r.child,u.memoizedState=Ff(i),u.childLanes=Yf(t,C,i),r.memoizedState=qf,r=h);else if(Zr(r),Am(O)){if(C=O.nextSibling&&O.nextSibling.dataset,C)var ee=C.dgst;C=ee,u=Error(o(419)),u.stack="",u.digest=C,ti({value:u,source:null,stack:null}),r=Xf(t,r,i)}else if(zt||ni(t,r,i,!1),C=(i&t.childLanes)!==0,zt||C){if(C=pt,C!==null&&(u=i&-i,u=(u&42)!==0?1:Oa(u),u=(u&(C.suspendedLanes|i))!==0?0:u,u!==0&&u!==q.retryLane))throw q.retryLane=u,Ao(t,u),hn(C,t,u),f0;O.data==="$?"||fm(),r=Xf(t,r,i)}else O.data==="$?"?(r.flags|=192,r.child=t.child,r=null):(t=q.treeContext,Nt=zn(O.nextSibling),Zt=r,ot=!0,Bs=null,Yn=!1,t!==null&&(_n[jn++]=xr,_n[jn++]=yr,_n[jn++]=Ls,xr=t.id,yr=t.overflow,Ls=r),r=Gf(r,u.children),r.flags|=4096);return r}return h?(Wr(),h=u.fallback,O=r.mode,q=t.child,ee=q.sibling,u=gr(q,{mode:"hidden",children:u.children}),u.subtreeFlags=q.subtreeFlags&65011712,ee!==null?h=gr(ee,h):(h=Is(h,O,i,null),h.flags|=2),h.return=r,u.return=r,u.sibling=h,r.child=u,u=h,h=r.child,O=t.child.memoizedState,O===null?O=Ff(i):(q=O.cachePool,q!==null?(ee=Mt._currentValue,q=q.parent!==ee?{parent:ee,pool:ee}:q):q=ix(),O={baseLanes:O.baseLanes|i,cachePool:q}),h.memoizedState=O,h.childLanes=Yf(t,C,i),r.memoizedState=qf,u):(Zr(r),i=t.child,t=i.sibling,i=gr(i,{mode:"visible",children:u.children}),i.return=r,i.sibling=null,t!==null&&(C=r.deletions,C===null?(r.deletions=[t],r.flags|=16):C.push(t)),r.child=i,r.memoizedState=null,i)}function Gf(t,r){return r=pc({mode:"visible",children:r},t.mode),r.return=t,t.child=r}function pc(t,r){return t=cn(22,t,null,r),t.lanes=0,t.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null},t}function Xf(t,r,i){return Po(r,t.child,null,i),t=Gf(r,r.pendingProps.children),t.flags|=2,r.memoizedState=null,t}function N0(t,r,i){t.lanes|=r;var u=t.alternate;u!==null&&(u.lanes|=r),ff(t.return,r,i)}function Zf(t,r,i,u,h){var y=t.memoizedState;y===null?t.memoizedState={isBackwards:r,rendering:null,renderingStartTime:0,last:u,tail:i,tailMode:h}:(y.isBackwards=r,y.rendering=null,y.renderingStartTime=0,y.last=u,y.tail=i,y.tailMode=h)}function S0(t,r,i){var u=r.pendingProps,h=u.revealOrder,y=u.tail;if(Bt(t,r,u.children,i),u=Tt.current,(u&2)!==0)u=u&1|2,r.flags|=128;else{if(t!==null&&(t.flags&128)!==0)e:for(t=r.child;t!==null;){if(t.tag===13)t.memoizedState!==null&&N0(t,i,r);else if(t.tag===19)N0(t,i,r);else if(t.child!==null){t.child.return=t,t=t.child;continue}if(t===r)break e;for(;t.sibling===null;){if(t.return===null||t.return===r)break e;t=t.return}t.sibling.return=t.return,t=t.sibling}u&=1}switch(Z(Tt,u),h){case"forwards":for(i=r.child,h=null;i!==null;)t=i.alternate,t!==null&&dc(t)===null&&(h=i),i=i.sibling;i=h,i===null?(h=r.child,r.child=null):(h=i.sibling,i.sibling=null),Zf(r,!1,h,i,y);break;case"backwards":for(i=null,h=r.child,r.child=null;h!==null;){if(t=h.alternate,t!==null&&dc(t)===null){r.child=h;break}t=h.sibling,h.sibling=i,i=h,h=t}Zf(r,!0,i,null,y);break;case"together":Zf(r,!1,null,null,void 0);break;default:r.memoizedState=null}return r.child}function Sr(t,r,i){if(t!==null&&(r.dependencies=t.dependencies),ts|=r.lanes,(i&r.childLanes)===0)if(t!==null){if(ni(t,r,i,!1),(i&r.childLanes)===0)return null}else return null;if(t!==null&&r.child!==t.child)throw Error(o(153));if(r.child!==null){for(t=r.child,i=gr(t,t.pendingProps),r.child=i,i.return=r;t.sibling!==null;)t=t.sibling,i=i.sibling=gr(t,t.pendingProps),i.return=r;i.sibling=null}return r.child}function Wf(t,r){return(t.lanes&r)!==0?!0:(t=t.dependencies,!!(t!==null&&Zl(t)))}function pj(t,r,i){switch(r.tag){case 3:ce(r,r.stateNode.containerInfo),qr(r,Mt,t.memoizedState.cache),ei();break;case 27:case 5:_e(r);break;case 4:ce(r,r.stateNode.containerInfo);break;case 10:qr(r,r.type,r.memoizedProps.value);break;case 13:var u=r.memoizedState;if(u!==null)return u.dehydrated!==null?(Zr(r),r.flags|=128,null):(i&r.child.childLanes)!==0?w0(t,r,i):(Zr(r),t=Sr(t,r,i),t!==null?t.sibling:null);Zr(r);break;case 19:var h=(t.flags&128)!==0;if(u=(i&r.childLanes)!==0,u||(ni(t,r,i,!1),u=(i&r.childLanes)!==0),h){if(u)return S0(t,r,i);r.flags|=128}if(h=r.memoizedState,h!==null&&(h.rendering=null,h.tail=null,h.lastEffect=null),Z(Tt,Tt.current),u)break;return null;case 22:case 23:return r.lanes=0,g0(t,r,i);case 24:qr(r,Mt,t.memoizedState.cache)}return Sr(t,r,i)}function _0(t,r,i){if(t!==null)if(t.memoizedProps!==r.pendingProps)zt=!0;else{if(!Wf(t,i)&&(r.flags&128)===0)return zt=!1,pj(t,r,i);zt=(t.flags&131072)!==0}else zt=!1,ot&&(r.flags&1048576)!==0&&ex(r,Xl,r.index);switch(r.lanes=0,r.tag){case 16:e:{t=r.pendingProps;var u=r.elementType,h=u._init;if(u=h(u._payload),r.type=u,typeof u=="function")sf(u)?(t=Fs(u,t),r.tag=1,r=v0(null,r,u,t,i)):(r.tag=0,r=Vf(null,r,u,t,i));else{if(u!=null){if(h=u.$$typeof,h===k){r.tag=11,r=m0(null,r,u,t,i);break e}else if(h===z){r.tag=14,r=h0(null,r,u,t,i);break e}}throw r=L(u)||u,Error(o(306,r,""))}}return r;case 0:return Vf(t,r,r.type,r.pendingProps,i);case 1:return u=r.type,h=Fs(u,r.pendingProps),v0(t,r,u,h,i);case 3:e:{if(ce(r,r.stateNode.containerInfo),t===null)throw Error(o(387));u=r.pendingProps;var y=r.memoizedState;h=y.element,vf(t,r),ci(r,u,null,i);var C=r.memoizedState;if(u=C.cache,qr(r,Mt,u),u!==y.cache&&mf(r,[Mt],i,!0),li(),u=C.element,y.isDehydrated)if(y={element:u,isDehydrated:!1,cache:C.cache},r.updateQueue.baseState=y,r.memoizedState=y,r.flags&256){r=b0(t,r,u,i);break e}else if(u!==h){h=Nn(Error(o(424)),r),ti(h),r=b0(t,r,u,i);break e}else{switch(t=r.stateNode.containerInfo,t.nodeType){case 9:t=t.body;break;default:t=t.nodeName==="HTML"?t.ownerDocument.body:t}for(Nt=zn(t.firstChild),Zt=r,ot=!0,Bs=null,Yn=!0,i=n0(r,null,u,i),r.child=i;i;)i.flags=i.flags&-3|4096,i=i.sibling}else{if(ei(),u===h){r=Sr(t,r,i);break e}Bt(t,r,u,i)}r=r.child}return r;case 26:return hc(t,r),t===null?(i=ky(r.type,null,r.pendingProps,null))?r.memoizedState=i:ot||(i=r.type,t=r.pendingProps,u=Ac(pe.current).createElement(i),u[Ot]=r,u[qt]=t,$t(u,i,t),Et(u),r.stateNode=u):r.memoizedState=ky(r.type,t.memoizedProps,r.pendingProps,t.memoizedState),null;case 27:return _e(r),t===null&&ot&&(u=r.stateNode=jy(r.type,r.pendingProps,pe.current),Zt=r,Yn=!0,h=Nt,os(r.type)?(Mm=h,Nt=zn(u.firstChild)):Nt=h),Bt(t,r,r.pendingProps.children,i),hc(t,r),t===null&&(r.flags|=4194304),r.child;case 5:return t===null&&ot&&((h=u=Nt)&&(u=Vj(u,r.type,r.pendingProps,Yn),u!==null?(r.stateNode=u,Zt=r,Nt=zn(u.firstChild),Yn=!1,h=!0):h=!1),h||Ps(r)),_e(r),h=r.type,y=r.pendingProps,C=t!==null?t.memoizedProps:null,u=y.children,Em(h,y)?u=null:C!==null&&Em(h,C)&&(r.flags|=32),r.memoizedState!==null&&(h=jf(t,r,ij,null,null,i),Ti._currentValue=h),hc(t,r),Bt(t,r,u,i),r.child;case 6:return t===null&&ot&&((t=i=Nt)&&(i=qj(i,r.pendingProps,Yn),i!==null?(r.stateNode=i,Zt=r,Nt=null,t=!0):t=!1),t||Ps(r)),null;case 13:return w0(t,r,i);case 4:return ce(r,r.stateNode.containerInfo),u=r.pendingProps,t===null?r.child=Po(r,null,u,i):Bt(t,r,u,i),r.child;case 11:return m0(t,r,r.type,r.pendingProps,i);case 7:return Bt(t,r,r.pendingProps,i),r.child;case 8:return Bt(t,r,r.pendingProps.children,i),r.child;case 12:return Bt(t,r,r.pendingProps.children,i),r.child;case 10:return u=r.pendingProps,qr(r,r.type,u.value),Bt(t,r,u.children,i),r.child;case 9:return h=r.type._context,u=r.pendingProps.children,Us(r),h=Ft(h),u=u(h),r.flags|=1,Bt(t,r,u,i),r.child;case 14:return h0(t,r,r.type,r.pendingProps,i);case 15:return p0(t,r,r.type,r.pendingProps,i);case 19:return S0(t,r,i);case 31:return u=r.pendingProps,i=r.mode,u={mode:u.mode,children:u.children},t===null?(i=pc(u,i),i.ref=r.ref,r.child=i,i.return=r,r=i):(i=gr(t.child,u),i.ref=r.ref,r.child=i,i.return=r,r=i),r;case 22:return g0(t,r,i);case 24:return Us(r),u=Ft(Mt),t===null?(h=gf(),h===null&&(h=pt,y=hf(),h.pooledCache=y,y.refCount++,y!==null&&(h.pooledCacheLanes|=i),h=y),r.memoizedState={parent:u,cache:h},yf(r),qr(r,Mt,h)):((t.lanes&i)!==0&&(vf(t,r),ci(r,null,null,i),li()),h=t.memoizedState,y=r.memoizedState,h.parent!==u?(h={parent:u,cache:u},r.memoizedState=h,r.lanes===0&&(r.memoizedState=r.updateQueue.baseState=h),qr(r,Mt,u)):(u=y.cache,qr(r,Mt,u),u!==h.cache&&mf(r,[Mt],i,!0))),Bt(t,r,r.pendingProps.children,i),r.child;case 29:throw r.pendingProps}throw Error(o(156,r.tag))}function _r(t){t.flags|=4}function j0(t,r){if(r.type!=="stylesheet"||(r.state.loading&4)!==0)t.flags&=-16777217;else if(t.flags|=16777216,!Dy(r)){if(r=En.current,r!==null&&((tt&4194048)===tt?Gn!==null:(tt&62914560)!==tt&&(tt&536870912)===0||r!==Gn))throw ai=xf,lx;t.flags|=8192}}function gc(t,r){r!==null&&(t.flags|=4),t.flags&16384&&(r=t.tag!==22?qn():536870912,t.lanes|=r,qo|=r)}function gi(t,r){if(!ot)switch(t.tailMode){case"hidden":r=t.tail;for(var i=null;r!==null;)r.alternate!==null&&(i=r),r=r.sibling;i===null?t.tail=null:i.sibling=null;break;case"collapsed":i=t.tail;for(var u=null;i!==null;)i.alternate!==null&&(u=i),i=i.sibling;u===null?r||t.tail===null?t.tail=null:t.tail.sibling=null:u.sibling=null}}function vt(t){var r=t.alternate!==null&&t.alternate.child===t.child,i=0,u=0;if(r)for(var h=t.child;h!==null;)i|=h.lanes|h.childLanes,u|=h.subtreeFlags&65011712,u|=h.flags&65011712,h.return=t,h=h.sibling;else for(h=t.child;h!==null;)i|=h.lanes|h.childLanes,u|=h.subtreeFlags,u|=h.flags,h.return=t,h=h.sibling;return t.subtreeFlags|=u,t.childLanes=i,r}function gj(t,r,i){var u=r.pendingProps;switch(cf(r),r.tag){case 31:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return vt(r),null;case 1:return vt(r),null;case 3:return i=r.stateNode,u=null,t!==null&&(u=t.memoizedState.cache),r.memoizedState.cache!==u&&(r.flags|=2048),br(Mt),be(),i.pendingContext&&(i.context=i.pendingContext,i.pendingContext=null),(t===null||t.child===null)&&(Ja(r)?_r(r):t===null||t.memoizedState.isDehydrated&&(r.flags&256)===0||(r.flags|=1024,rx())),vt(r),null;case 26:return i=r.memoizedState,t===null?(_r(r),i!==null?(vt(r),j0(r,i)):(vt(r),r.flags&=-16777217)):i?i!==t.memoizedState?(_r(r),vt(r),j0(r,i)):(vt(r),r.flags&=-16777217):(t.memoizedProps!==u&&_r(r),vt(r),r.flags&=-16777217),null;case 27:je(r),i=pe.current;var h=r.type;if(t!==null&&r.stateNode!=null)t.memoizedProps!==u&&_r(r);else{if(!u){if(r.stateNode===null)throw Error(o(166));return vt(r),null}t=re.current,Ja(r)?tx(r):(t=jy(h,u,i),r.stateNode=t,_r(r))}return vt(r),null;case 5:if(je(r),i=r.type,t!==null&&r.stateNode!=null)t.memoizedProps!==u&&_r(r);else{if(!u){if(r.stateNode===null)throw Error(o(166));return vt(r),null}if(t=re.current,Ja(r))tx(r);else{switch(h=Ac(pe.current),t){case 1:t=h.createElementNS("http://www.w3.org/2000/svg",i);break;case 2:t=h.createElementNS("http://www.w3.org/1998/Math/MathML",i);break;default:switch(i){case"svg":t=h.createElementNS("http://www.w3.org/2000/svg",i);break;case"math":t=h.createElementNS("http://www.w3.org/1998/Math/MathML",i);break;case"script":t=h.createElement("div"),t.innerHTML="