From 0f8d8551a6af32e4ef3461262e0b0c53ccab9d54 Mon Sep 17 00:00:00 2001 From: Pratham Mishra <99235987+Pratham-Mishra04@users.noreply.github.com> Date: Fri, 18 Jul 2025 12:19:25 +0530 Subject: [PATCH] feat: azure and vertex config to keys and optimised key management in store --- core/bifrost.go | 28 +- core/providers/anthropic.go | 14 +- core/providers/azure.go | 58 +- core/providers/bedrock.go | 18 +- core/providers/cohere.go | 14 +- core/providers/groq.go | 17 +- core/providers/mistral.go | 14 +- core/providers/ollama.go | 16 +- core/providers/openai.go | 14 +- core/providers/sgl.go | 16 +- core/providers/vertex.go | 176 +++-- core/schemas/account.go | 25 +- core/schemas/meta/azure.go | 40 -- core/schemas/meta/bedrock.go | 7 - core/schemas/meta/vertex.go | 39 -- core/schemas/provider.go | 18 +- core/utils.go | 4 +- docs/usage/go-package/account.md | 30 +- .../http-transport/configuration/providers.md | 34 +- .../integrations/genai-compatible.md | 32 +- docs/usage/http-transport/openapi.json | 44 ++ docs/usage/providers.md | 75 +- tests/core-providers/config/account.go | 32 +- tests/core-providers/go.mod | 2 + tests/core-providers/vertex_test.go | 3 - .../bifrost-http/handlers/completions.go | 4 +- transports/bifrost-http/handlers/providers.go | 249 ++++--- transports/bifrost-http/lib/account.go | 4 +- transports/bifrost-http/lib/config.go | 2 +- transports/bifrost-http/lib/store.go | 656 +++++++++++------- transports/bifrost-http/ui/404.html | 4 +- transports/bifrost-http/ui/404/index.html | 4 +- .../app/mcp-clients/page-b2d03b2155b1dfee.js | 1 - .../app/mcp-clients/page-b787aebb95ad9490.js | 1 + .../app/providers/page-b25fa0ce16423da1.js | 1 - .../app/providers/page-d68ad7e5de246698.js | 1 + .../ui/_next/static/css/215063fa9dc64c3d.css | 1 - .../ui/_next/static/css/e2d791eafd0cc299.css | 1 + transports/bifrost-http/ui/config/index.html | 4 +- transports/bifrost-http/ui/config/index.txt | 4 +- transports/bifrost-http/ui/docs/index.html | 4 +- transports/bifrost-http/ui/docs/index.txt | 4 +- transports/bifrost-http/ui/index.html | 4 +- transports/bifrost-http/ui/index.txt | 4 +- .../bifrost-http/ui/mcp-clients/index.html | 4 +- .../bifrost-http/ui/mcp-clients/index.txt | 6 +- transports/bifrost-http/ui/plugins/index.html | 4 +- transports/bifrost-http/ui/plugins/index.txt | 4 +- .../bifrost-http/ui/providers/index.html | 4 +- .../bifrost-http/ui/providers/index.txt | 6 +- transports/config.example.json | 33 +- ui/components/config/meta-config-renderer.tsx | 54 +- ui/components/config/provider-form.tsx | 371 +++++++--- ui/components/config/providers-list.tsx | 328 ++++----- ui/lib/types/config.ts | 26 +- ui/lib/utils/validation.ts | 110 +++ 56 files changed, 1611 insertions(+), 1062 deletions(-) delete mode 100644 core/schemas/meta/azure.go delete mode 100644 core/schemas/meta/vertex.go delete mode 100644 transports/bifrost-http/ui/_next/static/chunks/app/mcp-clients/page-b2d03b2155b1dfee.js create mode 100644 transports/bifrost-http/ui/_next/static/chunks/app/mcp-clients/page-b787aebb95ad9490.js delete mode 100644 transports/bifrost-http/ui/_next/static/chunks/app/providers/page-b25fa0ce16423da1.js create mode 100644 transports/bifrost-http/ui/_next/static/chunks/app/providers/page-d68ad7e5de246698.js delete mode 100644 transports/bifrost-http/ui/_next/static/css/215063fa9dc64c3d.css create mode 100644 transports/bifrost-http/ui/_next/static/css/e2d791eafd0cc299.css diff --git a/core/bifrost.go b/core/bifrost.go index aa53cdd5cb..c0de1d3889 100644 --- a/core/bifrost.go +++ b/core/bifrost.go @@ -28,7 +28,7 @@ const ( ) // executor is a function type that handles specific request types. -type executor func(provider schemas.Provider, req *ChannelMessage, key string) (*schemas.BifrostResponse, *schemas.BifrostError) +type executor func(provider schemas.Provider, req *ChannelMessage, key schemas.Key) (*schemas.BifrostResponse, *schemas.BifrostError) // messageExecutors is a factory map for handling different request types. var messageExecutors = map[RequestType]executor{ @@ -1092,7 +1092,7 @@ func (bifrost *Bifrost) requestWorker(provider schemas.Provider, queue chan Chan var bifrostError *schemas.BifrostError var err error - key := "" + key := schemas.Key{} if providerRequiresKey(provider.GetProviderKey()) { key, err = bifrost.selectKeyFromProviderForModel(provider.GetProviderKey(), req.Model) if err != nil { @@ -1207,7 +1207,7 @@ func (bifrost *Bifrost) requestWorker(provider schemas.Provider, queue chan Chan } // handleTextCompletion executes a text completion request -func handleTextCompletion(provider schemas.Provider, req *ChannelMessage, key string) (*schemas.BifrostResponse, *schemas.BifrostError) { +func handleTextCompletion(provider schemas.Provider, req *ChannelMessage, key schemas.Key) (*schemas.BifrostResponse, *schemas.BifrostError) { if req.Input.TextCompletionInput == nil { return nil, &schemas.BifrostError{ IsBifrostError: false, @@ -1220,7 +1220,7 @@ func handleTextCompletion(provider schemas.Provider, req *ChannelMessage, key st } // handleChatCompletion executes a chat completion request -func handleChatCompletion(provider schemas.Provider, req *ChannelMessage, key string) (*schemas.BifrostResponse, *schemas.BifrostError) { +func handleChatCompletion(provider schemas.Provider, req *ChannelMessage, key schemas.Key) (*schemas.BifrostResponse, *schemas.BifrostError) { if req.Input.ChatCompletionInput == nil { return nil, &schemas.BifrostError{ IsBifrostError: false, @@ -1233,7 +1233,7 @@ func handleChatCompletion(provider schemas.Provider, req *ChannelMessage, key st } // handleEmbedding executes an embedding request -func handleEmbedding(provider schemas.Provider, req *ChannelMessage, key string) (*schemas.BifrostResponse, *schemas.BifrostError) { +func handleEmbedding(provider schemas.Provider, req *ChannelMessage, key schemas.Key) (*schemas.BifrostResponse, *schemas.BifrostError) { if req.Input.EmbeddingInput == nil { return nil, &schemas.BifrostError{ IsBifrostError: false, @@ -1246,7 +1246,7 @@ func handleEmbedding(provider schemas.Provider, req *ChannelMessage, key string) } // handleChatCompletionStream executes a chat completion stream request -func handleChatCompletionStream(provider schemas.Provider, req *ChannelMessage, key string, postHookRunner schemas.PostHookRunner) (chan *schemas.BifrostStream, *schemas.BifrostError) { +func handleChatCompletionStream(provider schemas.Provider, req *ChannelMessage, key schemas.Key, postHookRunner schemas.PostHookRunner) (chan *schemas.BifrostStream, *schemas.BifrostError) { if req.Input.ChatCompletionInput == nil { return nil, &schemas.BifrostError{ IsBifrostError: false, @@ -1384,30 +1384,30 @@ func (bifrost *Bifrost) releaseChannelMessage(msg *ChannelMessage) { // selectKeyFromProviderForModel selects an appropriate API key for a given provider and model. // It uses weighted random selection if multiple keys are available. -func (bifrost *Bifrost) selectKeyFromProviderForModel(providerKey schemas.ModelProvider, model string) (string, error) { +func (bifrost *Bifrost) selectKeyFromProviderForModel(providerKey schemas.ModelProvider, model string) (schemas.Key, error) { keys, err := bifrost.account.GetKeysForProvider(providerKey) if err != nil { - return "", err + return schemas.Key{}, err } if len(keys) == 0 { - return "", fmt.Errorf("no keys found for provider: %v", providerKey) + return schemas.Key{}, fmt.Errorf("no keys found for provider: %v", providerKey) } // filter out keys which dont support the model, if the key has no models, it is supported for all models var supportedKeys []schemas.Key for _, key := range keys { - if (slices.Contains(key.Models, model) && strings.TrimSpace(key.Value) != "") || len(key.Models) == 0 { + if (slices.Contains(key.Models, model) && (strings.TrimSpace(key.Value) != "" || providerKey == schemas.Vertex)) || len(key.Models) == 0 { supportedKeys = append(supportedKeys, key) } } if len(supportedKeys) == 0 { - return "", fmt.Errorf("no keys found that support model: %s", model) + return schemas.Key{}, fmt.Errorf("no keys found that support model: %s", model) } if len(supportedKeys) == 1 { - return supportedKeys[0].Value, nil + return supportedKeys[0], nil } // Use a weighted random selection based on key weights @@ -1425,12 +1425,12 @@ func (bifrost *Bifrost) selectKeyFromProviderForModel(providerKey schemas.ModelP for _, key := range supportedKeys { currentWeight += int(key.Weight * 100) if randomValue < currentWeight { - return key.Value, nil + return key, nil } } // Fallback to first key if something goes wrong - return supportedKeys[0].Value, nil + return supportedKeys[0], nil } // CLEANUP diff --git a/core/providers/anthropic.go b/core/providers/anthropic.go index 02ab13bd87..d4b9f25901 100644 --- a/core/providers/anthropic.go +++ b/core/providers/anthropic.go @@ -318,7 +318,7 @@ func (provider *AnthropicProvider) completeRequest(ctx context.Context, requestB // TextCompletion performs a text completion request to Anthropic's API. // It formats the request, sends it to Anthropic, and processes the response. // Returns a BifrostResponse containing the completion results or an error if the request fails. -func (provider *AnthropicProvider) TextCompletion(ctx context.Context, model, key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *AnthropicProvider) TextCompletion(ctx context.Context, model string, key schemas.Key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { preparedParams := provider.prepareTextCompletionParams(prepareParams(params)) // Merge additional parameters @@ -327,7 +327,7 @@ func (provider *AnthropicProvider) TextCompletion(ctx context.Context, model, ke "prompt": fmt.Sprintf("\n\nHuman: %s\n\nAssistant:", text), }, preparedParams) - responseBody, err := provider.completeRequest(ctx, requestBody, provider.networkConfig.BaseURL+"/v1/complete", key) + responseBody, err := provider.completeRequest(ctx, requestBody, provider.networkConfig.BaseURL+"/v1/complete", key.Value) if err != nil { return nil, err } @@ -379,7 +379,7 @@ func (provider *AnthropicProvider) TextCompletion(ctx context.Context, model, ke // ChatCompletion performs a chat completion request to Anthropic's API. // It formats the request, sends it to Anthropic, and processes the response. // Returns a BifrostResponse containing the completion results or an error if the request fails. -func (provider *AnthropicProvider) ChatCompletion(ctx context.Context, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *AnthropicProvider) ChatCompletion(ctx context.Context, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { formattedMessages, preparedParams := prepareAnthropicChatRequest(messages, params) // Merge additional parameters @@ -388,7 +388,7 @@ func (provider *AnthropicProvider) ChatCompletion(ctx context.Context, model, ke "messages": formattedMessages, }, preparedParams) - responseBody, err := provider.completeRequest(ctx, requestBody, provider.networkConfig.BaseURL+"/v1/messages", key) + responseBody, err := provider.completeRequest(ctx, requestBody, provider.networkConfig.BaseURL+"/v1/messages", key.Value) if err != nil { return nil, err } @@ -775,14 +775,14 @@ func parseAnthropicResponse(response *AnthropicChatResponse, bifrostResponse *sc } // Embedding is not supported by the Anthropic provider. -func (provider *AnthropicProvider) Embedding(ctx context.Context, model, key string, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *AnthropicProvider) Embedding(ctx context.Context, model string, key schemas.Key, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { return nil, newUnsupportedOperationError("embedding", "anthropic") } // ChatCompletionStream performs a streaming chat completion request to the Anthropic API. // It supports real-time streaming of responses using Server-Sent Events (SSE). // Returns a channel containing BifrostResponse objects representing the stream or an error if the request fails. -func (provider *AnthropicProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { +func (provider *AnthropicProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { formattedMessages, preparedParams := prepareAnthropicChatRequest(messages, params) // Merge additional parameters and set stream to true @@ -795,7 +795,7 @@ func (provider *AnthropicProvider) ChatCompletionStream(ctx context.Context, pos // Prepare Anthropic headers headers := map[string]string{ "Content-Type": "application/json", - "x-api-key": key, + "x-api-key": key.Value, "anthropic-version": provider.apiVersion, "Accept": "text/event-stream", "Cache-Control": "no-cache", diff --git a/core/providers/azure.go b/core/providers/azure.go index 8637ccf05e..6ef52b3593 100644 --- a/core/providers/azure.go +++ b/core/providers/azure.go @@ -117,7 +117,6 @@ type AzureProvider struct { logger schemas.Logger // Logger for provider operations client *fasthttp.Client // HTTP client for API requests streamClient *http.Client // HTTP client for streaming requests - meta schemas.MetaConfig // Azure-specific configuration networkConfig schemas.NetworkConfig // Network configuration including extra headers } @@ -127,10 +126,6 @@ type AzureProvider struct { func NewAzureProvider(config *schemas.ProviderConfig, logger schemas.Logger) (*AzureProvider, error) { config.CheckAndSetDefaults() - if config.MetaConfig == nil { - return nil, fmt.Errorf("meta config is not set") - } - client := &fasthttp.Client{ ReadTimeout: time.Second * time.Duration(config.NetworkConfig.DefaultRequestTimeoutInSeconds), WriteTimeout: time.Second * time.Duration(config.NetworkConfig.DefaultRequestTimeoutInSeconds), @@ -156,7 +151,6 @@ func NewAzureProvider(config *schemas.ProviderConfig, logger schemas.Logger) (*A logger: logger, client: client, streamClient: streamClient, - meta: config.MetaConfig, networkConfig: config.NetworkConfig, }, nil } @@ -169,7 +163,16 @@ func (provider *AzureProvider) GetProviderKey() schemas.ModelProvider { // completeRequest sends a request to Azure's API and handles the response. // It constructs the API URL, sets up authentication, and processes the response. // Returns the response body or an error if the request fails. -func (provider *AzureProvider) completeRequest(ctx context.Context, requestBody map[string]interface{}, path string, key string, model string) ([]byte, *schemas.BifrostError) { +func (provider *AzureProvider) completeRequest(ctx context.Context, requestBody map[string]interface{}, path string, key schemas.Key, model string) ([]byte, *schemas.BifrostError) { + if key.AzureKeyConfig == nil { + return nil, &schemas.BifrostError{ + IsBifrostError: false, + Error: schemas.ErrorField{ + Message: "azure key config not set", + }, + } + } + // Marshal the request body jsonData, err := json.Marshal(requestBody) if err != nil { @@ -182,7 +185,7 @@ func (provider *AzureProvider) completeRequest(ctx context.Context, requestBody } } - if provider.meta.GetEndpoint() == nil { + if key.AzureKeyConfig.Endpoint == "" { return nil, &schemas.BifrostError{ IsBifrostError: false, Error: schemas.ErrorField{ @@ -191,10 +194,10 @@ func (provider *AzureProvider) completeRequest(ctx context.Context, requestBody } } - url := *provider.meta.GetEndpoint() + url := key.AzureKeyConfig.Endpoint - if provider.meta.GetDeployments() != nil { - deployment := provider.meta.GetDeployments()[model] + if key.AzureKeyConfig.Deployments != nil { + deployment := key.AzureKeyConfig.Deployments[model] if deployment == "" { return nil, &schemas.BifrostError{ IsBifrostError: false, @@ -204,7 +207,7 @@ func (provider *AzureProvider) completeRequest(ctx context.Context, requestBody } } - apiVersion := provider.meta.GetAPIVersion() + apiVersion := key.AzureKeyConfig.APIVersion if apiVersion == nil { apiVersion = StrPtr("2024-02-01") } @@ -236,7 +239,7 @@ func (provider *AzureProvider) completeRequest(ctx context.Context, requestBody // Ensure api-key is not accidentally present (from extra headers, etc.) req.Header.Del("api-key") } else { - req.Header.Set("api-key", key) + req.Header.Set("api-key", key.Value) } req.SetBody(jsonData) @@ -269,7 +272,7 @@ func (provider *AzureProvider) completeRequest(ctx context.Context, requestBody // TextCompletion performs a text completion request to Azure's API. // It formats the request, sends it to Azure, and processes the response. // Returns a BifrostResponse containing the completion results or an error if the request fails. -func (provider *AzureProvider) TextCompletion(ctx context.Context, model, key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *AzureProvider) TextCompletion(ctx context.Context, model string, key schemas.Key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { preparedParams := prepareParams(params) // Merge additional parameters @@ -337,7 +340,7 @@ func (provider *AzureProvider) TextCompletion(ctx context.Context, model, key, t // ChatCompletion performs a chat completion request to Azure's API. // It formats the request, sends it to Azure, and processes the response. // Returns a BifrostResponse containing the completion results or an error if the request fails. -func (provider *AzureProvider) ChatCompletion(ctx context.Context, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *AzureProvider) ChatCompletion(ctx context.Context, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { formattedMessages, preparedParams := prepareOpenAIChatRequest(messages, params) // Merge additional parameters @@ -384,7 +387,7 @@ func (provider *AzureProvider) ChatCompletion(ctx context.Context, model, key st // Embedding generates embeddings for the given input text(s) using Azure OpenAI. // The input can be either a single string or a slice of strings for batch embedding. // Returns a BifrostResponse containing the embedding(s) and any error that occurred. -func (provider *AzureProvider) Embedding(ctx context.Context, model string, key string, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *AzureProvider) Embedding(ctx context.Context, model string, key schemas.Key, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { if len(input.Texts) == 0 { return nil, &schemas.BifrostError{ IsBifrostError: true, @@ -493,9 +496,18 @@ func (provider *AzureProvider) Embedding(ctx context.Context, model string, key // It supports real-time streaming of responses using Server-Sent Events (SSE). // Uses Azure-specific URL construction with deployments and supports both api-key and Bearer token authentication. // Returns a channel containing BifrostResponse objects representing the stream or an error if the request fails. -func (provider *AzureProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { +func (provider *AzureProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { formattedMessages, preparedParams := prepareOpenAIChatRequest(messages, params) + if key.AzureKeyConfig == nil { + return nil, &schemas.BifrostError{ + IsBifrostError: false, + Error: schemas.ErrorField{ + Message: "azure key config not set", + }, + } + } + // Merge additional parameters and set stream to true requestBody := mergeConfig(map[string]interface{}{ "model": model, @@ -504,7 +516,7 @@ func (provider *AzureProvider) ChatCompletionStream(ctx context.Context, postHoo }, preparedParams) // Construct Azure-specific URL with deployment - if provider.meta.GetEndpoint() == nil { + if key.AzureKeyConfig.Endpoint == "" { return nil, &schemas.BifrostError{ IsBifrostError: false, Error: schemas.ErrorField{ @@ -513,11 +525,11 @@ func (provider *AzureProvider) ChatCompletionStream(ctx context.Context, postHoo } } - baseURL := *provider.meta.GetEndpoint() + baseURL := key.AzureKeyConfig.Endpoint var fullURL string - if provider.meta.GetDeployments() != nil { - deployment := provider.meta.GetDeployments()[model] + if key.AzureKeyConfig.Deployments != nil { + deployment := key.AzureKeyConfig.Deployments[model] if deployment == "" { return nil, &schemas.BifrostError{ IsBifrostError: false, @@ -527,7 +539,7 @@ func (provider *AzureProvider) ChatCompletionStream(ctx context.Context, postHoo } } - apiVersion := provider.meta.GetAPIVersion() + apiVersion := key.AzureKeyConfig.APIVersion if apiVersion == nil { apiVersion = StrPtr("2024-02-01") } @@ -552,7 +564,7 @@ func (provider *AzureProvider) ChatCompletionStream(ctx context.Context, postHoo if authToken, ok := ctx.Value(AzureAuthorizationTokenKey).(string); ok { headers["Authorization"] = fmt.Sprintf("Bearer %s", authToken) } else { - headers["api-key"] = key + headers["api-key"] = key.Value } // Use shared streaming logic from OpenAI diff --git a/core/providers/bedrock.go b/core/providers/bedrock.go index 1727ef8b4d..b60a0b456f 100644 --- a/core/providers/bedrock.go +++ b/core/providers/bedrock.go @@ -829,14 +829,14 @@ func (provider *BedrockProvider) prepareTextCompletionParams(params map[string]i // TextCompletion performs a text completion request to Bedrock's API. // It formats the request, sends it to Bedrock, and processes the response. // Returns a BifrostResponse containing the completion results or an error if the request fails. -func (provider *BedrockProvider) TextCompletion(ctx context.Context, model, key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *BedrockProvider) TextCompletion(ctx context.Context, model string, key schemas.Key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { preparedParams := provider.prepareTextCompletionParams(prepareParams(params), model) requestBody := mergeConfig(map[string]interface{}{ "prompt": text, }, preparedParams) - body, err := provider.completeRequest(ctx, requestBody, fmt.Sprintf("%s/invoke", model), key) + body, err := provider.completeRequest(ctx, requestBody, fmt.Sprintf("%s/invoke", model), key.Value) if err != nil { return nil, err } @@ -920,7 +920,7 @@ func (provider *BedrockProvider) extractToolsFromHistory(messages []schemas.Bifr // ChatCompletion performs a chat completion request to Bedrock's API. // It formats the request, sends it to Bedrock, and processes the response. // Returns a BifrostResponse containing the completion results or an error if the request fails. -func (provider *BedrockProvider) ChatCompletion(ctx context.Context, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *BedrockProvider) ChatCompletion(ctx context.Context, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { messageBody, err := provider.prepareChatCompletionMessages(messages, model) if err != nil { return nil, err @@ -962,7 +962,7 @@ func (provider *BedrockProvider) ChatCompletion(ctx context.Context, model, key } // Create the signed request - responseBody, err := provider.completeRequest(ctx, requestBody, path, key) + responseBody, err := provider.completeRequest(ctx, requestBody, path, key.Value) if err != nil { return nil, err } @@ -1150,12 +1150,12 @@ func signAWSRequest(req *http.Request, accessKey, secretKey string, sessionToken // Embedding generates embeddings for the given input text(s) using Amazon Bedrock. // Supports Titan and Cohere embedding models. Returns a BifrostResponse containing the embedding(s) and any error that occurred. -func (provider *BedrockProvider) Embedding(ctx context.Context, model string, key string, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *BedrockProvider) Embedding(ctx context.Context, model string, key schemas.Key, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { switch { case strings.HasPrefix(model, "amazon.titan-embed-text"): - return provider.handleTitanEmbedding(ctx, model, key, input, params) + return provider.handleTitanEmbedding(ctx, model, key.Value, input, params) case strings.HasPrefix(model, "cohere.embed"): - return provider.handleCohereEmbedding(ctx, model, key, input, params) + return provider.handleCohereEmbedding(ctx, model, key.Value, input, params) default: return nil, &schemas.BifrostError{ IsBifrostError: false, @@ -1308,7 +1308,7 @@ func (provider *BedrockProvider) handleCohereEmbedding(ctx context.Context, mode // ChatCompletionStream performs a streaming chat completion request to Bedrock's API. // It formats the request, sends it to Bedrock, and processes the streaming response. // Returns a channel for streaming BifrostResponse objects or an error if the request fails. -func (provider *BedrockProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { +func (provider *BedrockProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { messageBody, err := provider.prepareChatCompletionMessages(messages, model) if err != nil { return nil, err @@ -1392,7 +1392,7 @@ func (provider *BedrockProvider) ChatCompletionStream(ctx context.Context, postH // Sign the request for AWS if provider.meta.GetSecretAccessKey() != nil { - if signErr := signAWSRequest(req, key, *provider.meta.GetSecretAccessKey(), provider.meta.GetSessionToken(), region, "bedrock"); signErr != nil { + if signErr := signAWSRequest(req, key.Value, *provider.meta.GetSecretAccessKey(), provider.meta.GetSessionToken(), region, "bedrock"); signErr != nil { return nil, signErr } } else { diff --git a/core/providers/cohere.go b/core/providers/cohere.go index 4be00be190..15f2491385 100644 --- a/core/providers/cohere.go +++ b/core/providers/cohere.go @@ -183,14 +183,14 @@ func (provider *CohereProvider) GetProviderKey() schemas.ModelProvider { // TextCompletion is not supported by the Cohere provider. // Returns an error indicating that text completion is not supported. -func (provider *CohereProvider) TextCompletion(ctx context.Context, model, key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *CohereProvider) TextCompletion(ctx context.Context, model string, key schemas.Key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { return nil, newUnsupportedOperationError("text completion", "cohere") } // ChatCompletion performs a chat completion request to the Cohere API. // It formats the request, sends it to Cohere, and processes the response. // Returns a BifrostResponse containing the completion results or an error if the request fails. -func (provider *CohereProvider) ChatCompletion(ctx context.Context, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *CohereProvider) ChatCompletion(ctx context.Context, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { // Prepare request body using shared function requestBody, err := prepareCohereChatRequest(messages, params, model, false) if err != nil { @@ -227,7 +227,7 @@ func (provider *CohereProvider) ChatCompletion(ctx context.Context, model, key s req.SetRequestURI(provider.networkConfig.BaseURL + "/v1/chat") req.Header.SetMethod("POST") req.Header.SetContentType("application/json") - req.Header.Set("Authorization", "Bearer "+key) + req.Header.Set("Authorization", "Bearer "+key.Value) req.SetBody(jsonBody) @@ -590,7 +590,7 @@ func convertChatHistory(history []struct { // Embedding generates embeddings for the given input text(s) using the Cohere API. // Supports Cohere's embedding models and returns a BifrostResponse containing the embedding(s). -func (provider *CohereProvider) Embedding(ctx context.Context, model string, key string, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *CohereProvider) Embedding(ctx context.Context, model string, key schemas.Key, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { if len(input.Texts) == 0 { return nil, &schemas.BifrostError{ IsBifrostError: true, @@ -654,7 +654,7 @@ func (provider *CohereProvider) Embedding(ctx context.Context, model string, key req.SetRequestURI(provider.networkConfig.BaseURL + "/v2/embed") req.Header.SetMethod("POST") req.Header.SetContentType("application/json") - req.Header.Set("Authorization", "Bearer "+key) + req.Header.Set("Authorization", "Bearer "+key.Value) req.SetBody(jsonBody) @@ -727,7 +727,7 @@ func (provider *CohereProvider) Embedding(ctx context.Context, model string, key // ChatCompletionStream performs a streaming chat completion request to the Cohere API. // It supports real-time streaming of responses using Server-Sent Events (SSE). // Returns a channel containing BifrostResponse objects representing the stream or an error if the request fails. -func (provider *CohereProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { +func (provider *CohereProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { // Prepare request body using shared function requestBody, err := prepareCohereChatRequest(messages, params, model, true) if err != nil { @@ -765,7 +765,7 @@ func (provider *CohereProvider) ChatCompletionStream(ctx context.Context, postHo // Set headers req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+key) + req.Header.Set("Authorization", "Bearer "+key.Value) req.Header.Set("Accept", "text/event-stream") req.Header.Set("Cache-Control", "no-cache") diff --git a/core/providers/groq.go b/core/providers/groq.go index 04a28a6e85..b898d07e86 100644 --- a/core/providers/groq.go +++ b/core/providers/groq.go @@ -100,12 +100,12 @@ func (provider *GroqProvider) GetProviderKey() schemas.ModelProvider { } // TextCompletion is not supported by the Groq provider. -func (provider *GroqProvider) TextCompletion(ctx context.Context, model, key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *GroqProvider) TextCompletion(ctx context.Context, model string, key schemas.Key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { return nil, newUnsupportedOperationError("text completion", "groq") } // ChatCompletion performs a chat completion request to the Groq API. -func (provider *GroqProvider) ChatCompletion(ctx context.Context, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *GroqProvider) ChatCompletion(ctx context.Context, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { formattedMessages, preparedParams := prepareOpenAIChatRequest(messages, params) requestBody := mergeConfig(map[string]interface{}{ @@ -136,9 +136,7 @@ func (provider *GroqProvider) ChatCompletion(ctx context.Context, model, key str req.SetRequestURI(provider.networkConfig.BaseURL + "/v1/chat/completions") req.Header.SetMethod("POST") req.Header.SetContentType("application/json") - if key != "" { - req.Header.Set("Authorization", "Bearer "+key) - } + req.Header.Set("Authorization", "Bearer "+key.Value) req.SetBody(jsonBody) @@ -192,7 +190,7 @@ func (provider *GroqProvider) ChatCompletion(ctx context.Context, model, key str } // Embedding is not supported by the Groq provider. -func (provider *GroqProvider) Embedding(ctx context.Context, model string, key string, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *GroqProvider) Embedding(ctx context.Context, model string, key schemas.Key, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { return nil, newUnsupportedOperationError("embedding", "groq") } @@ -200,7 +198,7 @@ func (provider *GroqProvider) Embedding(ctx context.Context, model string, key s // It supports real-time streaming of responses using Server-Sent Events (SSE). // Uses Groq's OpenAI-compatible streaming format. // Returns a channel containing BifrostResponse objects representing the stream or an error if the request fails. -func (provider *GroqProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { +func (provider *GroqProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { formattedMessages, preparedParams := prepareOpenAIChatRequest(messages, params) requestBody := mergeConfig(map[string]interface{}{ @@ -216,10 +214,7 @@ func (provider *GroqProvider) ChatCompletionStream(ctx context.Context, postHook "Cache-Control": "no-cache", } - // Only add Authorization header if key is provided (Groq can run without auth) - if key != "" { - headers["Authorization"] = "Bearer " + key - } + headers["Authorization"] = "Bearer " + key.Value // Use shared OpenAI-compatible streaming logic return handleOpenAIStreaming( diff --git a/core/providers/mistral.go b/core/providers/mistral.go index 27377ffe37..1fd5fadf4d 100644 --- a/core/providers/mistral.go +++ b/core/providers/mistral.go @@ -114,12 +114,12 @@ func (provider *MistralProvider) GetProviderKey() schemas.ModelProvider { } // TextCompletion is not supported by the Mistral provider. -func (provider *MistralProvider) TextCompletion(ctx context.Context, model, key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *MistralProvider) TextCompletion(ctx context.Context, model string, key schemas.Key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { return nil, newUnsupportedOperationError("text completion", "mistral") } // ChatCompletion performs a chat completion request to the Mistral API. -func (provider *MistralProvider) ChatCompletion(ctx context.Context, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *MistralProvider) ChatCompletion(ctx context.Context, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { formattedMessages, preparedParams := prepareOpenAIChatRequest(messages, params) requestBody := mergeConfig(map[string]interface{}{ @@ -150,7 +150,7 @@ func (provider *MistralProvider) ChatCompletion(ctx context.Context, model, key req.SetRequestURI(provider.networkConfig.BaseURL + "/v1/chat/completions") req.Header.SetMethod("POST") req.Header.SetContentType("application/json") - req.Header.Set("Authorization", "Bearer "+key) + req.Header.Set("Authorization", "Bearer "+key.Value) req.SetBody(jsonBody) @@ -205,7 +205,7 @@ func (provider *MistralProvider) ChatCompletion(ctx context.Context, model, key // Embedding generates embeddings for the given input text(s) using the Mistral API. // Supports Mistral's embedding models and returns a BifrostResponse containing the embedding(s). -func (provider *MistralProvider) Embedding(ctx context.Context, model string, key string, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *MistralProvider) Embedding(ctx context.Context, model string, key schemas.Key, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { if len(input.Texts) == 0 { return nil, &schemas.BifrostError{ IsBifrostError: true, @@ -271,7 +271,7 @@ func (provider *MistralProvider) Embedding(ctx context.Context, model string, ke req.SetRequestURI(provider.networkConfig.BaseURL + "/v1/embeddings") req.Header.SetMethod("POST") req.Header.SetContentType("application/json") - req.Header.Set("Authorization", "Bearer "+key) + req.Header.Set("Authorization", "Bearer "+key.Value) req.SetBody(jsonBody) @@ -349,7 +349,7 @@ func (provider *MistralProvider) Embedding(ctx context.Context, model string, ke // It supports real-time streaming of responses using Server-Sent Events (SSE). // Uses Mistral's OpenAI-compatible streaming format. // Returns a channel containing BifrostResponse objects representing the stream or an error if the request fails. -func (provider *MistralProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { +func (provider *MistralProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { formattedMessages, preparedParams := prepareOpenAIChatRequest(messages, params) requestBody := mergeConfig(map[string]interface{}{ @@ -361,7 +361,7 @@ func (provider *MistralProvider) ChatCompletionStream(ctx context.Context, postH // Prepare Mistral headers headers := map[string]string{ "Content-Type": "application/json", - "Authorization": "Bearer " + key, + "Authorization": "Bearer " + key.Value, "Accept": "text/event-stream", "Cache-Control": "no-cache", } diff --git a/core/providers/ollama.go b/core/providers/ollama.go index 4412459df6..f60c261f97 100644 --- a/core/providers/ollama.go +++ b/core/providers/ollama.go @@ -101,12 +101,12 @@ func (provider *OllamaProvider) GetProviderKey() schemas.ModelProvider { } // TextCompletion is not supported by the Ollama provider. -func (provider *OllamaProvider) TextCompletion(ctx context.Context, model, key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *OllamaProvider) TextCompletion(ctx context.Context, model string, key schemas.Key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { return nil, newUnsupportedOperationError("text completion", "ollama") } // ChatCompletion performs a chat completion request to the Ollama API. -func (provider *OllamaProvider) ChatCompletion(ctx context.Context, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *OllamaProvider) ChatCompletion(ctx context.Context, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { formattedMessages, preparedParams := prepareOpenAIChatRequest(messages, params) requestBody := mergeConfig(map[string]interface{}{ @@ -137,8 +137,8 @@ func (provider *OllamaProvider) ChatCompletion(ctx context.Context, model, key s req.SetRequestURI(provider.networkConfig.BaseURL + "/v1/chat/completions") req.Header.SetMethod("POST") req.Header.SetContentType("application/json") - if key != "" { - req.Header.Set("Authorization", "Bearer "+key) + if key.Value != "" { + req.Header.Set("Authorization", "Bearer "+key.Value) } req.SetBody(jsonBody) @@ -193,7 +193,7 @@ func (provider *OllamaProvider) ChatCompletion(ctx context.Context, model, key s } // Embedding is not supported by the Ollama provider. -func (provider *OllamaProvider) Embedding(ctx context.Context, model string, key string, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *OllamaProvider) Embedding(ctx context.Context, model string, key schemas.Key, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { return nil, newUnsupportedOperationError("embedding", "ollama") } @@ -201,7 +201,7 @@ func (provider *OllamaProvider) Embedding(ctx context.Context, model string, key // It supports real-time streaming of responses using Server-Sent Events (SSE). // Uses Ollama's OpenAI-compatible streaming format. // Returns a channel containing BifrostResponse objects representing the stream or an error if the request fails. -func (provider *OllamaProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { +func (provider *OllamaProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { formattedMessages, preparedParams := prepareOpenAIChatRequest(messages, params) requestBody := mergeConfig(map[string]interface{}{ @@ -218,8 +218,8 @@ func (provider *OllamaProvider) ChatCompletionStream(ctx context.Context, postHo } // Only add Authorization header if key is provided (Ollama can run without auth) - if key != "" { - headers["Authorization"] = "Bearer " + key + if key.Value != "" { + headers["Authorization"] = "Bearer " + key.Value } // Use shared OpenAI-compatible streaming logic diff --git a/core/providers/openai.go b/core/providers/openai.go index 91451aec3f..b6814a5422 100644 --- a/core/providers/openai.go +++ b/core/providers/openai.go @@ -127,14 +127,14 @@ func (provider *OpenAIProvider) GetProviderKey() schemas.ModelProvider { // TextCompletion is not supported by the OpenAI provider. // Returns an error indicating that text completion is not available. -func (provider *OpenAIProvider) TextCompletion(ctx context.Context, model, key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *OpenAIProvider) TextCompletion(ctx context.Context, model string, key schemas.Key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { return nil, newUnsupportedOperationError("text completion", "openai") } // ChatCompletion performs a chat completion request to the OpenAI API. // It supports both text and image content in messages. // Returns a BifrostResponse containing the completion results or an error if the request fails. -func (provider *OpenAIProvider) ChatCompletion(ctx context.Context, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *OpenAIProvider) ChatCompletion(ctx context.Context, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { formattedMessages, preparedParams := prepareOpenAIChatRequest(messages, params) requestBody := mergeConfig(map[string]interface{}{ @@ -165,7 +165,7 @@ func (provider *OpenAIProvider) ChatCompletion(ctx context.Context, model, key s req.SetRequestURI(provider.networkConfig.BaseURL + "/v1/chat/completions") req.Header.SetMethod("POST") req.Header.SetContentType("application/json") - req.Header.Set("Authorization", "Bearer "+key) + req.Header.Set("Authorization", "Bearer "+key.Value) req.SetBody(jsonBody) @@ -280,7 +280,7 @@ func prepareOpenAIChatRequest(messages []schemas.BifrostMessage, params *schemas // Embedding generates embeddings for the given input text(s). // The input can be either a single string or a slice of strings for batch embedding. // Returns a BifrostResponse containing the embedding(s) and any error that occurred. -func (provider *OpenAIProvider) Embedding(ctx context.Context, model string, key string, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *OpenAIProvider) Embedding(ctx context.Context, model string, key schemas.Key, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { // Validate input texts are not empty if len(input.Texts) == 0 { return nil, &schemas.BifrostError{ @@ -337,7 +337,7 @@ func (provider *OpenAIProvider) Embedding(ctx context.Context, model string, key req.SetRequestURI(provider.networkConfig.BaseURL + "/v1/embeddings") req.Header.SetMethod("POST") req.Header.SetContentType("application/json") - req.Header.Set("Authorization", "Bearer "+key) + req.Header.Set("Authorization", "Bearer "+key.Value) req.SetBody(jsonBody) @@ -466,7 +466,7 @@ func (provider *OpenAIProvider) Embedding(ctx context.Context, model string, key return bifrostResponse, nil } -func (provider *OpenAIProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { +func (provider *OpenAIProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { formattedMessages, preparedParams := prepareOpenAIChatRequest(messages, params) requestBody := mergeConfig(map[string]interface{}{ @@ -478,7 +478,7 @@ func (provider *OpenAIProvider) ChatCompletionStream(ctx context.Context, postHo // Prepare OpenAI headers headers := map[string]string{ "Content-Type": "application/json", - "Authorization": "Bearer " + key, + "Authorization": "Bearer " + key.Value, "Accept": "text/event-stream", "Cache-Control": "no-cache", } diff --git a/core/providers/sgl.go b/core/providers/sgl.go index 74479cce8a..846d0b8584 100644 --- a/core/providers/sgl.go +++ b/core/providers/sgl.go @@ -101,12 +101,12 @@ func (provider *SGLProvider) GetProviderKey() schemas.ModelProvider { } // TextCompletion is not supported by the SGL provider. -func (provider *SGLProvider) TextCompletion(ctx context.Context, model, key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *SGLProvider) TextCompletion(ctx context.Context, model string, key schemas.Key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { return nil, newUnsupportedOperationError("text completion", "sgl") } // ChatCompletion performs a chat completion request to the SGL API. -func (provider *SGLProvider) ChatCompletion(ctx context.Context, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *SGLProvider) ChatCompletion(ctx context.Context, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { formattedMessages, preparedParams := prepareOpenAIChatRequest(messages, params) requestBody := mergeConfig(map[string]interface{}{ @@ -137,8 +137,8 @@ func (provider *SGLProvider) ChatCompletion(ctx context.Context, model, key stri req.SetRequestURI(provider.networkConfig.BaseURL + "/v1/chat/completions") req.Header.SetMethod("POST") req.Header.SetContentType("application/json") - if key != "" { - req.Header.Set("Authorization", "Bearer "+key) + if key.Value != "" { + req.Header.Set("Authorization", "Bearer "+key.Value) } req.SetBody(jsonBody) @@ -193,7 +193,7 @@ func (provider *SGLProvider) ChatCompletion(ctx context.Context, model, key stri } // Embedding is not supported by the SGL provider. -func (provider *SGLProvider) Embedding(ctx context.Context, model string, key string, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *SGLProvider) Embedding(ctx context.Context, model string, key schemas.Key, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { return nil, newUnsupportedOperationError("embedding", "sgl") } @@ -201,7 +201,7 @@ func (provider *SGLProvider) Embedding(ctx context.Context, model string, key st // It supports real-time streaming of responses using Server-Sent Events (SSE). // Uses SGL's OpenAI-compatible streaming format. // Returns a channel containing BifrostResponse objects representing the stream or an error if the request fails. -func (provider *SGLProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { +func (provider *SGLProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { formattedMessages, preparedParams := prepareOpenAIChatRequest(messages, params) requestBody := mergeConfig(map[string]interface{}{ @@ -218,8 +218,8 @@ func (provider *SGLProvider) ChatCompletionStream(ctx context.Context, postHookR } // Only add Authorization header if key is provided (SGL can run without auth) - if key != "" { - headers["Authorization"] = "Bearer " + key + if key.Value != "" { + headers["Authorization"] = "Bearer " + key.Value } // Use shared OpenAI-compatible streaming logic diff --git a/core/providers/vertex.go b/core/providers/vertex.go index 71c5c937cc..7528773c09 100644 --- a/core/providers/vertex.go +++ b/core/providers/vertex.go @@ -5,11 +5,14 @@ package providers import ( "bytes" "context" + "crypto/sha256" + "encoding/hex" "errors" "fmt" "io" "net/http" "strings" + "sync" "github.com/goccy/go-json" "golang.org/x/oauth2/google" @@ -25,11 +28,32 @@ type VertexError struct { } `json:"error"` } +// vertexClientPool provides a pool/cache for authenticated Vertex HTTP clients. +// This avoids creating and authenticating clients for every request. +// Uses sync.Map for atomic operations without explicit locking. +var vertexClientPool sync.Map + +// getClientKey generates a unique key for caching authenticated clients. +// It uses a hash of the auth credentials for security. +func getClientKey(authCredentials string) string { + hash := sha256.Sum256([]byte(authCredentials)) + return hex.EncodeToString(hash[:]) +} + +// removeVertexClient removes a specific client from the pool. +// This should be called when: +// - API returns authentication/authorization errors (401, 403) +// - Auth client creation fails +// - Network errors that might indicate credential issues +// This ensures we don't keep using potentially invalid clients. +func removeVertexClient(authCredentials string) { + clientKey := getClientKey(authCredentials) + vertexClientPool.Delete(clientKey) +} + // VertexProvider implements the Provider interface for Google's Vertex AI API. type VertexProvider struct { logger schemas.Logger // Logger for provider operations - client *http.Client // HTTP client for API requests - meta schemas.MetaConfig // Vertex-specific configuration networkConfig schemas.NetworkConfig // Network configuration including extra headers } @@ -39,24 +63,6 @@ type VertexProvider struct { func NewVertexProvider(config *schemas.ProviderConfig, logger schemas.Logger) (*VertexProvider, error) { config.CheckAndSetDefaults() - if config.MetaConfig == nil { - return nil, fmt.Errorf("meta config is not set") - } - - authCredentials := config.MetaConfig.GetAuthCredentials() - if authCredentials == nil { - return nil, fmt.Errorf("auth credentials are not set") - } - - // Get a Google JWT Config for the correct scope - conf, err := google.JWTConfigFromJSON([]byte(*authCredentials), "https://www.googleapis.com/auth/cloud-platform") - if err != nil { - return nil, fmt.Errorf("failed to create JWT config: %w", err) - } - - // Get an access token - client := conf.Client(context.Background()) - // Pre-warm response pools for range config.ConcurrencyAndBufferSize.Concurrency { openAIResponsePool.Put(&OpenAIResponse{}) @@ -66,12 +72,49 @@ func NewVertexProvider(config *schemas.ProviderConfig, logger schemas.Logger) (* return &VertexProvider{ logger: logger, - client: client, - meta: config.MetaConfig, networkConfig: config.NetworkConfig, }, nil } +// getAuthClient returns an authenticated HTTP client for Vertex AI API requests. +// This function implements client pooling to avoid creating and authenticating +// clients for every request, which significantly improves performance by: +// - Avoiding repeated JWT config creation +// - Reusing OAuth2 token refresh logic +// - Reducing authentication overhead +func getAuthClient(key schemas.Key) (*http.Client, error) { + if key.VertexKeyConfig == nil { + return nil, fmt.Errorf("vertex key config is not set") + } + + authCredentials := key.VertexKeyConfig.AuthCredentials + + if authCredentials == "" { + return nil, fmt.Errorf("auth credentials are not set") + } + + // Generate cache key from credentials + clientKey := getClientKey(authCredentials) + + // Try to get existing client from pool + if value, exists := vertexClientPool.Load(clientKey); exists { + return value.(*http.Client), nil + } + + // Create new authenticated client + conf, err := google.JWTConfigFromJSON([]byte(authCredentials), "https://www.googleapis.com/auth/cloud-platform") + if err != nil { + return nil, fmt.Errorf("failed to create JWT config: %w", err) + } + + client := conf.Client(context.Background()) + + // Store the client using LoadOrStore to handle race conditions + // If another goroutine stored a client while we were creating ours, use theirs + actual, _ := vertexClientPool.LoadOrStore(clientKey, client) + return actual.(*http.Client), nil +} + // GetProviderKey returns the provider identifier for Vertex. func (provider *VertexProvider) GetProviderKey() schemas.ModelProvider { return schemas.Vertex @@ -79,14 +122,23 @@ func (provider *VertexProvider) GetProviderKey() schemas.ModelProvider { // TextCompletion is not supported by the Vertex provider. // Returns an error indicating that text completion is not available. -func (provider *VertexProvider) TextCompletion(ctx context.Context, model, key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *VertexProvider) TextCompletion(ctx context.Context, model string, key schemas.Key, text string, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { return nil, newUnsupportedOperationError("text completion", "vertex") } // ChatCompletion performs a chat completion request to the Vertex API. // It supports both text and image content in messages. // Returns a BifrostResponse containing the completion results or an error if the request fails. -func (provider *VertexProvider) ChatCompletion(ctx context.Context, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *VertexProvider) ChatCompletion(ctx context.Context, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { + if key.VertexKeyConfig == nil { + return nil, &schemas.BifrostError{ + IsBifrostError: false, + Error: schemas.ErrorField{ + Message: "vertex key config is not set", + }, + } + } + // Format messages for Vertex API var formattedMessages []map[string]interface{} var preparedParams map[string]interface{} @@ -123,8 +175,8 @@ func (provider *VertexProvider) ChatCompletion(ctx context.Context, model, key s } } - projectID := provider.meta.GetProjectID() - if projectID == nil { + projectID := key.VertexKeyConfig.ProjectID + if projectID == "" { return nil, &schemas.BifrostError{ IsBifrostError: false, Error: schemas.ErrorField{ @@ -133,8 +185,8 @@ func (provider *VertexProvider) ChatCompletion(ctx context.Context, model, key s } } - region := provider.meta.GetRegion() - if region == nil { + region := key.VertexKeyConfig.Region + if region == "" { return nil, &schemas.BifrostError{ IsBifrostError: false, Error: schemas.ErrorField{ @@ -143,10 +195,10 @@ func (provider *VertexProvider) ChatCompletion(ctx context.Context, model, key s } } - url := fmt.Sprintf("https://%s-aiplatform.googleapis.com/v1beta1/projects/%s/locations/%s/endpoints/openapi/chat/completions", *region, *projectID, *region) + url := fmt.Sprintf("https://%s-aiplatform.googleapis.com/v1beta1/projects/%s/locations/%s/endpoints/openapi/chat/completions", region, projectID, region) if strings.Contains(model, "claude") { - url = fmt.Sprintf("https://%s-aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/anthropic/models/%s:streamRawPredict", *region, *projectID, *region, model) + url = fmt.Sprintf("https://%s-aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/anthropic/models/%s:streamRawPredict", region, projectID, region, model) } // Create request @@ -166,8 +218,21 @@ func (provider *VertexProvider) ChatCompletion(ctx context.Context, model, key s req.Header.Set("Content-Type", "application/json") + client, err := getAuthClient(key) + if err != nil { + // Remove client from pool if auth client creation fails + removeVertexClient(key.VertexKeyConfig.AuthCredentials) + return nil, &schemas.BifrostError{ + IsBifrostError: false, + Error: schemas.ErrorField{ + Message: "error creating auth client", + Error: err, + }, + } + } + // Make request - resp, err := provider.client.Do(req) + resp, err := client.Do(req) if err != nil { if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return nil, &schemas.BifrostError{ @@ -179,6 +244,8 @@ func (provider *VertexProvider) ChatCompletion(ctx context.Context, model, key s }, } } + // Remove client from pool for non-context errors (could be auth/network issues) + removeVertexClient(key.VertexKeyConfig.AuthCredentials) return nil, &schemas.BifrostError{ IsBifrostError: false, Error: schemas.ErrorField{ @@ -203,6 +270,11 @@ func (provider *VertexProvider) ChatCompletion(ctx context.Context, model, key s } if resp.StatusCode != http.StatusOK { + // Remove client from pool for authentication/authorization errors + if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden { + removeVertexClient(key.VertexKeyConfig.AuthCredentials) + } + var openAIErr OpenAIError var vertexErr []VertexError @@ -304,16 +376,25 @@ func (provider *VertexProvider) ChatCompletion(ctx context.Context, model, key s } // Embedding is not supported by the Vertex provider. -func (provider *VertexProvider) Embedding(ctx context.Context, model string, key string, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { +func (provider *VertexProvider) Embedding(ctx context.Context, model string, key schemas.Key, input *schemas.EmbeddingInput, params *schemas.ModelParameters) (*schemas.BifrostResponse, *schemas.BifrostError) { return nil, newUnsupportedOperationError("embedding", "vertex") } // ChatCompletionStream performs a streaming chat completion request to the Vertex API. // It supports both OpenAI-style streaming (for non-Claude models) and Anthropic-style streaming (for Claude models). // Returns a channel of BifrostResponse objects for streaming results or an error if the request fails. -func (provider *VertexProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model, key string, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { - projectID := provider.meta.GetProjectID() - if projectID == nil { +func (provider *VertexProvider) ChatCompletionStream(ctx context.Context, postHookRunner schemas.PostHookRunner, model string, key schemas.Key, messages []schemas.BifrostMessage, params *schemas.ModelParameters) (chan *schemas.BifrostStream, *schemas.BifrostError) { + if key.VertexKeyConfig == nil { + return nil, &schemas.BifrostError{ + IsBifrostError: false, + Error: schemas.ErrorField{ + Message: "vertex key config is not set", + }, + } + } + + projectID := key.VertexKeyConfig.ProjectID + if projectID == "" { return nil, &schemas.BifrostError{ IsBifrostError: false, Error: schemas.ErrorField{ @@ -322,8 +403,8 @@ func (provider *VertexProvider) ChatCompletionStream(ctx context.Context, postHo } } - region := provider.meta.GetRegion() - if region == nil { + region := key.VertexKeyConfig.Region + if region == "" { return nil, &schemas.BifrostError{ IsBifrostError: false, Error: schemas.ErrorField{ @@ -332,6 +413,19 @@ func (provider *VertexProvider) ChatCompletionStream(ctx context.Context, postHo } } + client, err := getAuthClient(key) + if err != nil { + // Remove client from pool if auth client creation fails + removeVertexClient(key.VertexKeyConfig.AuthCredentials) + return nil, &schemas.BifrostError{ + IsBifrostError: false, + Error: schemas.ErrorField{ + Message: "error creating auth client", + Error: err, + }, + } + } + if strings.Contains(model, "claude") { // Use Anthropic-style streaming for Claude models formattedMessages, preparedParams := prepareAnthropicChatRequest(messages, params) @@ -348,7 +442,7 @@ func (provider *VertexProvider) ChatCompletionStream(ctx context.Context, postHo delete(requestBody, "model") delete(requestBody, "region") - url := fmt.Sprintf("https://%s-aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/anthropic/models/%s:streamRawPredict", *region, *projectID, *region, model) + url := fmt.Sprintf("https://%s-aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/anthropic/models/%s:streamRawPredict", region, projectID, region, model) // Prepare headers for Vertex Anthropic headers := map[string]string{ @@ -360,7 +454,7 @@ func (provider *VertexProvider) ChatCompletionStream(ctx context.Context, postHo // Use shared Anthropic streaming logic return handleAnthropicStreaming( ctx, - provider.client, + client, url, requestBody, headers, @@ -382,7 +476,7 @@ func (provider *VertexProvider) ChatCompletionStream(ctx context.Context, postHo delete(requestBody, "region") - url := fmt.Sprintf("https://%s-aiplatform.googleapis.com/v1beta1/projects/%s/locations/%s/endpoints/openapi/chat/completions", *region, *projectID, *region) + url := fmt.Sprintf("https://%s-aiplatform.googleapis.com/v1beta1/projects/%s/locations/%s/endpoints/openapi/chat/completions", region, projectID, region) // Prepare headers for Vertex OpenAI-compatible headers := map[string]string{ @@ -394,7 +488,7 @@ func (provider *VertexProvider) ChatCompletionStream(ctx context.Context, postHo // Use shared OpenAI streaming logic return handleOpenAIStreaming( ctx, - provider.client, + client, url, requestBody, headers, diff --git a/core/schemas/account.go b/core/schemas/account.go index 7800c2dd3c..9ab2431017 100644 --- a/core/schemas/account.go +++ b/core/schemas/account.go @@ -4,9 +4,28 @@ package schemas // Key represents an API key and its associated configuration for a provider. // It contains the key value, supported models, and a weight for load balancing. type Key struct { - Value string `json:"value"` // The actual API key value - Models []string `json:"models"` // List of models this key can access - Weight float64 `json:"weight"` // Weight for load balancing between multiple keys + ID string `json:"id"` // The unique identifier for the key (not used by bifrost, but can be used by users to identify the key) + Value string `json:"value"` // The actual API key value + Models []string `json:"models"` // List of models this key can access + Weight float64 `json:"weight"` // Weight for load balancing between multiple keys + AzureKeyConfig *AzureKeyConfig `json:"azure_key_config,omitempty"` // Azure-specific key configuration + VertexKeyConfig *VertexKeyConfig `json:"vertex_key_config,omitempty"` // Vertex-specific key configuration +} + +// AzureKeyConfig represents the Azure-specific configuration. +// It contains Azure-specific settings required for service access and deployment management. +type AzureKeyConfig struct { + Endpoint string `json:"endpoint"` // Azure service endpoint URL + Deployments map[string]string `json:"deployments,omitempty"` // Mapping of model names to deployment names + APIVersion *string `json:"api_version,omitempty"` // Azure API version to use; defaults to "2024-02-01" +} + +// VertexKeyConfig represents the Vertex-specific configuration. +// It contains Vertex-specific settings required for authentication and service access. +type VertexKeyConfig struct { + ProjectID string `json:"project_id,omitempty"` + Region string `json:"region,omitempty"` + AuthCredentials string `json:"auth_credentials,omitempty"` } // Account defines the interface for managing provider accounts and their configurations. diff --git a/core/schemas/meta/azure.go b/core/schemas/meta/azure.go deleted file mode 100644 index 58abbd071c..0000000000 --- a/core/schemas/meta/azure.go +++ /dev/null @@ -1,40 +0,0 @@ -// Package meta provides provider-specific configuration structures and schemas. -// This file contains the Azure-specific configuration implementation. - -package meta - -// AzureMetaConfig represents the Azure-specific configuration. -// It contains Azure-specific settings required for service access and deployment management. -type AzureMetaConfig struct { - Endpoint string `json:"endpoint"` // Azure service endpoint URL - Deployments map[string]string `json:"deployments,omitempty"` // Mapping of model names to deployment names - APIVersion *string `json:"api_version,omitempty"` // Azure API version to use; defaults to "2024-02-01" -} - -// GetEndpoint returns the Azure service endpoint. -// This specifies the base URL for Azure API requests. -func (c *AzureMetaConfig) GetEndpoint() *string { - return &c.Endpoint -} - -// GetDeployments returns the deployment configurations. -// This maps model names to their corresponding Azure deployment names. -// E.g. "gpt-4o": "your-deployment-name-for-gpt-4o" -func (c *AzureMetaConfig) GetDeployments() map[string]string { - return c.Deployments -} - -// GetAPIVersion returns the Azure API version. -// This specifies which version of the Azure API to use. -func (c *AzureMetaConfig) GetAPIVersion() *string { - return c.APIVersion -} - -// These are not used for Azure. -func (c *AzureMetaConfig) GetARN() *string { return nil } -func (c *AzureMetaConfig) GetAuthCredentials() *string { return nil } -func (c *AzureMetaConfig) GetInferenceProfiles() map[string]string { return nil } -func (c *AzureMetaConfig) GetProjectID() *string { return nil } -func (c *AzureMetaConfig) GetRegion() *string { return nil } -func (c *AzureMetaConfig) GetSecretAccessKey() *string { return nil } -func (c *AzureMetaConfig) GetSessionToken() *string { return nil } diff --git a/core/schemas/meta/bedrock.go b/core/schemas/meta/bedrock.go index bdff19e76a..fe3561c11b 100644 --- a/core/schemas/meta/bedrock.go +++ b/core/schemas/meta/bedrock.go @@ -42,10 +42,3 @@ func (c *BedrockMetaConfig) GetARN() *string { func (c *BedrockMetaConfig) GetInferenceProfiles() map[string]string { return c.InferenceProfiles } - -// These are not used for Bedrock. -func (c *BedrockMetaConfig) GetAPIVersion() *string { return nil } -func (c *BedrockMetaConfig) GetAuthCredentials() *string { return nil } -func (c *BedrockMetaConfig) GetDeployments() map[string]string { return nil } -func (c *BedrockMetaConfig) GetEndpoint() *string { return nil } -func (c *BedrockMetaConfig) GetProjectID() *string { return nil } diff --git a/core/schemas/meta/vertex.go b/core/schemas/meta/vertex.go deleted file mode 100644 index a82e46380d..0000000000 --- a/core/schemas/meta/vertex.go +++ /dev/null @@ -1,39 +0,0 @@ -// Package meta provides provider-specific configuration structures and schemas. -// This file contains the AWS Vertex-specific configuration implementation. - -package meta - -// VertexMetaConfig represents the Vertex-specific configuration. -// It contains Vertex-specific settings required for authentication and service access. -type VertexMetaConfig struct { - ProjectID string `json:"project_id,omitempty"` - Region string `json:"region,omitempty"` - AuthCredentials string `json:"auth_credentials,omitempty"` -} - -// GetRegion returns the Vertex region. -// This is the region for the Vertex project. -func (c *VertexMetaConfig) GetRegion() *string { - return &c.Region -} - -// GetProjectID returns the Vertex project ID. -// This is the project ID for the Vertex project. -func (c *VertexMetaConfig) GetProjectID() *string { - return &c.ProjectID -} - -// GetAuthCredentials returns the authentication credentials for the provider. -// This is the authentication credentials for the google cloud api. -func (c *VertexMetaConfig) GetAuthCredentials() *string { - return &c.AuthCredentials -} - -// These are not used for Vertex. -func (c *VertexMetaConfig) GetAPIVersion() *string { return nil } -func (c *VertexMetaConfig) GetARN() *string { return nil } -func (c *VertexMetaConfig) GetDeployments() map[string]string { return nil } -func (c *VertexMetaConfig) GetEndpoint() *string { return nil } -func (c *VertexMetaConfig) GetInferenceProfiles() map[string]string { return nil } -func (c *VertexMetaConfig) GetSecretAccessKey() *string { return nil } -func (c *VertexMetaConfig) GetSessionToken() *string { return nil } diff --git a/core/schemas/provider.go b/core/schemas/provider.go index 8b13b3c259..f2df45e17c 100644 --- a/core/schemas/provider.go +++ b/core/schemas/provider.go @@ -60,16 +60,6 @@ type MetaConfig interface { GetARN() *string // GetInferenceProfiles returns the inference profiles GetInferenceProfiles() map[string]string - // GetEndpoint returns the provider endpoint - GetEndpoint() *string - // GetDeployments returns the deployment configurations - GetDeployments() map[string]string - // GetAPIVersion returns the API version - GetAPIVersion() *string - // GetProjectID returns the project ID - GetProjectID() *string - // GetAuthCredentials returns the authentication credentials for the provider - GetAuthCredentials() *string } // ConcurrencyAndBufferSize represents configuration for concurrent operations and buffer sizes. @@ -158,11 +148,11 @@ type Provider interface { // GetProviderKey returns the provider's identifier GetProviderKey() ModelProvider // TextCompletion performs a text completion request - TextCompletion(ctx context.Context, model, key, text string, params *ModelParameters) (*BifrostResponse, *BifrostError) + TextCompletion(ctx context.Context, model string, key Key, text string, params *ModelParameters) (*BifrostResponse, *BifrostError) // ChatCompletion performs a chat completion request - ChatCompletion(ctx context.Context, model, key string, messages []BifrostMessage, params *ModelParameters) (*BifrostResponse, *BifrostError) + ChatCompletion(ctx context.Context, model string, key Key, messages []BifrostMessage, params *ModelParameters) (*BifrostResponse, *BifrostError) // ChatCompletionStream performs a chat completion stream request - ChatCompletionStream(ctx context.Context, postHookRunner PostHookRunner, model, key string, messages []BifrostMessage, params *ModelParameters) (chan *BifrostStream, *BifrostError) + ChatCompletionStream(ctx context.Context, postHookRunner PostHookRunner, model string, key Key, messages []BifrostMessage, params *ModelParameters) (chan *BifrostStream, *BifrostError) // Embedding performs an embedding request - Embedding(ctx context.Context, model string, key string, input *EmbeddingInput, params *ModelParameters) (*BifrostResponse, *BifrostError) + Embedding(ctx context.Context, model string, key Key, input *EmbeddingInput, params *ModelParameters) (*BifrostResponse, *BifrostError) } diff --git a/core/utils.go b/core/utils.go index 8aa329d5c5..fb0c4256df 100644 --- a/core/utils.go +++ b/core/utils.go @@ -12,9 +12,9 @@ func Ptr[T any](v T) *T { } // providerRequiresKey returns true if the given provider requires an API key for authentication. -// Some providers like Vertex and Ollama are keyless and don't require API keys. +// Some providers like Ollama and SGL are keyless and don't require API keys. func providerRequiresKey(providerKey schemas.ModelProvider) bool { - return providerKey != schemas.Vertex && providerKey != schemas.Ollama && providerKey != schemas.SGL + return providerKey != schemas.Ollama && providerKey != schemas.SGL } // calculateBackoff implements exponential backoff with jitter for retry attempts. diff --git a/docs/usage/go-package/account.md b/docs/usage/go-package/account.md index 9ccde39273..535b334bcc 100644 --- a/docs/usage/go-package/account.md +++ b/docs/usage/go-package/account.md @@ -131,6 +131,13 @@ func (a *MultiProviderAccount) GetKeysForProvider(provider schemas.ModelProvider Value: os.Getenv("AZURE_API_KEY"), Models: []string{"gpt-4o"}, Weight: 1.0, + AzureKeyConfig: &schemas.AzureKeyConfig{ + Endpoint: os.Getenv("AZURE_ENDPOINT"), + APIVersion: bifrost.Ptr("2024-08-01-preview"), + Deployments: map[string]string{ + "gpt-4o": "gpt-4o-deployment", + }, + }, }}, nil case schemas.Bedrock: @@ -141,8 +148,15 @@ func (a *MultiProviderAccount) GetKeysForProvider(provider schemas.ModelProvider }}, nil case schemas.Vertex: - // Vertex is keyless (uses Google Cloud credentials) - return []schemas.Key{}, nil + return []schemas.Key{{ + Models: []string{"google/gemini-2.0-flash-001"}, + Weight: 1.0, + VertexKeyConfig: &schemas.VertexKeyConfig{ + ProjectID: os.Getenv("VERTEX_PROJECT_ID"), + Region: "us-central1", + AuthCredentials: os.Getenv("VERTEX_CREDENTIALS"), + }, + }}, nil } return nil, fmt.Errorf("provider %s not supported", provider) @@ -171,13 +185,6 @@ func (a *MultiProviderAccount) GetConfigForProvider(provider schemas.ModelProvid RetryBackoffMax: 10 * time.Second, }, ConcurrencyAndBufferSize: schemas.DefaultConcurrencyAndBufferSize, - MetaConfig: &meta.AzureMetaConfig{ - Endpoint: os.Getenv("AZURE_ENDPOINT"), - APIVersion: bifrost.Ptr("2024-08-01-preview"), - Deployments: map[string]string{ - "gpt-4o": "gpt-4o-deployment", - }, - }, }, nil case schemas.Bedrock: @@ -194,11 +201,6 @@ func (a *MultiProviderAccount) GetConfigForProvider(provider schemas.ModelProvid return &schemas.ProviderConfig{ NetworkConfig: schemas.DefaultNetworkConfig, ConcurrencyAndBufferSize: schemas.DefaultConcurrencyAndBufferSize, - MetaConfig: &meta.VertexMetaConfig{ - ProjectID: os.Getenv("VERTEX_PROJECT_ID"), - Region: "us-central1", - AuthCredentials: os.Getenv("VERTEX_CREDENTIALS"), - }, }, nil } diff --git a/docs/usage/http-transport/configuration/providers.md b/docs/usage/http-transport/configuration/providers.md index eab055b82d..f7ae62ae44 100644 --- a/docs/usage/http-transport/configuration/providers.md +++ b/docs/usage/http-transport/configuration/providers.md @@ -158,7 +158,14 @@ Provider configuration in `config.json` defines: { "value": "env.AZURE_API_KEY", "models": ["gpt-4o"], - "weight": 1.0 + "weight": 1.0, + "azure_key_config": { + "endpoint": "env.AZURE_ENDPOINT", + "deployments": { + "gpt-4o": "gpt-4o-aug" + }, + "api_version": "2024-08-01-preview" + } } ], "network_config": { @@ -167,13 +174,6 @@ Provider configuration in `config.json` defines: "retry_backoff_initial_ms": 100, "retry_backoff_max_ms": 2000 }, - "meta_config": { - "endpoint": "env.AZURE_ENDPOINT", - "deployments": { - "gpt-4o": "gpt-4o-aug" - }, - "api_version": "2024-08-01-preview" - }, "concurrency_and_buffer_size": { "concurrency": 3, "buffer_size": 10 @@ -189,12 +189,18 @@ Provider configuration in `config.json` defines: { "providers": { "vertex": { - "keys": [], - "meta_config": { - "project_id": "env.VERTEX_PROJECT_ID", - "region": "us-central1", - "auth_credentials": "env.VERTEX_CREDENTIALS" - }, + "keys": [ + { + "value": "env.VERTEX_API_KEY", + "models": ["gemini-2.0-flash-001"], + "weight": 1.0, + "vertex_key_config": { + "project_id": "env.VERTEX_PROJECT_ID", + "region": "us-central1", + "auth_credentials": "env.VERTEX_CREDENTIALS" + } + } + ], "concurrency_and_buffer_size": { "concurrency": 3, "buffer_size": 10 diff --git a/docs/usage/http-transport/integrations/genai-compatible.md b/docs/usage/http-transport/integrations/genai-compatible.md index 8e986c9e15..f45df1ac4b 100644 --- a/docs/usage/http-transport/integrations/genai-compatible.md +++ b/docs/usage/http-transport/integrations/genai-compatible.md @@ -544,12 +544,17 @@ response3 = model3.generate_content("Hello!") { "providers": { "vertex": { - "keys": [], - "meta_config": { - "project_id": "env.VERTEX_PROJECT_ID", - "region": "us-central1", - "auth_credentials": "env.VERTEX_CREDENTIALS" - }, + "keys": [ + { + "models": ["gemini-2.0-flash-001"], + "weight": 1.0, + "vertex_key_config": { + "project_id": "env.VERTEX_PROJECT_ID", + "region": "us-central1", + "auth_credentials": "env.VERTEX_CREDENTIALS" + } + } + ], "network_config": { "default_request_timeout_in_seconds": 30, "max_retries": 2, @@ -609,10 +614,17 @@ genai.configure( { "providers": { "vertex": { - "meta_config": { - "project_id": "env.VERTEX_PROJECT_ID", - "region": "us-central1" - } + "keys": [ + { + "models": ["gemini-2.0-flash-001"], + "weight": 1.0, + "vertex_key_config": { + "project_id": "env.VERTEX_PROJECT_ID", + "region": "us-central1", + "auth_credentials": "env.VERTEX_CREDENTIALS" + } + } + ] }, "openai": { "keys": [ diff --git a/docs/usage/http-transport/openapi.json b/docs/usage/http-transport/openapi.json index 3f935e6dd4..810eacb5b6 100644 --- a/docs/usage/http-transport/openapi.json +++ b/docs/usage/http-transport/openapi.json @@ -2334,6 +2334,50 @@ }, "description": "Models this key can access", "example": ["gpt-4o", "gpt-4o-mini"] + }, + "azure_key_config": { + "type": "object", + "properties": { + "endpoint": { + "type": "string", + "description": "Azure endpoint", + "example": "https://your-resource.openai.azure.com" + }, + "deployments": { + "type": "object", + "description": "Azure deployments", + "example": { + "gpt-4o": "gpt-4o-deployment" + } + }, + "api_version": { + "type": "string", + "description": "Azure API version", + "example": "2024-02-15-preview" + } + }, + "description": "Azure key configuration" + }, + "vertex_key_config": { + "type": "object", + "properties": { + "project_id": { + "type": "string", + "description": "Vertex project ID", + "example": "your-project-id" + }, + "region": { + "type": "string", + "description": "Vertex region", + "example": "us-central1" + }, + "auth_credentials": { + "type": "string", + "description": "Vertex auth credentials", + "example": "env.VERTEX_AUTH_CREDENTIALS" + } + }, + "description": "Vertex key configuration" } } }, diff --git a/docs/usage/providers.md b/docs/usage/providers.md index e5b68e564f..3cb5d79d5b 100644 --- a/docs/usage/providers.md +++ b/docs/usage/providers.md @@ -314,19 +314,24 @@ echo "$response" **Go Package:** ```go -func (a *MyAccount) GetConfigForProvider(provider schemas.ModelProvider) (*schemas.ProviderConfig, error) { +func (a *MyAccount) GetKeysForProvider(provider schemas.ModelProvider) ([]schemas.Key, error) { if provider == schemas.Azure { - return &schemas.ProviderConfig{ - NetworkConfig: schemas.NetworkConfig{ - BaseURL: "https://your-resource.openai.azure.com", - }, - MetaConfig: map[string]interface{}{ - "api_version": "2024-02-15-preview", - "deployment": "gpt-4o-deployment", + return []schemas.Key{ + { + Value: "your-azure-api-key", + Models: []string{"gpt-4o"}, // These models are mapped to the deployment + Weight: 1.0, + AzureKeyConfig: &schemas.AzureKeyConfig{ + Endpoint: "https://your-resource.openai.azure.com", + Deployments: map[string]string{ + "gpt-4o": "gpt-4o-deployment", + }, + APIVersion: StrPtr("2024-02-15-preview"), + }, }, }, nil } - return &schemas.ProviderConfig{}, nil + return nil, fmt.Errorf("provider not configured") } ``` @@ -340,16 +345,16 @@ func (a *MyAccount) GetConfigForProvider(provider schemas.ModelProvider) (*schem { "value": "env.AZURE_OPENAI_API_KEY", "models": ["gpt-4o"], - "weight": 1.0 + "weight": 1.0, + "azure_key_config": { + "endpoint": "https://your-resource.openai.azure.com", + "deployments": { + "gpt-4o": "gpt-4o-deployment" + }, + "api_version": "2024-02-15-preview" + } } - ], - "network_config": { - "base_url": "https://your-resource.openai.azure.com" - }, - "meta_config": { - "api_version": "2024-02-15-preview", - "deployment": "gpt-4o-deployment" - } + ] } } } @@ -363,17 +368,21 @@ func (a *MyAccount) GetConfigForProvider(provider schemas.ModelProvider) (*schem **Go Package:** ```go -func (a *MyAccount) GetConfigForProvider(provider schemas.ModelProvider) (*schemas.ProviderConfig, error) { +func (a *MyAccount) GetKeysForProvider(provider schemas.ModelProvider) ([]schemas.Key, error) { if provider == schemas.Vertex { - return &schemas.ProviderConfig{ - MetaConfig: map[string]interface{}{ - "project_id": "your-project-id", - "location": "us-central1", - "credentials_path": "/path/to/service-account.json", + return []schemas.Key{ + { + Models: []string{"gemini-pro"}, // These models are just for mapping to keys + Weight: 1.0, + VertexKeyConfig: &schemas.VertexKeyConfig{ + ProjectID: "your-project-id", + Location: "us-central1", + AuthCredentials: os.Getenv("VERTEX_AUTH_CREDENTIALS"), // Or read from file + }, }, }, nil } - return &schemas.ProviderConfig{}, nil + return nil, fmt.Errorf("provider not configured") } ``` @@ -385,15 +394,15 @@ func (a *MyAccount) GetConfigForProvider(provider schemas.ModelProvider) (*schem "vertex": { "keys": [ { - "value": "file:/path/to/service-account.json", - "models": ["gemini-pro"], - "weight": 1.0 + "models": ["google/gemini-2.0-flash-001"], + "weight": 1.0, + "vertex_key_config": { + "project_id": "your-project-id", + "region": "us-central1", + "auth_credentials": "env.VERTEX_AUTH_CREDENTIALS" + } } - ], - "meta_config": { - "project_id": "your-project-id", - "location": "us-central1" - } + ] } } } diff --git a/tests/core-providers/config/account.go b/tests/core-providers/config/account.go index f8bbf414cb..e3cf907dbf 100644 --- a/tests/core-providers/config/account.go +++ b/tests/core-providers/config/account.go @@ -109,14 +109,29 @@ func (account *ComprehensiveTestAccount) GetKeysForProvider(providerKey schemas. Value: os.Getenv("AZURE_API_KEY"), Models: []string{"gpt-4o"}, Weight: 1.0, + AzureKeyConfig: &schemas.AzureKeyConfig{ + Endpoint: os.Getenv("AZURE_ENDPOINT"), + Deployments: map[string]string{ + "gpt-4o": "gpt-4o-aug", + }, + // Use environment variable for API version with fallback to current preview version + // Note: This is a preview API version that may change over time. Update as needed. + // Set AZURE_API_VERSION environment variable to override the default. + APIVersion: bifrost.Ptr(getEnvWithDefault("AZURE_API_VERSION", "2024-08-01-preview")), + }, }, }, nil case schemas.Vertex: return []schemas.Key{ { Value: os.Getenv("VERTEX_API_KEY"), - Models: []string{"gemini-pro"}, + Models: []string{}, Weight: 1.0, + VertexKeyConfig: &schemas.VertexKeyConfig{ + ProjectID: os.Getenv("VERTEX_PROJECT_ID"), + Region: getEnvWithDefault("VERTEX_REGION", "us-central1"), + AuthCredentials: os.Getenv("VERTEX_CREDENTIALS"), + }, }, }, nil case schemas.Mistral: @@ -191,16 +206,6 @@ func (account *ComprehensiveTestAccount) GetConfigForProvider(providerKey schema RetryBackoffInitial: 100 * time.Millisecond, RetryBackoffMax: 2 * time.Second, }, - MetaConfig: &meta.AzureMetaConfig{ - Endpoint: os.Getenv("AZURE_ENDPOINT"), - Deployments: map[string]string{ - "gpt-4o": "gpt-4o-aug", - }, - // Use environment variable for API version with fallback to current preview version - // Note: This is a preview API version that may change over time. Update as needed. - // Set AZURE_API_VERSION environment variable to override the default. - APIVersion: bifrost.Ptr(getEnvWithDefault("AZURE_API_VERSION", "2024-08-01-preview")), - }, ConcurrencyAndBufferSize: schemas.ConcurrencyAndBufferSize{ Concurrency: 3, BufferSize: 10, @@ -214,11 +219,6 @@ func (account *ComprehensiveTestAccount) GetConfigForProvider(providerKey schema RetryBackoffInitial: 100 * time.Millisecond, RetryBackoffMax: 2 * time.Second, }, - MetaConfig: &meta.VertexMetaConfig{ - ProjectID: os.Getenv("VERTEX_PROJECT_ID"), - Region: getEnvWithDefault("VERTEX_REGION", "us-central1"), - AuthCredentials: os.Getenv("VERTEX_CREDENTIALS"), - }, ConcurrencyAndBufferSize: schemas.ConcurrencyAndBufferSize{ Concurrency: 3, BufferSize: 10, diff --git a/tests/core-providers/go.mod b/tests/core-providers/go.mod index bfb845fb66..2033e99b8f 100644 --- a/tests/core-providers/go.mod +++ b/tests/core-providers/go.mod @@ -38,3 +38,5 @@ require ( golang.org/x/text v0.24.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/maximhq/bifrost/core => ../../core \ No newline at end of file diff --git a/tests/core-providers/vertex_test.go b/tests/core-providers/vertex_test.go index b2698381b3..ac61cfb144 100644 --- a/tests/core-providers/vertex_test.go +++ b/tests/core-providers/vertex_test.go @@ -35,9 +35,6 @@ func TestVertex(t *testing.T) { CompleteEnd2End: true, ProviderSpecific: true, }, - Fallbacks: []schemas.Fallback{ - {Provider: schemas.Anthropic, Model: "claude-3-7-sonnet-20250219"}, - }, } runAllComprehensiveTests(t, client, ctx, testConfig) diff --git a/transports/bifrost-http/handlers/completions.go b/transports/bifrost-http/handlers/completions.go index 935b28e908..4e06bbb193 100644 --- a/transports/bifrost-http/handlers/completions.go +++ b/transports/bifrost-http/handlers/completions.go @@ -78,8 +78,8 @@ func (h *CompletionHandler) handleCompletion(ctx *fasthttp.RequestCtx, completio return } - model := strings.Split(req.Model, "/") - if len(model) != 2 { + model := strings.SplitN(req.Model, "/", 2) + if len(model) < 2 { SendError(ctx, fasthttp.StatusBadRequest, "Model must be in the format of 'provider/model'", h.logger) return } diff --git a/transports/bifrost-http/handlers/providers.go b/transports/bifrost-http/handlers/providers.go index 8e83f2879e..87c28d5ee4 100644 --- a/transports/bifrost-http/handlers/providers.go +++ b/transports/bifrost-http/handlers/providers.go @@ -5,7 +5,9 @@ package handlers import ( "encoding/json" "fmt" + "slices" "sort" + "strings" "github.com/fasthttp/router" bifrost "github.com/maximhq/bifrost/core" @@ -153,7 +155,7 @@ func (h *ProviderHandler) AddProvider(ctx *fasthttp.RequestCtx) { } // Validate required keys - if len(req.Keys) == 0 && req.Provider != schemas.Vertex && req.Provider != schemas.Ollama && req.Provider != schemas.SGL { + if len(req.Keys) == 0 && req.Provider != schemas.Ollama && req.Provider != schemas.SGL { SendError(ctx, fasthttp.StatusBadRequest, "At least one API key is required", h.logger) return } @@ -237,79 +239,51 @@ func (h *ProviderHandler) UpdateProvider(ctx *fasthttp.RequestCtx) { return } + oldConfigRedacted, err := h.store.GetProviderConfigRedacted(provider) + if err != nil { + SendError(ctx, fasthttp.StatusNotFound, fmt.Sprintf("Provider not found: %v", err), h.logger) + return + } + // Construct ProviderConfig from individual fields config := lib.ProviderConfig{ Keys: oldConfigRaw.Keys, NetworkConfig: oldConfigRaw.NetworkConfig, ConcurrencyAndBufferSize: oldConfigRaw.ConcurrencyAndBufferSize, + ProxyConfig: oldConfigRaw.ProxyConfig, } - // For now, don't replace any environment keys - preserve all existing ones - // TODO: Implement proper tracking of which env keys should be dropped - envKeysToReplace := make(map[string]struct{}) + // Environment variable cleanup is now handled automatically by mergeKeys function - // Validate and process keys - if req.Keys != nil { - if len(req.Keys) == 0 && provider != schemas.Vertex && provider != schemas.Ollama && provider != schemas.SGL { - SendError(ctx, fasthttp.StatusBadRequest, "At least one API key is required", h.logger) - return - } + var keysToAdd []schemas.Key + var keysToUpdate []schemas.Key - // Create a map of old keys by model patterns for quick lookup - oldKeysByModels := make(map[string][]schemas.Key) - for _, oldKey := range oldConfigRaw.Keys { - for _, model := range oldKey.Models { - oldKeysByModels[model] = append(oldKeysByModels[model], oldKey) - } + for _, key := range req.Keys { + if !slices.ContainsFunc(oldConfigRaw.Keys, func(k schemas.Key) bool { + return k.ID == key.ID + }) { + keysToAdd = append(keysToAdd, key) + } else { + keysToUpdate = append(keysToUpdate, key) } + } - // Process each key in the request - for i, newKey := range req.Keys { - // If the key is redacted, try to find and use the old key for the same models - if lib.IsRedacted(newKey.Value) { - // Look for matching old keys - var matchingKeys []schemas.Key - for _, model := range newKey.Models { - if oldKeys, exists := oldKeysByModels[model]; exists { - matchingKeys = append(matchingKeys, oldKeys...) - } - } - - // If we found matching keys, use the most appropriate one - if len(matchingKeys) > 0 { - // Try to find a key that matches all the same models - var bestMatch schemas.Key - bestMatchScore := 0 - - for _, oldKey := range matchingKeys { - // Calculate how many models match between the old and new key - matchCount := 0 - oldModelsMap := make(map[string]bool) - for _, m := range oldKey.Models { - oldModelsMap[m] = true - } - - for _, m := range newKey.Models { - if oldModelsMap[m] { - matchCount++ - } - } - - // Update best match if this key has more matching models - if matchCount > bestMatchScore { - bestMatch = oldKey - bestMatchScore = matchCount - } - } - - // Use the best matching key's value - req.Keys[i].Value = bestMatch.Value - } - } + var keysToDelete []schemas.Key + for _, key := range oldConfigRaw.Keys { + if !slices.ContainsFunc(req.Keys, func(k schemas.Key) bool { + return k.ID == key.ID + }) { + keysToDelete = append(keysToDelete, key) } - config.Keys = req.Keys } + keys, err := h.mergeKeys(provider, oldConfigRaw.Keys, oldConfigRedacted.Keys, keysToAdd, keysToDelete, keysToUpdate) + if err != nil { + SendError(ctx, fasthttp.StatusBadRequest, fmt.Sprintf("Invalid keys: %v", err), h.logger) + return + } + config.Keys = keys + // Handle meta config if provided if req.MetaConfig != nil && len(*req.MetaConfig) > 0 { // Merge new meta config with old, preserving redacted values @@ -340,7 +314,7 @@ func (h *ProviderHandler) UpdateProvider(ctx *fasthttp.RequestCtx) { config.ProxyConfig = req.ProxyConfig // Update provider config in store (env vars will be processed by store) - if err := h.store.UpdateProviderConfig(provider, config, envKeysToReplace); err != nil { + if err := h.store.UpdateProviderConfig(provider, config); err != nil { h.logger.Warn(fmt.Sprintf("Failed to update provider %s: %v", provider, err)) SendError(ctx, fasthttp.StatusInternalServerError, fmt.Sprintf("Failed to update provider: %v", err), h.logger) return @@ -415,14 +389,6 @@ func (h *ProviderHandler) convertToProviderMetaConfig(provider schemas.ModelProv } switch provider { - case schemas.Azure: - var azureMetaConfig meta.AzureMetaConfig - if err := json.Unmarshal(metaConfigJSON, &azureMetaConfig); err != nil { - return nil, fmt.Errorf("failed to unmarshal Azure meta config: %w", err) - } - var metaConfig schemas.MetaConfig = &azureMetaConfig - return &metaConfig, nil - case schemas.Bedrock: var bedrockMetaConfig meta.BedrockMetaConfig if err := json.Unmarshal(metaConfigJSON, &bedrockMetaConfig); err != nil { @@ -431,50 +397,108 @@ func (h *ProviderHandler) convertToProviderMetaConfig(provider schemas.ModelProv var metaConfig schemas.MetaConfig = &bedrockMetaConfig return &metaConfig, nil - case schemas.Vertex: - var vertexMetaConfig meta.VertexMetaConfig - if err := json.Unmarshal(metaConfigJSON, &vertexMetaConfig); err != nil { - return nil, fmt.Errorf("failed to unmarshal Vertex meta config: %w", err) - } - var metaConfig schemas.MetaConfig = &vertexMetaConfig - return &metaConfig, nil - default: // For providers that don't support meta config, return nil return nil, nil } } -// mergeMetaConfig merges new meta config with old, preserving values that are redacted in the new config -func (h *ProviderHandler) mergeMetaConfig(provider schemas.ModelProvider, oldConfig *schemas.MetaConfig, newConfigMap map[string]interface{}) (*schemas.MetaConfig, error) { - if oldConfig == nil || len(newConfigMap) == 0 { - return h.convertToProviderMetaConfig(provider, newConfigMap) +// mergeKeys merges new keys with old, preserving values that are redacted in the new config +func (h *ProviderHandler) mergeKeys(provider schemas.ModelProvider, oldRawKeys []schemas.Key, oldRedactedKeys []schemas.Key, keysToAdd []schemas.Key, keysToDelete []schemas.Key, keysToUpdate []schemas.Key) ([]schemas.Key, error) { + // Clean up environment variables for deleted and updated keys + h.store.CleanupEnvKeysForKeys(string(provider), keysToDelete) + h.store.CleanupEnvKeysForUpdatedKeys(string(provider), keysToUpdate) + // Create a map of indices to delete + toDelete := make(map[int]bool) + for _, key := range keysToDelete { + for i, oldKey := range oldRawKeys { + if oldKey.ID == key.ID { + toDelete[i] = true + break + } + } } - switch provider { - case schemas.Azure: - var newAzureConfig meta.AzureMetaConfig - newConfigJSON, _ := json.Marshal(newConfigMap) - if err := json.Unmarshal(newConfigJSON, &newAzureConfig); err != nil { - return nil, fmt.Errorf("failed to unmarshal new Azure meta config: %w", err) - } + // Create a map of updates by ID for quick lookup + updates := make(map[string]schemas.Key) + for _, key := range keysToUpdate { + updates[key.ID] = key + } - oldAzureConfig, ok := (*oldConfig).(*meta.AzureMetaConfig) - if !ok { - return nil, fmt.Errorf("existing meta config type mismatch: expected AzureMetaConfig") + // Process existing keys (handle updates and deletions) + var resultKeys []schemas.Key + for i, oldRawKey := range oldRawKeys { + // Skip if this key should be deleted + if toDelete[i] { + continue } - // Preserve old values if new ones are redacted - if lib.IsRedacted(newAzureConfig.Endpoint) { - newAzureConfig.Endpoint = oldAzureConfig.Endpoint - } - if newAzureConfig.APIVersion != nil && oldAzureConfig.APIVersion != nil && lib.IsRedacted(*newAzureConfig.APIVersion) { - newAzureConfig.APIVersion = oldAzureConfig.APIVersion + // Check if this key should be updated + if updateKey, exists := updates[oldRawKey.ID]; exists { + mergedKey := updateKey + + // Handle redacted values + if lib.IsRedacted(updateKey.Value) && + (!strings.HasPrefix(updateKey.Value, "env.") || + !strings.EqualFold(updateKey.Value, oldRedactedKeys[i].Value)) { + mergedKey.Value = oldRawKey.Value + } + + // Handle Azure config redacted values + if updateKey.AzureKeyConfig != nil && oldRedactedKeys[i].AzureKeyConfig != nil { + if lib.IsRedacted(updateKey.AzureKeyConfig.Endpoint) && + (!strings.HasPrefix(updateKey.AzureKeyConfig.Endpoint, "env.") || + !strings.EqualFold(updateKey.AzureKeyConfig.Endpoint, oldRedactedKeys[i].AzureKeyConfig.Endpoint)) { + mergedKey.AzureKeyConfig.Endpoint = oldRawKey.AzureKeyConfig.Endpoint + } + if updateKey.AzureKeyConfig.APIVersion != nil { + if lib.IsRedacted(*updateKey.AzureKeyConfig.APIVersion) && + (!strings.HasPrefix(*updateKey.AzureKeyConfig.APIVersion, "env.") || + !strings.EqualFold(*updateKey.AzureKeyConfig.APIVersion, *oldRedactedKeys[i].AzureKeyConfig.APIVersion)) { + mergedKey.AzureKeyConfig.APIVersion = oldRawKey.AzureKeyConfig.APIVersion + } + } + } + + // Handle Vertex config redacted values + if updateKey.VertexKeyConfig != nil && oldRedactedKeys[i].VertexKeyConfig != nil { + if lib.IsRedacted(updateKey.VertexKeyConfig.ProjectID) && + (!strings.HasPrefix(updateKey.VertexKeyConfig.ProjectID, "env.") || + !strings.EqualFold(updateKey.VertexKeyConfig.ProjectID, oldRedactedKeys[i].VertexKeyConfig.ProjectID)) { + mergedKey.VertexKeyConfig.ProjectID = oldRawKey.VertexKeyConfig.ProjectID + } + if lib.IsRedacted(updateKey.VertexKeyConfig.Region) && + (!strings.HasPrefix(updateKey.VertexKeyConfig.Region, "env.") || + !strings.EqualFold(updateKey.VertexKeyConfig.Region, oldRedactedKeys[i].VertexKeyConfig.Region)) { + mergedKey.VertexKeyConfig.Region = oldRawKey.VertexKeyConfig.Region + } + if lib.IsRedacted(updateKey.VertexKeyConfig.AuthCredentials) && + (!strings.HasPrefix(updateKey.VertexKeyConfig.AuthCredentials, "env.") || + !strings.EqualFold(updateKey.VertexKeyConfig.AuthCredentials, oldRedactedKeys[i].VertexKeyConfig.AuthCredentials)) { + mergedKey.VertexKeyConfig.AuthCredentials = oldRawKey.VertexKeyConfig.AuthCredentials + } + } + + resultKeys = append(resultKeys, mergedKey) + } else { + // Keep unchanged key + resultKeys = append(resultKeys, oldRawKey) } + } - var metaConfig schemas.MetaConfig = &newAzureConfig - return &metaConfig, nil + // Add new keys + resultKeys = append(resultKeys, keysToAdd...) + + return resultKeys, nil +} +// mergeMetaConfig merges new meta config with old, preserving values that are redacted in the new config +func (h *ProviderHandler) mergeMetaConfig(provider schemas.ModelProvider, oldConfig *schemas.MetaConfig, newConfigMap map[string]interface{}) (*schemas.MetaConfig, error) { + if oldConfig == nil || len(newConfigMap) == 0 { + return h.convertToProviderMetaConfig(provider, newConfigMap) + } + + switch provider { case schemas.Bedrock: var newBedrockConfig meta.BedrockMetaConfig newConfigJSON, _ := json.Marshal(newConfigMap) @@ -503,33 +527,6 @@ func (h *ProviderHandler) mergeMetaConfig(provider schemas.ModelProvider, oldCon var metaConfig schemas.MetaConfig = &newBedrockConfig return &metaConfig, nil - - case schemas.Vertex: - var newVertexConfig meta.VertexMetaConfig - newConfigJSON, _ := json.Marshal(newConfigMap) - if err := json.Unmarshal(newConfigJSON, &newVertexConfig); err != nil { - return nil, fmt.Errorf("failed to unmarshal new Vertex meta config: %w", err) - } - - oldVertexConfig, ok := (*oldConfig).(*meta.VertexMetaConfig) - if !ok { - return nil, fmt.Errorf("existing meta config type mismatch: expected VertexMetaConfig") - } - - // Preserve old values if new ones are redacted - if lib.IsRedacted(newVertexConfig.ProjectID) { - newVertexConfig.ProjectID = oldVertexConfig.ProjectID - } - if lib.IsRedacted(newVertexConfig.Region) { - newVertexConfig.Region = oldVertexConfig.Region - } - if lib.IsRedacted(newVertexConfig.AuthCredentials) { - newVertexConfig.AuthCredentials = oldVertexConfig.AuthCredentials - } - - var metaConfig schemas.MetaConfig = &newVertexConfig - return &metaConfig, nil - default: return nil, nil } diff --git a/transports/bifrost-http/lib/account.go b/transports/bifrost-http/lib/account.go index e611616345..85bbe0c477 100644 --- a/transports/bifrost-http/lib/account.go +++ b/transports/bifrost-http/lib/account.go @@ -9,10 +9,10 @@ import ( ) // BaseAccount implements the Account interface for Bifrost. -// It manages provider configurations using a bbolt store for persistent storage. +// It manages provider configurations using a in-memory store for persistent storage. // All data processing (environment variables, meta configs) is done upfront in the store. type BaseAccount struct { - store *ConfigStore // bbolt store for persistent configuration + store *ConfigStore // store for in-memory configuration } // NewBaseAccount creates a new BaseAccount with the given store diff --git a/transports/bifrost-http/lib/config.go b/transports/bifrost-http/lib/config.go index 81cd7553b4..d224713719 100644 --- a/transports/bifrost-http/lib/config.go +++ b/transports/bifrost-http/lib/config.go @@ -18,7 +18,7 @@ type ClientConfig struct { // ProviderConfig represents the configuration for a specific AI model provider. // It includes API keys, network settings, provider-specific metadata, and concurrency settings. type ProviderConfig struct { - Keys []schemas.Key `json:"keys"` // API keys for the provider + Keys []schemas.Key `json:"keys"` // API keys for the provider with UUIDs NetworkConfig *schemas.NetworkConfig `json:"network_config,omitempty"` // Network-related settings MetaConfig *schemas.MetaConfig `json:"-"` // Provider-specific metadata ConcurrencyAndBufferSize *schemas.ConcurrencyAndBufferSize `json:"concurrency_and_buffer_size,omitempty"` // Concurrency settings diff --git a/transports/bifrost-http/lib/store.go b/transports/bifrost-http/lib/store.go index aaae7d77d7..6ca88f88bc 100644 --- a/transports/bifrost-http/lib/store.go +++ b/transports/bifrost-http/lib/store.go @@ -9,6 +9,7 @@ import ( "strings" "sync" + "github.com/google/uuid" bifrost "github.com/maximhq/bifrost/core" "github.com/maximhq/bifrost/core/schemas" "github.com/maximhq/bifrost/core/schemas/meta" @@ -20,11 +21,11 @@ import ( // // Features: // - Pure in-memory storage for ultra-fast access -// - Environment variable processing for API keys and meta configurations +// - Environment variable processing for API keys and key-level configurations // - Thread-safe operations with read-write mutexes // - Real-time configuration updates via HTTP API // - Explicit persistence control via WriteConfigToFile() -// - Support for all provider-specific meta configurations (Azure, Bedrock, Vertex) +// - Support for provider-specific key configurations (Azure, Vertex) and meta configurations (Bedrock) type ConfigStore struct { mu sync.RWMutex muMCP sync.RWMutex @@ -45,8 +46,9 @@ type ConfigStore struct { type EnvKeyInfo struct { EnvVar string // The environment variable name (without env. prefix) Provider string // The provider this key belongs to (empty for core/mcp configs) - KeyType string // Type of key (e.g., "api_key", "meta_config", "connection_string") + KeyType string // Type of key (e.g., "api_key", "azure_config", "vertex_config", "meta_config", "connection_string") ConfigPath string // Path in config where this env var is used + KeyID string // The key ID this env var belongs to (empty for non-key configs like meta_config, connection_string) } var DefaultClientConfig = ClientConfig{ @@ -66,7 +68,7 @@ func NewConfigStore(logger schemas.Logger) (*ConfigStore, error) { } // LoadFromConfig loads initial configuration from a JSON config file into memory -// with full preprocessing including environment variable resolution and meta config parsing. +// with full preprocessing including environment variable resolution and key config parsing. // All processing is done upfront to ensure zero latency when retrieving data. // // If the config file doesn't exist, the system starts with default configuration @@ -75,7 +77,8 @@ func NewConfigStore(logger schemas.Logger) (*ConfigStore, error) { // This method handles: // - JSON config file parsing // - Environment variable substitution for API keys (env.VARIABLE_NAME) -// - Provider-specific meta config processing (Azure, Bedrock, Vertex) +// - Key-level config processing for Azure and Vertex (Endpoint, APIVersion, ProjectID, Region, AuthCredentials) +// - Provider-specific meta config processing (Bedrock only) // - Case conversion for provider names (e.g., "OpenAI" -> "openai") // - In-memory storage for ultra-fast access during request processing // - Graceful handling of missing config files @@ -178,8 +181,13 @@ func (s *ConfigStore) LoadFromConfig(configPath string) error { } } - // Process environment variables in keys + // Process environment variables in keys (including key-level configs) for i, key := range cfg.Keys { + if key.ID == "" { + cfg.Keys[i].ID = uuid.NewString() + } + + // Process API key value processedValue, envVar, err := s.processEnvValue(key.Value) if err != nil { s.cleanupEnvKeys(string(provider), "", newEnvKeys) @@ -195,9 +203,28 @@ func (s *ConfigStore) LoadFromConfig(configPath string) error { EnvVar: envVar, Provider: string(provider), KeyType: "api_key", - ConfigPath: fmt.Sprintf("providers.%s.keys[%d]", provider, i), + ConfigPath: fmt.Sprintf("providers.%s.keys[%s]", provider, key.ID), + KeyID: key.ID, }) } + + // Process Azure key config if present + if key.AzureKeyConfig != nil { + if err := s.processAzureKeyConfigEnvVars(&cfg.Keys[i], provider, i, newEnvKeys); err != nil { + s.cleanupEnvKeys(string(provider), "", newEnvKeys) + s.logger.Warn(fmt.Sprintf("failed to process Azure key config env vars for %s: %v", provider, err)) + continue + } + } + + // Process Vertex key config if present + if key.VertexKeyConfig != nil { + if err := s.processVertexKeyConfigEnvVars(&cfg.Keys[i], provider, i, newEnvKeys); err != nil { + s.cleanupEnvKeys(string(provider), "", newEnvKeys) + s.logger.Warn(fmt.Sprintf("failed to process Vertex key config env vars for %s: %v", provider, err)) + continue + } + } } processedProviders[provider] = cfg @@ -280,16 +307,77 @@ func (s *ConfigStore) writeConfigToFile(configPath string) error { redactedKeys := make([]schemas.Key, len(config.Keys)) for i, key := range config.Keys { redactedKeys[i] = schemas.Key{ + ID: key.ID, Models: key.Models, Weight: key.Weight, } - path := fmt.Sprintf("providers.%s.keys[%d]", provider, i) + // Restore API key value + path := fmt.Sprintf("providers.%s.keys[%s]", provider, key.ID) if envVar, ok := envVarsByPath[path]; ok { redactedKeys[i].Value = "env." + envVar } else { redactedKeys[i].Value = key.Value // Keep actual value, no asterisk redaction } + + // Restore Azure key config if present + if key.AzureKeyConfig != nil { + azureConfig := &schemas.AzureKeyConfig{ + Deployments: key.AzureKeyConfig.Deployments, + } + + // Restore Endpoint + path = fmt.Sprintf("providers.%s.keys[%s].azure_key_config.endpoint", provider, key.ID) + if envVar, ok := envVarsByPath[path]; ok { + azureConfig.Endpoint = "env." + envVar + } else { + azureConfig.Endpoint = key.AzureKeyConfig.Endpoint + } + + // Restore APIVersion if present + if key.AzureKeyConfig.APIVersion != nil { + path = fmt.Sprintf("providers.%s.keys[%s].azure_key_config.api_version", provider, key.ID) + if envVar, ok := envVarsByPath[path]; ok { + apiVersion := "env." + envVar + azureConfig.APIVersion = &apiVersion + } else { + azureConfig.APIVersion = key.AzureKeyConfig.APIVersion + } + } + + redactedKeys[i].AzureKeyConfig = azureConfig + } + + // Restore Vertex key config if present + if key.VertexKeyConfig != nil { + vertexConfig := &schemas.VertexKeyConfig{} + + // Restore ProjectID + path = fmt.Sprintf("providers.%s.keys[%s].vertex_key_config.project_id", provider, key.ID) + if envVar, ok := envVarsByPath[path]; ok { + vertexConfig.ProjectID = "env." + envVar + } else { + vertexConfig.ProjectID = key.VertexKeyConfig.ProjectID + } + + // Restore Region + path = fmt.Sprintf("providers.%s.keys[%s].vertex_key_config.region", provider, key.ID) + if envVar, ok := envVarsByPath[path]; ok { + vertexConfig.Region = "env." + envVar + } else { + vertexConfig.Region = key.VertexKeyConfig.Region + } + + // Restore AuthCredentials + path = fmt.Sprintf("providers.%s.keys[%s].vertex_key_config.auth_credentials", provider, key.ID) + if envVar, ok := envVarsByPath[path]; ok { + vertexConfig.AuthCredentials = "env." + envVar + } else { + vertexConfig.AuthCredentials = key.VertexKeyConfig.AuthCredentials + } + + redactedKeys[i].VertexKeyConfig = vertexConfig + } } // Create provider config with restored env references @@ -370,28 +458,6 @@ func (s *ConfigStore) getRestoredMCPConfig(envVarsByPath map[string]string) *sch // restoreMetaConfigEnvVars creates a copy of meta config with env variable references restored func (s *ConfigStore) restoreMetaConfigEnvVars(provider schemas.ModelProvider, metaConfig schemas.MetaConfig, envVarsByPath map[string]string) interface{} { switch m := metaConfig.(type) { - case *meta.AzureMetaConfig: - azureConfig := *m // Copy the struct - - // Restore endpoint if it came from env var - path := fmt.Sprintf("providers.%s.meta_config.endpoint", provider) - if envVar, ok := envVarsByPath[path]; ok { - azureConfig.Endpoint = "env." + envVar - } - // Otherwise keep actual value (no asterisk redaction) - - // Restore API version if it came from env var - if azureConfig.APIVersion != nil { - path = fmt.Sprintf("providers.%s.meta_config.api_version", provider) - if envVar, ok := envVarsByPath[path]; ok { - apiVersion := "env." + envVar - azureConfig.APIVersion = &apiVersion - } - // Otherwise keep actual value (no asterisk redaction) - } - - return azureConfig - case *meta.BedrockMetaConfig: bedrockConfig := *m // Copy the struct @@ -434,32 +500,6 @@ func (s *ConfigStore) restoreMetaConfigEnvVars(provider schemas.ModelProvider, m return bedrockConfig - case *meta.VertexMetaConfig: - vertexConfig := *m // Copy the struct - - // Restore project ID if it came from env var - path := fmt.Sprintf("providers.%s.meta_config.project_id", provider) - if envVar, ok := envVarsByPath[path]; ok { - vertexConfig.ProjectID = "env." + envVar - } - // Otherwise keep actual value (no asterisk redaction) - - // Restore region if it came from env var - path = fmt.Sprintf("providers.%s.meta_config.region", provider) - if envVar, ok := envVarsByPath[path]; ok { - vertexConfig.Region = "env." + envVar - } - // Otherwise keep actual value (no asterisk redaction) - - // Restore auth credentials if it came from env var - path = fmt.Sprintf("providers.%s.meta_config.auth_credentials", provider) - if envVar, ok := envVarsByPath[path]; ok { - vertexConfig.AuthCredentials = "env." + envVar - } - // Otherwise keep actual value (no asterisk redaction) - - return vertexConfig - default: return metaConfig } @@ -476,14 +516,6 @@ func (s *ConfigStore) SaveConfig() error { // parseMetaConfig converts raw JSON to the appropriate provider-specific meta config interface func (s *ConfigStore) parseMetaConfig(rawMetaConfig json.RawMessage, provider schemas.ModelProvider) (*schemas.MetaConfig, error) { switch provider { - case schemas.Azure: - var azureMetaConfig meta.AzureMetaConfig - if err := json.Unmarshal(rawMetaConfig, &azureMetaConfig); err != nil { - return nil, fmt.Errorf("failed to unmarshal Azure meta config: %w", err) - } - var metaConfig schemas.MetaConfig = &azureMetaConfig - return &metaConfig, nil - case schemas.Bedrock: var bedrockMetaConfig meta.BedrockMetaConfig if err := json.Unmarshal(rawMetaConfig, &bedrockMetaConfig); err != nil { @@ -491,14 +523,6 @@ func (s *ConfigStore) parseMetaConfig(rawMetaConfig json.RawMessage, provider sc } var metaConfig schemas.MetaConfig = &bedrockMetaConfig return &metaConfig, nil - - case schemas.Vertex: - var vertexMetaConfig meta.VertexMetaConfig - if err := json.Unmarshal(rawMetaConfig, &vertexMetaConfig); err != nil { - return nil, fmt.Errorf("failed to unmarshal Vertex meta config: %w", err) - } - var metaConfig schemas.MetaConfig = &vertexMetaConfig - return &metaConfig, nil } return nil, fmt.Errorf("unsupported provider for meta config: %s", provider) @@ -509,9 +533,7 @@ func (s *ConfigStore) parseMetaConfig(rawMetaConfig json.RawMessage, provider sc // variables in their fields, ensuring type safety and proper field handling. // // Supported providers and their processed fields: -// - Azure: Endpoint, APIVersion // - Bedrock: SecretAccessKey, Region, SessionToken, ARN -// - Vertex: ProjectID, Region, AuthCredentials // // For unsupported providers, the meta config is returned unchanged. // This approach ensures type safety while supporting environment variable substitution. @@ -520,50 +542,6 @@ func (s *ConfigStore) processMetaConfigEnvVars(rawMetaConfig json.RawMessage, pr newEnvKeys := make(map[string]struct{}) switch provider { - case schemas.Azure: - var azureMetaConfig meta.AzureMetaConfig - if err := json.Unmarshal(rawMetaConfig, &azureMetaConfig); err != nil { - return nil, newEnvKeys, fmt.Errorf("failed to unmarshal Azure meta config: %w", err) - } - - endpoint, envVar, err := s.processEnvValue(azureMetaConfig.Endpoint) - if err != nil { - return nil, newEnvKeys, err - } - if envVar != "" { - newEnvKeys[envVar] = struct{}{} - s.EnvKeys[envVar] = append(s.EnvKeys[envVar], EnvKeyInfo{ - EnvVar: envVar, - Provider: string(provider), - KeyType: "meta_config", - ConfigPath: fmt.Sprintf("providers.%s.meta_config.endpoint", provider), - }) - } - azureMetaConfig.Endpoint = endpoint - - if azureMetaConfig.APIVersion != nil { - apiVersion, envVar, err := s.processEnvValue(*azureMetaConfig.APIVersion) - if err != nil { - return nil, newEnvKeys, err - } - if envVar != "" { - newEnvKeys[envVar] = struct{}{} - s.EnvKeys[envVar] = append(s.EnvKeys[envVar], EnvKeyInfo{ - EnvVar: envVar, - Provider: string(provider), - KeyType: "meta_config", - ConfigPath: fmt.Sprintf("providers.%s.meta_config.api_version", provider), - }) - } - azureMetaConfig.APIVersion = &apiVersion - } - - processedJSON, err := json.Marshal(azureMetaConfig) - if err != nil { - return nil, newEnvKeys, fmt.Errorf("failed to marshal processed Azure meta config: %w", err) - } - return processedJSON, newEnvKeys, nil - case schemas.Bedrock: var bedrockMetaConfig meta.BedrockMetaConfig if err := json.Unmarshal(rawMetaConfig, &bedrockMetaConfig); err != nil { @@ -581,6 +559,7 @@ func (s *ConfigStore) processMetaConfigEnvVars(rawMetaConfig json.RawMessage, pr Provider: string(provider), KeyType: "meta_config", ConfigPath: fmt.Sprintf("providers.%s.meta_config.secret_access_key", provider), + KeyID: "", // Empty for meta config entries }) } bedrockMetaConfig.SecretAccessKey = secretAccessKey @@ -597,6 +576,7 @@ func (s *ConfigStore) processMetaConfigEnvVars(rawMetaConfig json.RawMessage, pr Provider: string(provider), KeyType: "meta_config", ConfigPath: fmt.Sprintf("providers.%s.meta_config.region", provider), + KeyID: "", // Empty for meta config entries }) } bedrockMetaConfig.Region = ®ion @@ -614,6 +594,7 @@ func (s *ConfigStore) processMetaConfigEnvVars(rawMetaConfig json.RawMessage, pr Provider: string(provider), KeyType: "meta_config", ConfigPath: fmt.Sprintf("providers.%s.meta_config.session_token", provider), + KeyID: "", // Empty for meta config entries }) } bedrockMetaConfig.SessionToken = &sessionToken @@ -631,6 +612,7 @@ func (s *ConfigStore) processMetaConfigEnvVars(rawMetaConfig json.RawMessage, pr Provider: string(provider), KeyType: "meta_config", ConfigPath: fmt.Sprintf("providers.%s.meta_config.arn", provider), + KeyID: "", // Empty for meta config entries }) } bedrockMetaConfig.ARN = &arn @@ -641,63 +623,6 @@ func (s *ConfigStore) processMetaConfigEnvVars(rawMetaConfig json.RawMessage, pr return nil, newEnvKeys, fmt.Errorf("failed to marshal processed Bedrock meta config: %w", err) } return processedJSON, newEnvKeys, nil - - case schemas.Vertex: - var vertexMetaConfig meta.VertexMetaConfig - if err := json.Unmarshal(rawMetaConfig, &vertexMetaConfig); err != nil { - return nil, newEnvKeys, fmt.Errorf("failed to unmarshal Vertex meta config: %w", err) - } - - projectID, envVar, err := s.processEnvValue(vertexMetaConfig.ProjectID) - if err != nil { - return nil, newEnvKeys, err - } - if envVar != "" { - newEnvKeys[envVar] = struct{}{} - s.EnvKeys[envVar] = append(s.EnvKeys[envVar], EnvKeyInfo{ - EnvVar: envVar, - Provider: string(provider), - KeyType: "meta_config", - ConfigPath: fmt.Sprintf("providers.%s.meta_config.project_id", provider), - }) - } - vertexMetaConfig.ProjectID = projectID - - region, envVar, err := s.processEnvValue(vertexMetaConfig.Region) - if err != nil { - return nil, newEnvKeys, err - } - if envVar != "" { - newEnvKeys[envVar] = struct{}{} - s.EnvKeys[envVar] = append(s.EnvKeys[envVar], EnvKeyInfo{ - EnvVar: envVar, - Provider: string(provider), - KeyType: "meta_config", - ConfigPath: fmt.Sprintf("providers.%s.meta_config.region", provider), - }) - } - vertexMetaConfig.Region = region - - authCredentials, envVar, err := s.processEnvValue(vertexMetaConfig.AuthCredentials) - if err != nil { - return nil, newEnvKeys, err - } - if envVar != "" { - newEnvKeys[envVar] = struct{}{} - s.EnvKeys[envVar] = append(s.EnvKeys[envVar], EnvKeyInfo{ - EnvVar: envVar, - Provider: string(provider), - KeyType: "meta_config", - ConfigPath: fmt.Sprintf("providers.%s.meta_config.auth_credentials", provider), - }) - } - vertexMetaConfig.AuthCredentials = authCredentials - - processedJSON, err := json.Marshal(vertexMetaConfig) - if err != nil { - return nil, newEnvKeys, fmt.Errorf("failed to marshal processed Vertex meta config: %w", err) - } - return processedJSON, newEnvKeys, nil } return rawMetaConfig, newEnvKeys, nil @@ -763,19 +688,81 @@ func (s *ConfigStore) GetProviderConfigRedacted(provider schemas.ModelProvider) redactedConfig.Keys = make([]schemas.Key, len(config.Keys)) for i, key := range config.Keys { redactedConfig.Keys[i] = schemas.Key{ + ID: key.ID, Models: key.Models, // Copy slice reference - read-only so safe Weight: key.Weight, } - path := fmt.Sprintf("providers.%s.keys[%d]", provider, i) + // Redact API key value + path := fmt.Sprintf("providers.%s.keys[%s]", provider, key.ID) if envVar, ok := envVarsByPath[path]; ok { redactedConfig.Keys[i].Value = "env." + envVar } else { redactedConfig.Keys[i].Value = RedactKey(key.Value) } + + // Redact Azure key config if present + if key.AzureKeyConfig != nil { + azureConfig := &schemas.AzureKeyConfig{ + Deployments: key.AzureKeyConfig.Deployments, + } + + // Redact Endpoint + path = fmt.Sprintf("providers.%s.keys[%s].azure_key_config.endpoint", provider, key.ID) + if envVar, ok := envVarsByPath[path]; ok { + azureConfig.Endpoint = "env." + envVar + } else { + azureConfig.Endpoint = RedactKey(key.AzureKeyConfig.Endpoint) + } + + // Redact APIVersion if present + if key.AzureKeyConfig.APIVersion != nil { + path = fmt.Sprintf("providers.%s.keys[%s].azure_key_config.api_version", provider, key.ID) + if envVar, ok := envVarsByPath[path]; ok { + apiVersion := "env." + envVar + azureConfig.APIVersion = &apiVersion + } else { + // APIVersion is not sensitive, keep as-is + azureConfig.APIVersion = key.AzureKeyConfig.APIVersion + } + } + + redactedConfig.Keys[i].AzureKeyConfig = azureConfig + } + + // Redact Vertex key config if present + if key.VertexKeyConfig != nil { + vertexConfig := &schemas.VertexKeyConfig{} + + // Redact ProjectID + path = fmt.Sprintf("providers.%s.keys[%s].vertex_key_config.project_id", provider, key.ID) + if envVar, ok := envVarsByPath[path]; ok { + vertexConfig.ProjectID = "env." + envVar + } else { + vertexConfig.ProjectID = RedactKey(key.VertexKeyConfig.ProjectID) + } + + // Region is not sensitive, handle env vars only + path = fmt.Sprintf("providers.%s.keys[%s].vertex_key_config.region", provider, key.ID) + if envVar, ok := envVarsByPath[path]; ok { + vertexConfig.Region = "env." + envVar + } else { + vertexConfig.Region = key.VertexKeyConfig.Region + } + + // Redact AuthCredentials + path = fmt.Sprintf("providers.%s.keys[%s].vertex_key_config.auth_credentials", provider, key.ID) + if envVar, ok := envVarsByPath[path]; ok { + vertexConfig.AuthCredentials = "env." + envVar + } else { + vertexConfig.AuthCredentials = RedactKey(key.VertexKeyConfig.AuthCredentials) + } + + redactedConfig.Keys[i].VertexKeyConfig = vertexConfig + } } - // Handle meta config redaction if present + // Handle meta config redaction if present (Bedrock only) if config.MetaConfig != nil { redactedMetaConfig := s.redactMetaConfig(provider, *config.MetaConfig, envVarsByPath) redactedConfig.MetaConfig = &redactedMetaConfig @@ -785,25 +772,9 @@ func (s *ConfigStore) GetProviderConfigRedacted(provider schemas.ModelProvider) } // redactMetaConfig creates a redacted copy of meta config based on provider type +// Note: Only Bedrock is supported for meta config now, Azure and Vertex moved to key level func (s *ConfigStore) redactMetaConfig(provider schemas.ModelProvider, metaConfig schemas.MetaConfig, envVarsByPath map[string]string) schemas.MetaConfig { switch m := metaConfig.(type) { - case *meta.AzureMetaConfig: - azureConfig := *m // Copy the struct - path := fmt.Sprintf("providers.%s.meta_config.endpoint", provider) - if envVar, ok := envVarsByPath[path]; ok { - azureConfig.Endpoint = "env." + envVar - } else { - azureConfig.Endpoint = RedactKey(azureConfig.Endpoint) - } - if azureConfig.APIVersion != nil { - path = fmt.Sprintf("providers.%s.meta_config.api_version", provider) - if envVar, ok := envVarsByPath[path]; ok { - apiVersion := "env." + envVar - azureConfig.APIVersion = &apiVersion - } - } - return &azureConfig - case *meta.BedrockMetaConfig: bedrockConfig := *m // Copy the struct path := fmt.Sprintf("providers.%s.meta_config.secret_access_key", provider) @@ -838,24 +809,6 @@ func (s *ConfigStore) redactMetaConfig(provider schemas.ModelProvider, metaConfi } return &bedrockConfig - case *meta.VertexMetaConfig: - vertexConfig := *m // Copy the struct - path := fmt.Sprintf("providers.%s.meta_config.project_id", provider) - if envVar, ok := envVarsByPath[path]; ok { - vertexConfig.ProjectID = "env." + envVar - } - path = fmt.Sprintf("providers.%s.meta_config.region", provider) - if envVar, ok := envVarsByPath[path]; ok { - vertexConfig.Region = "env." + envVar - } - path = fmt.Sprintf("providers.%s.meta_config.auth_credentials", provider) - if envVar, ok := envVarsByPath[path]; ok { - vertexConfig.AuthCredentials = "env." + envVar - } else { - vertexConfig.AuthCredentials = RedactKey(vertexConfig.AuthCredentials) - } - return &vertexConfig - default: return metaConfig } @@ -879,7 +832,7 @@ func (s *ConfigStore) GetAllProviders() ([]schemas.ModelProvider, error) { // // The method: // - Validates that the provider doesn't already exist -// - Processes environment variables in API keys and meta configurations +// - Processes environment variables in API keys, key-level configs, and meta configurations // - Stores the processed configuration in memory // - Updates metadata and timestamps func (s *ConfigStore) AddProvider(provider schemas.ModelProvider, config ProviderConfig) error { @@ -916,8 +869,13 @@ func (s *ConfigStore) AddProvider(provider schemas.ModelProvider, config Provide config.MetaConfig = metaConfig } - // Process environment variables in keys + // Process environment variables in keys (including key-level configs) for i, key := range config.Keys { + if key.ID == "" { + config.Keys[i].ID = uuid.NewString() + } + + // Process API key value processedValue, envVar, err := s.processEnvValue(key.Value) if err != nil { s.cleanupEnvKeys(string(provider), "", newEnvKeys) @@ -932,9 +890,26 @@ func (s *ConfigStore) AddProvider(provider schemas.ModelProvider, config Provide EnvVar: envVar, Provider: string(provider), KeyType: "api_key", - ConfigPath: fmt.Sprintf("providers.%s.keys[%d]", provider, i), + ConfigPath: fmt.Sprintf("providers.%s.keys[%s]", provider, key.ID), + KeyID: key.ID, }) } + + // Process Azure key config if present + if key.AzureKeyConfig != nil { + if err := s.processAzureKeyConfigEnvVars(&config.Keys[i], provider, i, newEnvKeys); err != nil { + s.cleanupEnvKeys(string(provider), "", newEnvKeys) + return fmt.Errorf("failed to process Azure key config env vars: %w", err) + } + } + + // Process Vertex key config if present + if key.VertexKeyConfig != nil { + if err := s.processVertexKeyConfigEnvVars(&config.Keys[i], provider, i, newEnvKeys); err != nil { + s.cleanupEnvKeys(string(provider), "", newEnvKeys) + return fmt.Errorf("failed to process Vertex key config env vars: %w", err) + } + } } s.Providers[provider] = config @@ -948,25 +923,24 @@ func (s *ConfigStore) AddProvider(provider schemas.ModelProvider, config Provide // via the HTTP API and ensures all data processing is done upfront. // // The method: -// - Processes environment variables in API keys and meta configurations +// - Processes environment variables in API keys, key-level configs, and meta configurations // - Stores the processed configuration in memory // - Updates metadata and timestamps // - Thread-safe operation with write locks // +// Note: Environment variable cleanup for deleted/updated keys is now handled automatically +// by the mergeKeys function before this method is called. +// // Parameters: // - provider: The provider to update // - config: The new configuration -// - envKeysToReplace: Map of environment keys that should be replaced (only these will be cleaned up) -func (s *ConfigStore) UpdateProviderConfig(provider schemas.ModelProvider, config ProviderConfig, envKeysToReplace map[string]struct{}) error { +func (s *ConfigStore) UpdateProviderConfig(provider schemas.ModelProvider, config ProviderConfig) error { s.mu.Lock() defer s.mu.Unlock() // Track new environment variables being added newEnvKeys := make(map[string]struct{}) - // Track which old env vars will be replaced (only those specified in envKeysToReplace) - oldEnvKeys := make(map[string]struct{}) - // Process environment variables in meta config if present if config.MetaConfig != nil { rawMetaData, err := json.Marshal(*config.MetaConfig) @@ -974,17 +948,6 @@ func (s *ConfigStore) UpdateProviderConfig(provider schemas.ModelProvider, confi return fmt.Errorf("failed to marshal meta config: %w", err) } - // Find old meta config env vars that should be replaced - for envVar, infos := range s.EnvKeys { - for _, info := range infos { - if info.Provider == string(provider) && info.KeyType == "meta_config" { - if _, shouldReplace := envKeysToReplace[envVar]; shouldReplace { - oldEnvKeys[envVar] = struct{}{} - } - } - } - } - processedMetaData, envKeys, err := s.processMetaConfigEnvVars(rawMetaData, provider) if err != nil { s.cleanupEnvKeys(string(provider), "", envKeys) // Clean up only new vars on failure @@ -1004,19 +967,13 @@ func (s *ConfigStore) UpdateProviderConfig(provider schemas.ModelProvider, confi } } - // Find old API key env vars that should be replaced - for envVar, infos := range s.EnvKeys { - for _, info := range infos { - if info.Provider == string(provider) && info.KeyType == "api_key" { - if _, shouldReplace := envKeysToReplace[envVar]; shouldReplace { - oldEnvKeys[envVar] = struct{}{} - } - } + // Process environment variables in keys (including key-level configs) + for i, key := range config.Keys { + if key.ID == "" { + config.Keys[i].ID = uuid.NewString() } - } - // Process environment variables in keys - for i, key := range config.Keys { + // Process API key value processedValue, envVar, err := s.processEnvValue(key.Value) if err != nil { s.cleanupEnvKeys(string(provider), "", newEnvKeys) // Clean up only new vars on failure @@ -1031,16 +988,30 @@ func (s *ConfigStore) UpdateProviderConfig(provider schemas.ModelProvider, confi EnvVar: envVar, Provider: string(provider), KeyType: "api_key", - ConfigPath: fmt.Sprintf("providers.%s.keys[%d]", provider, i), + ConfigPath: fmt.Sprintf("providers.%s.keys[%s]", provider, key.ID), + KeyID: key.ID, }) } + + // Process Azure key config if present + if key.AzureKeyConfig != nil { + if err := s.processAzureKeyConfigEnvVars(&config.Keys[i], provider, i, newEnvKeys); err != nil { + s.cleanupEnvKeys(string(provider), "", newEnvKeys) + return fmt.Errorf("failed to process Azure key config env vars: %w", err) + } + } + + // Process Vertex key config if present + if key.VertexKeyConfig != nil { + if err := s.processVertexKeyConfigEnvVars(&config.Keys[i], provider, i, newEnvKeys); err != nil { + s.cleanupEnvKeys(string(provider), "", newEnvKeys) + return fmt.Errorf("failed to process Vertex key config env vars: %w", err) + } + } } s.Providers[provider] = config - // Clean up old env vars that were replaced - s.cleanupEnvKeys(string(provider), "", oldEnvKeys) - s.logger.Info(fmt.Sprintf("Updated configuration for provider: %s", provider)) return nil } @@ -1089,6 +1060,7 @@ func (s *ConfigStore) processMCPEnvVars() error { Provider: "", KeyType: "connection_string", ConfigPath: fmt.Sprintf("mcp.client_configs[%d].connection_string", i), + KeyID: "", // Empty for MCP connection strings }) } s.MCPConfig.ClientConfigs[i].ConnectionString = &newValue @@ -1150,6 +1122,7 @@ func (s *ConfigStore) AddMCPClient(clientConfig schemas.MCPClientConfig) error { Provider: "", KeyType: "connection_string", ConfigPath: fmt.Sprintf("mcp.client_configs.%s.connection_string", clientConfig.Name), + KeyID: "", // Empty for MCP connection strings }) } s.MCPConfig.ClientConfigs[len(s.MCPConfig.ClientConfigs)-1].ConnectionString = &processedValue @@ -1365,6 +1338,87 @@ func (s *ConfigStore) cleanupEnvVar(envVar, provider, mcpClientName string) { } } +// CleanupEnvKeysForKeys removes environment variable entries for specific keys that are being deleted. +// This function targets key-specific environment variables based on key IDs. +// +// Parameters: +// - provider: Provider name the keys belong to +// - keysToDelete: List of keys being deleted (uses their IDs to identify env vars to clean up) +func (s *ConfigStore) CleanupEnvKeysForKeys(provider string, keysToDelete []schemas.Key) { + // Create a set of key IDs to delete for efficient lookup + keyIDsToDelete := make(map[string]bool) + for _, key := range keysToDelete { + keyIDsToDelete[key.ID] = true + } + + // Iterate through all environment variables and remove entries for deleted keys + for envVar, infos := range s.EnvKeys { + filteredInfos := make([]EnvKeyInfo, 0, len(infos)) + + for _, info := range infos { + // Keep entries that either: + // 1. Don't belong to this provider, OR + // 2. Don't have a KeyID (meta config, MCP), OR + // 3. Have a KeyID that's not being deleted + shouldKeep := info.Provider != provider || + info.KeyID == "" || + !keyIDsToDelete[info.KeyID] + + if shouldKeep { + filteredInfos = append(filteredInfos, info) + } + } + + // Update or delete the environment variable entry + if len(filteredInfos) == 0 { + delete(s.EnvKeys, envVar) + } else { + s.EnvKeys[envVar] = filteredInfos + } + } +} + +// CleanupEnvKeysForUpdatedKeys removes environment variable entries for keys that are being updated +// but whose environment variables are changing. This prevents stale env var references. +// +// Parameters: +// - provider: Provider name the keys belong to +// - keysToUpdate: List of keys being updated (uses their IDs to identify env vars to clean up) +func (s *ConfigStore) CleanupEnvKeysForUpdatedKeys(provider string, keysToUpdate []schemas.Key) { + // Create a set of key IDs to update for efficient lookup + keyIDsToUpdate := make(map[string]bool) + for _, key := range keysToUpdate { + keyIDsToUpdate[key.ID] = true + } + + // Iterate through all environment variables and remove entries for updated keys + // The updated keys will re-add their env vars during processing + for envVar, infos := range s.EnvKeys { + filteredInfos := make([]EnvKeyInfo, 0, len(infos)) + + for _, info := range infos { + // Keep entries that either: + // 1. Don't belong to this provider, OR + // 2. Don't have a KeyID (meta config, MCP), OR + // 3. Have a KeyID that's not being updated + shouldKeep := info.Provider != provider || + info.KeyID == "" || + !keyIDsToUpdate[info.KeyID] + + if shouldKeep { + filteredInfos = append(filteredInfos, info) + } + } + + // Update or delete the environment variable entry + if len(filteredInfos) == 0 { + delete(s.EnvKeys, envVar) + } else { + s.EnvKeys[envVar] = filteredInfos + } + } +} + // autoDetectProviders automatically detects common environment variables and sets up providers // when no configuration file exists. This enables zero-config startup when users have set // standard environment variables like OPENAI_API_KEY, ANTHROPIC_API_KEY, etc. @@ -1391,10 +1445,14 @@ func (s *ConfigStore) autoDetectProviders() { for provider, envVars := range providerEnvVars { for _, envVar := range envVars { if apiKey := os.Getenv(envVar); apiKey != "" { + // Generate a unique ID for the auto-detected key + keyID := uuid.NewString() + // Create default provider configuration providerConfig := ProviderConfig{ Keys: []schemas.Key{ { + ID: keyID, Value: apiKey, Models: []string{}, // Empty means all supported models Weight: 1.0, @@ -1411,7 +1469,8 @@ func (s *ConfigStore) autoDetectProviders() { EnvVar: envVar, Provider: string(provider), KeyType: "api_key", - ConfigPath: fmt.Sprintf("providers.%s.keys[0]", provider), + ConfigPath: fmt.Sprintf("providers.%s.keys[%s]", provider, keyID), + KeyID: keyID, }) s.logger.Info(fmt.Sprintf("Auto-detected %s provider from environment variable %s", provider, envVar)) @@ -1425,3 +1484,104 @@ func (s *ConfigStore) autoDetectProviders() { s.logger.Info(fmt.Sprintf("Auto-configured %d provider(s) from environment variables", detectedCount)) } } + +// processAzureKeyConfigEnvVars processes environment variables in Azure key configuration +func (s *ConfigStore) processAzureKeyConfigEnvVars(key *schemas.Key, provider schemas.ModelProvider, keyIndex int, newEnvKeys map[string]struct{}) error { + azureConfig := key.AzureKeyConfig + + // Process Endpoint + processedEndpoint, envVar, err := s.processEnvValue(azureConfig.Endpoint) + if err != nil { + return err + } + if envVar != "" { + newEnvKeys[envVar] = struct{}{} + s.EnvKeys[envVar] = append(s.EnvKeys[envVar], EnvKeyInfo{ + EnvVar: envVar, + Provider: string(provider), + KeyType: "azure_config", + ConfigPath: fmt.Sprintf("providers.%s.keys[%s].azure_key_config.endpoint", provider, key.ID), + KeyID: key.ID, + }) + } + azureConfig.Endpoint = processedEndpoint + + // Process APIVersion if present + if azureConfig.APIVersion != nil { + processedAPIVersion, envVar, err := s.processEnvValue(*azureConfig.APIVersion) + if err != nil { + return err + } + if envVar != "" { + newEnvKeys[envVar] = struct{}{} + s.EnvKeys[envVar] = append(s.EnvKeys[envVar], EnvKeyInfo{ + EnvVar: envVar, + Provider: string(provider), + KeyType: "azure_config", + ConfigPath: fmt.Sprintf("providers.%s.keys[%s].azure_key_config.api_version", provider, key.ID), + KeyID: key.ID, + }) + } + azureConfig.APIVersion = &processedAPIVersion + } + + return nil +} + +// processVertexKeyConfigEnvVars processes environment variables in Vertex key configuration +func (s *ConfigStore) processVertexKeyConfigEnvVars(key *schemas.Key, provider schemas.ModelProvider, keyIndex int, newEnvKeys map[string]struct{}) error { + vertexConfig := key.VertexKeyConfig + + // Process ProjectID + processedProjectID, envVar, err := s.processEnvValue(vertexConfig.ProjectID) + if err != nil { + return err + } + if envVar != "" { + newEnvKeys[envVar] = struct{}{} + s.EnvKeys[envVar] = append(s.EnvKeys[envVar], EnvKeyInfo{ + EnvVar: envVar, + Provider: string(provider), + KeyType: "vertex_config", + ConfigPath: fmt.Sprintf("providers.%s.keys[%s].vertex_key_config.project_id", provider, key.ID), + KeyID: key.ID, + }) + } + vertexConfig.ProjectID = processedProjectID + + // Process Region + processedRegion, envVar, err := s.processEnvValue(vertexConfig.Region) + if err != nil { + return err + } + if envVar != "" { + newEnvKeys[envVar] = struct{}{} + s.EnvKeys[envVar] = append(s.EnvKeys[envVar], EnvKeyInfo{ + EnvVar: envVar, + Provider: string(provider), + KeyType: "vertex_config", + ConfigPath: fmt.Sprintf("providers.%s.keys[%s].vertex_key_config.region", provider, key.ID), + KeyID: key.ID, + }) + } + vertexConfig.Region = processedRegion + + // Process AuthCredentials + processedAuthCredentials, envVar, err := s.processEnvValue(vertexConfig.AuthCredentials) + if err != nil { + return err + } + if envVar != "" { + newEnvKeys[envVar] = struct{}{} + s.EnvKeys[envVar] = append(s.EnvKeys[envVar], EnvKeyInfo{ + EnvVar: envVar, + Provider: string(provider), + KeyType: "vertex_config", + ConfigPath: fmt.Sprintf("providers.%s.keys[%s].vertex_key_config.auth_credentials", provider, key.ID), + KeyID: key.ID, + }) + } + vertexConfig.AuthCredentials = processedAuthCredentials + + return nil +} diff --git a/transports/bifrost-http/ui/404.html b/transports/bifrost-http/ui/404.html index a0fd813772..1a654e3737 100644 --- a/transports/bifrost-http/ui/404.html +++ b/transports/bifrost-http/ui/404.html @@ -1,4 +1,4 @@ -404: This page could not be found.Bifrost - The fastest LLM gateway

404

This page could not be found.

\ No newline at end of file +

404

This page could not be found.

\ No newline at end of file diff --git a/transports/bifrost-http/ui/404/index.html b/transports/bifrost-http/ui/404/index.html index 7289c14ee5..eac454963a 100644 --- a/transports/bifrost-http/ui/404/index.html +++ b/transports/bifrost-http/ui/404/index.html @@ -1,4 +1,4 @@ -404: This page could not be found.Bifrost - The fastest LLM gateway

404

This page could not be found.

\ No newline at end of file +

404

This page could not be found.

\ No newline at end of file diff --git a/transports/bifrost-http/ui/_next/static/chunks/app/mcp-clients/page-b2d03b2155b1dfee.js b/transports/bifrost-http/ui/_next/static/chunks/app/mcp-clients/page-b2d03b2155b1dfee.js deleted file mode 100644 index 7531366c8d..0000000000 --- a/transports/bifrost-http/ui/_next/static/chunks/app/mcp-clients/page-b2d03b2155b1dfee.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[364],{1284:(e,t,n)=>{"use strict";n.d(t,{A:()=>a});let a=(0,n(9946).A)("info",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]])},2067:(e,t,n)=>{"use strict";n.d(t,{Dr:()=>a});class a{isValid(){return!this.rules.some(e=>!e.isValid)}getErrors(){return this.rules.filter(e=>!e.isValid).map(e=>e.message)}getFirstError(){let e=this.rules.find(e=>!e.isValid);return null==e?void 0:e.message}static required(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"This field is required";return{isValid:null!=e&&""!==e&&0!==e,message:t}}static minValue(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at least ".concat(t);return{isValid:!isNaN(e)&&e>=t,message:n}}static maxValue(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at most ".concat(t);return{isValid:!isNaN(e)&&e<=t,message:n}}static pattern(e,t,n){return{isValid:t.test(e||""),message:n}}static email(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Must be a valid email";return this.pattern(e,/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,t)}static url(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Must be a valid URL";return this.pattern(e,/^https?:\/\/.+/,t)}static minLength(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at least ".concat(t," characters");return{isValid:(e||"").length>=t,message:n}}static maxLength(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at most ".concat(t," characters");return{isValid:(e||"").length<=t,message:n}}static arrayMinLength(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must have at least ".concat(t," items");return{isValid:(null==e?void 0:e.length)>=t,message:n}}static arrayMaxLength(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must have at most ".concat(t," items");return{isValid:(null==e?void 0:e.length)<=t,message:n}}static arrayUnique(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Must have unique items";return{isValid:(null==e?void 0:e.length)===new Set(e).size,message:t}}static arraysEqual(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be equal";return{isValid:(null==e?void 0:e.length)===(null==t?void 0:t.length)&&(null==e?void 0:e.every((e,n)=>e===t[n])),message:n}}static custom(e,t){return{isValid:e,message:t}}static all(e){return e.find(e=>!e.isValid)||{isValid:!0,message:""}}constructor(e){this.rules=e.filter(e=>void 0!==e)}}},2525:(e,t,n)=>{"use strict";n.d(t,{A:()=>a});let a=(0,n(9946).A)("trash-2",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]])},3557:(e,t,n)=>{"use strict";n.d(t,{Ob:()=>s,w8:()=>a});let a=e=>e.split(",").map(e=>e.trim()).filter(e=>e.length>0),s=(e,t)=>(null==e?void 0:e.length)===(null==t?void 0:t.length)&&(null==e?void 0:e.every((e,n)=>e===t[n]))},3580:(e,t,n)=>{"use strict";n.d(t,{d:()=>s});var a=n(6671);function s(){return{toast:e=>{let{title:t,description:n,variant:s}=e,i=n?"".concat(t,": ").concat(n):t;"destructive"===s?a.o.error(i):a.o.success(i)}}}},4616:(e,t,n)=>{"use strict";n.d(t,{A:()=>a});let a=(0,n(9946).A)("plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]])},6034:(e,t,n)=>{"use strict";n.d(t,{Dk:()=>i,mR:()=>a,qV:()=>s});let a={base_url:"",default_request_timeout_in_seconds:30,max_retries:0,retry_backoff_initial:1e3,retry_backoff_max:1e4},s={concurrency:10,buffer_size:100},i={connected:"bg-green-100 text-green-800",error:"bg-red-100 text-red-800",disconnected:"bg-gray-100 text-gray-800"}},6559:(e,t,n)=>{"use strict";n.d(t,{$v:()=>x,EO:()=>u,Lt:()=>l,Rx:()=>g,Zr:()=>v,ck:()=>h,r7:()=>p,tv:()=>o,wd:()=>m});var a=n(5155);n(2115);var s=n(7649),i=n(3999),r=n(7168);function l(e){let{...t}=e;return(0,a.jsx)(s.bL,{"data-slot":"alert-dialog",...t})}function o(e){let{...t}=e;return(0,a.jsx)(s.l9,{"data-slot":"alert-dialog-trigger",...t})}function c(e){let{...t}=e;return(0,a.jsx)(s.ZL,{"data-slot":"alert-dialog-portal",...t})}function d(e){let{className:t,...n}=e;return(0,a.jsx)(s.hJ,{"data-slot":"alert-dialog-overlay",className:(0,i.cn)("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",t),...n})}function u(e){let{className:t,...n}=e;return(0,a.jsxs)(c,{children:[(0,a.jsx)(d,{}),(0,a.jsx)(s.UC,{"data-slot":"alert-dialog-content",className:(0,i.cn)("bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",t),...n})]})}function m(e){let{className:t,...n}=e;return(0,a.jsx)("div",{"data-slot":"alert-dialog-header",className:(0,i.cn)("flex flex-col gap-2 text-center sm:text-left",t),...n})}function h(e){let{className:t,...n}=e;return(0,a.jsx)("div",{"data-slot":"alert-dialog-footer",className:(0,i.cn)("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",t),...n})}function p(e){let{className:t,...n}=e;return(0,a.jsx)(s.hE,{"data-slot":"alert-dialog-title",className:(0,i.cn)("text-lg font-semibold",t),...n})}function x(e){let{className:t,...n}=e;return(0,a.jsx)(s.VY,{"data-slot":"alert-dialog-description",className:(0,i.cn)("text-muted-foreground text-sm",t),...n})}function g(e){let{className:t,...n}=e;return(0,a.jsx)(s.rc,{className:(0,i.cn)((0,r.r)({variant:"destructive"}),t),...n})}function v(e){let{className:t,...n}=e;return(0,a.jsx)(s.ZD,{className:(0,i.cn)((0,r.r)({variant:"outline"}),t),...n})}},7649:(e,t,n)=>{"use strict";n.d(t,{UC:()=>T,VY:()=>q,ZD:()=>z,ZL:()=>L,bL:()=>E,hE:()=>Z,hJ:()=>R,l9:()=>V,rc:()=>P});var a=n(2115),s=n(6081),i=n(6101),r=n(5452),l=n(5185),o=n(9708),c=n(5155),d="AlertDialog",[u,m]=(0,s.A)(d,[r.Hs]),h=(0,r.Hs)(),p=e=>{let{__scopeAlertDialog:t,...n}=e,a=h(t);return(0,c.jsx)(r.bL,{...a,...n,modal:!0})};p.displayName=d;var x=a.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...a}=e,s=h(n);return(0,c.jsx)(r.l9,{...s,...a,ref:t})});x.displayName="AlertDialogTrigger";var g=e=>{let{__scopeAlertDialog:t,...n}=e,a=h(t);return(0,c.jsx)(r.ZL,{...a,...n})};g.displayName="AlertDialogPortal";var v=a.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...a}=e,s=h(n);return(0,c.jsx)(r.hJ,{...s,...a,ref:t})});v.displayName="AlertDialogOverlay";var f="AlertDialogContent",[j,y]=u(f),_=(0,o.Dc)("AlertDialogContent"),b=a.forwardRef((e,t)=>{let{__scopeAlertDialog:n,children:s,...o}=e,d=h(n),u=a.useRef(null),m=(0,i.s)(t,u),p=a.useRef(null);return(0,c.jsx)(r.G$,{contentName:f,titleName:C,docsSlug:"alert-dialog",children:(0,c.jsx)(j,{scope:n,cancelRef:p,children:(0,c.jsxs)(r.UC,{role:"alertdialog",...d,...o,ref:m,onOpenAutoFocus:(0,l.m)(o.onOpenAutoFocus,e=>{var t;e.preventDefault(),null==(t=p.current)||t.focus({preventScroll:!0})}),onPointerDownOutside:e=>e.preventDefault(),onInteractOutside:e=>e.preventDefault(),children:[(0,c.jsx)(_,{children:s}),(0,c.jsx)(S,{contentRef:u})]})})})});b.displayName=f;var C="AlertDialogTitle",w=a.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...a}=e,s=h(n);return(0,c.jsx)(r.hE,{...s,...a,ref:t})});w.displayName=C;var N="AlertDialogDescription",k=a.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...a}=e,s=h(n);return(0,c.jsx)(r.VY,{...s,...a,ref:t})});k.displayName=N;var A=a.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...a}=e,s=h(n);return(0,c.jsx)(r.bm,{...s,...a,ref:t})});A.displayName="AlertDialogAction";var D="AlertDialogCancel",M=a.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...a}=e,{cancelRef:s}=y(D,n),l=h(n),o=(0,i.s)(t,s);return(0,c.jsx)(r.bm,{...l,...a,ref:o})});M.displayName=D;var S=e=>{let{contentRef:t}=e,n="`".concat(f,"` requires a description for the component to be accessible for screen reader users.\n\nYou can add a description to the `").concat(f,"` by passing a `").concat(N,"` component as a child, which also benefits sighted users by adding visible context to the dialog.\n\nAlternatively, you can use your own component as a description by assigning it an `id` and passing the same value to the `aria-describedby` prop in `").concat(f,"`. If the description is confusing or duplicative for sighted users, you can use the `@radix-ui/react-visually-hidden` primitive as a wrapper around your description component.\n\nFor more information, see https://radix-ui.com/primitives/docs/components/alert-dialog");return a.useEffect(()=>{var e;document.getElementById(null==(e=t.current)?void 0:e.getAttribute("aria-describedby"))||console.warn(n)},[n,t]),null},E=p,V=x,L=g,R=v,T=b,P=A,z=M,Z=w,q=k},7765:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>R});var a=n(5155),s=n(7168),i=n(9840),r=n(9852),l=n(2115),o=n(3655),c=l.forwardRef((e,t)=>(0,a.jsx)(o.sG.label,{...e,ref:t,onMouseDown:t=>{var n;t.target.closest("button, input, select, textarea")||(null==(n=e.onMouseDown)||n.call(e,t),!t.defaultPrevented&&t.detail>1&&t.preventDefault())}}));c.displayName="Label";var d=n(3999);function u(e){let{className:t,...n}=e;return(0,a.jsx)(c,{"data-slot":"label",className:(0,d.cn)("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",t),...n})}var m=n(5784),h=n(9474),p=n(7777),x=n(3580),g=n(1886),v=n(3557),f=n(2067),j=n(1284);let y={name:"",connection_type:"http",connection_string:"",stdio_config:{command:"",args:[],envs:[]},tools_to_skip:[],tools_to_execute:[]},_=e=>{var t,n,o,c,d,_;let{client:b,open:C,onClose:w,onSaved:N}=e,[k,A]=(0,l.useState)(y),[D,M]=(0,l.useState)(!1),[S,E]=(0,l.useState)(""),[V,L]=(0,l.useState)(""),[R,T]=(0,l.useState)(""),[P,z]=(0,l.useState)(""),{toast:Z}=(0,x.d)();(0,l.useEffect)(()=>{if(b){var e,t,n,a,s;A({name:b.name,connection_type:b.config.connection_type,connection_string:b.config.connection_string||"",stdio_config:{command:(null==(e=b.config.stdio_config)?void 0:e.command)||"",args:(null==(t=b.config.stdio_config)?void 0:t.args)||[],envs:(null==(n=b.config.stdio_config)?void 0:n.envs)||[]},tools_to_skip:b.config.tools_to_skip||[],tools_to_execute:b.config.tools_to_execute||[]}),E(((null==(a=b.config.stdio_config)?void 0:a.args)||[]).join(", ")),L(((null==(s=b.config.stdio_config)?void 0:s.envs)||[]).join(", ")),T((b.config.tools_to_skip||[]).join(", ")),z((b.config.tools_to_execute||[]).join(", "))}else A(y),E(""),L(""),T(""),z("")},[b]);let q=(e,t)=>{A(n=>({...n,[e]:t}))},O=(e,t)=>{A(n=>({...n,stdio_config:{command:"",args:[],envs:[],...n.stdio_config||{},[e]:t}}))},$=new f.Dr([f.Dr.required(null==(t=k.name)?void 0:t.trim(),"Client name is required"),f.Dr.pattern(k.name||"",/^[a-zA-Z0-9-_]+$/,"Client name can only contain letters, numbers, hyphens and underscores"),f.Dr.minLength(k.name||"",3,"Client name must be at least 3 characters"),f.Dr.maxLength(k.name||"",50,"Client name cannot exceed 50 characters"),...("http"===k.connection_type||"sse"===k.connection_type)&&!b?[f.Dr.required(null==(n=k.connection_string)?void 0:n.trim(),"Connection URL is required"),f.Dr.pattern(k.connection_string||"",/^(http:\/\/|https:\/\/|env\.[A-Z_]+$)/,"Connection URL must start with http://, https://, or be an environment variable (env.VAR_NAME)")]:[],..."stdio"===k.connection_type&&!b?[f.Dr.required(null==(c=k.stdio_config)||null==(o=c.command)?void 0:o.trim(),"Command is required for STDIO connections"),...!b?[f.Dr.pattern((null==(d=k.stdio_config)?void 0:d.command)||"",/^[^<>|&;]+$/,"Command cannot contain special shell characters")]:[]]:[],...P.trim()?[f.Dr.pattern(P,/^[a-zA-Z0-9_,\s-]+$/,"Tools to execute can only contain letters, numbers, underscores, and commas")]:[],...R.trim()?[f.Dr.pattern(R,/^[a-zA-Z0-9_,\s-]+$/,"Tools to skip can only contain letters, numbers, underscores, and commas")]:[],...b?[f.Dr.custom(!(0,v.Ob)(k.tools_to_execute||[],(0,v.w8)(P))||!(0,v.Ob)(k.tools_to_skip||[],(0,v.w8)(R)),"No changes to save")]:[],f.Dr.custom(!((e,t)=>{let n=new Set((0,v.w8)(e)),a=new Set((0,v.w8)(t));return Array.from(n).some(e=>a.has(e))})(P,R),"Tools cannot appear in both execute and skip lists")]),U=async()=>{var e;M(!0);let t=null,n={...k,stdio_config:"stdio"===k.connection_type?{command:(null==(e=k.stdio_config)?void 0:e.command)||"",args:(0,v.w8)(S),envs:(0,v.w8)(V)}:void 0,tools_to_skip:(0,v.w8)(R),tools_to_execute:(0,v.w8)(P)};if(b){let e={tools_to_execute:n.tools_to_execute,tools_to_skip:n.tools_to_skip};[,t]=await g.K.updateMCPClient(b.name,e)}else[,t]=await g.K.createMCPClient(n);M(!1),t?Z({title:"Error",description:t,variant:"destructive"}):(Z({title:"Success",description:b?"Client updated":"Client created"}),N(),w())};return(0,a.jsx)(i.lG,{open:C,onOpenChange:w,children:(0,a.jsxs)(i.Cf,{className:"max-h-[90vh] max-w-2xl overflow-y-auto",children:[(0,a.jsx)(i.c7,{children:(0,a.jsx)(i.L3,{children:b?"Edit MCP Client Tools":"New MCP Client"})}),(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(u,{children:"Name"}),(0,a.jsx)(r.p,{value:k.name,onChange:e=>q("name",e.target.value),placeholder:"Client name",disabled:!!b})]}),!b&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"w-full space-y-2",children:[(0,a.jsx)(u,{children:"Connection Type"}),(0,a.jsxs)(m.l6,{value:k.connection_type,onValueChange:e=>q("connection_type",e),children:[(0,a.jsx)(m.bq,{className:"w-full",children:(0,a.jsx)(m.yv,{placeholder:"Select connection type"})}),(0,a.jsxs)(m.gC,{children:[(0,a.jsx)(m.eb,{value:"http",children:"HTTP (Streamable)"}),(0,a.jsx)(m.eb,{value:"sse",children:"Server-Sent Events (SSE)"}),(0,a.jsx)(m.eb,{value:"stdio",children:"STDIO"})]})]})]}),("http"===k.connection_type||"sse"===k.connection_type)&&(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsxs)("div",{className:"flex w-fit items-center gap-1",children:[(0,a.jsx)(u,{children:"Connection URL"}),(0,a.jsx)(p.Bc,{children:(0,a.jsxs)(p.m_,{children:[(0,a.jsx)(p.k$,{asChild:!0,children:(0,a.jsx)("span",{children:(0,a.jsx)(j.A,{className:"text-muted-foreground ml-1 h-3 w-3"})})}),(0,a.jsx)(p.ZI,{className:"max-w-fit",children:(0,a.jsxs)("p",{children:["Use ",(0,a.jsx)("code",{className:"rounded bg-neutral-100 px-1 py-0.5 text-neutral-800",children:"env."})," to read the value from an environment variable."]})})]})})]}),(0,a.jsx)(r.p,{value:k.connection_string||"",onChange:e=>q("connection_string",e.target.value),placeholder:"http://your-mcp-server:3000 or env.MCP_SERVER_URL"})]}),"stdio"===k.connection_type&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(u,{children:"Command"}),(0,a.jsx)(r.p,{value:(null==(_=k.stdio_config)?void 0:_.command)||"",onChange:e=>O("command",e.target.value),placeholder:"node, python, /path/to/executable"})]}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(u,{children:"Arguments (comma-separated)"}),(0,a.jsx)(r.p,{value:S,onChange:e=>E(e.target.value),placeholder:"--port, 3000, --config, config.json"})]}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(u,{children:"Environment Variables (comma-separated)"}),(0,a.jsx)(r.p,{value:V,onChange:e=>L(e.target.value),placeholder:"API_KEY, DATABASE_URL"})]})]})]}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(u,{children:"Tools to Execute (comma-separated, leave empty for all)"}),(0,a.jsx)(h.T,{value:P,onChange:e=>z(e.target.value),placeholder:"tool1, tool2, tool3",rows:2})]}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(u,{children:"Tools to Skip (comma-separated)"}),(0,a.jsx)(h.T,{value:R,onChange:e=>T(e.target.value),placeholder:"skipTool1, skipTool2",rows:2})]})]}),(0,a.jsxs)(i.Es,{children:[(0,a.jsx)(s.$,{variant:"outline",onClick:w,disabled:D,children:"Cancel"}),(0,a.jsx)(p.Bc,{children:(0,a.jsxs)(p.m_,{children:[(0,a.jsx)(p.k$,{asChild:!0,children:(0,a.jsx)("span",{children:(0,a.jsx)(s.$,{onClick:U,disabled:!$.isValid()||D,isLoading:D,children:b?"Save":"Create"})})}),!$.isValid()&&(0,a.jsx)(p.ZI,{children:$.getFirstError()||"Please fix validation errors"})]})})]})]})})};var b=n(8145),C=n(8482),w=n(8524),N=n(6034),k=n(4616),A=n(9946);let D=(0,A.A)("refresh-ccw",[["path",{d:"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"14sxne"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16",key:"1hlbsb"}],["path",{d:"M16 16h5v5",key:"ccwih5"}]]),M=(0,A.A)("pencil",[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}],["path",{d:"m15 5 4 4",key:"1mk7zo"}]]);var S=n(2525),E=n(6559);function V(e){let{mcpClients:t}=e,[n,i]=(0,l.useState)(null),[r,o]=(0,l.useState)(t),[c,d]=(0,l.useState)(!1),{toast:u}=(0,x.d)(),m=async()=>{let[e,t]=await g.K.getMCPClients();t?u({title:"Error",description:t,variant:"destructive"}):o(e||[])};(0,l.useEffect)(()=>{m()},[]);let h=e=>{i(e),d(!0)},p=async e=>{let[,t]=await g.K.reconnectMCPClient(e.name);t?u({title:"Error",description:t,variant:"destructive"}):(u({title:"Reconnected",description:"Client reconnected."}),m())},v=async e=>{let[,t]=await g.K.deleteMCPClient(e.name);t?u({title:"Error",description:t,variant:"destructive"}):(u({title:"Deleted",description:"Client removed."}),m())},f=e=>{if("stdio"===e.config.connection_type){var t,n;return"".concat(null==(t=e.config.stdio_config)?void 0:t.command," ").concat(null==(n=e.config.stdio_config)?void 0:n.args.join(" "))||"STDIO"}return e.config.connection_string||"".concat(e.config.connection_type.toUpperCase())},j=e=>{switch(e){case"http":return"HTTP";case"sse":return"SSE";case"stdio":return"STDIO";default:return e.toUpperCase()}};return(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)(C.aR,{className:"mb-4 px-0",children:[(0,a.jsxs)(C.ZB,{className:"flex items-center justify-between",children:[(0,a.jsx)("div",{className:"flex items-center gap-2",children:"Registered MCP Clients"}),(0,a.jsxs)(s.$,{onClick:()=>{i(null),d(!0)},children:[(0,a.jsx)(k.A,{className:"h-4 w-4"})," New MCP Client"]})]}),(0,a.jsx)(C.BT,{children:"Manage clients that can connect to the MCP Tools endpoint."})]}),(0,a.jsx)("div",{className:"rounded-md border",children:(0,a.jsxs)(w.XI,{children:[(0,a.jsx)(w.A0,{children:(0,a.jsxs)(w.Hj,{children:[(0,a.jsx)(w.nd,{children:"Name"}),(0,a.jsx)(w.nd,{children:"Connection Type"}),(0,a.jsx)(w.nd,{children:"Connection Info"}),(0,a.jsx)(w.nd,{children:"State"}),(0,a.jsx)(w.nd,{className:"text-right",children:"Actions"})]})}),(0,a.jsxs)(w.BF,{children:[0===r.length&&(0,a.jsx)(w.Hj,{children:(0,a.jsx)(w.nA,{colSpan:5,className:"py-6 text-center",children:"No clients found."})}),r.map(e=>(0,a.jsxs)(w.Hj,{children:[(0,a.jsx)(w.nA,{className:"font-medium",children:e.name}),(0,a.jsx)(w.nA,{children:j(e.config.connection_type)}),(0,a.jsx)(w.nA,{className:"max-w-72 overflow-hidden text-ellipsis whitespace-nowrap",children:f(e)}),(0,a.jsx)(w.nA,{children:(0,a.jsx)(b.E,{className:N.Dk[e.state],children:e.state})}),(0,a.jsxs)(w.nA,{className:"space-x-2 text-right",children:["disconnected"===e.state?(0,a.jsx)(s.$,{variant:"ghost",size:"icon",onClick:()=>p(e),children:(0,a.jsx)(D,{className:"h-4 w-4"})}):"connected"===e.state&&(0,a.jsx)(s.$,{variant:"ghost",size:"icon",onClick:()=>h(e),children:(0,a.jsx)(M,{className:"h-4 w-4"})}),(0,a.jsxs)(E.Lt,{children:[(0,a.jsx)(E.tv,{asChild:!0,children:(0,a.jsx)(s.$,{variant:"ghost",size:"icon",disabled:"error"===e.state,children:(0,a.jsx)(S.A,{className:"h-4 w-4"})})}),(0,a.jsxs)(E.EO,{children:[(0,a.jsxs)(E.wd,{children:[(0,a.jsx)(E.r7,{children:"Remove MCP Client"}),(0,a.jsxs)(E.$v,{children:["Are you sure you want to remove MCP client ",e.name,"? You will need to reconnect the client to continue using it."]})]}),(0,a.jsxs)(E.ck,{children:[(0,a.jsx)(E.Zr,{children:"Cancel"}),(0,a.jsx)(E.Rx,{onClick:()=>v(e),children:"Delete"})]})]})]})]})]},e.name))]})]})}),c&&(0,a.jsx)(_,{open:c,client:n,onClose:()=>d(!1),onSaved:()=>{d(!1),m()}})]})}var L=n(2384);function R(){let[e,t]=(0,l.useState)([]),[n,s]=(0,l.useState)(!0),{toast:i}=(0,x.d)();(0,l.useEffect)(()=>{r()},[]);let r=async()=>{let[e,n]=await g.K.getMCPClients();if(s(!1),n)return void i({title:"Error",description:n,variant:"destructive"});t(e||[])};return(0,a.jsx)("div",{children:n?(0,a.jsx)(L.A,{}):(0,a.jsx)(V,{mcpClients:e})})}},7777:(e,t,n)=>{"use strict";n.d(t,{Bc:()=>r,ZI:()=>c,k$:()=>o,m_:()=>l});var a=n(5155);n(2115);var s=n(9613),i=n(3999);function r(e){let{delayDuration:t=0,...n}=e;return(0,a.jsx)(s.Kq,{"data-slot":"tooltip-provider",delayDuration:t,...n})}function l(e){let{...t}=e;return(0,a.jsx)(r,{children:(0,a.jsx)(s.bL,{"data-slot":"tooltip",...t})})}function o(e){let{...t}=e;return(0,a.jsx)(s.l9,{"data-slot":"tooltip-trigger",...t})}function c(e){let{className:t,sideOffset:n=0,children:r,...l}=e;return(0,a.jsx)(s.ZL,{children:(0,a.jsxs)(s.UC,{"data-slot":"tooltip-content",sideOffset:n,className:(0,i.cn)("bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",t),...l,children:[r,(0,a.jsx)(s.i3,{className:"bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]"})]})})}},8396:(e,t,n)=>{Promise.resolve().then(n.bind(n,7765))},9474:(e,t,n)=>{"use strict";n.d(t,{T:()=>i});var a=n(5155);n(2115);var s=n(3999);function i(e){let{className:t,...n}=e;return(0,a.jsx)("textarea",{"data-slot":"textarea",className:(0,s.cn)("border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",t),...n})}}},e=>{var t=t=>e(e.s=t);e.O(0,[106,189,944,231,412,62,613,915,441,684,358],()=>t(8396)),_N_E=e.O()}]); \ No newline at end of file diff --git a/transports/bifrost-http/ui/_next/static/chunks/app/mcp-clients/page-b787aebb95ad9490.js b/transports/bifrost-http/ui/_next/static/chunks/app/mcp-clients/page-b787aebb95ad9490.js new file mode 100644 index 0000000000..cf42120c15 --- /dev/null +++ b/transports/bifrost-http/ui/_next/static/chunks/app/mcp-clients/page-b787aebb95ad9490.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[364],{1284:(e,t,n)=>{"use strict";n.d(t,{A:()=>a});let a=(0,n(9946).A)("info",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]])},2067:(e,t,n)=>{"use strict";n.d(t,{Dr:()=>a,MZ:()=>r,pI:()=>i,vX:()=>s});class a{isValid(){return!this.rules.some(e=>!e.isValid)}getErrors(){return this.rules.filter(e=>!e.isValid).map(e=>e.message)}getFirstError(){let e=this.rules.find(e=>!e.isValid);return null==e?void 0:e.message}static required(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"This field is required";return{isValid:null!=e&&""!==e&&0!==e,message:t}}static minValue(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at least ".concat(t);return{isValid:!isNaN(e)&&e>=t,message:n}}static maxValue(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at most ".concat(t);return{isValid:!isNaN(e)&&e<=t,message:n}}static pattern(e,t,n){return{isValid:t.test(e||""),message:n}}static email(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Must be a valid email";return this.pattern(e,/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,t)}static url(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Must be a valid URL";return this.pattern(e,/^https?:\/\/.+/,t)}static minLength(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at least ".concat(t," characters");return{isValid:(e||"").length>=t,message:n}}static maxLength(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at most ".concat(t," characters");return{isValid:(e||"").length<=t,message:n}}static arrayMinLength(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must have at least ".concat(t," items");return{isValid:(null==e?void 0:e.length)>=t,message:n}}static arrayMaxLength(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must have at most ".concat(t," items");return{isValid:(null==e?void 0:e.length)<=t,message:n}}static arrayUnique(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Must have unique items";return{isValid:(null==e?void 0:e.length)===new Set(e).size,message:t}}static arraysEqual(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be equal";return{isValid:(null==e?void 0:e.length)===(null==t?void 0:t.length)&&(null==e?void 0:e.every((e,n)=>e===t[n])),message:n}}static custom(e,t){return{isValid:e,message:t}}static all(e){return e.find(e=>!e.isValid)||{isValid:!0,message:""}}constructor(e){this.rules=e.filter(e=>void 0!==e)}}function s(e){return!!e&&(!!e.startsWith("env.")||32===e.length&&e.substring(4,28)==="*".repeat(24)||!!(e.length<=8&&/^\*+$/.test(e)))}function i(e){if(!e||!e.trim())return!1;if(s(e))return!0;if(e.startsWith("env."))return e.length>4;try{let t=JSON.parse(e);return"object"==typeof t&&null!==t&&"service_account"===t.type&&t.project_id&&t.private_key}catch(e){return!1}}function r(e){if(!e)return!1;if("object"==typeof e)return Object.keys(e).length>0;if("string"==typeof e){if(s(e))return!0;try{let t=JSON.parse(e);return"object"==typeof t&&null!==t&&Object.keys(t).length>0}catch(e){}}return!1}},2525:(e,t,n)=>{"use strict";n.d(t,{A:()=>a});let a=(0,n(9946).A)("trash-2",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]])},3557:(e,t,n)=>{"use strict";n.d(t,{Ob:()=>s,w8:()=>a});let a=e=>e.split(",").map(e=>e.trim()).filter(e=>e.length>0),s=(e,t)=>(null==e?void 0:e.length)===(null==t?void 0:t.length)&&(null==e?void 0:e.every((e,n)=>e===t[n]))},3580:(e,t,n)=>{"use strict";n.d(t,{d:()=>s});var a=n(6671);function s(){return{toast:e=>{let{title:t,description:n,variant:s}=e,i=n?"".concat(t,": ").concat(n):t;"destructive"===s?a.o.error(i):a.o.success(i)}}}},4616:(e,t,n)=>{"use strict";n.d(t,{A:()=>a});let a=(0,n(9946).A)("plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]])},6034:(e,t,n)=>{"use strict";n.d(t,{Dk:()=>i,mR:()=>a,qV:()=>s});let a={base_url:"",default_request_timeout_in_seconds:30,max_retries:0,retry_backoff_initial:1e3,retry_backoff_max:1e4},s={concurrency:10,buffer_size:100},i={connected:"bg-green-100 text-green-800",error:"bg-red-100 text-red-800",disconnected:"bg-gray-100 text-gray-800"}},6559:(e,t,n)=>{"use strict";n.d(t,{$v:()=>x,EO:()=>u,Lt:()=>l,Rx:()=>g,Zr:()=>v,ck:()=>m,r7:()=>p,tv:()=>o,wd:()=>h});var a=n(5155);n(2115);var s=n(7649),i=n(3999),r=n(7168);function l(e){let{...t}=e;return(0,a.jsx)(s.bL,{"data-slot":"alert-dialog",...t})}function o(e){let{...t}=e;return(0,a.jsx)(s.l9,{"data-slot":"alert-dialog-trigger",...t})}function c(e){let{...t}=e;return(0,a.jsx)(s.ZL,{"data-slot":"alert-dialog-portal",...t})}function d(e){let{className:t,...n}=e;return(0,a.jsx)(s.hJ,{"data-slot":"alert-dialog-overlay",className:(0,i.cn)("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",t),...n})}function u(e){let{className:t,...n}=e;return(0,a.jsxs)(c,{children:[(0,a.jsx)(d,{}),(0,a.jsx)(s.UC,{"data-slot":"alert-dialog-content",className:(0,i.cn)("bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",t),...n})]})}function h(e){let{className:t,...n}=e;return(0,a.jsx)("div",{"data-slot":"alert-dialog-header",className:(0,i.cn)("flex flex-col gap-2 text-center sm:text-left",t),...n})}function m(e){let{className:t,...n}=e;return(0,a.jsx)("div",{"data-slot":"alert-dialog-footer",className:(0,i.cn)("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",t),...n})}function p(e){let{className:t,...n}=e;return(0,a.jsx)(s.hE,{"data-slot":"alert-dialog-title",className:(0,i.cn)("text-lg font-semibold",t),...n})}function x(e){let{className:t,...n}=e;return(0,a.jsx)(s.VY,{"data-slot":"alert-dialog-description",className:(0,i.cn)("text-muted-foreground text-sm",t),...n})}function g(e){let{className:t,...n}=e;return(0,a.jsx)(s.rc,{className:(0,i.cn)((0,r.r)({variant:"destructive"}),t),...n})}function v(e){let{className:t,...n}=e;return(0,a.jsx)(s.ZD,{className:(0,i.cn)((0,r.r)({variant:"outline"}),t),...n})}},7649:(e,t,n)=>{"use strict";n.d(t,{UC:()=>T,VY:()=>Z,ZD:()=>P,ZL:()=>L,bL:()=>E,hE:()=>z,hJ:()=>R,l9:()=>V,rc:()=>O});var a=n(2115),s=n(6081),i=n(6101),r=n(5452),l=n(5185),o=n(9708),c=n(5155),d="AlertDialog",[u,h]=(0,s.A)(d,[r.Hs]),m=(0,r.Hs)(),p=e=>{let{__scopeAlertDialog:t,...n}=e,a=m(t);return(0,c.jsx)(r.bL,{...a,...n,modal:!0})};p.displayName=d;var x=a.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...a}=e,s=m(n);return(0,c.jsx)(r.l9,{...s,...a,ref:t})});x.displayName="AlertDialogTrigger";var g=e=>{let{__scopeAlertDialog:t,...n}=e,a=m(t);return(0,c.jsx)(r.ZL,{...a,...n})};g.displayName="AlertDialogPortal";var v=a.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...a}=e,s=m(n);return(0,c.jsx)(r.hJ,{...s,...a,ref:t})});v.displayName="AlertDialogOverlay";var f="AlertDialogContent",[j,y]=u(f),_=(0,o.Dc)("AlertDialogContent"),b=a.forwardRef((e,t)=>{let{__scopeAlertDialog:n,children:s,...o}=e,d=m(n),u=a.useRef(null),h=(0,i.s)(t,u),p=a.useRef(null);return(0,c.jsx)(r.G$,{contentName:f,titleName:C,docsSlug:"alert-dialog",children:(0,c.jsx)(j,{scope:n,cancelRef:p,children:(0,c.jsxs)(r.UC,{role:"alertdialog",...d,...o,ref:h,onOpenAutoFocus:(0,l.m)(o.onOpenAutoFocus,e=>{var t;e.preventDefault(),null==(t=p.current)||t.focus({preventScroll:!0})}),onPointerDownOutside:e=>e.preventDefault(),onInteractOutside:e=>e.preventDefault(),children:[(0,c.jsx)(_,{children:s}),(0,c.jsx)(S,{contentRef:u})]})})})});b.displayName=f;var C="AlertDialogTitle",w=a.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...a}=e,s=m(n);return(0,c.jsx)(r.hE,{...s,...a,ref:t})});w.displayName=C;var N="AlertDialogDescription",k=a.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...a}=e,s=m(n);return(0,c.jsx)(r.VY,{...s,...a,ref:t})});k.displayName=N;var A=a.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...a}=e,s=m(n);return(0,c.jsx)(r.bm,{...s,...a,ref:t})});A.displayName="AlertDialogAction";var D="AlertDialogCancel",M=a.forwardRef((e,t)=>{let{__scopeAlertDialog:n,...a}=e,{cancelRef:s}=y(D,n),l=m(n),o=(0,i.s)(t,s);return(0,c.jsx)(r.bm,{...l,...a,ref:o})});M.displayName=D;var S=e=>{let{contentRef:t}=e,n="`".concat(f,"` requires a description for the component to be accessible for screen reader users.\n\nYou can add a description to the `").concat(f,"` by passing a `").concat(N,"` component as a child, which also benefits sighted users by adding visible context to the dialog.\n\nAlternatively, you can use your own component as a description by assigning it an `id` and passing the same value to the `aria-describedby` prop in `").concat(f,"`. If the description is confusing or duplicative for sighted users, you can use the `@radix-ui/react-visually-hidden` primitive as a wrapper around your description component.\n\nFor more information, see https://radix-ui.com/primitives/docs/components/alert-dialog");return a.useEffect(()=>{var e;document.getElementById(null==(e=t.current)?void 0:e.getAttribute("aria-describedby"))||console.warn(n)},[n,t]),null},E=p,V=x,L=g,R=v,T=b,O=A,P=M,z=w,Z=k},7765:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>R});var a=n(5155),s=n(7168),i=n(9840),r=n(9852),l=n(2115),o=n(3655),c=l.forwardRef((e,t)=>(0,a.jsx)(o.sG.label,{...e,ref:t,onMouseDown:t=>{var n;t.target.closest("button, input, select, textarea")||(null==(n=e.onMouseDown)||n.call(e,t),!t.defaultPrevented&&t.detail>1&&t.preventDefault())}}));c.displayName="Label";var d=n(3999);function u(e){let{className:t,...n}=e;return(0,a.jsx)(c,{"data-slot":"label",className:(0,d.cn)("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",t),...n})}var h=n(5784),m=n(9474),p=n(7777),x=n(3580),g=n(1886),v=n(3557),f=n(2067),j=n(1284);let y={name:"",connection_type:"http",connection_string:"",stdio_config:{command:"",args:[],envs:[]},tools_to_skip:[],tools_to_execute:[]},_=e=>{var t,n,o,c,d,_;let{client:b,open:C,onClose:w,onSaved:N}=e,[k,A]=(0,l.useState)(y),[D,M]=(0,l.useState)(!1),[S,E]=(0,l.useState)(""),[V,L]=(0,l.useState)(""),[R,T]=(0,l.useState)(""),[O,P]=(0,l.useState)(""),{toast:z}=(0,x.d)();(0,l.useEffect)(()=>{if(b){var e,t,n,a,s;A({name:b.name,connection_type:b.config.connection_type,connection_string:b.config.connection_string||"",stdio_config:{command:(null==(e=b.config.stdio_config)?void 0:e.command)||"",args:(null==(t=b.config.stdio_config)?void 0:t.args)||[],envs:(null==(n=b.config.stdio_config)?void 0:n.envs)||[]},tools_to_skip:b.config.tools_to_skip||[],tools_to_execute:b.config.tools_to_execute||[]}),E(((null==(a=b.config.stdio_config)?void 0:a.args)||[]).join(", ")),L(((null==(s=b.config.stdio_config)?void 0:s.envs)||[]).join(", ")),T((b.config.tools_to_skip||[]).join(", ")),P((b.config.tools_to_execute||[]).join(", "))}else A(y),E(""),L(""),T(""),P("")},[b]);let Z=(e,t)=>{A(n=>({...n,[e]:t}))},$=(e,t)=>{A(n=>({...n,stdio_config:{command:"",args:[],envs:[],...n.stdio_config||{},[e]:t}}))},q=new f.Dr([f.Dr.required(null==(t=k.name)?void 0:t.trim(),"Client name is required"),f.Dr.pattern(k.name||"",/^[a-zA-Z0-9-_]+$/,"Client name can only contain letters, numbers, hyphens and underscores"),f.Dr.minLength(k.name||"",3,"Client name must be at least 3 characters"),f.Dr.maxLength(k.name||"",50,"Client name cannot exceed 50 characters"),...("http"===k.connection_type||"sse"===k.connection_type)&&!b?[f.Dr.required(null==(n=k.connection_string)?void 0:n.trim(),"Connection URL is required"),f.Dr.pattern(k.connection_string||"",/^(http:\/\/|https:\/\/|env\.[A-Z_]+$)/,"Connection URL must start with http://, https://, or be an environment variable (env.VAR_NAME)")]:[],..."stdio"===k.connection_type&&!b?[f.Dr.required(null==(c=k.stdio_config)||null==(o=c.command)?void 0:o.trim(),"Command is required for STDIO connections"),...!b?[f.Dr.pattern((null==(d=k.stdio_config)?void 0:d.command)||"",/^[^<>|&;]+$/,"Command cannot contain special shell characters")]:[]]:[],...O.trim()?[f.Dr.pattern(O,/^[a-zA-Z0-9_,\s-]+$/,"Tools to execute can only contain letters, numbers, underscores, and commas")]:[],...R.trim()?[f.Dr.pattern(R,/^[a-zA-Z0-9_,\s-]+$/,"Tools to skip can only contain letters, numbers, underscores, and commas")]:[],...b?[f.Dr.custom(!(0,v.Ob)(k.tools_to_execute||[],(0,v.w8)(O))||!(0,v.Ob)(k.tools_to_skip||[],(0,v.w8)(R)),"No changes to save")]:[],f.Dr.custom(!((e,t)=>{let n=new Set((0,v.w8)(e)),a=new Set((0,v.w8)(t));return Array.from(n).some(e=>a.has(e))})(O,R),"Tools cannot appear in both execute and skip lists")]),I=async()=>{var e;M(!0);let t=null,n={...k,stdio_config:"stdio"===k.connection_type?{command:(null==(e=k.stdio_config)?void 0:e.command)||"",args:(0,v.w8)(S),envs:(0,v.w8)(V)}:void 0,tools_to_skip:(0,v.w8)(R),tools_to_execute:(0,v.w8)(O)};if(b){let e={tools_to_execute:n.tools_to_execute,tools_to_skip:n.tools_to_skip};[,t]=await g.K.updateMCPClient(b.name,e)}else[,t]=await g.K.createMCPClient(n);M(!1),t?z({title:"Error",description:t,variant:"destructive"}):(z({title:"Success",description:b?"Client updated":"Client created"}),N(),w())};return(0,a.jsx)(i.lG,{open:C,onOpenChange:w,children:(0,a.jsxs)(i.Cf,{className:"max-h-[90vh] max-w-2xl overflow-y-auto",children:[(0,a.jsx)(i.c7,{children:(0,a.jsx)(i.L3,{children:b?"Edit MCP Client Tools":"New MCP Client"})}),(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(u,{children:"Name"}),(0,a.jsx)(r.p,{value:k.name,onChange:e=>Z("name",e.target.value),placeholder:"Client name",disabled:!!b})]}),!b&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"w-full space-y-2",children:[(0,a.jsx)(u,{children:"Connection Type"}),(0,a.jsxs)(h.l6,{value:k.connection_type,onValueChange:e=>Z("connection_type",e),children:[(0,a.jsx)(h.bq,{className:"w-full",children:(0,a.jsx)(h.yv,{placeholder:"Select connection type"})}),(0,a.jsxs)(h.gC,{children:[(0,a.jsx)(h.eb,{value:"http",children:"HTTP (Streamable)"}),(0,a.jsx)(h.eb,{value:"sse",children:"Server-Sent Events (SSE)"}),(0,a.jsx)(h.eb,{value:"stdio",children:"STDIO"})]})]})]}),("http"===k.connection_type||"sse"===k.connection_type)&&(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsxs)("div",{className:"flex w-fit items-center gap-1",children:[(0,a.jsx)(u,{children:"Connection URL"}),(0,a.jsx)(p.Bc,{children:(0,a.jsxs)(p.m_,{children:[(0,a.jsx)(p.k$,{asChild:!0,children:(0,a.jsx)("span",{children:(0,a.jsx)(j.A,{className:"text-muted-foreground ml-1 h-3 w-3"})})}),(0,a.jsx)(p.ZI,{className:"max-w-fit",children:(0,a.jsxs)("p",{children:["Use ",(0,a.jsx)("code",{className:"rounded bg-neutral-100 px-1 py-0.5 text-neutral-800",children:"env."})," to read the value from an environment variable."]})})]})})]}),(0,a.jsx)(r.p,{value:k.connection_string||"",onChange:e=>Z("connection_string",e.target.value),placeholder:"http://your-mcp-server:3000 or env.MCP_SERVER_URL"})]}),"stdio"===k.connection_type&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(u,{children:"Command"}),(0,a.jsx)(r.p,{value:(null==(_=k.stdio_config)?void 0:_.command)||"",onChange:e=>$("command",e.target.value),placeholder:"node, python, /path/to/executable"})]}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(u,{children:"Arguments (comma-separated)"}),(0,a.jsx)(r.p,{value:S,onChange:e=>E(e.target.value),placeholder:"--port, 3000, --config, config.json"})]}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(u,{children:"Environment Variables (comma-separated)"}),(0,a.jsx)(r.p,{value:V,onChange:e=>L(e.target.value),placeholder:"API_KEY, DATABASE_URL"})]})]})]}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(u,{children:"Tools to Execute (comma-separated, leave empty for all)"}),(0,a.jsx)(m.T,{value:O,onChange:e=>P(e.target.value),placeholder:"tool1, tool2, tool3",rows:2})]}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(u,{children:"Tools to Skip (comma-separated)"}),(0,a.jsx)(m.T,{value:R,onChange:e=>T(e.target.value),placeholder:"skipTool1, skipTool2",rows:2})]})]}),(0,a.jsxs)(i.Es,{children:[(0,a.jsx)(s.$,{variant:"outline",onClick:w,disabled:D,children:"Cancel"}),(0,a.jsx)(p.Bc,{children:(0,a.jsxs)(p.m_,{children:[(0,a.jsx)(p.k$,{asChild:!0,children:(0,a.jsx)("span",{children:(0,a.jsx)(s.$,{onClick:I,disabled:!q.isValid()||D,isLoading:D,children:b?"Save":"Create"})})}),!q.isValid()&&(0,a.jsx)(p.ZI,{children:q.getFirstError()||"Please fix validation errors"})]})})]})]})})};var b=n(8145),C=n(8482),w=n(8524),N=n(6034),k=n(4616),A=n(9946);let D=(0,A.A)("refresh-ccw",[["path",{d:"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"14sxne"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16",key:"1hlbsb"}],["path",{d:"M16 16h5v5",key:"ccwih5"}]]),M=(0,A.A)("pencil",[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}],["path",{d:"m15 5 4 4",key:"1mk7zo"}]]);var S=n(2525),E=n(6559);function V(e){let{mcpClients:t}=e,[n,i]=(0,l.useState)(null),[r,o]=(0,l.useState)(t),[c,d]=(0,l.useState)(!1),{toast:u}=(0,x.d)(),h=async()=>{let[e,t]=await g.K.getMCPClients();t?u({title:"Error",description:t,variant:"destructive"}):o(e||[])};(0,l.useEffect)(()=>{h()},[]);let m=e=>{i(e),d(!0)},p=async e=>{let[,t]=await g.K.reconnectMCPClient(e.name);t?u({title:"Error",description:t,variant:"destructive"}):(u({title:"Reconnected",description:"Client reconnected."}),h())},v=async e=>{let[,t]=await g.K.deleteMCPClient(e.name);t?u({title:"Error",description:t,variant:"destructive"}):(u({title:"Deleted",description:"Client removed."}),h())},f=e=>{if("stdio"===e.config.connection_type){var t,n;return"".concat(null==(t=e.config.stdio_config)?void 0:t.command," ").concat(null==(n=e.config.stdio_config)?void 0:n.args.join(" "))||"STDIO"}return e.config.connection_string||"".concat(e.config.connection_type.toUpperCase())},j=e=>{switch(e){case"http":return"HTTP";case"sse":return"SSE";case"stdio":return"STDIO";default:return e.toUpperCase()}};return(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)(C.aR,{className:"mb-4 px-0",children:[(0,a.jsxs)(C.ZB,{className:"flex items-center justify-between",children:[(0,a.jsx)("div",{className:"flex items-center gap-2",children:"Registered MCP Clients"}),(0,a.jsxs)(s.$,{onClick:()=>{i(null),d(!0)},children:[(0,a.jsx)(k.A,{className:"h-4 w-4"})," New MCP Client"]})]}),(0,a.jsx)(C.BT,{children:"Manage clients that can connect to the MCP Tools endpoint."})]}),(0,a.jsx)("div",{className:"rounded-md border",children:(0,a.jsxs)(w.XI,{children:[(0,a.jsx)(w.A0,{children:(0,a.jsxs)(w.Hj,{children:[(0,a.jsx)(w.nd,{children:"Name"}),(0,a.jsx)(w.nd,{children:"Connection Type"}),(0,a.jsx)(w.nd,{children:"Connection Info"}),(0,a.jsx)(w.nd,{children:"State"}),(0,a.jsx)(w.nd,{className:"text-right",children:"Actions"})]})}),(0,a.jsxs)(w.BF,{children:[0===r.length&&(0,a.jsx)(w.Hj,{children:(0,a.jsx)(w.nA,{colSpan:5,className:"py-6 text-center",children:"No clients found."})}),r.map(e=>(0,a.jsxs)(w.Hj,{children:[(0,a.jsx)(w.nA,{className:"font-medium",children:e.name}),(0,a.jsx)(w.nA,{children:j(e.config.connection_type)}),(0,a.jsx)(w.nA,{className:"max-w-72 overflow-hidden text-ellipsis whitespace-nowrap",children:f(e)}),(0,a.jsx)(w.nA,{children:(0,a.jsx)(b.E,{className:N.Dk[e.state],children:e.state})}),(0,a.jsxs)(w.nA,{className:"space-x-2 text-right",children:["disconnected"===e.state?(0,a.jsx)(s.$,{variant:"ghost",size:"icon",onClick:()=>p(e),children:(0,a.jsx)(D,{className:"h-4 w-4"})}):"connected"===e.state&&(0,a.jsx)(s.$,{variant:"ghost",size:"icon",onClick:()=>m(e),children:(0,a.jsx)(M,{className:"h-4 w-4"})}),(0,a.jsxs)(E.Lt,{children:[(0,a.jsx)(E.tv,{asChild:!0,children:(0,a.jsx)(s.$,{variant:"ghost",size:"icon",disabled:"error"===e.state,children:(0,a.jsx)(S.A,{className:"h-4 w-4"})})}),(0,a.jsxs)(E.EO,{children:[(0,a.jsxs)(E.wd,{children:[(0,a.jsx)(E.r7,{children:"Remove MCP Client"}),(0,a.jsxs)(E.$v,{children:["Are you sure you want to remove MCP client ",e.name,"? You will need to reconnect the client to continue using it."]})]}),(0,a.jsxs)(E.ck,{children:[(0,a.jsx)(E.Zr,{children:"Cancel"}),(0,a.jsx)(E.Rx,{onClick:()=>v(e),children:"Delete"})]})]})]})]})]},e.name))]})]})}),c&&(0,a.jsx)(_,{open:c,client:n,onClose:()=>d(!1),onSaved:()=>{d(!1),h()}})]})}var L=n(2384);function R(){let[e,t]=(0,l.useState)([]),[n,s]=(0,l.useState)(!0),{toast:i}=(0,x.d)();(0,l.useEffect)(()=>{r()},[]);let r=async()=>{let[e,n]=await g.K.getMCPClients();if(s(!1),n)return void i({title:"Error",description:n,variant:"destructive"});t(e||[])};return(0,a.jsx)("div",{children:n?(0,a.jsx)(L.A,{}):(0,a.jsx)(V,{mcpClients:e})})}},7777:(e,t,n)=>{"use strict";n.d(t,{Bc:()=>r,ZI:()=>c,k$:()=>o,m_:()=>l});var a=n(5155);n(2115);var s=n(9613),i=n(3999);function r(e){let{delayDuration:t=0,...n}=e;return(0,a.jsx)(s.Kq,{"data-slot":"tooltip-provider",delayDuration:t,...n})}function l(e){let{...t}=e;return(0,a.jsx)(r,{children:(0,a.jsx)(s.bL,{"data-slot":"tooltip",...t})})}function o(e){let{...t}=e;return(0,a.jsx)(s.l9,{"data-slot":"tooltip-trigger",...t})}function c(e){let{className:t,sideOffset:n=0,children:r,...l}=e;return(0,a.jsx)(s.ZL,{children:(0,a.jsxs)(s.UC,{"data-slot":"tooltip-content",sideOffset:n,className:(0,i.cn)("bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",t),...l,children:[r,(0,a.jsx)(s.i3,{className:"bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]"})]})})}},8396:(e,t,n)=>{Promise.resolve().then(n.bind(n,7765))},9474:(e,t,n)=>{"use strict";n.d(t,{T:()=>i});var a=n(5155);n(2115);var s=n(3999);function i(e){let{className:t,...n}=e;return(0,a.jsx)("textarea",{"data-slot":"textarea",className:(0,s.cn)("border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",t),...n})}}},e=>{var t=t=>e(e.s=t);e.O(0,[106,189,944,231,412,62,613,915,441,684,358],()=>t(8396)),_N_E=e.O()}]); \ No newline at end of file diff --git a/transports/bifrost-http/ui/_next/static/chunks/app/providers/page-b25fa0ce16423da1.js b/transports/bifrost-http/ui/_next/static/chunks/app/providers/page-b25fa0ce16423da1.js deleted file mode 100644 index a3a70d3a63..0000000000 --- a/transports/bifrost-http/ui/_next/static/chunks/app/providers/page-b25fa0ce16423da1.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[387],{2067:(e,s,t)=>{"use strict";t.d(s,{Dr:()=>a});class a{isValid(){return!this.rules.some(e=>!e.isValid)}getErrors(){return this.rules.filter(e=>!e.isValid).map(e=>e.message)}getFirstError(){let e=this.rules.find(e=>!e.isValid);return null==e?void 0:e.message}static required(e){let s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"This field is required";return{isValid:null!=e&&""!==e&&0!==e,message:s}}static minValue(e,s){let t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at least ".concat(s);return{isValid:!isNaN(e)&&e>=s,message:t}}static maxValue(e,s){let t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at most ".concat(s);return{isValid:!isNaN(e)&&e<=s,message:t}}static pattern(e,s,t){return{isValid:s.test(e||""),message:t}}static email(e){let s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Must be a valid email";return this.pattern(e,/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,s)}static url(e){let s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Must be a valid URL";return this.pattern(e,/^https?:\/\/.+/,s)}static minLength(e,s){let t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at least ".concat(s," characters");return{isValid:(e||"").length>=s,message:t}}static maxLength(e,s){let t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at most ".concat(s," characters");return{isValid:(e||"").length<=s,message:t}}static arrayMinLength(e,s){let t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must have at least ".concat(s," items");return{isValid:(null==e?void 0:e.length)>=s,message:t}}static arrayMaxLength(e,s){let t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must have at most ".concat(s," items");return{isValid:(null==e?void 0:e.length)<=s,message:t}}static arrayUnique(e){let s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Must have unique items";return{isValid:(null==e?void 0:e.length)===new Set(e).size,message:s}}static arraysEqual(e,s){let t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be equal";return{isValid:(null==e?void 0:e.length)===(null==s?void 0:s.length)&&(null==e?void 0:e.every((e,t)=>e===s[t])),message:t}}static custom(e,s){return{isValid:e,message:s}}static all(e){return e.find(e=>!e.isValid)||{isValid:!0,message:""}}constructor(e){this.rules=e.filter(e=>void 0!==e)}}},3580:(e,s,t)=>{"use strict";t.d(s,{d:()=>r});var a=t(6671);function r(){return{toast:e=>{let{title:s,description:t,variant:r}=e,n=t?"".concat(s,": ").concat(t):s;"destructive"===r?a.o.error(n):a.o.success(n)}}}},3621:(e,s,t)=>{"use strict";t.r(s),t.d(s,{default:()=>F});var a=t(5155),r=t(6559),n=t(8145),i=t(7168),l=t(8524),d=t(1886),o=t(4432),c=t(7783),u=t(4616),m=t(9803),x=t(3717),h=t(1154),p=t(2525),f=t(2115),v=t(6671),g=t(8482),j=t(9840),y=t(9852),b=t(5784),N=t(4964),_=t(4416),w=t(3999);let k=f.forwardRef((e,s)=>{let{className:t,value:r,onValueChange:i,...l}=e,[d,o]=f.useState(""),c=e=>{i(r.filter(s=>s!==e))};return(0,a.jsxs)("div",{className:(0,w.cn)("border-input flex flex-wrap items-center gap-2 rounded-md border p-2",t),children:[r.map(e=>(0,a.jsxs)(n.E,{variant:"secondary",className:"flex items-center gap-1",children:[e,(0,a.jsx)("button",{type:"button",className:"ring-offset-background focus:ring-ring cursor-pointer rounded-full outline-none focus:ring-2 focus:ring-offset-2",onClick:()=>c(e),children:(0,a.jsx)(_.A,{className:"h-3 w-3"})})]},e)),(0,a.jsx)(y.p,{ref:s,type:"text",value:d,onChange:e=>{o(e.target.value)},onKeyDown:e=>{if("Enter"===e.key||","===e.key){e.preventDefault();let s=d.trim();s&&!r.includes(s)&&i([...r,s]),o("")}else"Backspace"===e.key&&""===d&&r.length>0&&i(r.slice(0,-1))},className:"flex-1 border-0 shadow-none focus-visible:ring-0",...l})]})});k.displayName="TagInput";var C=t(7777),A=t(6034),z=t(2067),P=t(9231),V=t.n(P),D=t(1284),q=t(4869),I=t(1539),S=t(1243),E=t(4229),R=t(9026),O=t(9474),M=t(8103);let B={azure:{title:"Azure OpenAI Meta Config",fields:[{name:"endpoint",label:"Endpoint",type:"text",placeholder:"https://your-resource.openai.azure.com or env.AZURE_ENDPOINT"},{name:"api_version",label:"API Version (Optional)",type:"text",placeholder:"YYYY-MM-DD or env.AZURE_VERSION"},{name:"deployments",label:"Deployments (JSON format)",type:"textarea",placeholder:'{ "gpt-4": "my-deployment" }',isJson:!0}]},bedrock:{title:"AWS Bedrock Meta Config",fields:[{name:"region",label:"Region",type:"text",placeholder:"us-east-1 or env.AWS_REGION"}]},vertex:{title:"Google Vertex AI Meta Config",fields:[{name:"project_id",label:"Project ID",type:"text",placeholder:"gcp-project-id or env.GCP_PROJECT"},{name:"region",label:"Region",type:"text",placeholder:"us-central1 or env.GCP_REGION"},{name:"auth_credentials",label:"Auth Credentials (JSON key)",type:"textarea",placeholder:"JSON key or env.GCP_CREDS"}]}},Z=e=>{let{provider:s,metaConfig:t,onMetaConfigChange:r}=e,n=B[s];if(!n)return null;let i=e=>{let s=t[e.name];return"textarea"===e.type?(0,a.jsx)(O.T,{placeholder:e.placeholder,value:e.isJson?"string"==typeof s?s:JSON.stringify(s,null,2):s||"",onChange:s=>{r(e.name,s.target.value)},onBlur:s=>{if(e.isJson)try{let t=JSON.parse(s.target.value);r(e.name,t)}catch(e){}},rows:4,className:"wrap-anywhere max-w-full font-mono text-sm"}):(0,a.jsx)(y.p,{placeholder:e.placeholder,value:s||"",onChange:s=>r(e.name,s.target.value)})};return(0,a.jsxs)("div",{className:"",children:[(0,a.jsx)(g.aR,{className:"mb-2 px-0",children:(0,a.jsxs)(g.ZB,{className:"flex items-center gap-2 text-base",children:[(0,a.jsx)(M.A,{className:"h-4 w-4"}),n.title,(0,a.jsx)(C.Bc,{children:(0,a.jsxs)(C.m_,{children:[(0,a.jsx)(C.k$,{asChild:!0,children:(0,a.jsx)("span",{children:(0,a.jsx)(D.A,{className:"text-muted-foreground ml-1 h-3 w-3"})})}),(0,a.jsx)(C.ZI,{className:"max-w-fit",children:(0,a.jsxs)("p",{children:["Use ",(0,a.jsx)("code",{className:"rounded bg-neutral-100 px-1 py-0.5 text-neutral-800",children:"env."})," to read the value from an environment variable."]})})]})})]})}),(0,a.jsx)(g.Wu,{className:"space-y-4 px-0",children:n.fields.map(e=>(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"block text-sm font-medium",children:e.label}),i(e)]},e.name))})]})},L=(e,s)=>{let t=!e,a=(null==e?void 0:e.name)||s||"",r=!["vertex","ollama","sgl"].includes(a);return{selectedProvider:a,keys:t&&r?[{value:"",models:[],weight:1}]:!t&&r&&(null==e?void 0:e.keys)?e.keys:[],networkConfig:(null==e?void 0:e.network_config)||A.mR,performanceConfig:(null==e?void 0:e.concurrency_and_buffer_size)||A.qV,metaConfig:(null==e?void 0:e.meta_config)||{endpoint:"",deployments:{},api_version:""},proxyConfig:(null==e?void 0:e.proxy_config)||{type:"none",url:"",username:"",password:""}}};function U(e){var s,t;let{provider:r,onSave:n,onCancel:l,existingProviders:m}=e,x=r?void 0:c.xq.find(e=>!m.includes(e))||"",[h]=(0,f.useState)(L(r,x)),[p,g]=(0,f.useState)({...h,isDirty:!1}),[A,P]=(0,f.useState)(!1),{selectedProvider:O,keys:M,networkConfig:B,performanceConfig:U,metaConfig:$,proxyConfig:K,isDirty:T}=p,F="ollama"===O||"sgl"===O,J=!["vertex","ollama","sgl"].includes(O);J&&M.every(e=>""!==e.value.trim()),J&&M.length;let H=U.concurrency>0&&U.buffer_size>0&&U.concurrency{let e=!0,s="";if("azure"===O){let t=!!$.endpoint&&""!==$.endpoint.trim(),a=!!($.deployments&&"object"==typeof $.deployments&&Object.keys($.deployments).length>0);(e=t&&a)||(s="Endpoint and at least one Deployment are required for Azure")}else if("bedrock"===O)(e=!!$.region&&""!==$.region.trim())||(s="Region is required for AWS Bedrock");else if("vertex"===O){let t=!!$.project_id&&""!==$.project_id.trim(),a=!!$.auth_credentials&&""!==$.auth_credentials.trim(),r=!!$.region&&""!==$.region.trim();(e=t&&a&&r)||(s="Project ID, Auth Credentials, and Region are required for Vertex AI")}return{valid:e,message:s}})();(0,f.useEffect)(()=>{let e={selectedProvider:O,keys:J?M:[],networkConfig:B,performanceConfig:U,metaConfig:$,proxyConfig:K};g(s=>({...s,isDirty:!V()(h,e)}))},[O,M,B,U,$,K,h,J]);let X=(e,s)=>{g(t=>({...t,[e]:s}))},Q=(e,s)=>{X("proxyConfig",{...K,[e]:s})},ee=r?c.xq:c.xq.filter(e=>!m.includes(e)),es=async e=>{if(!et.isValid())return void v.o.error(et.getFirstError());e.preventDefault(),P(!0);let s=null;if(r){let e={keys:J?M.filter(e=>""!==e.value.trim()):[],network_config:B,concurrency_and_buffer_size:U,meta_config:$,proxy_config:K};[,s]=await d.K.updateProvider(r.name,e)}else{let e={provider:O,keys:J?M.filter(e=>""!==e.value.trim()):[],network_config:B,concurrency_and_buffer_size:U,meta_config:$,proxy_config:K};[,s]=await d.K.createProvider(e)}P(!1),s?v.o.error(s):(v.o.success("Provider ".concat(r?"updated":"added"," successfully")),n())},et=new z.Dr([z.Dr.required(O,"Please select a provider"),z.Dr.custom(T,"No changes to save"),...F?[z.Dr.required(B.base_url,"Base URL is required for Ollama provider"),z.Dr.pattern(B.base_url||"",/^https?:\/\/.+/,"Base URL must start with http:// or https://")]:[],...J?[z.Dr.minValue(M.length,1,"At least one API key is required"),z.Dr.custom(M.every(e=>""!==e.value.trim()),"API key value cannot be empty")]:[],z.Dr.minValue(B.default_request_timeout_in_seconds,1,"Timeout must be greater than 0 seconds"),z.Dr.minValue(B.max_retries,0,"Max retries cannot be negative"),z.Dr.minValue(U.concurrency,1,"Concurrency must be greater than 0"),z.Dr.minValue(U.buffer_size,1,"Buffer size must be greater than 0"),z.Dr.custom(U.concurrency{X("keys",M.filter((s,t)=>t!==e))},er=(e,s,t)=>{let a=[...M],r={...a[e]};"models"===s&&Array.isArray(t)?r.models=t:"value"===s&&"string"==typeof t?r.value=t:"weight"===s&&"string"==typeof t&&(r.weight=Number.parseFloat(t)||1),a[e]=r,X("keys",a)},en=(0,f.useMemo)(()=>{let e=[];return J&&e.push({id:"api-keys",label:"API Keys"}),("azure"===O||"bedrock"===O||"vertex"===O)&&e.push({id:"meta-config",label:"Meta Config"}),e.push({id:"network",label:"Network"}),e.push({id:"performance",label:"Performance"}),e},[J,O]),[ei,el]=(0,f.useState)((null==(s=en[0])?void 0:s.id)||"api-keys");return(0,f.useEffect)(()=>{if(!en.map(e=>e.id).includes(ei)){var e;el((null==(e=en[0])?void 0:e.id)||"api-keys")}},[en]),(0,a.jsx)(j.lG,{open:!0,onOpenChange:l,children:(0,a.jsxs)(j.Cf,{className:"custom-scrollbar max-h-[90vh] overflow-y-auto p-0 sm:max-w-4xl",showCloseButton:!1,children:[(0,a.jsxs)(j.c7,{className:"z-10 px-6 pt-6",children:[(0,a.jsx)(j.L3,{children:r?(0,a.jsxs)("div",{className:"flex items-center gap-2",children:[(0,o.FO)(r.name,{size:20}),(0,a.jsx)("span",{className:"font-semibold",children:c.oU[r.name]})]}):(0,a.jsx)("div",{className:"flex items-center gap-2",children:"Add Provider"})}),(0,a.jsx)(j.rr,{children:"Configure AI provider settings, API keys, and network options."})]}),(0,a.jsxs)("form",{onSubmit:es,className:"flex flex-row gap-2 px-6 pt-6",children:[!r&&(0,a.jsx)(C.Bc,{children:(0,a.jsx)("div",{className:"flex w-[250px] flex-col gap-1 pb-10",children:c.xq.map(e=>(0,a.jsxs)(C.m_,{children:[(0,a.jsx)(C.k$,{className:(0,w.cn)("flex w-full items-center gap-2 rounded-lg border px-4 py-2 text-sm transition-all duration-200 ease-in-out",O===e?"bg-secondary opacity-100 hover:opacity-100":ee.includes(e)?"hover:bg-secondary cursor-pointer border-transparent opacity-100 hover:border":"cursor-not-allowed border-transparent opacity-30"),onClick:s=>{s.preventDefault(),ee.includes(e)&&X("selectedProvider",e)},asChild:!0,children:(0,a.jsxs)("span",{children:[(0,o.FO)(e,{size:"sm",className:"w-5 h-5"}),(0,a.jsx)("div",{className:"text-sm",children:c.oU[e]})]})}),!ee.includes(e)&&(0,a.jsx)(C.ZI,{children:"Provider is already configured"})]},e))})}),(0,a.jsxs)("div",{className:"flex h-full w-full flex-col justify-between px-2",children:[(0,a.jsxs)(N.tU,{defaultValue:null==(t=en[0])?void 0:t.id,value:ei,onValueChange:el,className:"space-y-6",children:[(0,a.jsx)(N.j7,{style:{gridTemplateColumns:"repeat(".concat(en.length,", 1fr)")},className:"mb-4 grid h-10 w-full",children:en.map(e=>(0,a.jsx)(N.Xi,{value:e.id,className:"flex items-center gap-2 transition-all duration-200 ease-in-out",children:e.label},e.id))}),(0,a.jsx)("div",{className:"relative overflow-hidden",children:(0,a.jsxs)("div",{className:"transition-all duration-300 ease-in-out",style:{maxHeight:"2000px",opacity:1},children:[J&&"api-keys"===ei&&(0,a.jsxs)("div",{className:"animate-in fade-in-0 slide-in-from-right-2 space-y-4 duration-300",children:[(0,a.jsxs)("div",{className:"flex items-center justify-between",children:[(0,a.jsxs)("div",{className:"flex items-center gap-2",children:[(0,a.jsx)("h3",{className:"text-base font-medium",children:"API Keys"}),(0,a.jsx)(C.Bc,{children:(0,a.jsxs)(C.m_,{children:[(0,a.jsx)(C.k$,{asChild:!0,children:(0,a.jsx)("span",{children:(0,a.jsx)(D.A,{className:"text-muted-foreground ml-1 h-3 w-3"})})}),(0,a.jsx)(C.ZI,{className:"max-w-fit",children:(0,a.jsxs)("p",{children:["Use ",(0,a.jsx)("code",{className:"rounded bg-neutral-100 px-1 py-0.5 text-neutral-800",children:"env."})," to read the value from an environment variable."]})})]})})]}),(0,a.jsxs)(i.$,{type:"button",variant:"outline",size:"sm",onClick:()=>{X("keys",[...M,{value:"",models:[],weight:1}])},children:[(0,a.jsx)(u.A,{className:"h-4 w-4"}),"Add Key"]})]}),(0,a.jsx)("div",{className:"space-y-4",children:M.map((e,s)=>(0,a.jsxs)("div",{className:"animate-in fade-in-0 slide-in-from-left-2 space-y-4 rounded-md border p-4 duration-300",style:{animationDelay:"".concat(50*s,"ms")},children:[(0,a.jsxs)("div",{className:"flex gap-4",children:[(0,a.jsxs)("div",{className:"flex-1",children:[(0,a.jsx)("div",{className:"text-sm font-medium",children:"API Key"}),(0,a.jsx)(y.p,{placeholder:"API Key or env.MY_KEY",value:e.value,onChange:e=>er(s,"value",e.target.value),type:"text",className:"flex-1 transition-all duration-200 ease-in-out ".concat(J&&""===e.value.trim()?"border-destructive":"")})]}),(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"flex items-center gap-4",children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Weight"}),(0,a.jsx)(C.Bc,{children:(0,a.jsxs)(C.m_,{children:[(0,a.jsx)(C.k$,{asChild:!0,children:(0,a.jsx)("span",{children:(0,a.jsx)(D.A,{className:"text-muted-foreground h-3 w-3"})})}),(0,a.jsx)(C.ZI,{children:(0,a.jsx)("p",{children:"Determines traffic distribution between keys. Higher weights receive more requests."})})]})})]}),(0,a.jsx)(y.p,{placeholder:"1.0",value:e.weight,onChange:e=>er(s,"weight",e.target.value),type:"number",step:"0.1",min:"0",max:"1.0",className:"w-20 transition-all duration-200 ease-in-out"})]})]}),(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"flex items-center gap-2",children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Models (Optional)"}),(0,a.jsx)(C.Bc,{children:(0,a.jsxs)(C.m_,{children:[(0,a.jsx)(C.k$,{asChild:!0,children:(0,a.jsx)("span",{children:(0,a.jsx)(D.A,{className:"text-muted-foreground h-3 w-3"})})}),(0,a.jsx)(C.ZI,{children:(0,a.jsx)("p",{children:"Comma-separated list of models this key applies to. Leave blank for all models."})})]})})]}),(0,a.jsx)(k,{placeholder:"e.g. gpt-4, gpt-3.5-turbo",value:e.models||[],onValueChange:e=>er(s,"models",e)})]}),M.length>1&&(0,a.jsxs)(i.$,{type:"button",variant:"destructive",size:"sm",onClick:()=>ea(s),className:"mt-2 transition-all duration-200 ease-in-out",children:[(0,a.jsx)(_.A,{className:"h-4 w-4"}),"Remove Key"]})]},s))})]}),"anthropic"!==O&&"openai"!==O&&"cohere"!==O&&"meta-config"===ei&&(0,a.jsx)("div",{className:"animate-in fade-in-0 slide-in-from-right-2 duration-300",children:(0,a.jsx)(Z,{provider:O,metaConfig:$,onMetaConfigChange:(e,s)=>{X("metaConfig",{...$,[e]:s})}})}),"network"===ei&&(0,a.jsxs)("div",{className:"animate-in fade-in-0 slide-in-from-right-2 space-y-6 duration-300",children:[(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)("div",{className:"flex items-center gap-2",children:[(0,a.jsx)(q.A,{className:"h-4 w-4"}),(0,a.jsx)("h3",{className:"text-base font-medium",children:"Network Configuration"})]}),(0,a.jsxs)("div",{className:"grid grid-cols-1 gap-4",children:[(0,a.jsxs)("div",{children:[(0,a.jsxs)("label",{className:"text-sm font-medium",children:["Base URL ",F?"(Required)":"(Optional)"]}),(0,a.jsx)(y.p,{placeholder:"https://api.example.com",value:B.base_url||"",onChange:e=>X("networkConfig",{...B,base_url:e.target.value}),className:"transition-all duration-200 ease-in-out ".concat(F&&!B.base_url?"border-destructive":"")})]}),(0,a.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Timeout (seconds)"}),(0,a.jsx)(y.p,{type:"number",placeholder:"30",value:B.default_request_timeout_in_seconds,onChange:e=>X("networkConfig",{...B,default_request_timeout_in_seconds:Number.parseInt(e.target.value)||30}),className:"transition-all duration-200 ease-in-out"})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Max Retries"}),(0,a.jsx)(y.p,{type:"number",placeholder:"0",value:B.max_retries,onChange:e=>X("networkConfig",{...B,max_retries:Number.parseInt(e.target.value)||0}),className:"transition-all duration-200 ease-in-out"})]})]})]})]}),(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)("div",{className:"flex items-center gap-2",children:[(0,a.jsx)(q.A,{className:"h-4 w-4"}),(0,a.jsx)("h3",{className:"text-base font-medium",children:"Proxy Settings"})]}),(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Proxy Type"}),(0,a.jsxs)(b.l6,{value:K.type,onValueChange:e=>Q("type",e),children:[(0,a.jsx)(b.bq,{className:"w-48 transition-all duration-200 ease-in-out",children:(0,a.jsx)(b.yv,{placeholder:"Select type"})}),(0,a.jsxs)(b.gC,{children:[(0,a.jsx)(b.eb,{value:"none",children:"None"}),(0,a.jsx)(b.eb,{value:"http",children:"HTTP"}),(0,a.jsx)(b.eb,{value:"socks5",children:"SOCKS5"}),(0,a.jsx)(b.eb,{value:"environment",children:"Environment"})]})]})]}),(0,a.jsx)("div",{className:"overflow-hidden transition-all duration-300 ease-in-out",style:{maxHeight:"none"!==K.type&&"environment"!==K.type?"300px":"0px",opacity:+("none"!==K.type&&"environment"!==K.type)},children:(0,a.jsxs)("div",{className:"space-y-4 pt-2",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Proxy URL"}),(0,a.jsx)(y.p,{placeholder:"http://proxy.example.com",value:K.url||"",onChange:e=>Q("url",e.target.value),className:"transition-all duration-200 ease-in-out"})]}),(0,a.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Username"}),(0,a.jsx)(y.p,{value:K.username||"",onChange:e=>Q("username",e.target.value),placeholder:"Proxy username",className:"transition-all duration-200 ease-in-out"})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Password"}),(0,a.jsx)(y.p,{type:"password",value:K.password||"",onChange:e=>Q("password",e.target.value),placeholder:"Proxy password",className:"transition-all duration-200 ease-in-out"})]})]})]})})]})]})]}),"performance"===ei&&(0,a.jsxs)("div",{className:"animate-in fade-in-0 slide-in-from-right-2 space-y-4 duration-300",children:[(0,a.jsxs)("div",{className:"flex items-center gap-2",children:[(0,a.jsx)(I.A,{className:"h-4 w-4"}),(0,a.jsx)("h3",{className:"text-base font-medium",children:"Performance Settings"})]}),(0,a.jsx)("div",{className:"overflow-hidden transition-all duration-300 ease-in-out",style:{maxHeight:G?"200px":"0px",opacity:+!!G},children:(0,a.jsxs)(R.Fc,{className:"mb-4",children:[(0,a.jsx)(S.A,{className:"h-4 w-4"}),(0,a.jsxs)(R.TN,{children:[(0,a.jsx)("strong",{children:"Heads up:"})," Changing concurrency or buffer size may temporarily affect request latency for this provider while the new settings are being applied."]})]})}),(0,a.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Concurrency"}),(0,a.jsx)(y.p,{type:"number",value:U.concurrency,onChange:e=>X("performanceConfig",{...U,concurrency:Number.parseInt(e.target.value)||0}),className:"transition-all duration-200 ease-in-out ".concat(H?"":"border-destructive")})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Buffer Size"}),(0,a.jsx)(y.p,{type:"number",value:U.buffer_size,onChange:e=>X("performanceConfig",{...U,buffer_size:Number.parseInt(e.target.value)||0}),className:"transition-all duration-200 ease-in-out ".concat(H?"":"border-destructive")})]})]})]})]})})]}),(0,a.jsx)("div",{className:"bg-background sticky bottom-0 py-3",children:ee.length>0&&(0,a.jsxs)("div",{className:"flex justify-end space-x-3",children:[(0,a.jsx)(i.$,{type:"button",variant:"outline",onClick:l,className:"transition-all duration-200 ease-in-out",children:"Cancel"}),(0,a.jsx)(C.Bc,{children:(0,a.jsxs)(C.m_,{children:[(0,a.jsx)(C.k$,{asChild:!0,children:(0,a.jsx)("span",{children:(0,a.jsxs)(i.$,{type:"submit",disabled:!et.isValid()||A,isLoading:A,className:"transition-all duration-200 ease-in-out",children:[(0,a.jsx)(E.A,{className:"h-4 w-4"}),A?"Saving...":"Save Provider"]})})}),(!et.isValid()||A)&&(0,a.jsx)(C.ZI,{children:(0,a.jsx)("p",{children:A?"Saving...":et.getFirstError()||"Please fix validation errors"})})]})})]})})]})]})]})})}function $(e){let{providers:s,onRefresh:t}=e,[j,y]=(0,f.useState)(!1),[b,N]=(0,f.useState)(null),[_,w]=(0,f.useState)(null),k=async e=>{w(e);let[,s]=await d.K.deleteProvider(e);w(null),s?v.o.error(s):(v.o.success("Provider deleted successfully"),t())},C=e=>{N(e),y(!0)};return(0,a.jsxs)(a.Fragment,{children:[j&&(0,a.jsx)(U,{provider:b,onSave:()=>{y(!1),N(null),t()},onCancel:()=>y(!1),existingProviders:s.map(e=>e.name)}),(0,a.jsxs)(g.aR,{className:"mb-4 px-0",children:[(0,a.jsxs)(g.ZB,{className:"flex items-center justify-between",children:[(0,a.jsx)("div",{className:"flex items-center gap-2",children:"AI Providers"}),(0,a.jsxs)(i.$,{onClick:()=>{N(null),y(!0)},children:[(0,a.jsx)(u.A,{className:"h-4 w-4"}),"Add Provider"]})]}),(0,a.jsx)(g.BT,{children:"Manage AI model providers, their API keys, and configuration settings."})]}),(0,a.jsx)("div",{className:"rounded-md border",children:(0,a.jsxs)(l.XI,{children:[(0,a.jsx)(l.A0,{children:(0,a.jsxs)(l.Hj,{children:[(0,a.jsx)(l.nd,{children:"Provider"}),(0,a.jsx)(l.nd,{children:"Concurrency"}),(0,a.jsx)(l.nd,{children:"Buffer Size"}),(0,a.jsx)(l.nd,{children:"Max Retries"}),(0,a.jsx)(l.nd,{children:"API Keys"}),(0,a.jsx)(l.nd,{className:"text-right",children:"Actions"})]})}),(0,a.jsxs)(l.BF,{children:[0===s.length&&(0,a.jsx)(l.Hj,{children:(0,a.jsx)(l.nA,{colSpan:6,className:"py-6 text-center",children:"No providers found."})}),s.map(e=>{var s,t,d,u;return(0,a.jsxs)(l.Hj,{children:[(0,a.jsx)(l.nA,{children:(0,a.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,o.FO)(e.name,{size:16}),(0,a.jsx)("p",{className:"font-medium",children:c.oU[e.name]||e.name})]})}),(0,a.jsx)(l.nA,{children:(0,a.jsx)("div",{className:"flex items-center space-x-2",children:(0,a.jsx)(n.E,{variant:"outline",children:(null==(s=e.concurrency_and_buffer_size)?void 0:s.concurrency)||1})})}),(0,a.jsx)(l.nA,{children:(0,a.jsx)("div",{className:"flex items-center space-x-2",children:(0,a.jsx)(n.E,{variant:"outline",children:(null==(t=e.concurrency_and_buffer_size)?void 0:t.buffer_size)||10})})}),(0,a.jsx)(l.nA,{children:(0,a.jsx)("div",{className:"flex items-center space-x-2",children:(0,a.jsx)(n.E,{variant:"outline",children:(null==(d=e.network_config)?void 0:d.max_retries)||0})})}),(0,a.jsx)(l.nA,{children:(0,a.jsx)("div",{className:"flex items-center space-x-2",children:"vertex"!==e.name&&"ollama"!==e.name?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(m.A,{className:"text-muted-foreground h-4 w-4"}),(0,a.jsxs)("span",{className:"text-sm",children:[(null==(u=e.keys)?void 0:u.length)||0," keys"]})]}):(0,a.jsx)("span",{className:"text-sm",children:"N/A"})})}),(0,a.jsx)(l.nA,{className:"text-right",children:(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-2",children:[(0,a.jsx)(i.$,{variant:"outline",size:"sm",onClick:()=>C(e),children:(0,a.jsx)(x.A,{className:"h-4 w-4"})}),(0,a.jsxs)(r.Lt,{children:[(0,a.jsx)(r.tv,{asChild:!0,children:(0,a.jsx)(i.$,{variant:"outline",size:"sm",disabled:_===e.name,children:_===e.name?(0,a.jsx)(h.A,{className:"h-4 w-4 animate-spin"}):(0,a.jsx)(p.A,{className:"h-4 w-4"})})}),(0,a.jsxs)(r.EO,{children:[(0,a.jsxs)(r.wd,{children:[(0,a.jsx)(r.r7,{children:"Delete Provider"}),(0,a.jsxs)(r.$v,{children:["Are you sure you want to delete provider ",e.name,"? This action cannot be undone."]})]}),(0,a.jsxs)(r.ck,{children:[(0,a.jsx)(r.Zr,{children:"Cancel"}),(0,a.jsx)(r.Rx,{onClick:()=>k(e.name),children:"Delete"})]})]})]})]})})]},e.name)})]})]})})]})}var K=t(2384),T=t(3580);function F(){let[e,s]=(0,f.useState)(!0),[t,r]=(0,f.useState)([]),{toast:n}=(0,T.d)();(0,f.useEffect)(()=>{i()},[]);let i=async()=>{let[e,t]=await d.K.getProviders();if(s(!1),t)return void n({title:"Error",description:t,variant:"destructive"});r((null==e?void 0:e.providers)||[])};return(0,a.jsx)("div",{children:e?(0,a.jsx)(K.A,{}):(0,a.jsx)($,{providers:t,onRefresh:i})})}},6034:(e,s,t)=>{"use strict";t.d(s,{Dk:()=>n,mR:()=>a,qV:()=>r});let a={base_url:"",default_request_timeout_in_seconds:30,max_retries:0,retry_backoff_initial:1e3,retry_backoff_max:1e4},r={concurrency:10,buffer_size:100},n={connected:"bg-green-100 text-green-800",error:"bg-red-100 text-red-800",disconnected:"bg-gray-100 text-gray-800"}},6559:(e,s,t)=>{"use strict";t.d(s,{$v:()=>p,EO:()=>u,Lt:()=>l,Rx:()=>f,Zr:()=>v,ck:()=>x,r7:()=>h,tv:()=>d,wd:()=>m});var a=t(5155);t(2115);var r=t(7649),n=t(3999),i=t(7168);function l(e){let{...s}=e;return(0,a.jsx)(r.bL,{"data-slot":"alert-dialog",...s})}function d(e){let{...s}=e;return(0,a.jsx)(r.l9,{"data-slot":"alert-dialog-trigger",...s})}function o(e){let{...s}=e;return(0,a.jsx)(r.ZL,{"data-slot":"alert-dialog-portal",...s})}function c(e){let{className:s,...t}=e;return(0,a.jsx)(r.hJ,{"data-slot":"alert-dialog-overlay",className:(0,n.cn)("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",s),...t})}function u(e){let{className:s,...t}=e;return(0,a.jsxs)(o,{children:[(0,a.jsx)(c,{}),(0,a.jsx)(r.UC,{"data-slot":"alert-dialog-content",className:(0,n.cn)("bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",s),...t})]})}function m(e){let{className:s,...t}=e;return(0,a.jsx)("div",{"data-slot":"alert-dialog-header",className:(0,n.cn)("flex flex-col gap-2 text-center sm:text-left",s),...t})}function x(e){let{className:s,...t}=e;return(0,a.jsx)("div",{"data-slot":"alert-dialog-footer",className:(0,n.cn)("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",s),...t})}function h(e){let{className:s,...t}=e;return(0,a.jsx)(r.hE,{"data-slot":"alert-dialog-title",className:(0,n.cn)("text-lg font-semibold",s),...t})}function p(e){let{className:s,...t}=e;return(0,a.jsx)(r.VY,{"data-slot":"alert-dialog-description",className:(0,n.cn)("text-muted-foreground text-sm",s),...t})}function f(e){let{className:s,...t}=e;return(0,a.jsx)(r.rc,{className:(0,n.cn)((0,i.r)({variant:"destructive"}),s),...t})}function v(e){let{className:s,...t}=e;return(0,a.jsx)(r.ZD,{className:(0,n.cn)((0,i.r)({variant:"outline"}),s),...t})}},7777:(e,s,t)=>{"use strict";t.d(s,{Bc:()=>i,ZI:()=>o,k$:()=>d,m_:()=>l});var a=t(5155);t(2115);var r=t(9613),n=t(3999);function i(e){let{delayDuration:s=0,...t}=e;return(0,a.jsx)(r.Kq,{"data-slot":"tooltip-provider",delayDuration:s,...t})}function l(e){let{...s}=e;return(0,a.jsx)(i,{children:(0,a.jsx)(r.bL,{"data-slot":"tooltip",...s})})}function d(e){let{...s}=e;return(0,a.jsx)(r.l9,{"data-slot":"tooltip-trigger",...s})}function o(e){let{className:s,sideOffset:t=0,children:i,...l}=e;return(0,a.jsx)(r.ZL,{children:(0,a.jsxs)(r.UC,{"data-slot":"tooltip-content",sideOffset:t,className:(0,n.cn)("bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",s),...l,children:[i,(0,a.jsx)(r.i3,{className:"bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]"})]})})}},9385:(e,s,t)=>{Promise.resolve().then(t.bind(t,3621))},9474:(e,s,t)=>{"use strict";t.d(s,{T:()=>n});var a=t(5155);t(2115);var r=t(3999);function n(e){let{className:s,...t}=e;return(0,a.jsx)("textarea",{"data-slot":"textarea",className:(0,r.cn)("border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",s),...t})}}},e=>{var s=s=>e(e.s=s);e.O(0,[106,189,944,231,412,62,822,613,925,915,830,441,684,358],()=>s(9385)),_N_E=e.O()}]); \ No newline at end of file diff --git a/transports/bifrost-http/ui/_next/static/chunks/app/providers/page-d68ad7e5de246698.js b/transports/bifrost-http/ui/_next/static/chunks/app/providers/page-d68ad7e5de246698.js new file mode 100644 index 0000000000..59891ea8e7 --- /dev/null +++ b/transports/bifrost-http/ui/_next/static/chunks/app/providers/page-d68ad7e5de246698.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[387],{2067:(e,t,s)=>{"use strict";s.d(t,{Dr:()=>a,MZ:()=>i,pI:()=>n,vX:()=>r});class a{isValid(){return!this.rules.some(e=>!e.isValid)}getErrors(){return this.rules.filter(e=>!e.isValid).map(e=>e.message)}getFirstError(){let e=this.rules.find(e=>!e.isValid);return null==e?void 0:e.message}static required(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"This field is required";return{isValid:null!=e&&""!==e&&0!==e,message:t}}static minValue(e,t){let s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at least ".concat(t);return{isValid:!isNaN(e)&&e>=t,message:s}}static maxValue(e,t){let s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at most ".concat(t);return{isValid:!isNaN(e)&&e<=t,message:s}}static pattern(e,t,s){return{isValid:t.test(e||""),message:s}}static email(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Must be a valid email";return this.pattern(e,/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,t)}static url(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Must be a valid URL";return this.pattern(e,/^https?:\/\/.+/,t)}static minLength(e,t){let s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at least ".concat(t," characters");return{isValid:(e||"").length>=t,message:s}}static maxLength(e,t){let s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be at most ".concat(t," characters");return{isValid:(e||"").length<=t,message:s}}static arrayMinLength(e,t){let s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must have at least ".concat(t," items");return{isValid:(null==e?void 0:e.length)>=t,message:s}}static arrayMaxLength(e,t){let s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must have at most ".concat(t," items");return{isValid:(null==e?void 0:e.length)<=t,message:s}}static arrayUnique(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Must have unique items";return{isValid:(null==e?void 0:e.length)===new Set(e).size,message:t}}static arraysEqual(e,t){let s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Must be equal";return{isValid:(null==e?void 0:e.length)===(null==t?void 0:t.length)&&(null==e?void 0:e.every((e,s)=>e===t[s])),message:s}}static custom(e,t){return{isValid:e,message:t}}static all(e){return e.find(e=>!e.isValid)||{isValid:!0,message:""}}constructor(e){this.rules=e.filter(e=>void 0!==e)}}function r(e){return!!e&&(!!e.startsWith("env.")||32===e.length&&e.substring(4,28)==="*".repeat(24)||!!(e.length<=8&&/^\*+$/.test(e)))}function n(e){if(!e||!e.trim())return!1;if(r(e))return!0;if(e.startsWith("env."))return e.length>4;try{let t=JSON.parse(e);return"object"==typeof t&&null!==t&&"service_account"===t.type&&t.project_id&&t.private_key}catch(e){return!1}}function i(e){if(!e)return!1;if("object"==typeof e)return Object.keys(e).length>0;if("string"==typeof e){if(r(e))return!0;try{let t=JSON.parse(e);return"object"==typeof t&&null!==t&&Object.keys(t).length>0}catch(e){}}return!1}},3580:(e,t,s)=>{"use strict";s.d(t,{d:()=>r});var a=s(6671);function r(){return{toast:e=>{let{title:t,description:s,variant:r}=e,n=s?"".concat(t,": ").concat(s):t;"destructive"===r?a.o.error(n):a.o.success(n)}}}},3621:(e,t,s)=>{"use strict";s.r(t),s.d(t,{default:()=>J});var a=s(5155),r=s(6559),n=s(8145),i=s(7168),l=s(8524),o=s(1886),c=s(4432),d=s(7783),u=s(4616),m=s(9803),x=s(3717),h=s(1154),p=s(2525),f=s(2115),v=s(6671),g=s(8482),j=s(9840),y=s(9852),_=s(5784),b=s(4964),N=s(4416),k=s(3999);let w=f.forwardRef((e,t)=>{let{className:s,value:r,onValueChange:i,...l}=e,[o,c]=f.useState(""),d=e=>{i(r.filter(t=>t!==e))};return(0,a.jsxs)("div",{className:(0,k.cn)("border-input flex flex-wrap items-center gap-2 rounded-md border p-2",s),children:[r.map(e=>(0,a.jsxs)(n.E,{variant:"secondary",className:"flex items-center gap-1",children:[e,(0,a.jsx)("button",{type:"button",className:"ring-offset-background focus:ring-ring cursor-pointer rounded-full outline-none focus:ring-2 focus:ring-offset-2",onClick:()=>d(e),children:(0,a.jsx)(N.A,{className:"h-3 w-3"})})]},e)),(0,a.jsx)(y.p,{ref:t,type:"text",value:o,onChange:e=>{c(e.target.value)},onKeyDown:e=>{if("Enter"===e.key||","===e.key){e.preventDefault();let t=o.trim();t&&!r.includes(t)&&i([...r,t]),c("")}else"Backspace"===e.key&&""===o&&r.length>0&&i(r.slice(0,-1))},className:"flex-1 border-0 shadow-none focus-visible:ring-0",...l})]})});w.displayName="TagInput";var C=s(7777),A=s(6034),z=s(2067),S=s(9231),E=s.n(S),P=s(1284),R=s(4869),V=s(1539),I=s(1243),O=s(4229),D=s(9026),q=s(9474),M=s(8103);let B={bedrock:{title:"AWS Bedrock Meta Config",fields:[{name:"region",label:"Region",type:"text",placeholder:"us-east-1 or env.AWS_REGION"},{name:"secret_access_key",label:"Secret Access Key (Optional)",type:"text",placeholder:"Your AWS secret access key or env.AWS_SECRET_ACCESS_KEY"},{name:"session_token",label:"Session Token (Optional)",type:"text",placeholder:"Your AWS session token or env.AWS_SESSION_TOKEN"},{name:"arn",label:"ARN (Optional)",type:"text",placeholder:"AWS ARN or env.AWS_ARN"},{name:"inference_profiles",label:"Inference Profiles (JSON format, Optional)",type:"textarea",placeholder:'{ "model-id": "profile-name" }',isJson:!0}]}},T=e=>{let{provider:t,metaConfig:s,onMetaConfigChange:r}=e,n=B[t];if(!n)return null;let i=e=>{let t=s[e.name];return"textarea"===e.type?(0,a.jsx)(q.T,{placeholder:e.placeholder,value:e.isJson?"string"==typeof t?t:JSON.stringify(t,null,2):t||"",onChange:t=>{r(e.name,t.target.value)},onBlur:t=>{if(e.isJson)try{let s=JSON.parse(t.target.value);r(e.name,s)}catch(e){}},rows:4,className:"wrap-anywhere max-w-full font-mono text-sm"}):(0,a.jsx)(y.p,{placeholder:e.placeholder,value:t||"",onChange:t=>r(e.name,t.target.value)})};return(0,a.jsxs)("div",{className:"",children:[(0,a.jsx)(g.aR,{className:"mb-2 px-0",children:(0,a.jsxs)(g.ZB,{className:"flex items-center gap-2 text-base",children:[(0,a.jsx)(M.A,{className:"h-4 w-4"}),n.title,(0,a.jsx)(C.Bc,{children:(0,a.jsxs)(C.m_,{children:[(0,a.jsx)(C.k$,{asChild:!0,children:(0,a.jsx)("span",{children:(0,a.jsx)(P.A,{className:"text-muted-foreground ml-1 h-3 w-3"})})}),(0,a.jsx)(C.ZI,{className:"max-w-fit",children:(0,a.jsxs)("p",{children:["Use ",(0,a.jsx)("code",{className:"rounded bg-neutral-100 px-1 py-0.5 text-neutral-800",children:"env."})," to read the value from an environment variable."]})})]})})]})}),(0,a.jsx)(g.Wu,{className:"space-y-4 px-0",children:n.fields.map(e=>(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"block text-sm font-medium",children:e.label}),i(e)]},e.name))})]})},Z=(e,t)=>{let s=!e,a=(null==e?void 0:e.name)||t||"",r=!["ollama","sgl"].includes(a);return{selectedProvider:a,keys:s&&r?[(()=>{let e={id:"",value:"",models:[],weight:1};return"azure"===a?e.azure_key_config={endpoint:"",deployments:{},api_version:"2024-02-01"}:"vertex"===a&&(e.vertex_key_config={project_id:"",region:"",auth_credentials:""}),e})()]:!s&&r&&(null==e?void 0:e.keys)?e.keys:[],networkConfig:(null==e?void 0:e.network_config)||A.mR,performanceConfig:(null==e?void 0:e.concurrency_and_buffer_size)||A.qV,metaConfig:(null==e?void 0:e.meta_config)||{region:"",secret_access_key:""},proxyConfig:(null==e?void 0:e.proxy_config)||{type:"none",url:"",username:"",password:""}}};function K(e){var t,s;let r,n,{provider:l,onSave:m,onCancel:x,existingProviders:h}=e,p=l?void 0:d.xq.find(e=>!h.includes(e))||"",[g]=(0,f.useState)(Z(l,p)),[A,S]=(0,f.useState)({...g,isDirty:!1}),[M,B]=(0,f.useState)(!1),{selectedProvider:K,keys:L,networkConfig:$,performanceConfig:U,metaConfig:J,proxyConfig:W,isDirty:F}=A,H="ollama"===K||"sgl"===K,X=!["ollama","sgl"].includes(K);X&&L.every(e=>"vertex"===K||""!==e.value.trim()),X&&L.length;let Y=U.concurrency>0&&U.buffer_size>0&&U.concurrency{let e=!0,t="";for(let s of L)if("azure"===K&&s.azure_key_config){let a=!!s.azure_key_config.endpoint&&""!==s.azure_key_config.endpoint.trim(),r=(0,z.MZ)(s.azure_key_config.deployments);if(!a||!r){e=!1,t="Endpoint and valid Deployments (JSON object) are required for Azure keys";break}}else if("vertex"===K&&s.vertex_key_config){let a=!!s.vertex_key_config.project_id&&""!==s.vertex_key_config.project_id.trim(),r=!!s.vertex_key_config.region&&""!==s.vertex_key_config.region.trim(),n=(0,z.pI)(s.vertex_key_config.auth_credentials);if(!a||!n||!r){e=!1,t="Project ID, valid Auth Credentials (JSON object or env.VAR), and Region are required for Vertex AI keys";break}}return{valid:e,message:t}})(),{valid:et,message:es}=(r=!0,n="","bedrock"===K&&((r=!!J.region&&""!==J.region.trim())||(n="Region is required for AWS Bedrock")),{valid:r,message:n});(0,f.useEffect)(()=>{let e={selectedProvider:K,keys:X?L:[],networkConfig:$,performanceConfig:U,metaConfig:J,proxyConfig:W};S(t=>({...t,isDirty:!E()(g,e)}))},[K,L,$,U,J,W,g,X]);let ea=(e,t)=>{S(s=>({...s,[e]:t}))},er=(e,t)=>{ea("proxyConfig",{...W,[e]:t})},en=l?d.xq:d.xq.filter(e=>!h.includes(e)),ei=async e=>{if(!el.isValid())return void v.o.error(el.getFirstError());e.preventDefault(),B(!0);let t=null;if(l){let e={keys:X?L.filter(e=>"vertex"===K||""!==e.value.trim()):[],network_config:$,concurrency_and_buffer_size:U,meta_config:J,proxy_config:W};[,t]=await o.K.updateProvider(l.name,e)}else{let e={provider:K,keys:X?L.filter(e=>"vertex"===K||""!==e.value.trim()):[],network_config:$,concurrency_and_buffer_size:U,meta_config:J,proxy_config:W};[,t]=await o.K.createProvider(e)}B(!1),t?v.o.error(t):(v.o.success("Provider ".concat(l?"updated":"added"," successfully")),m())},el=new z.Dr([z.Dr.required(K,"Please select a provider"),z.Dr.custom(F,"No changes to save"),...H?[z.Dr.required($.base_url,"Base URL is required for Ollama provider"),z.Dr.pattern($.base_url||"",/^https?:\/\/.+/,"Base URL must start with http:// or https://")]:[],...X?[z.Dr.minValue(L.length,1,"At least one API key is required"),z.Dr.custom(L.every(e=>"vertex"===K||""!==e.value.trim()),"API key value cannot be empty"),z.Dr.custom(L.every(e=>e.weight>=0&&e.weight<=1),"Key weights must be between 0 and 1")]:[],z.Dr.minValue($.default_request_timeout_in_seconds,1,"Timeout must be greater than 0 seconds"),z.Dr.minValue($.max_retries,0,"Max retries cannot be negative"),z.Dr.minValue(U.concurrency,1,"Concurrency must be greater than 0"),z.Dr.minValue(U.buffer_size,1,"Buffer size must be greater than 0"),z.Dr.custom(U.concurrency{ea("keys",L.filter((t,s)=>s!==e))},ec=(e,t,s)=>{let a=[...L],r={...a[e]};"models"===t&&Array.isArray(s)?r.models=s:"value"===t&&"string"==typeof s?r.value=s:"weight"===t&&"string"==typeof s&&(r.weight=Number.parseFloat(s)||1),a[e]=r,ea("keys",a)},ed=(e,t,s)=>{let a=[...L],r={...a[e]};r.azure_key_config||(r.azure_key_config={endpoint:"",deployments:{},api_version:"2024-02-01"}),r.azure_key_config={...r.azure_key_config,[t]:s},a[e]=r,ea("keys",a)},eu=(e,t,s)=>{let a=[...L],r={...a[e]};r.vertex_key_config||(r.vertex_key_config={project_id:"",region:"",auth_credentials:""}),r.vertex_key_config={...r.vertex_key_config,[t]:s},a[e]=r,ea("keys",a)},em=(0,f.useMemo)(()=>{let e=[];return X&&e.push({id:"api-keys",label:"API Keys"}),"bedrock"===K&&e.push({id:"meta-config",label:"Meta Config"}),e.push({id:"network",label:"Network"}),e.push({id:"performance",label:"Performance"}),e},[X,K]),[ex,eh]=(0,f.useState)((null==(t=em[0])?void 0:t.id)||"api-keys");return(0,f.useEffect)(()=>{if(!em.map(e=>e.id).includes(ex)){var e;eh((null==(e=em[0])?void 0:e.id)||"api-keys")}},[em]),(0,a.jsx)(j.lG,{open:!0,onOpenChange:x,children:(0,a.jsxs)(j.Cf,{className:"custom-scrollbar max-h-[90vh] overflow-y-auto p-0 sm:max-w-4xl",showCloseButton:!1,children:[(0,a.jsxs)(j.c7,{className:"z-10 px-6 pt-6",children:[(0,a.jsx)(j.L3,{children:l?(0,a.jsxs)("div",{className:"flex items-center gap-2",children:[(0,c.FO)(l.name,{size:20}),(0,a.jsx)("span",{className:"font-semibold",children:d.oU[l.name]})]}):(0,a.jsx)("div",{className:"flex items-center gap-2",children:"Add Provider"})}),(0,a.jsx)(j.rr,{children:"Configure AI provider settings, API keys, and network options."})]}),(0,a.jsxs)("form",{onSubmit:ei,className:"flex flex-row gap-2 px-6 pt-6",children:[!l&&(0,a.jsx)(C.Bc,{children:(0,a.jsx)("div",{className:"flex w-[250px] flex-col gap-1 pb-10",children:d.xq.map(e=>(0,a.jsxs)(C.m_,{children:[(0,a.jsx)(C.k$,{className:(0,k.cn)("flex w-full items-center gap-2 rounded-lg border px-4 py-2 text-sm transition-all duration-200 ease-in-out",K===e?"bg-secondary opacity-100 hover:opacity-100":en.includes(e)?"hover:bg-secondary cursor-pointer border-transparent opacity-100 hover:border":"cursor-not-allowed border-transparent opacity-30"),onClick:t=>{t.preventDefault(),en.includes(e)&&ea("selectedProvider",e)},asChild:!0,children:(0,a.jsxs)("span",{children:[(0,c.FO)(e,{size:"sm",className:"w-5 h-5"}),(0,a.jsx)("div",{className:"text-sm",children:d.oU[e]})]})}),!en.includes(e)&&(0,a.jsx)(C.ZI,{children:"Provider is already configured"})]},e))})}),(0,a.jsxs)("div",{className:"flex h-full w-full flex-col justify-between px-2",children:[(0,a.jsxs)(b.tU,{defaultValue:null==(s=em[0])?void 0:s.id,value:ex,onValueChange:eh,className:"space-y-6",children:[(0,a.jsx)(b.j7,{style:{gridTemplateColumns:"repeat(".concat(em.length,", 1fr)")},className:"mb-4 grid h-10 w-full",children:em.map(e=>(0,a.jsx)(b.Xi,{value:e.id,className:"flex items-center gap-2 transition-all duration-200 ease-in-out",children:e.label},e.id))}),(0,a.jsx)("div",{className:"relative overflow-hidden",children:(0,a.jsxs)("div",{className:"transition-all duration-300 ease-in-out",style:{maxHeight:"2000px",opacity:1},children:[X&&"api-keys"===ex&&(0,a.jsxs)("div",{className:"animate-in fade-in-0 slide-in-from-right-2 space-y-4 duration-300",children:[(0,a.jsxs)("div",{className:"flex items-center justify-between",children:[(0,a.jsxs)("div",{className:"flex items-center gap-2",children:[(0,a.jsx)("h3",{className:"text-base font-medium",children:"API Keys"}),(0,a.jsx)(C.Bc,{children:(0,a.jsxs)(C.m_,{children:[(0,a.jsx)(C.k$,{asChild:!0,children:(0,a.jsx)("span",{children:(0,a.jsx)(P.A,{className:"text-muted-foreground ml-1 h-3 w-3"})})}),(0,a.jsx)(C.ZI,{className:"max-w-fit",children:(0,a.jsxs)("p",{children:["Use ",(0,a.jsx)("code",{className:"rounded bg-neutral-100 px-1 py-0.5 text-neutral-800",children:"env."})," to read the value from an environment variable."]})})]})})]}),(0,a.jsxs)(i.$,{type:"button",variant:"outline",size:"sm",onClick:()=>{let e={id:"",value:"",models:[],weight:1};"azure"===K?e.azure_key_config={endpoint:"",deployments:{},api_version:"2024-02-01"}:"vertex"===K&&(e.vertex_key_config={project_id:"",region:"",auth_credentials:""}),ea("keys",[...L,e])},children:[(0,a.jsx)(u.A,{className:"h-4 w-4"}),"Add Key"]})]}),(0,a.jsx)("div",{className:"space-y-4",children:L.map((e,t)=>{var s,r,n,l,o,c,d,u,m,x,h,p,f,v,g;return(0,a.jsxs)("div",{className:"animate-in fade-in-0 slide-in-from-right-2 space-y-4 rounded-md border p-4 duration-300",style:{animationDelay:"".concat(50*t,"ms")},children:[(0,a.jsxs)("div",{className:"flex gap-4",children:["vertex"!==K&&(0,a.jsxs)("div",{className:"flex-1",children:[(0,a.jsx)("div",{className:"text-sm font-medium",children:"API Key"}),(0,a.jsx)(y.p,{placeholder:"API Key or env.MY_KEY",value:e.value,onChange:e=>ec(t,"value",e.target.value),type:"text",className:"flex-1 transition-all duration-200 ease-in-out ".concat(X&&"vertex"!==K&&""===e.value.trim()?"border-destructive":"")})]}),(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"flex items-center gap-4",children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Weight"}),(0,a.jsx)(C.Bc,{children:(0,a.jsxs)(C.m_,{children:[(0,a.jsx)(C.k$,{asChild:!0,children:(0,a.jsx)("span",{children:(0,a.jsx)(P.A,{className:"text-muted-foreground h-3 w-3"})})}),(0,a.jsx)(C.ZI,{children:(0,a.jsx)("p",{children:"Determines traffic distribution between keys. Higher weights receive more requests."})})]})})]}),(0,a.jsx)(y.p,{placeholder:"1.0",value:e.weight,onChange:e=>ec(t,"weight",e.target.value),type:"number",step:"0.01",min:"0",className:"w-20 transition-all duration-200 ease-in-out ".concat(X&&(e.weight<0||e.weight>1)?"border-destructive":"")})]})]}),(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"flex items-center gap-2",children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Models (Optional)"}),(0,a.jsx)(C.Bc,{children:(0,a.jsxs)(C.m_,{children:[(0,a.jsx)(C.k$,{asChild:!0,children:(0,a.jsx)("span",{children:(0,a.jsx)(P.A,{className:"text-muted-foreground h-3 w-3"})})}),(0,a.jsx)(C.ZI,{children:(0,a.jsx)("p",{children:"Comma-separated list of models this key applies to. Leave blank for all models."})})]})})]}),(0,a.jsx)(w,{placeholder:"e.g. gpt-4, gpt-3.5-turbo",value:e.models||[],onValueChange:e=>ec(t,"models",e)})]}),"azure"===K&&(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Endpoint (Required)"}),(0,a.jsx)(y.p,{placeholder:"https://your-resource.openai.azure.com or env.AZURE_ENDPOINT",value:(null==(s=e.azure_key_config)?void 0:s.endpoint)||"",onChange:e=>ed(t,"endpoint",e.target.value),className:"transition-all duration-200 ease-in-out ".concat((null==(n=e.azure_key_config)||null==(r=n.endpoint)?void 0:r.trim())?"":"border-destructive")})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"API Version (Optional)"}),(0,a.jsx)(y.p,{placeholder:"2024-02-01 or env.AZURE_API_VERSION",value:(null==(l=e.azure_key_config)?void 0:l.api_version)||"",onChange:e=>ed(t,"api_version",e.target.value)})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Deployments (Required)"}),(0,a.jsx)("div",{className:"text-muted-foreground mb-2 text-xs",children:"JSON object mapping model names to deployment names"}),(0,a.jsx)(q.T,{placeholder:'{"gpt-4": "my-gpt4-deployment", "gpt-3.5-turbo": "my-gpt35-deployment"}',value:"string"==typeof(null==(o=e.azure_key_config)?void 0:o.deployments)?e.azure_key_config.deployments:JSON.stringify((null==(c=e.azure_key_config)?void 0:c.deployments)||{},null,2),onChange:e=>{ed(t,"deployments",e.target.value)},onBlur:e=>{let s=e.target.value.trim();if(s)try{let e=JSON.parse(s);"object"==typeof e&&null!==e&&ed(t,"deployments",e)}catch(e){}},rows:3,className:"wrap-anywhere max-w-full font-mono text-sm"})]})]}),"vertex"===K&&(0,a.jsxs)("div",{className:"space-y-4 pt-2",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Project ID (Required)"}),(0,a.jsx)(y.p,{placeholder:"your-gcp-project-id or env.VERTEX_PROJECT_ID",value:(null==(d=e.vertex_key_config)?void 0:d.project_id)||"",onChange:e=>eu(t,"project_id",e.target.value),className:"transition-all duration-200 ease-in-out ".concat((null==(m=e.vertex_key_config)||null==(u=m.project_id)?void 0:u.trim())?"":"border-destructive")})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Region (Required)"}),(0,a.jsx)(y.p,{placeholder:"us-central1 or env.VERTEX_REGION",value:(null==(x=e.vertex_key_config)?void 0:x.region)||"",onChange:e=>eu(t,"region",e.target.value),className:"transition-all duration-200 ease-in-out ".concat((null==(p=e.vertex_key_config)||null==(h=p.region)?void 0:h.trim())?"":"border-destructive")})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Auth Credentials (Required)"}),(0,a.jsx)("div",{className:"text-muted-foreground mb-2 text-xs",children:"Service account JSON object or env.VAR_NAME"}),(0,a.jsx)(q.T,{placeholder:'{"type":"service_account","project_id":"your-gcp-project",...} or env.VERTEX_CREDENTIALS',value:(null==(f=e.vertex_key_config)?void 0:f.auth_credentials)||"",onChange:e=>{eu(t,"auth_credentials",e.target.value)},rows:4,className:"wrap-anywhere max-w-full font-mono text-sm ".concat((0,z.pI)((null==(v=e.vertex_key_config)?void 0:v.auth_credentials)||"")?"":"border-destructive")}),(0,z.vX)((null==(g=e.vertex_key_config)?void 0:g.auth_credentials)||"")&&(0,a.jsxs)("div",{className:"text-muted-foreground mt-1 flex items-center gap-1 text-xs",children:[(0,a.jsx)(P.A,{className:"h-3 w-3"}),(0,a.jsx)("span",{children:"Credentials are stored securely. Edit to update."})]})]})]}),L.length>1&&(0,a.jsxs)(i.$,{type:"button",variant:"destructive",size:"sm",onClick:()=>eo(t),className:"mt-2 transition-all duration-200 ease-in-out",children:[(0,a.jsx)(N.A,{className:"h-4 w-4"}),"Remove Key"]})]},t)})})]}),"bedrock"===K&&"meta-config"===ex&&(0,a.jsx)("div",{className:"animate-in fade-in-0 slide-in-from-right-2 duration-300",children:(0,a.jsx)(T,{provider:K,metaConfig:J,onMetaConfigChange:(e,t)=>{ea("metaConfig",{...J,[e]:t})}})}),"network"===ex&&(0,a.jsxs)("div",{className:"animate-in fade-in-0 slide-in-from-right-2 space-y-6 duration-300",children:[(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)("div",{className:"flex items-center gap-2",children:[(0,a.jsx)(R.A,{className:"h-4 w-4"}),(0,a.jsx)("h3",{className:"text-base font-medium",children:"Network Configuration"})]}),(0,a.jsxs)("div",{className:"grid grid-cols-1 gap-4",children:[(0,a.jsxs)("div",{children:[(0,a.jsxs)("label",{className:"text-sm font-medium",children:["Base URL ",H?"(Required)":"(Optional)"]}),(0,a.jsx)(y.p,{placeholder:"https://api.example.com",value:$.base_url||"",onChange:e=>ea("networkConfig",{...$,base_url:e.target.value}),className:"transition-all duration-200 ease-in-out ".concat(H&&!$.base_url?"border-destructive":"")})]}),(0,a.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Timeout (seconds)"}),(0,a.jsx)(y.p,{type:"number",placeholder:"30",value:$.default_request_timeout_in_seconds,onChange:e=>ea("networkConfig",{...$,default_request_timeout_in_seconds:Number.parseInt(e.target.value)||30}),className:"transition-all duration-200 ease-in-out"})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Max Retries"}),(0,a.jsx)(y.p,{type:"number",placeholder:"0",value:$.max_retries,onChange:e=>ea("networkConfig",{...$,max_retries:Number.parseInt(e.target.value)||0}),className:"transition-all duration-200 ease-in-out"})]})]})]})]}),(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)("div",{className:"flex items-center gap-2",children:[(0,a.jsx)(R.A,{className:"h-4 w-4"}),(0,a.jsx)("h3",{className:"text-base font-medium",children:"Proxy Settings"})]}),(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Proxy Type"}),(0,a.jsxs)(_.l6,{value:W.type,onValueChange:e=>er("type",e),children:[(0,a.jsx)(_.bq,{className:"w-48 transition-all duration-200 ease-in-out",children:(0,a.jsx)(_.yv,{placeholder:"Select type"})}),(0,a.jsxs)(_.gC,{children:[(0,a.jsx)(_.eb,{value:"none",children:"None"}),(0,a.jsx)(_.eb,{value:"http",children:"HTTP"}),(0,a.jsx)(_.eb,{value:"socks5",children:"SOCKS5"}),(0,a.jsx)(_.eb,{value:"environment",children:"Environment"})]})]})]}),(0,a.jsx)("div",{className:"overflow-hidden transition-all duration-300 ease-in-out",style:{maxHeight:"none"!==W.type&&"environment"!==W.type?"300px":"0px",opacity:+("none"!==W.type&&"environment"!==W.type)},children:(0,a.jsxs)("div",{className:"space-y-4 pt-2",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Proxy URL"}),(0,a.jsx)(y.p,{placeholder:"http://proxy.example.com",value:W.url||"",onChange:e=>er("url",e.target.value),className:"transition-all duration-200 ease-in-out"})]}),(0,a.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Username"}),(0,a.jsx)(y.p,{value:W.username||"",onChange:e=>er("username",e.target.value),placeholder:"Proxy username",className:"transition-all duration-200 ease-in-out"})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Password"}),(0,a.jsx)(y.p,{type:"password",value:W.password||"",onChange:e=>er("password",e.target.value),placeholder:"Proxy password",className:"transition-all duration-200 ease-in-out"})]})]})]})})]})]})]}),"performance"===ex&&(0,a.jsxs)("div",{className:"animate-in fade-in-0 slide-in-from-right-2 space-y-2 duration-300",children:[(0,a.jsxs)("div",{className:"flex items-center gap-2",children:[(0,a.jsx)(V.A,{className:"h-4 w-4"}),(0,a.jsx)("h3",{className:"text-base font-medium",children:"Performance Settings"})]}),(0,a.jsx)("div",{className:"overflow-hidden transition-all duration-300 ease-in-out",style:{maxHeight:G?"200px":"0px",opacity:+!!G},children:(0,a.jsxs)(D.Fc,{className:"mb-4",children:[(0,a.jsx)(I.A,{className:"h-4 w-4"}),(0,a.jsxs)(D.TN,{children:[(0,a.jsx)("strong",{children:"Heads up:"})," Changing concurrency or buffer size may temporarily affect request latency for this provider while the new settings are being applied."]})]})}),(0,a.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Concurrency"}),(0,a.jsx)(y.p,{type:"number",value:U.concurrency,onChange:e=>ea("performanceConfig",{...U,concurrency:Number.parseInt(e.target.value)||0}),className:"transition-all duration-200 ease-in-out ".concat(Y?"":"border-destructive")})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"text-sm font-medium",children:"Buffer Size"}),(0,a.jsx)(y.p,{type:"number",value:U.buffer_size,onChange:e=>ea("performanceConfig",{...U,buffer_size:Number.parseInt(e.target.value)||0}),className:"transition-all duration-200 ease-in-out ".concat(Y?"":"border-destructive")})]})]})]})]})})]}),(0,a.jsx)("div",{className:"bg-background sticky bottom-0 py-3",children:en.length>0&&(0,a.jsxs)("div",{className:"flex justify-end space-x-3",children:[(0,a.jsx)(i.$,{type:"button",variant:"outline",onClick:x,className:"transition-all duration-200 ease-in-out",children:"Cancel"}),(0,a.jsx)(C.Bc,{children:(0,a.jsxs)(C.m_,{children:[(0,a.jsx)(C.k$,{asChild:!0,children:(0,a.jsx)("span",{children:(0,a.jsxs)(i.$,{type:"submit",disabled:!el.isValid()||M,isLoading:M,className:"transition-all duration-200 ease-in-out",children:[(0,a.jsx)(O.A,{className:"h-4 w-4"}),M?"Saving...":"Save Provider"]})})}),(!el.isValid()||M)&&(0,a.jsx)(C.ZI,{children:(0,a.jsx)("p",{children:M?"Saving...":el.getFirstError()||"Please fix validation errors"})})]})})]})})]})]})]})})}function L(e){let{providers:t,onRefresh:s}=e,[j,y]=(0,f.useState)(!1),[_,b]=(0,f.useState)(null),[N,k]=(0,f.useState)(null),w=async e=>{k(e);let[,t]=await o.K.deleteProvider(e);k(null),t?v.o.error(t):(v.o.success("Provider deleted successfully"),s())},C=e=>{b(e),y(!0)};return(0,a.jsxs)(a.Fragment,{children:[j&&(0,a.jsx)(K,{provider:_,onSave:()=>{y(!1),b(null),s()},onCancel:()=>y(!1),existingProviders:t.map(e=>e.name)}),(0,a.jsxs)(g.aR,{className:"mb-4 px-0",children:[(0,a.jsxs)(g.ZB,{className:"flex items-center justify-between",children:[(0,a.jsx)("div",{className:"flex items-center gap-2",children:"AI Providers"}),(0,a.jsxs)(i.$,{onClick:()=>{b(null),y(!0)},children:[(0,a.jsx)(u.A,{className:"h-4 w-4"}),"Add Provider"]})]}),(0,a.jsx)(g.BT,{children:"Manage AI model providers, their API keys, and configuration settings."})]}),(0,a.jsx)("div",{className:"rounded-md border",children:(0,a.jsxs)(l.XI,{children:[(0,a.jsx)(l.A0,{children:(0,a.jsxs)(l.Hj,{children:[(0,a.jsx)(l.nd,{children:"Provider"}),(0,a.jsx)(l.nd,{children:"Concurrency"}),(0,a.jsx)(l.nd,{children:"Buffer Size"}),(0,a.jsx)(l.nd,{children:"Max Retries"}),(0,a.jsx)(l.nd,{children:"API Keys"}),(0,a.jsx)(l.nd,{className:"text-right",children:"Actions"})]})}),(0,a.jsxs)(l.BF,{children:[0===t.length&&(0,a.jsx)(l.Hj,{children:(0,a.jsx)(l.nA,{colSpan:6,className:"py-6 text-center",children:"No providers found."})}),t.map(e=>{var t,s,o,u;return(0,a.jsxs)(l.Hj,{children:[(0,a.jsx)(l.nA,{children:(0,a.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,c.FO)(e.name,{size:16}),(0,a.jsx)("p",{className:"font-medium",children:d.oU[e.name]||e.name})]})}),(0,a.jsx)(l.nA,{children:(0,a.jsx)("div",{className:"flex items-center space-x-2",children:(0,a.jsx)(n.E,{variant:"outline",children:(null==(t=e.concurrency_and_buffer_size)?void 0:t.concurrency)||1})})}),(0,a.jsx)(l.nA,{children:(0,a.jsx)("div",{className:"flex items-center space-x-2",children:(0,a.jsx)(n.E,{variant:"outline",children:(null==(s=e.concurrency_and_buffer_size)?void 0:s.buffer_size)||10})})}),(0,a.jsx)(l.nA,{children:(0,a.jsx)("div",{className:"flex items-center space-x-2",children:(0,a.jsx)(n.E,{variant:"outline",children:(null==(o=e.network_config)?void 0:o.max_retries)||0})})}),(0,a.jsx)(l.nA,{children:(0,a.jsx)("div",{className:"flex items-center space-x-2",children:"ollama"!==e.name?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(m.A,{className:"text-muted-foreground h-4 w-4"}),(0,a.jsxs)("span",{className:"text-sm",children:[(null==(u=e.keys)?void 0:u.length)||0," keys"]})]}):(0,a.jsx)("span",{className:"text-sm",children:"N/A"})})}),(0,a.jsx)(l.nA,{className:"text-right",children:(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-2",children:[(0,a.jsx)(i.$,{variant:"outline",size:"sm",onClick:()=>C(e),children:(0,a.jsx)(x.A,{className:"h-4 w-4"})}),(0,a.jsxs)(r.Lt,{children:[(0,a.jsx)(r.tv,{asChild:!0,children:(0,a.jsx)(i.$,{variant:"outline",size:"sm",disabled:N===e.name,children:N===e.name?(0,a.jsx)(h.A,{className:"h-4 w-4 animate-spin"}):(0,a.jsx)(p.A,{className:"h-4 w-4"})})}),(0,a.jsxs)(r.EO,{children:[(0,a.jsxs)(r.wd,{children:[(0,a.jsx)(r.r7,{children:"Delete Provider"}),(0,a.jsxs)(r.$v,{children:["Are you sure you want to delete provider ",e.name,"? This action cannot be undone."]})]}),(0,a.jsxs)(r.ck,{children:[(0,a.jsx)(r.Zr,{children:"Cancel"}),(0,a.jsx)(r.Rx,{onClick:()=>w(e.name),children:"Delete"})]})]})]})]})})]},e.name)})]})]})})]})}var $=s(2384),U=s(3580);function J(){let[e,t]=(0,f.useState)(!0),[s,r]=(0,f.useState)([]),{toast:n}=(0,U.d)();(0,f.useEffect)(()=>{i()},[]);let i=async()=>{let[e,s]=await o.K.getProviders();if(t(!1),s)return void n({title:"Error",description:s,variant:"destructive"});r((null==e?void 0:e.providers)||[])};return(0,a.jsx)("div",{children:e?(0,a.jsx)($.A,{}):(0,a.jsx)(L,{providers:s,onRefresh:i})})}},6034:(e,t,s)=>{"use strict";s.d(t,{Dk:()=>n,mR:()=>a,qV:()=>r});let a={base_url:"",default_request_timeout_in_seconds:30,max_retries:0,retry_backoff_initial:1e3,retry_backoff_max:1e4},r={concurrency:10,buffer_size:100},n={connected:"bg-green-100 text-green-800",error:"bg-red-100 text-red-800",disconnected:"bg-gray-100 text-gray-800"}},6559:(e,t,s)=>{"use strict";s.d(t,{$v:()=>p,EO:()=>u,Lt:()=>l,Rx:()=>f,Zr:()=>v,ck:()=>x,r7:()=>h,tv:()=>o,wd:()=>m});var a=s(5155);s(2115);var r=s(7649),n=s(3999),i=s(7168);function l(e){let{...t}=e;return(0,a.jsx)(r.bL,{"data-slot":"alert-dialog",...t})}function o(e){let{...t}=e;return(0,a.jsx)(r.l9,{"data-slot":"alert-dialog-trigger",...t})}function c(e){let{...t}=e;return(0,a.jsx)(r.ZL,{"data-slot":"alert-dialog-portal",...t})}function d(e){let{className:t,...s}=e;return(0,a.jsx)(r.hJ,{"data-slot":"alert-dialog-overlay",className:(0,n.cn)("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",t),...s})}function u(e){let{className:t,...s}=e;return(0,a.jsxs)(c,{children:[(0,a.jsx)(d,{}),(0,a.jsx)(r.UC,{"data-slot":"alert-dialog-content",className:(0,n.cn)("bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",t),...s})]})}function m(e){let{className:t,...s}=e;return(0,a.jsx)("div",{"data-slot":"alert-dialog-header",className:(0,n.cn)("flex flex-col gap-2 text-center sm:text-left",t),...s})}function x(e){let{className:t,...s}=e;return(0,a.jsx)("div",{"data-slot":"alert-dialog-footer",className:(0,n.cn)("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",t),...s})}function h(e){let{className:t,...s}=e;return(0,a.jsx)(r.hE,{"data-slot":"alert-dialog-title",className:(0,n.cn)("text-lg font-semibold",t),...s})}function p(e){let{className:t,...s}=e;return(0,a.jsx)(r.VY,{"data-slot":"alert-dialog-description",className:(0,n.cn)("text-muted-foreground text-sm",t),...s})}function f(e){let{className:t,...s}=e;return(0,a.jsx)(r.rc,{className:(0,n.cn)((0,i.r)({variant:"destructive"}),t),...s})}function v(e){let{className:t,...s}=e;return(0,a.jsx)(r.ZD,{className:(0,n.cn)((0,i.r)({variant:"outline"}),t),...s})}},7777:(e,t,s)=>{"use strict";s.d(t,{Bc:()=>i,ZI:()=>c,k$:()=>o,m_:()=>l});var a=s(5155);s(2115);var r=s(9613),n=s(3999);function i(e){let{delayDuration:t=0,...s}=e;return(0,a.jsx)(r.Kq,{"data-slot":"tooltip-provider",delayDuration:t,...s})}function l(e){let{...t}=e;return(0,a.jsx)(i,{children:(0,a.jsx)(r.bL,{"data-slot":"tooltip",...t})})}function o(e){let{...t}=e;return(0,a.jsx)(r.l9,{"data-slot":"tooltip-trigger",...t})}function c(e){let{className:t,sideOffset:s=0,children:i,...l}=e;return(0,a.jsx)(r.ZL,{children:(0,a.jsxs)(r.UC,{"data-slot":"tooltip-content",sideOffset:s,className:(0,n.cn)("bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",t),...l,children:[i,(0,a.jsx)(r.i3,{className:"bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]"})]})})}},9385:(e,t,s)=>{Promise.resolve().then(s.bind(s,3621))},9474:(e,t,s)=>{"use strict";s.d(t,{T:()=>n});var a=s(5155);s(2115);var r=s(3999);function n(e){let{className:t,...s}=e;return(0,a.jsx)("textarea",{"data-slot":"textarea",className:(0,r.cn)("border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",t),...s})}}},e=>{var t=t=>e(e.s=t);e.O(0,[106,189,944,231,412,62,822,613,925,915,830,441,684,358],()=>t(9385)),_N_E=e.O()}]); \ No newline at end of file diff --git a/transports/bifrost-http/ui/_next/static/css/215063fa9dc64c3d.css b/transports/bifrost-http/ui/_next/static/css/215063fa9dc64c3d.css deleted file mode 100644 index fc947ea97a..0000000000 --- a/transports/bifrost-http/ui/_next/static/css/215063fa9dc64c3d.css +++ /dev/null @@ -1 +0,0 @@ -/*! tailwindcss v4.1.11 | 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)))){*,::backdrop,:after,:before{--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-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction: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-duration:initial;--tw-ease:initial;--tw-content:"";--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y: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{:host,:root{--color-red-100:oklch(93.6% .032 17.717);--color-red-800:oklch(44.4% .177 26.899);--color-orange-500:oklch(70.5% .213 47.604);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--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-950:oklch(27.9% .077 45.635);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-800:oklch(47.6% .114 61.907);--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-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-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-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-200:oklch(90.2% .063 306.703);--color-purple-300:oklch(82.7% .119 306.383);--color-purple-600:oklch(55.8% .288 302.321);--color-purple-700:oklch(49.6% .265 301.924);--color-purple-800:oklch(43.8% .218 303.724);--color-purple-900:oklch(38.1% .176 304.987);--color-purple-950:oklch(29.1% .149 302.717);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-800:oklch(27.8% .033 256.848);--color-neutral-100:oklch(97% 0 0);--color-neutral-800:oklch(26.9% 0 0);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--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:calc(1.5/1);--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);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25/1.875);--text-5xl:3rem;--text-5xl--line-height:1;--font-weight-normal:400;--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-relaxed:1.625;--radius-xs:.125rem;--radius-2xl:1rem;--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-ping:ping 1s cubic-bezier(0,0,.2,1)infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-geist-sans);--default-mono-font-family:var(--font-geist-mono)}}@layer base{*,::backdrop,:after,:before{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}:host,html{-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,pre,samp{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}menu,ol,ul{list-style:none}audio,canvas,embed,iframe,img,object,svg,video{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,optgroup,select,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,::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-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,::-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}.custom-scrollbar{scrollbar-width:thin;scrollbar-color:#e4e4e7 transparent;overflow:auto!important}.custom-scrollbar::-webkit-scrollbar{--custom-scrollbar-width:8px;--custom-scrollbar-height:8px;width:var(--custom-scrollbar-width,8px);height:var(--custom-scrollbar-height,8px);touch-action:none}.custom-scrollbar::-webkit-scrollbar-track{background-color:#0000}.custom-scrollbar::-webkit-scrollbar-thumb{opacity:0;visibility:hidden;border-radius:8px;--tw-bg-opacity:1!important;background-color:rgba(228,228,231,var(--tw-bg-opacity))!important}.custom-scrollbar:hover::-webkit-scrollbar-thumb{opacity:1;visibility:visible}.custom-scrollbar::-webkit-scrollbar-thumb:hover{--tw-bg-opacity:1!important;background-color:rgba(82,82,91,var(--tw-bg-opacity))!important}.custom-scrollbar::-webkit-scrollbar-thumb:horizontal{background-color:rgba(228,228,231,var(--tw-bg-opacity))!important}.custom-scrollbar::-webkit-scrollbar-thumb:vertical{background-color:rgba(228,228,231,var(--tw-bg-opacity))!important}.custom-scrollbar:hover::-webkit-scrollbar-thumb:horizontal{background-color:rgba(82,82,91,var(--tw-bg-opacity))!important}.custom-scrollbar:hover::-webkit-scrollbar-thumb:vertical{background-color:rgba(82,82,91,var(--tw-bg-opacity))!important}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden}.absolute,.sr-only{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing)*0)}.inset-x-0{inset-inline:calc(var(--spacing)*0)}.inset-y-0{inset-block:calc(var(--spacing)*0)}.top-0{top:calc(var(--spacing)*0)}.top-1\.5{top:calc(var(--spacing)*1.5)}.top-3\.5{top:calc(var(--spacing)*3.5)}.top-4{top:calc(var(--spacing)*4)}.top-\[50\%\]{top:50%}.right-0{right:calc(var(--spacing)*0)}.right-1{right:calc(var(--spacing)*1)}.right-2{right:calc(var(--spacing)*2)}.right-3{right:calc(var(--spacing)*3)}.right-4{right:calc(var(--spacing)*4)}.bottom-0{bottom:calc(var(--spacing)*0)}.left-\(--sidebar-width\){left:var(--sidebar-width)}.left-0{left:calc(var(--spacing)*0)}.left-2{left:calc(var(--spacing)*2)}.left-\[50\%\]{left:50%}.z-0{z-index:0}.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}.-mx-1{margin-inline:calc(var(--spacing)*-1)}.mx-2{margin-inline:calc(var(--spacing)*2)}.mx-3\.5{margin-inline:calc(var(--spacing)*3.5)}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing)*1)}.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-auto{margin-top:auto}.mr-1{margin-right:calc(var(--spacing)*1)}.mr-2{margin-right:calc(var(--spacing)*2)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-auto{margin-left:auto}.line-clamp-1{-webkit-line-clamp:1;-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-flex{display:inline-flex}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.table-row{display:table-row}.field-sizing-content{field-sizing:content}.aspect-square{aspect-ratio:1}.size-2{width:calc(var(--spacing)*2);height:calc(var(--spacing)*2)}.size-2\.5{width:calc(var(--spacing)*2.5);height:calc(var(--spacing)*2.5)}.size-3{width:calc(var(--spacing)*3);height:calc(var(--spacing)*3)}.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-5{width:calc(var(--spacing)*5);height:calc(var(--spacing)*5)}.size-7{width:calc(var(--spacing)*7);height:calc(var(--spacing)*7)}.size-8{width:calc(var(--spacing)*8);height:calc(var(--spacing)*8)}.size-9{width:calc(var(--spacing)*9);height:calc(var(--spacing)*9)}.size-full{width:100%;height:100%}.h-1\.5{height:calc(var(--spacing)*1.5)}.h-2{height:calc(var(--spacing)*2)}.h-2\.5{height:calc(var(--spacing)*2.5)}.h-3{height:calc(var(--spacing)*3)}.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-24{height:calc(var(--spacing)*24)}.h-64{height:calc(var(--spacing)*64)}.h-\[1\.2rem\]{height:1.2rem}.h-\[1px\]{height:1px}.h-\[calc\(100\%-1px\)\]{height:calc(100% - 1px)}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-base{height:calc(100vh - 6rem)}.h-full{height:100%}.h-px{height:1px}.h-svh{height:100svh}.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-\[90vh\]{max-height:90vh}.max-h-\[300px\]{max-height:300px}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-4{min-height:calc(var(--spacing)*4)}.min-h-16{min-height:calc(var(--spacing)*16)}.min-h-screen{min-height:100vh}.min-h-svh{min-height:100svh}.w-\(--sidebar-width\){width:var(--sidebar-width)}.w-1\.5{width:calc(var(--spacing)*1.5)}.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\/4{width:75%}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-5xl{width:var(--container-5xl)}.w-6{width:calc(var(--spacing)*6)}.w-8{width:calc(var(--spacing)*8)}.w-9{width:calc(var(--spacing)*9)}.w-11{width:calc(var(--spacing)*11)}.w-12{width:calc(var(--spacing)*12)}.w-16{width:calc(var(--spacing)*16)}.w-20{width:calc(var(--spacing)*20)}.w-24{width:calc(var(--spacing)*24)}.w-48{width:calc(var(--spacing)*48)}.w-64{width:calc(var(--spacing)*64)}.w-72{width:calc(var(--spacing)*72)}.w-\[1\.2rem\]{width:1.2rem}.w-\[200px\]{width:200px}.w-\[250px\]{width:250px}.w-auto{width:auto}.w-fit{width:fit-content}.w-full{width:100%}.max-w-\(--skeleton-width\){max-width:var(--skeleton-width)}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-7xl{max-width:var(--container-7xl)}.max-w-72{max-width:calc(var(--spacing)*72)}.max-w-\[240px\]{max-width:240px}.max-w-\[calc\(100\%-2rem\)\]{max-width:calc(100% - 2rem)}.max-w-fit{max-width:fit-content}.max-w-full{max-width:100%}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-5{min-width:calc(var(--spacing)*5)}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.caption-bottom{caption-side:bottom}.origin-\(--radix-dropdown-menu-content-transform-origin\){transform-origin:var(--radix-dropdown-menu-content-transform-origin)}.origin-\(--radix-hover-card-content-transform-origin\){transform-origin:var(--radix-hover-card-content-transform-origin)}.origin-\(--radix-popover-content-transform-origin\){transform-origin:var(--radix-popover-content-transform-origin)}.origin-\(--radix-select-content-transform-origin\){transform-origin:var(--radix-select-content-transform-origin)}.origin-\(--radix-tooltip-content-transform-origin\){transform-origin:var(--radix-tooltip-content-transform-origin)}.-translate-x-1\/2{--tw-translate-x:calc(calc(1/2*100%)*-1)}.-translate-x-1\/2,.-translate-x-px{translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-x-px{--tw-translate-x:-1px}.translate-x-\[-50\%\]{--tw-translate-x:-50%}.translate-x-\[-50\%\],.translate-x-px{translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-px{--tw-translate-x:1px}.translate-y-0\.5{--tw-translate-y:calc(var(--spacing)*.5)}.translate-y-0\.5,.translate-y-\[-50\%\]{translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-y-\[-50\%\]{--tw-translate-y:-50%}.translate-y-\[calc\(-50\%_-_2px\)\]{--tw-translate-y:calc(-50% - 2px);translate:var(--tw-translate-x)var(--tw-translate-y)}.scale-0{--tw-scale-x:0%;--tw-scale-y:0%;--tw-scale-z:0%}.scale-0,.scale-100{scale:var(--tw-scale-x)var(--tw-scale-y)}.scale-100{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%}.rotate-0{rotate:none}.rotate-45{rotate:45deg}.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-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-ping{animation:var(--animate-ping)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.scroll-my-1{scroll-margin-block:calc(var(--spacing)*1)}.scroll-py-1{scroll-padding-block:calc(var(--spacing)*1)}.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-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.grid-cols-\[0_1fr\]{grid-template-columns:0 1fr}.grid-rows-\[auto_auto\]{grid-template-rows:auto auto}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-items-start{justify-items:start}.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)}.gap-8{gap:calc(var(--spacing)*8)}: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-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-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*8)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*8)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-12>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*12)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*12)*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)))}:where(.space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*3)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*4)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-x-reverse)))}.gap-y-0\.5{row-gap:calc(var(--spacing)*.5)}.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{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\[2px\]{border-radius:2px}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:3.40282e+38px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:calc(var(--radius) + 4px)}.rounded-xs{border-radius:var(--radius-xs)}.rounded-t-lg{border-top-left-radius:var(--radius);border-top-right-radius:var(--radius)}.rounded-b-none{border-bottom-right-radius:0;border-bottom-left-radius:0}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-1{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-dashed{--tw-border-style:dashed;border-style:dashed}.border-none{--tw-border-style:none;border-style:none}.border-amber-200{border-color:var(--color-amber-200)}.border-blue-200{border-color:var(--color-blue-200)}.border-border{border-color:var(--border)}.border-destructive{border-color:var(--destructive)}.border-green-200{border-color:var(--color-green-200)}.border-input{border-color:var(--input)}.border-muted-foreground\/30{border-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.border-muted-foreground\/30{border-color:color-mix(in oklab,var(--muted-foreground)30%,transparent)}}.border-primary,.border-primary\/10{border-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.border-primary\/10{border-color:color-mix(in oklab,var(--primary)10%,transparent)}}.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-purple-200{border-color:var(--color-purple-200)}.border-sidebar-border{border-color:var(--sidebar-border)}.border-transparent{border-color:#0000}.border-t-transparent{border-top-color:#0000}.border-l-transparent{border-left-color:#0000}.bg-accent{background-color:var(--accent)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-background{background-color:var(--background)}.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-blue-50{background-color:var(--color-blue-50)}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-500{background-color:var(--color-blue-500)}.bg-border{background-color:var(--border)}.bg-card{background-color:var(--card)}.bg-destructive{background-color:var(--destructive)}.bg-gray-100{background-color:var(--color-gray-100)}.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-600{background-color:var(--color-green-600)}.bg-green-700{background-color:var(--color-green-700)}.bg-green-800{background-color:var(--color-green-800)}.bg-muted,.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-neutral-100{background-color:var(--color-neutral-100)}.bg-orange-500{background-color:var(--color-orange-500)}.bg-popover{background-color:var(--popover)}.bg-primary,.bg-primary\/5{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/5{background-color:color-mix(in oklab,var(--primary)5%,transparent)}}.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\/20{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/20{background-color:color-mix(in oklab,var(--primary)20%,transparent)}}.bg-purple-50{background-color:var(--color-purple-50)}.bg-purple-100{background-color:var(--color-purple-100)}.bg-red-100{background-color:var(--color-red-100)}.bg-secondary{background-color:var(--secondary)}.bg-sidebar{background-color:var(--sidebar)}.bg-sidebar-border{background-color:var(--sidebar-border)}.bg-transparent{background-color:#0000}.bg-yellow-100{background-color:var(--color-yellow-100)}.bg-gradient-to-r{--tw-gradient-position:to right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-primary{--tw-gradient-from:var(--primary);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-primary\/5{--tw-gradient-from:var(--primary)}@supports (color:color-mix(in lab,red,red)){.from-primary\/5{--tw-gradient-from:color-mix(in oklab,var(--primary)5%,transparent)}}.from-primary\/5{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-green-600{--tw-gradient-to:var(--color-green-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-green-600\/5{--tw-gradient-to:#00a5440d}@supports (color:color-mix(in lab,red,red)){.to-green-600\/5{--tw-gradient-to:color-mix(in oklab,var(--color-green-600)5%,transparent)}}.to-green-600\/5{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.fill-current{fill:currentColor}.fill-primary{fill:var(--primary)}.p-0{padding:calc(var(--spacing)*0)}.p-1{padding:calc(var(--spacing)*1)}.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-\[3px\]{padding:3px}.p-px{padding:1px}.px-0{padding-inline:calc(var(--spacing)*0)}.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)}.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-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.py-6{padding-block:calc(var(--spacing)*6)}.py-12{padding-block:calc(var(--spacing)*12)}.pt-0{padding-top:calc(var(--spacing)*0)}.pt-2{padding-top:calc(var(--spacing)*2)}.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-8{padding-right:calc(var(--spacing)*8)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-4{padding-bottom:calc(var(--spacing)*4)}.pb-10{padding-bottom:calc(var(--spacing)*10)}.pb-24{padding-bottom:calc(var(--spacing)*24)}.pl-2{padding-left:calc(var(--spacing)*2)}.pl-8{padding-left:calc(var(--spacing)*8)}.text-center{text-align:center}.text-end{text-align:end}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.font-mono{font-family:var(--font-geist-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--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))}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.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-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.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)}.text-balance{text-wrap:balance}.wrap-anywhere{overflow-wrap:anywhere}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.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-blue-600{color:var(--color-blue-600)}.text-blue-700{color:var(--color-blue-700)}.text-blue-800{color:var(--color-blue-800)}.text-card-foreground{color:var(--card-foreground)}.text-destructive{color:var(--destructive)}.text-foreground{color:var(--foreground)}.text-gray-600{color:var(--color-gray-600)}.text-gray-800{color:var(--color-gray-800)}.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-muted-foreground{color:var(--muted-foreground)}.text-neutral-800{color:var(--color-neutral-800)}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-purple-600{color:var(--color-purple-600)}.text-purple-700{color:var(--color-purple-700)}.text-purple-800{color:var(--color-purple-800)}.text-red-800{color:var(--color-red-800)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-sidebar-foreground,.text-sidebar-foreground\/70{color:var(--sidebar-foreground)}@supports (color:color-mix(in lab,red,red)){.text-sidebar-foreground\/70{color:color-mix(in oklab,var(--sidebar-foreground)70%,transparent)}}.text-transparent{color:#0000}.text-white{color:var(--color-white)}.text-yellow-800{color:var(--color-yellow-800)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.underline-offset-4{text-underline-offset:4px}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.opacity-100{opacity:1}.shadow-\[0_0_0_1px_hsl\(var\(--sidebar-border\)\)\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,hsl(var(--sidebar-border)))}.shadow-\[0_0_0_1px_hsl\(var\(--sidebar-border\)\)\],.shadow-lg{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)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a)}.shadow-md,.shadow-none{box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-none{--tw-shadow:0 0 #0000}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a)}.shadow-sm,.shadow-xs{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)}.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-sidebar-ring{--tw-ring-color:var(--sidebar-ring)}.ring-offset-background{--tw-ring-offset-color:var(--background)}.ring-offset-transparent{--tw-ring-offset-color:transparent}.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}.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,)}.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-\[left\,right\,width\]{transition-property:left,right,width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[margin\,opacity\]{transition-property:margin,opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\,height\,padding\]{transition-property:width,height,padding;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\]{transition-property:width;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-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))}.delay-75{transition-delay:75ms}.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-linear{--tw-ease:linear;transition-timing-function:linear}.delay-75{--tw-animation-delay:75ms;animation-delay:75ms}.fade-in-0{--tw-enter-opacity:0}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.zoom-in-95{--tw-enter-scale:.95}.running{animation-play-state:running}.slide-in-from-left-2{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.slide-in-from-right-2{--tw-enter-translate-x:calc(2*var(--spacing))}.group-focus-within\/menu-item\:opacity-100:is(:where(.group\/menu-item):focus-within *){opacity:1}@media (hover:hover){.group-hover\:bg-primary\/20:is(:where(.group):hover *){background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.group-hover\:bg-primary\/20:is(:where(.group):hover *){background-color:color-mix(in oklab,var(--primary)20%,transparent)}}.group-hover\:text-primary:is(:where(.group):hover *){color:var(--primary)}.group-hover\/menu-item\:opacity-100:is(:where(.group\/menu-item):hover *),.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.group-has-data-\[sidebar\=menu-action\]\/menu-item\:pr-8:is(:where(.group\/menu-item):has([data-sidebar=menu-action]) *){padding-right:calc(var(--spacing)*8)}.group-data-\[collapsible\=icon\]\:-mt-8:is(:where(.group)[data-collapsible=icon] *){margin-top:calc(var(--spacing)*-8)}.group-data-\[collapsible\=icon\]\:hidden:is(:where(.group)[data-collapsible=icon] *){display:none}.group-data-\[collapsible\=icon\]\:size-8\!:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--spacing)*8)!important;height:calc(var(--spacing)*8)!important}.group-data-\[collapsible\=icon\]\:w-\(--sidebar-width-icon\):is(:where(.group)[data-collapsible=icon] *){width:var(--sidebar-width-icon)}.group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\)\]:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--spacing)*4)))}.group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\+2px\)\]:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--spacing)*4)) + 2px)}.group-data-\[collapsible\=icon\]\:overflow-hidden:is(:where(.group)[data-collapsible=icon] *){overflow:hidden}.group-data-\[collapsible\=icon\]\:p-0\!:is(:where(.group)[data-collapsible=icon] *){padding:calc(var(--spacing)*0)!important}.group-data-\[collapsible\=icon\]\:p-2\!:is(:where(.group)[data-collapsible=icon] *){padding:calc(var(--spacing)*2)!important}.group-data-\[collapsible\=icon\]\:opacity-0:is(:where(.group)[data-collapsible=icon] *){opacity:0}.group-data-\[collapsible\=offcanvas\]\:right-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible=offcanvas] *){right:calc(var(--sidebar-width)*-1)}.group-data-\[collapsible\=offcanvas\]\:left-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible=offcanvas] *){left:calc(var(--sidebar-width)*-1)}.group-data-\[collapsible\=offcanvas\]\:w-0:is(:where(.group)[data-collapsible=offcanvas] *){width:calc(var(--spacing)*0)}.group-data-\[collapsible\=offcanvas\]\:translate-x-0:is(:where(.group)[data-collapsible=offcanvas] *){--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.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}.group-data-\[side\=left\]\:-right-4:is(:where(.group)[data-side=left] *){right:calc(var(--spacing)*-4)}.group-data-\[side\=left\]\:border-r:is(:where(.group)[data-side=left] *){border-right-style:var(--tw-border-style);border-right-width:1px}.group-data-\[side\=right\]\:left-0:is(:where(.group)[data-side=right] *){left:calc(var(--spacing)*0)}.group-data-\[side\=right\]\:rotate-180:is(:where(.group)[data-side=right] *){rotate:180deg}.group-data-\[side\=right\]\:border-l:is(:where(.group)[data-side=right] *){border-left-style:var(--tw-border-style);border-left-width:1px}.group-data-\[variant\=floating\]\:rounded-lg:is(:where(.group)[data-variant=floating] *){border-radius:var(--radius)}.group-data-\[variant\=floating\]\:border:is(:where(.group)[data-variant=floating] *){border-style:var(--tw-border-style);border-width:1px}.group-data-\[variant\=floating\]\:border-sidebar-border:is(:where(.group)[data-variant=floating] *){border-color:var(--sidebar-border)}.group-data-\[variant\=floating\]\:shadow-sm:is(:where(.group)[data-variant=floating] *){--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)}@media (hover:hover){.peer-hover\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button):hover~*){color:var(--sidebar-accent-foreground)}}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-50:is(:where(.peer):disabled~*){opacity:.5}.peer-data-\[active\=true\]\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button)[data-active=true]~*){color:var(--sidebar-accent-foreground)}.peer-data-\[size\=default\]\/menu-button\:top-1\.5:is(:where(.peer\/menu-button)[data-size=default]~*){top:calc(var(--spacing)*1.5)}.peer-data-\[size\=lg\]\/menu-button\:top-2\.5:is(:where(.peer\/menu-button)[data-size=lg]~*){top:calc(var(--spacing)*2.5)}.peer-data-\[size\=sm\]\/menu-button\:top-1:is(:where(.peer\/menu-button)[data-size=sm]~*){top:calc(var(--spacing)*1)}.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)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:-inset-2:after{content:var(--tw-content);inset:calc(var(--spacing)*-2)}.after\:inset-y-0:after{content:var(--tw-content);inset-block:calc(var(--spacing)*0)}.after\:left-1\/2:after{content:var(--tw-content);left:50%}.after\:w-\[2px\]:after{content:var(--tw-content);width:2px}.group-data-\[collapsible\=offcanvas\]\:after\:left-full:is(:where(.group)[data-collapsible=offcanvas] *):after{content:var(--tw-content);left:100%}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media (hover:hover){.hover\:border:hover{border-style:var(--tw-border-style);border-width:1px}.hover\:border-primary\/50:hover{border-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:border-primary\/50:hover{border-color:color-mix(in oklab,var(--primary)50%,transparent)}}.hover\:bg-accent:hover{background-color:var(--accent)}.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\/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\/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-secondary:hover,.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-sidebar-accent:hover{background-color:var(--sidebar-accent)}.hover\:bg-transparent:hover{background-color:#0000}.hover\:text-accent-foreground:hover{color:var(--accent-foreground)}.hover\:text-sidebar-accent-foreground:hover{color:var(--sidebar-accent-foreground)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-\[0_0_0_1px_hsl\(var\(--sidebar-accent\)\)\]:hover{--tw-shadow:0 0 0 1px var(--tw-shadow-color,hsl(var(--sidebar-accent)))}.hover\:shadow-\[0_0_0_1px_hsl\(var\(--sidebar-accent\)\)\]:hover,.hover\:shadow-lg:hover{box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a)}.hover\:shadow-xl:hover{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px 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)}.hover\:group-data-\[collapsible\=offcanvas\]\:bg-sidebar:hover:is(:where(.group)[data-collapsible=offcanvas] *){background-color:var(--sidebar)}.hover\:after\:bg-sidebar-border:hover:after{content:var(--tw-content);background-color:var(--sidebar-border)}}.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-hidden:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.focus\:outline-hidden:focus{outline-offset:2px;outline:2px solid #0000}}.focus-visible\:border-ring:focus-visible{border-color:var(--ring)}.focus-visible\:ring-0:focus-visible{--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)}.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-1:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\:outline-ring:focus-visible{outline-color:var(--ring)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.active\:bg-sidebar-accent:active{background-color:var(--sidebar-accent)}.active\:text-sidebar-accent-foreground:active{color:var(--sidebar-accent-foreground)}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}:where([data-side=left]) .in-data-\[side\=left\]\:cursor-w-resize{cursor:w-resize}:where([data-side=right]) .in-data-\[side\=right\]\:cursor-e-resize{cursor:e-resize}.has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has([data-slot=card-action]){grid-template-columns:1fr auto}.has-data-\[variant\=inset\]\:bg-sidebar:has([data-variant=inset]){background-color:var(--sidebar)}.has-\[\>svg\]\:grid-cols-\[calc\(var\(--spacing\)\*4\)_1fr\]:has(>svg){grid-template-columns:calc(var(--spacing)*4)1fr}.has-\[\>svg\]\:gap-x-3:has(>svg){column-gap:calc(var(--spacing)*3)}.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-disabled\:pointer-events-none[aria-disabled=true]{pointer-events:none}.aria-disabled\:opacity-50[aria-disabled=true]{opacity:.5}.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-\[active\=true\]\:bg-sidebar-accent[data-active=true]{background-color:var(--sidebar-accent)}.data-\[active\=true\]\:font-medium[data-active=true]{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.data-\[active\=true\]\:text-sidebar-accent-foreground[data-active=true]{color:var(--sidebar-accent-foreground)}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[disabled\=true\]\:pointer-events-none[data-disabled=true]{pointer-events:none}.data-\[disabled\=true\]\:opacity-50[data-disabled=true]{opacity:.5}.data-\[inset\]\:pl-8[data-inset]{padding-left:calc(var(--spacing)*8)}.data-\[orientation\=horizontal\]\:h-px[data-orientation=horizontal]{height:1px}.data-\[orientation\=horizontal\]\:w-full[data-orientation=horizontal]{width:100%}.data-\[orientation\=vertical\]\:h-full[data-orientation=vertical]{height:100%}.data-\[orientation\=vertical\]\:w-px[data-orientation=vertical]{width:1px}.data-\[placeholder\]\:text-muted-foreground[data-placeholder]{color:var(--muted-foreground)}.data-\[selected\=true\]\:bg-accent[data-selected=true]{background-color:var(--accent)}.data-\[selected\=true\]\:text-accent-foreground[data-selected=true]{color:var(--accent-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\=alert-description\]\:text-destructive\/90>*)[data-slot=alert-description]{color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){:is(.\*\:data-\[slot\=alert-description\]\:text-destructive\/90>*)[data-slot=alert-description]{color:color-mix(in oklab,var(--destructive)90%,transparent)}}:is(.\*\*\:data-\[slot\=command-input-wrapper\]\:h-12 *)[data-slot=command-input-wrapper]{height:calc(var(--spacing)*12)}: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\]\:shadow-sm[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-5[data-state=checked]{--tw-translate-x:calc(var(--spacing)*5);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:var(--primary)}.data-\[state\=closed\]\:animate-accordion-up[data-state=closed]{animation:accordion-up var(--tw-animation-duration,var(--tw-duration,.2s))ease-out}.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\]\:duration-300[data-state=closed]{--tw-duration:.3s;transition-duration:.3s}.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\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y:100%}.data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x:-100%}.data-\[state\=closed\]\:slide-out-to-right[data-state=closed]{--tw-exit-translate-x:100%}.data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y:-100%}.data-\[state\=open\]\:animate-accordion-down[data-state=open]{animation:accordion-down var(--tw-animation-duration,var(--tw-duration,.2s))ease-out}.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\]\:bg-secondary[data-state=open]{background-color:var(--secondary)}.data-\[state\=open\]\:text-accent-foreground[data-state=open]{color:var(--accent-foreground)}.data-\[state\=open\]\:text-muted-foreground[data-state=open]{color:var(--muted-foreground)}.data-\[state\=open\]\:opacity-100[data-state=open]{opacity:1}.data-\[state\=open\]\:duration-500[data-state=open]{--tw-duration:.5s;transition-duration:.5s}.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\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y:100%}.data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x:-100%}.data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x:100%}.data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y:-100%}@media (hover:hover){.data-\[state\=open\]\:hover\:bg-sidebar-accent[data-state=open]:hover{background-color:var(--sidebar-accent)}.data-\[state\=open\]\:hover\:text-sidebar-accent-foreground[data-state=open]:hover{color:var(--sidebar-accent-foreground)}}.data-\[state\=selected\]\:bg-muted[data-state=selected]{background-color:var(--muted)}.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\:mr-3{margin-right:calc(var(--spacing)*3)}.sm\:flex{display:flex}.sm\:max-w-2xl{max-width:var(--container-2xl)}.sm\:max-w-4xl{max-width:var(--container-4xl)}.sm\:max-w-lg{max-width:var(--container-lg)}.sm\:max-w-sm{max-width:var(--container-sm)}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:px-4{padding-inline:calc(var(--spacing)*4)}.sm\:text-left{text-align:left}.sm\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}}@media (min-width:48rem){.md\:block{display:block}.md\:flex{display:flex}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.md\:opacity-0{opacity:0}.md\:peer-data-\[variant\=inset\]\:m-2:is(:where(.peer)[data-variant=inset]~*){margin:calc(var(--spacing)*2)}.md\:peer-data-\[variant\=inset\]\:ml-0:is(:where(.peer)[data-variant=inset]~*){margin-left:calc(var(--spacing)*0)}.md\:peer-data-\[variant\=inset\]\:rounded-xl:is(:where(.peer)[data-variant=inset]~*){border-radius:calc(var(--radius) + 4px)}.md\:peer-data-\[variant\=inset\]\:shadow-sm:is(:where(.peer)[data-variant=inset]~*){--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)}.md\:peer-data-\[variant\=inset\]\:peer-data-\[state\=collapsed\]\:ml-2:is(:where(.peer)[data-variant=inset]~*):is(:where(.peer)[data-state=collapsed]~*){margin-left:calc(var(--spacing)*2)}.md\:after\:hidden:after{content:var(--tw-content);display:none}}@media (min-width:64rem){.lg\:grid-cols-3{grid-template-columns:repeat(3,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-amber-800:is(.dark *){border-color:var(--color-amber-800)}.dark\:border-blue-800:is(.dark *){border-color:var(--color-blue-800)}.dark\:border-green-800:is(.dark *){border-color:var(--color-green-800)}.dark\:border-input:is(.dark *){border-color:var(--input)}.dark\:border-purple-800:is(.dark *){border-color:var(--color-purple-800)}.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-blue-900:is(.dark *){background-color:var(--color-blue-900)}.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-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-green-200:is(.dark *){background-color:var(--color-green-200)}.dark\:bg-green-900:is(.dark *){background-color:var(--color-green-900)}.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-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-purple-900:is(.dark *){background-color:var(--color-purple-900)}.dark\:bg-purple-950\/20:is(.dark *){background-color:#3c036633}@supports (color:color-mix(in lab,red,red)){.dark\:bg-purple-950\/20:is(.dark *){background-color:color-mix(in oklab,var(--color-purple-950)20%,transparent)}}.dark\:text-amber-200:is(.dark *){color:var(--color-amber-200)}.dark\:text-amber-300:is(.dark *){color:var(--color-amber-300)}.dark\:text-blue-200:is(.dark *){color:var(--color-blue-200)}.dark\:text-blue-300:is(.dark *){color:var(--color-blue-300)}.dark\:text-green-200:is(.dark *){color:var(--color-green-200)}.dark\:text-green-300:is(.dark *){color:var(--color-green-300)}.dark\:text-muted-foreground:is(.dark *){color:var(--muted-foreground)}.dark\:text-purple-200:is(.dark *){color:var(--color-purple-200)}.dark\:text-purple-300:is(.dark *){color:var(--color-purple-300)}@media (hover:hover){.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-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\: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\=active\]\:border-input:is(.dark *)[data-state=active]{border-color:var(--input)}.dark\:data-\[state\=active\]\:bg-input\/30:is(.dark *)[data-state=active]{background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:data-\[state\=active\]\:bg-input\/30:is(.dark *)[data-state=active]{background-color:color-mix(in oklab,var(--input)30%,transparent)}}.dark\:data-\[state\=active\]\:text-foreground:is(.dark *)[data-state=active]{color:var(--foreground)}.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)}}.\[\&_\[cmdk-group-heading\]\]\:px-2 [cmdk-group-heading]{padding-inline:calc(var(--spacing)*2)}.\[\&_\[cmdk-group-heading\]\]\:py-1\.5 [cmdk-group-heading]{padding-block:calc(var(--spacing)*1.5)}.\[\&_\[cmdk-group-heading\]\]\:text-xs [cmdk-group-heading]{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.\[\&_\[cmdk-group-heading\]\]\:font-medium [cmdk-group-heading]{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.\[\&_\[cmdk-group-heading\]\]\:text-muted-foreground [cmdk-group-heading]{color:var(--muted-foreground)}.\[\&_\[cmdk-group\]\]\:px-2 [cmdk-group]{padding-inline:calc(var(--spacing)*2)}.\[\&_\[cmdk-group\]\:not\(\[hidden\]\)_\~\[cmdk-group\]\]\:pt-0 [cmdk-group]:not([hidden])~[cmdk-group]{padding-top:calc(var(--spacing)*0)}.\[\&_\[cmdk-input-wrapper\]_svg\]\:h-5 [cmdk-input-wrapper] svg{height:calc(var(--spacing)*5)}.\[\&_\[cmdk-input-wrapper\]_svg\]\:w-5 [cmdk-input-wrapper] svg{width:calc(var(--spacing)*5)}.\[\&_\[cmdk-input\]\]\:h-12 [cmdk-input]{height:calc(var(--spacing)*12)}.\[\&_\[cmdk-item\]\]\:px-2 [cmdk-item]{padding-inline:calc(var(--spacing)*2)}.\[\&_\[cmdk-item\]\]\:py-3 [cmdk-item]{padding-block:calc(var(--spacing)*3)}.\[\&_\[cmdk-item\]_svg\]\:h-5 [cmdk-item] svg{height:calc(var(--spacing)*5)}.\[\&_\[cmdk-item\]_svg\]\:w-5 [cmdk-item] svg{width:calc(var(--spacing)*5)}.\[\&_p\]\:leading-relaxed p{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:invisible svg{visibility:hidden}.\[\&_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)}.\[\&_tr\]\:border-b tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.\[\&_tr\:last-child\]\:border-0 tr:last-child{border-style:var(--tw-border-style);border-width:0}.\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]){padding-right:calc(var(--spacing)*0)}.\[\.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}.\[\&\>\[role\=checkbox\]\]\:translate-y-\[2px\]>[role=checkbox]{--tw-translate-y:2px;translate:var(--tw-translate-x)var(--tw-translate-y)}.\[\&\>button\]\:hidden>button{display:none}.\[\&\>span\:last-child\]\:truncate>span:last-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.\[\&\>svg\]\:pointer-events-none>svg{pointer-events:none}.\[\&\>svg\]\:size-3>svg{width:calc(var(--spacing)*3);height:calc(var(--spacing)*3)}.\[\&\>svg\]\:size-4>svg{width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.\[\&\>svg\]\:shrink-0>svg{flex-shrink:0}.\[\&\>svg\]\:translate-y-0\.5>svg{--tw-translate-y:calc(var(--spacing)*.5);translate:var(--tw-translate-x)var(--tw-translate-y)}.\[\&\>svg\]\:text-current>svg{color:currentColor}.\[\&\>svg\]\:text-sidebar-accent-foreground>svg{color:var(--sidebar-accent-foreground)}.\[\&\>tr\]\:last\:border-b-0>tr:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.\[\&\[data-state\=open\]\>svg\]\:rotate-180[data-state=open]>svg{rotate:180deg}[data-side=left][data-collapsible=offcanvas] .\[\[data-side\=left\]\[data-collapsible\=offcanvas\]_\&\]\:-right-2{right:calc(var(--spacing)*-2)}[data-side=left][data-state=collapsed] .\[\[data-side\=left\]\[data-state\=collapsed\]_\&\]\:cursor-e-resize{cursor:e-resize}[data-side=right][data-collapsible=offcanvas] .\[\[data-side\=right\]\[data-collapsible\=offcanvas\]_\&\]\:-left-2{left:calc(var(--spacing)*-2)}[data-side=right][data-state=collapsed] .\[\[data-side\=right\]\[data-state\=collapsed\]_\&\]\:cursor-w-resize{cursor:w-resize}@media (hover:hover){a.\[a\&\]\:hover\:bg-accent:hover{background-color:var(--accent)}a.\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab,var(--destructive)90%,transparent)}}a.\[a\&\]\:hover\:bg-green-700\/90:hover{background-color:#008138e6}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-green-700\/90:hover{background-color:color-mix(in oklab,var(--color-green-700)90%,transparent)}}a.\[a\&\]\:hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--primary)90%,transparent)}}a.\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:color-mix(in oklab,var(--secondary)90%,transparent)}}a.\[a\&\]\:hover\:text-accent-foreground:hover{color:var(--accent-foreground)}}}@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-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-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.1% .005 285.823);--card:oklch(100% 0 0);--card-foreground:oklch(14.1% .005 285.823);--popover:oklch(100% 0 0);--popover-foreground:oklch(14.1% .005 285.823);--primary:oklch(50.81% .1049 165.61);--primary-foreground:oklch(98.5% 0 0);--secondary:oklch(96.7% .001 286.375);--secondary-foreground:oklch(21% .006 285.885);--muted:oklch(96.7% .001 286.375);--muted-foreground:oklch(55.2% .016 285.938);--accent:oklch(96.7% .001 286.375);--accent-foreground:oklch(21% .006 285.885);--destructive:oklch(57.7% .245 27.325);--border:oklch(92% .004 286.32);--input:oklch(92% .004 286.32);--ring:oklch(70.5% .015 286.067);--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.1% .005 285.823);--sidebar-primary:oklch(21% .006 285.885);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(96.7% .001 286.375);--sidebar-accent-foreground:oklch(21% .006 285.885);--sidebar-border:oklch(92% .004 286.32);--sidebar-ring:oklch(70.5% .015 286.067)}.dark{--background:oklch(14.1% .005 285.823);--foreground:oklch(98.5% 0 0);--card:oklch(21% .006 285.885);--card-foreground:oklch(98.5% 0 0);--popover:oklch(21% .006 285.885);--popover-foreground:oklch(98.5% 0 0);--primary:oklch(92% .004 286.32);--primary-foreground:oklch(21% .006 285.885);--secondary:oklch(27.4% .006 286.033);--secondary-foreground:oklch(98.5% 0 0);--muted:oklch(27.4% .006 286.033);--muted-foreground:oklch(70.5% .015 286.067);--accent:oklch(27.4% .006 286.033);--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.2% .016 285.938);--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(21% .006 285.885);--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(27.4% .006 286.033);--sidebar-accent-foreground:oklch(98.5% 0 0);--sidebar-border:oklch(100% 0 0/.1);--sidebar-ring:oklch(55.2% .016 285.938)}@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-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"";inherits:false;initial-value:0}@property --tw-gradient-via-position{syntax:"";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{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-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes spin{to{transform:rotate(1turn)}}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}@keyframes pulse{50%{opacity:.5}}@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))}}@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))}}@keyframes accordion-down{0%{height:0}to{height:var(--radix-accordion-content-height,var(--bits-accordion-content-height,var(--reka-accordion-content-height,var(--kb-accordion-content-height,auto))))}}@keyframes accordion-up{0%{height:var(--radix-accordion-content-height,var(--bits-accordion-content-height,var(--reka-accordion-content-height,var(--kb-accordion-content-height,auto))))}to{height:0}}@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/8d697b304b401681-s.woff2) format("woff2");unicode-range:u+0301,u+0400-045f,u+0490-0491,u+04b0-04b1,u+2116}@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/ba015fad6dcf6784-s.woff2) format("woff2");unicode-range:u+0100-02ba,u+02bd-02c5,u+02c7-02cc,u+02ce-02d7,u+02dd-02ff,u+0304,u+0308,u+0329,u+1d00-1dbf,u+1e00-1e9f,u+1ef2-1eff,u+2020,u+20a0-20ab,u+20ad-20c0,u+2113,u+2c60-2c7f,u+a720-a7ff}@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/569ce4b8f30dc480-s.p.woff2) format("woff2");unicode-range:u+00??,u+0131,u+0152-0153,u+02bb-02bc,u+02c6,u+02da,u+02dc,u+0304,u+0308,u+0329,u+2000-206f,u+20ac,u+2122,u+2191,u+2193,u+2212,u+2215,u+feff,u+fffd}@font-face{font-family:Geist Fallback;src:local("Arial");ascent-override:95.94%;descent-override:28.16%;line-gap-override:0.00%;size-adjust:104.76%}.__className_5cfdac{font-family:Geist,Geist Fallback;font-style:normal}.__variable_5cfdac{--font-geist-sans:"Geist","Geist Fallback"}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/9610d9e46709d722-s.woff2) format("woff2");unicode-range:u+0301,u+0400-045f,u+0490-0491,u+04b0-04b1,u+2116}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/747892c23ea88013-s.woff2) format("woff2");unicode-range:u+0100-02ba,u+02bd-02c5,u+02c7-02cc,u+02ce-02d7,u+02dd-02ff,u+0304,u+0308,u+0329,u+1d00-1dbf,u+1e00-1e9f,u+1ef2-1eff,u+2020,u+20a0-20ab,u+20ad-20c0,u+2113,u+2c60-2c7f,u+a720-a7ff}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/93f479601ee12b01-s.p.woff2) format("woff2");unicode-range:u+00??,u+0131,u+0152-0153,u+02bb-02bc,u+02c6,u+02da,u+02dc,u+0304,u+0308,u+0329,u+2000-206f,u+20ac,u+2122,u+2191,u+2193,u+2212,u+2215,u+feff,u+fffd}@font-face{font-family:Geist Mono Fallback;src:local("Arial");ascent-override:74.67%;descent-override:21.92%;line-gap-override:0.00%;size-adjust:134.59%}.__className_9a8899{font-family:Geist Mono,Geist Mono Fallback;font-style:normal}.__variable_9a8899{--font-geist-mono:"Geist Mono","Geist Mono Fallback"} \ No newline at end of file diff --git a/transports/bifrost-http/ui/_next/static/css/e2d791eafd0cc299.css b/transports/bifrost-http/ui/_next/static/css/e2d791eafd0cc299.css new file mode 100644 index 0000000000..f6da07ab3b --- /dev/null +++ b/transports/bifrost-http/ui/_next/static/css/e2d791eafd0cc299.css @@ -0,0 +1 @@ +/*! tailwindcss v4.1.11 | 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)))){*,::backdrop,:after,:before{--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-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction: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-duration:initial;--tw-ease:initial;--tw-content:"";--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y: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{:host,:root{--color-red-100:oklch(93.6% .032 17.717);--color-red-800:oklch(44.4% .177 26.899);--color-orange-500:oklch(70.5% .213 47.604);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--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-950:oklch(27.9% .077 45.635);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-800:oklch(47.6% .114 61.907);--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-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-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-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-200:oklch(90.2% .063 306.703);--color-purple-300:oklch(82.7% .119 306.383);--color-purple-600:oklch(55.8% .288 302.321);--color-purple-700:oklch(49.6% .265 301.924);--color-purple-800:oklch(43.8% .218 303.724);--color-purple-900:oklch(38.1% .176 304.987);--color-purple-950:oklch(29.1% .149 302.717);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-800:oklch(27.8% .033 256.848);--color-neutral-100:oklch(97% 0 0);--color-neutral-800:oklch(26.9% 0 0);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--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:calc(1.5/1);--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);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25/1.875);--text-5xl:3rem;--text-5xl--line-height:1;--font-weight-normal:400;--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-relaxed:1.625;--radius-xs:.125rem;--radius-2xl:1rem;--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-ping:ping 1s cubic-bezier(0,0,.2,1)infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-geist-sans);--default-mono-font-family:var(--font-geist-mono)}}@layer base{*,::backdrop,:after,:before{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}:host,html{-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,pre,samp{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}menu,ol,ul{list-style:none}audio,canvas,embed,iframe,img,object,svg,video{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,optgroup,select,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,::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-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,::-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}.custom-scrollbar{scrollbar-width:thin;scrollbar-color:#e4e4e7 transparent;overflow:auto!important}.custom-scrollbar::-webkit-scrollbar{--custom-scrollbar-width:8px;--custom-scrollbar-height:8px;width:var(--custom-scrollbar-width,8px);height:var(--custom-scrollbar-height,8px);touch-action:none}.custom-scrollbar::-webkit-scrollbar-track{background-color:#0000}.custom-scrollbar::-webkit-scrollbar-thumb{opacity:0;visibility:hidden;border-radius:8px;--tw-bg-opacity:1!important;background-color:rgba(228,228,231,var(--tw-bg-opacity))!important}.custom-scrollbar:hover::-webkit-scrollbar-thumb{opacity:1;visibility:visible}.custom-scrollbar::-webkit-scrollbar-thumb:hover{--tw-bg-opacity:1!important;background-color:rgba(82,82,91,var(--tw-bg-opacity))!important}.custom-scrollbar::-webkit-scrollbar-thumb:horizontal{background-color:rgba(228,228,231,var(--tw-bg-opacity))!important}.custom-scrollbar::-webkit-scrollbar-thumb:vertical{background-color:rgba(228,228,231,var(--tw-bg-opacity))!important}.custom-scrollbar:hover::-webkit-scrollbar-thumb:horizontal{background-color:rgba(82,82,91,var(--tw-bg-opacity))!important}.custom-scrollbar:hover::-webkit-scrollbar-thumb:vertical{background-color:rgba(82,82,91,var(--tw-bg-opacity))!important}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden}.absolute,.sr-only{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing)*0)}.inset-x-0{inset-inline:calc(var(--spacing)*0)}.inset-y-0{inset-block:calc(var(--spacing)*0)}.top-0{top:calc(var(--spacing)*0)}.top-1\.5{top:calc(var(--spacing)*1.5)}.top-3\.5{top:calc(var(--spacing)*3.5)}.top-4{top:calc(var(--spacing)*4)}.top-\[50\%\]{top:50%}.right-0{right:calc(var(--spacing)*0)}.right-1{right:calc(var(--spacing)*1)}.right-2{right:calc(var(--spacing)*2)}.right-3{right:calc(var(--spacing)*3)}.right-4{right:calc(var(--spacing)*4)}.bottom-0{bottom:calc(var(--spacing)*0)}.left-\(--sidebar-width\){left:var(--sidebar-width)}.left-0{left:calc(var(--spacing)*0)}.left-2{left:calc(var(--spacing)*2)}.left-\[50\%\]{left:50%}.z-0{z-index:0}.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}.-mx-1{margin-inline:calc(var(--spacing)*-1)}.mx-2{margin-inline:calc(var(--spacing)*2)}.mx-3\.5{margin-inline:calc(var(--spacing)*3.5)}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing)*1)}.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-auto{margin-top:auto}.mr-1{margin-right:calc(var(--spacing)*1)}.mr-2{margin-right:calc(var(--spacing)*2)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-auto{margin-left:auto}.line-clamp-1{-webkit-line-clamp:1;-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-flex{display:inline-flex}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.table-row{display:table-row}.field-sizing-content{field-sizing:content}.aspect-square{aspect-ratio:1}.size-2{width:calc(var(--spacing)*2);height:calc(var(--spacing)*2)}.size-2\.5{width:calc(var(--spacing)*2.5);height:calc(var(--spacing)*2.5)}.size-3{width:calc(var(--spacing)*3);height:calc(var(--spacing)*3)}.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-5{width:calc(var(--spacing)*5);height:calc(var(--spacing)*5)}.size-7{width:calc(var(--spacing)*7);height:calc(var(--spacing)*7)}.size-8{width:calc(var(--spacing)*8);height:calc(var(--spacing)*8)}.size-9{width:calc(var(--spacing)*9);height:calc(var(--spacing)*9)}.size-full{width:100%;height:100%}.h-1\.5{height:calc(var(--spacing)*1.5)}.h-2{height:calc(var(--spacing)*2)}.h-2\.5{height:calc(var(--spacing)*2.5)}.h-3{height:calc(var(--spacing)*3)}.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-24{height:calc(var(--spacing)*24)}.h-64{height:calc(var(--spacing)*64)}.h-\[1\.2rem\]{height:1.2rem}.h-\[1px\]{height:1px}.h-\[calc\(100\%-1px\)\]{height:calc(100% - 1px)}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-base{height:calc(100vh - 6rem)}.h-full{height:100%}.h-px{height:1px}.h-svh{height:100svh}.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-\[90vh\]{max-height:90vh}.max-h-\[300px\]{max-height:300px}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-4{min-height:calc(var(--spacing)*4)}.min-h-16{min-height:calc(var(--spacing)*16)}.min-h-screen{min-height:100vh}.min-h-svh{min-height:100svh}.w-\(--sidebar-width\){width:var(--sidebar-width)}.w-1\.5{width:calc(var(--spacing)*1.5)}.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\/4{width:75%}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-5xl{width:var(--container-5xl)}.w-6{width:calc(var(--spacing)*6)}.w-8{width:calc(var(--spacing)*8)}.w-9{width:calc(var(--spacing)*9)}.w-11{width:calc(var(--spacing)*11)}.w-12{width:calc(var(--spacing)*12)}.w-16{width:calc(var(--spacing)*16)}.w-20{width:calc(var(--spacing)*20)}.w-24{width:calc(var(--spacing)*24)}.w-48{width:calc(var(--spacing)*48)}.w-64{width:calc(var(--spacing)*64)}.w-72{width:calc(var(--spacing)*72)}.w-\[1\.2rem\]{width:1.2rem}.w-\[200px\]{width:200px}.w-\[250px\]{width:250px}.w-auto{width:auto}.w-fit{width:fit-content}.w-full{width:100%}.max-w-\(--skeleton-width\){max-width:var(--skeleton-width)}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-7xl{max-width:var(--container-7xl)}.max-w-72{max-width:calc(var(--spacing)*72)}.max-w-\[240px\]{max-width:240px}.max-w-\[calc\(100\%-2rem\)\]{max-width:calc(100% - 2rem)}.max-w-fit{max-width:fit-content}.max-w-full{max-width:100%}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-5{min-width:calc(var(--spacing)*5)}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.caption-bottom{caption-side:bottom}.origin-\(--radix-dropdown-menu-content-transform-origin\){transform-origin:var(--radix-dropdown-menu-content-transform-origin)}.origin-\(--radix-hover-card-content-transform-origin\){transform-origin:var(--radix-hover-card-content-transform-origin)}.origin-\(--radix-popover-content-transform-origin\){transform-origin:var(--radix-popover-content-transform-origin)}.origin-\(--radix-select-content-transform-origin\){transform-origin:var(--radix-select-content-transform-origin)}.origin-\(--radix-tooltip-content-transform-origin\){transform-origin:var(--radix-tooltip-content-transform-origin)}.-translate-x-1\/2{--tw-translate-x:calc(calc(1/2*100%)*-1)}.-translate-x-1\/2,.-translate-x-px{translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-x-px{--tw-translate-x:-1px}.translate-x-\[-50\%\]{--tw-translate-x:-50%}.translate-x-\[-50\%\],.translate-x-px{translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-px{--tw-translate-x:1px}.translate-y-0\.5{--tw-translate-y:calc(var(--spacing)*.5)}.translate-y-0\.5,.translate-y-\[-50\%\]{translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-y-\[-50\%\]{--tw-translate-y:-50%}.translate-y-\[calc\(-50\%_-_2px\)\]{--tw-translate-y:calc(-50% - 2px);translate:var(--tw-translate-x)var(--tw-translate-y)}.scale-0{--tw-scale-x:0%;--tw-scale-y:0%;--tw-scale-z:0%}.scale-0,.scale-100{scale:var(--tw-scale-x)var(--tw-scale-y)}.scale-100{--tw-scale-x:100%;--tw-scale-y:100%;--tw-scale-z:100%}.rotate-0{rotate:none}.rotate-45{rotate:45deg}.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-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-ping{animation:var(--animate-ping)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.scroll-my-1{scroll-margin-block:calc(var(--spacing)*1)}.scroll-py-1{scroll-padding-block:calc(var(--spacing)*1)}.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-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.grid-cols-\[0_1fr\]{grid-template-columns:0 1fr}.grid-rows-\[auto_auto\]{grid-template-rows:auto auto}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-items-start{justify-items:start}.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)}.gap-8{gap:calc(var(--spacing)*8)}: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-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-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*8)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*8)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-12>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*12)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*12)*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)))}:where(.space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*3)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*4)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-x-reverse)))}.gap-y-0\.5{row-gap:calc(var(--spacing)*.5)}.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{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\[2px\]{border-radius:2px}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:3.40282e+38px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:calc(var(--radius) + 4px)}.rounded-xs{border-radius:var(--radius-xs)}.rounded-t-lg{border-top-left-radius:var(--radius);border-top-right-radius:var(--radius)}.rounded-b-none{border-bottom-right-radius:0;border-bottom-left-radius:0}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-1{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-dashed{--tw-border-style:dashed;border-style:dashed}.border-none{--tw-border-style:none;border-style:none}.border-amber-200{border-color:var(--color-amber-200)}.border-blue-200{border-color:var(--color-blue-200)}.border-border{border-color:var(--border)}.border-destructive{border-color:var(--destructive)}.border-green-200{border-color:var(--color-green-200)}.border-input{border-color:var(--input)}.border-muted-foreground\/30{border-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.border-muted-foreground\/30{border-color:color-mix(in oklab,var(--muted-foreground)30%,transparent)}}.border-primary,.border-primary\/10{border-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.border-primary\/10{border-color:color-mix(in oklab,var(--primary)10%,transparent)}}.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-purple-200{border-color:var(--color-purple-200)}.border-sidebar-border{border-color:var(--sidebar-border)}.border-transparent{border-color:#0000}.border-t-transparent{border-top-color:#0000}.border-l-transparent{border-left-color:#0000}.bg-accent{background-color:var(--accent)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-background{background-color:var(--background)}.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-blue-50{background-color:var(--color-blue-50)}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-500{background-color:var(--color-blue-500)}.bg-border{background-color:var(--border)}.bg-card{background-color:var(--card)}.bg-destructive{background-color:var(--destructive)}.bg-gray-100{background-color:var(--color-gray-100)}.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-600{background-color:var(--color-green-600)}.bg-green-700{background-color:var(--color-green-700)}.bg-green-800{background-color:var(--color-green-800)}.bg-muted,.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-neutral-100{background-color:var(--color-neutral-100)}.bg-orange-500{background-color:var(--color-orange-500)}.bg-popover{background-color:var(--popover)}.bg-primary,.bg-primary\/5{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/5{background-color:color-mix(in oklab,var(--primary)5%,transparent)}}.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\/20{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\/20{background-color:color-mix(in oklab,var(--primary)20%,transparent)}}.bg-purple-50{background-color:var(--color-purple-50)}.bg-purple-100{background-color:var(--color-purple-100)}.bg-red-100{background-color:var(--color-red-100)}.bg-secondary{background-color:var(--secondary)}.bg-sidebar{background-color:var(--sidebar)}.bg-sidebar-border{background-color:var(--sidebar-border)}.bg-transparent{background-color:#0000}.bg-yellow-100{background-color:var(--color-yellow-100)}.bg-gradient-to-r{--tw-gradient-position:to right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-primary{--tw-gradient-from:var(--primary);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-primary\/5{--tw-gradient-from:var(--primary)}@supports (color:color-mix(in lab,red,red)){.from-primary\/5{--tw-gradient-from:color-mix(in oklab,var(--primary)5%,transparent)}}.from-primary\/5{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-green-600{--tw-gradient-to:var(--color-green-600);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-green-600\/5{--tw-gradient-to:#00a5440d}@supports (color:color-mix(in lab,red,red)){.to-green-600\/5{--tw-gradient-to:color-mix(in oklab,var(--color-green-600)5%,transparent)}}.to-green-600\/5{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.fill-current{fill:currentColor}.fill-primary{fill:var(--primary)}.p-0{padding:calc(var(--spacing)*0)}.p-1{padding:calc(var(--spacing)*1)}.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-\[3px\]{padding:3px}.p-px{padding:1px}.px-0{padding-inline:calc(var(--spacing)*0)}.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)}.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-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.py-6{padding-block:calc(var(--spacing)*6)}.py-12{padding-block:calc(var(--spacing)*12)}.pt-0{padding-top:calc(var(--spacing)*0)}.pt-2{padding-top:calc(var(--spacing)*2)}.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-8{padding-right:calc(var(--spacing)*8)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-4{padding-bottom:calc(var(--spacing)*4)}.pb-10{padding-bottom:calc(var(--spacing)*10)}.pb-24{padding-bottom:calc(var(--spacing)*24)}.pl-2{padding-left:calc(var(--spacing)*2)}.pl-8{padding-left:calc(var(--spacing)*8)}.text-center{text-align:center}.text-end{text-align:end}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.font-mono{font-family:var(--font-geist-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-5xl{font-size:var(--text-5xl);line-height:var(--tw-leading,var(--text-5xl--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))}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.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-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.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)}.text-balance{text-wrap:balance}.wrap-anywhere{overflow-wrap:anywhere}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.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-blue-600{color:var(--color-blue-600)}.text-blue-700{color:var(--color-blue-700)}.text-blue-800{color:var(--color-blue-800)}.text-card-foreground{color:var(--card-foreground)}.text-destructive{color:var(--destructive)}.text-foreground{color:var(--foreground)}.text-gray-600{color:var(--color-gray-600)}.text-gray-800{color:var(--color-gray-800)}.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-muted-foreground{color:var(--muted-foreground)}.text-neutral-800{color:var(--color-neutral-800)}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-purple-600{color:var(--color-purple-600)}.text-purple-700{color:var(--color-purple-700)}.text-purple-800{color:var(--color-purple-800)}.text-red-800{color:var(--color-red-800)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-sidebar-foreground,.text-sidebar-foreground\/70{color:var(--sidebar-foreground)}@supports (color:color-mix(in lab,red,red)){.text-sidebar-foreground\/70{color:color-mix(in oklab,var(--sidebar-foreground)70%,transparent)}}.text-transparent{color:#0000}.text-white{color:var(--color-white)}.text-yellow-800{color:var(--color-yellow-800)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.underline-offset-4{text-underline-offset:4px}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.opacity-100{opacity:1}.shadow-\[0_0_0_1px_hsl\(var\(--sidebar-border\)\)\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,hsl(var(--sidebar-border)))}.shadow-\[0_0_0_1px_hsl\(var\(--sidebar-border\)\)\],.shadow-lg{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)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a)}.shadow-md,.shadow-none{box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-none{--tw-shadow:0 0 #0000}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a)}.shadow-sm,.shadow-xs{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)}.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-sidebar-ring{--tw-ring-color:var(--sidebar-ring)}.ring-offset-background{--tw-ring-offset-color:var(--background)}.ring-offset-transparent{--tw-ring-offset-color:transparent}.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}.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,)}.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-\[left\,right\,width\]{transition-property:left,right,width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[margin\,opacity\]{transition-property:margin,opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\,height\,padding\]{transition-property:width,height,padding;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\]{transition-property:width;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-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))}.delay-75{transition-delay:75ms}.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-linear{--tw-ease:linear;transition-timing-function:linear}.delay-75{--tw-animation-delay:75ms;animation-delay:75ms}.fade-in-0{--tw-enter-opacity:0}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.zoom-in-95{--tw-enter-scale:.95}.running{animation-play-state:running}.slide-in-from-right-2{--tw-enter-translate-x:calc(2*var(--spacing))}.group-focus-within\/menu-item\:opacity-100:is(:where(.group\/menu-item):focus-within *){opacity:1}@media (hover:hover){.group-hover\:bg-primary\/20:is(:where(.group):hover *){background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.group-hover\:bg-primary\/20:is(:where(.group):hover *){background-color:color-mix(in oklab,var(--primary)20%,transparent)}}.group-hover\:text-primary:is(:where(.group):hover *){color:var(--primary)}.group-hover\/menu-item\:opacity-100:is(:where(.group\/menu-item):hover *),.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.group-has-data-\[sidebar\=menu-action\]\/menu-item\:pr-8:is(:where(.group\/menu-item):has([data-sidebar=menu-action]) *){padding-right:calc(var(--spacing)*8)}.group-data-\[collapsible\=icon\]\:-mt-8:is(:where(.group)[data-collapsible=icon] *){margin-top:calc(var(--spacing)*-8)}.group-data-\[collapsible\=icon\]\:hidden:is(:where(.group)[data-collapsible=icon] *){display:none}.group-data-\[collapsible\=icon\]\:size-8\!:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--spacing)*8)!important;height:calc(var(--spacing)*8)!important}.group-data-\[collapsible\=icon\]\:w-\(--sidebar-width-icon\):is(:where(.group)[data-collapsible=icon] *){width:var(--sidebar-width-icon)}.group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\)\]:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--spacing)*4)))}.group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\+2px\)\]:is(:where(.group)[data-collapsible=icon] *){width:calc(var(--sidebar-width-icon) + (calc(var(--spacing)*4)) + 2px)}.group-data-\[collapsible\=icon\]\:overflow-hidden:is(:where(.group)[data-collapsible=icon] *){overflow:hidden}.group-data-\[collapsible\=icon\]\:p-0\!:is(:where(.group)[data-collapsible=icon] *){padding:calc(var(--spacing)*0)!important}.group-data-\[collapsible\=icon\]\:p-2\!:is(:where(.group)[data-collapsible=icon] *){padding:calc(var(--spacing)*2)!important}.group-data-\[collapsible\=icon\]\:opacity-0:is(:where(.group)[data-collapsible=icon] *){opacity:0}.group-data-\[collapsible\=offcanvas\]\:right-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible=offcanvas] *){right:calc(var(--sidebar-width)*-1)}.group-data-\[collapsible\=offcanvas\]\:left-\[calc\(var\(--sidebar-width\)\*-1\)\]:is(:where(.group)[data-collapsible=offcanvas] *){left:calc(var(--sidebar-width)*-1)}.group-data-\[collapsible\=offcanvas\]\:w-0:is(:where(.group)[data-collapsible=offcanvas] *){width:calc(var(--spacing)*0)}.group-data-\[collapsible\=offcanvas\]\:translate-x-0:is(:where(.group)[data-collapsible=offcanvas] *){--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.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}.group-data-\[side\=left\]\:-right-4:is(:where(.group)[data-side=left] *){right:calc(var(--spacing)*-4)}.group-data-\[side\=left\]\:border-r:is(:where(.group)[data-side=left] *){border-right-style:var(--tw-border-style);border-right-width:1px}.group-data-\[side\=right\]\:left-0:is(:where(.group)[data-side=right] *){left:calc(var(--spacing)*0)}.group-data-\[side\=right\]\:rotate-180:is(:where(.group)[data-side=right] *){rotate:180deg}.group-data-\[side\=right\]\:border-l:is(:where(.group)[data-side=right] *){border-left-style:var(--tw-border-style);border-left-width:1px}.group-data-\[variant\=floating\]\:rounded-lg:is(:where(.group)[data-variant=floating] *){border-radius:var(--radius)}.group-data-\[variant\=floating\]\:border:is(:where(.group)[data-variant=floating] *){border-style:var(--tw-border-style);border-width:1px}.group-data-\[variant\=floating\]\:border-sidebar-border:is(:where(.group)[data-variant=floating] *){border-color:var(--sidebar-border)}.group-data-\[variant\=floating\]\:shadow-sm:is(:where(.group)[data-variant=floating] *){--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)}@media (hover:hover){.peer-hover\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button):hover~*){color:var(--sidebar-accent-foreground)}}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-50:is(:where(.peer):disabled~*){opacity:.5}.peer-data-\[active\=true\]\/menu-button\:text-sidebar-accent-foreground:is(:where(.peer\/menu-button)[data-active=true]~*){color:var(--sidebar-accent-foreground)}.peer-data-\[size\=default\]\/menu-button\:top-1\.5:is(:where(.peer\/menu-button)[data-size=default]~*){top:calc(var(--spacing)*1.5)}.peer-data-\[size\=lg\]\/menu-button\:top-2\.5:is(:where(.peer\/menu-button)[data-size=lg]~*){top:calc(var(--spacing)*2.5)}.peer-data-\[size\=sm\]\/menu-button\:top-1:is(:where(.peer\/menu-button)[data-size=sm]~*){top:calc(var(--spacing)*1)}.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)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:-inset-2:after{content:var(--tw-content);inset:calc(var(--spacing)*-2)}.after\:inset-y-0:after{content:var(--tw-content);inset-block:calc(var(--spacing)*0)}.after\:left-1\/2:after{content:var(--tw-content);left:50%}.after\:w-\[2px\]:after{content:var(--tw-content);width:2px}.group-data-\[collapsible\=offcanvas\]\:after\:left-full:is(:where(.group)[data-collapsible=offcanvas] *):after{content:var(--tw-content);left:100%}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media (hover:hover){.hover\:border:hover{border-style:var(--tw-border-style);border-width:1px}.hover\:border-primary\/50:hover{border-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\:border-primary\/50:hover{border-color:color-mix(in oklab,var(--primary)50%,transparent)}}.hover\:bg-accent:hover{background-color:var(--accent)}.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\/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\/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-secondary:hover,.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-sidebar-accent:hover{background-color:var(--sidebar-accent)}.hover\:bg-transparent:hover{background-color:#0000}.hover\:text-accent-foreground:hover{color:var(--accent-foreground)}.hover\:text-sidebar-accent-foreground:hover{color:var(--sidebar-accent-foreground)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-\[0_0_0_1px_hsl\(var\(--sidebar-accent\)\)\]:hover{--tw-shadow:0 0 0 1px var(--tw-shadow-color,hsl(var(--sidebar-accent)))}.hover\:shadow-\[0_0_0_1px_hsl\(var\(--sidebar-accent\)\)\]:hover,.hover\:shadow-lg:hover{box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a)}.hover\:shadow-xl:hover{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px 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)}.hover\:group-data-\[collapsible\=offcanvas\]\:bg-sidebar:hover:is(:where(.group)[data-collapsible=offcanvas] *){background-color:var(--sidebar)}.hover\:after\:bg-sidebar-border:hover:after{content:var(--tw-content);background-color:var(--sidebar-border)}}.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-hidden:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.focus\:outline-hidden:focus{outline-offset:2px;outline:2px solid #0000}}.focus-visible\:border-ring:focus-visible{border-color:var(--ring)}.focus-visible\:ring-0:focus-visible{--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)}.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-1:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\:outline-ring:focus-visible{outline-color:var(--ring)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.active\:bg-sidebar-accent:active{background-color:var(--sidebar-accent)}.active\:text-sidebar-accent-foreground:active{color:var(--sidebar-accent-foreground)}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}:where([data-side=left]) .in-data-\[side\=left\]\:cursor-w-resize{cursor:w-resize}:where([data-side=right]) .in-data-\[side\=right\]\:cursor-e-resize{cursor:e-resize}.has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has([data-slot=card-action]){grid-template-columns:1fr auto}.has-data-\[variant\=inset\]\:bg-sidebar:has([data-variant=inset]){background-color:var(--sidebar)}.has-\[\>svg\]\:grid-cols-\[calc\(var\(--spacing\)\*4\)_1fr\]:has(>svg){grid-template-columns:calc(var(--spacing)*4)1fr}.has-\[\>svg\]\:gap-x-3:has(>svg){column-gap:calc(var(--spacing)*3)}.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-disabled\:pointer-events-none[aria-disabled=true]{pointer-events:none}.aria-disabled\:opacity-50[aria-disabled=true]{opacity:.5}.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-\[active\=true\]\:bg-sidebar-accent[data-active=true]{background-color:var(--sidebar-accent)}.data-\[active\=true\]\:font-medium[data-active=true]{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.data-\[active\=true\]\:text-sidebar-accent-foreground[data-active=true]{color:var(--sidebar-accent-foreground)}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[disabled\=true\]\:pointer-events-none[data-disabled=true]{pointer-events:none}.data-\[disabled\=true\]\:opacity-50[data-disabled=true]{opacity:.5}.data-\[inset\]\:pl-8[data-inset]{padding-left:calc(var(--spacing)*8)}.data-\[orientation\=horizontal\]\:h-px[data-orientation=horizontal]{height:1px}.data-\[orientation\=horizontal\]\:w-full[data-orientation=horizontal]{width:100%}.data-\[orientation\=vertical\]\:h-full[data-orientation=vertical]{height:100%}.data-\[orientation\=vertical\]\:w-px[data-orientation=vertical]{width:1px}.data-\[placeholder\]\:text-muted-foreground[data-placeholder]{color:var(--muted-foreground)}.data-\[selected\=true\]\:bg-accent[data-selected=true]{background-color:var(--accent)}.data-\[selected\=true\]\:text-accent-foreground[data-selected=true]{color:var(--accent-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\=alert-description\]\:text-destructive\/90>*)[data-slot=alert-description]{color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){:is(.\*\:data-\[slot\=alert-description\]\:text-destructive\/90>*)[data-slot=alert-description]{color:color-mix(in oklab,var(--destructive)90%,transparent)}}:is(.\*\*\:data-\[slot\=command-input-wrapper\]\:h-12 *)[data-slot=command-input-wrapper]{height:calc(var(--spacing)*12)}: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\]\:shadow-sm[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-5[data-state=checked]{--tw-translate-x:calc(var(--spacing)*5);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:var(--primary)}.data-\[state\=closed\]\:animate-accordion-up[data-state=closed]{animation:accordion-up var(--tw-animation-duration,var(--tw-duration,.2s))ease-out}.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\]\:duration-300[data-state=closed]{--tw-duration:.3s;transition-duration:.3s}.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\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y:100%}.data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x:-100%}.data-\[state\=closed\]\:slide-out-to-right[data-state=closed]{--tw-exit-translate-x:100%}.data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y:-100%}.data-\[state\=open\]\:animate-accordion-down[data-state=open]{animation:accordion-down var(--tw-animation-duration,var(--tw-duration,.2s))ease-out}.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\]\:bg-secondary[data-state=open]{background-color:var(--secondary)}.data-\[state\=open\]\:text-accent-foreground[data-state=open]{color:var(--accent-foreground)}.data-\[state\=open\]\:text-muted-foreground[data-state=open]{color:var(--muted-foreground)}.data-\[state\=open\]\:opacity-100[data-state=open]{opacity:1}.data-\[state\=open\]\:duration-500[data-state=open]{--tw-duration:.5s;transition-duration:.5s}.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\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y:100%}.data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x:-100%}.data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x:100%}.data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y:-100%}@media (hover:hover){.data-\[state\=open\]\:hover\:bg-sidebar-accent[data-state=open]:hover{background-color:var(--sidebar-accent)}.data-\[state\=open\]\:hover\:text-sidebar-accent-foreground[data-state=open]:hover{color:var(--sidebar-accent-foreground)}}.data-\[state\=selected\]\:bg-muted[data-state=selected]{background-color:var(--muted)}.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\:mr-3{margin-right:calc(var(--spacing)*3)}.sm\:flex{display:flex}.sm\:max-w-2xl{max-width:var(--container-2xl)}.sm\:max-w-4xl{max-width:var(--container-4xl)}.sm\:max-w-lg{max-width:var(--container-lg)}.sm\:max-w-sm{max-width:var(--container-sm)}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:px-4{padding-inline:calc(var(--spacing)*4)}.sm\:text-left{text-align:left}.sm\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}}@media (min-width:48rem){.md\:block{display:block}.md\:flex{display:flex}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.md\:opacity-0{opacity:0}.md\:peer-data-\[variant\=inset\]\:m-2:is(:where(.peer)[data-variant=inset]~*){margin:calc(var(--spacing)*2)}.md\:peer-data-\[variant\=inset\]\:ml-0:is(:where(.peer)[data-variant=inset]~*){margin-left:calc(var(--spacing)*0)}.md\:peer-data-\[variant\=inset\]\:rounded-xl:is(:where(.peer)[data-variant=inset]~*){border-radius:calc(var(--radius) + 4px)}.md\:peer-data-\[variant\=inset\]\:shadow-sm:is(:where(.peer)[data-variant=inset]~*){--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)}.md\:peer-data-\[variant\=inset\]\:peer-data-\[state\=collapsed\]\:ml-2:is(:where(.peer)[data-variant=inset]~*):is(:where(.peer)[data-state=collapsed]~*){margin-left:calc(var(--spacing)*2)}.md\:after\:hidden:after{content:var(--tw-content);display:none}}@media (min-width:64rem){.lg\:grid-cols-3{grid-template-columns:repeat(3,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-amber-800:is(.dark *){border-color:var(--color-amber-800)}.dark\:border-blue-800:is(.dark *){border-color:var(--color-blue-800)}.dark\:border-green-800:is(.dark *){border-color:var(--color-green-800)}.dark\:border-input:is(.dark *){border-color:var(--input)}.dark\:border-purple-800:is(.dark *){border-color:var(--color-purple-800)}.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-blue-900:is(.dark *){background-color:var(--color-blue-900)}.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-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-green-200:is(.dark *){background-color:var(--color-green-200)}.dark\:bg-green-900:is(.dark *){background-color:var(--color-green-900)}.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-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-purple-900:is(.dark *){background-color:var(--color-purple-900)}.dark\:bg-purple-950\/20:is(.dark *){background-color:#3c036633}@supports (color:color-mix(in lab,red,red)){.dark\:bg-purple-950\/20:is(.dark *){background-color:color-mix(in oklab,var(--color-purple-950)20%,transparent)}}.dark\:text-amber-200:is(.dark *){color:var(--color-amber-200)}.dark\:text-amber-300:is(.dark *){color:var(--color-amber-300)}.dark\:text-blue-200:is(.dark *){color:var(--color-blue-200)}.dark\:text-blue-300:is(.dark *){color:var(--color-blue-300)}.dark\:text-green-200:is(.dark *){color:var(--color-green-200)}.dark\:text-green-300:is(.dark *){color:var(--color-green-300)}.dark\:text-muted-foreground:is(.dark *){color:var(--muted-foreground)}.dark\:text-purple-200:is(.dark *){color:var(--color-purple-200)}.dark\:text-purple-300:is(.dark *){color:var(--color-purple-300)}@media (hover:hover){.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-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\: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\=active\]\:border-input:is(.dark *)[data-state=active]{border-color:var(--input)}.dark\:data-\[state\=active\]\:bg-input\/30:is(.dark *)[data-state=active]{background-color:var(--input)}@supports (color:color-mix(in lab,red,red)){.dark\:data-\[state\=active\]\:bg-input\/30:is(.dark *)[data-state=active]{background-color:color-mix(in oklab,var(--input)30%,transparent)}}.dark\:data-\[state\=active\]\:text-foreground:is(.dark *)[data-state=active]{color:var(--foreground)}.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)}}.\[\&_\[cmdk-group-heading\]\]\:px-2 [cmdk-group-heading]{padding-inline:calc(var(--spacing)*2)}.\[\&_\[cmdk-group-heading\]\]\:py-1\.5 [cmdk-group-heading]{padding-block:calc(var(--spacing)*1.5)}.\[\&_\[cmdk-group-heading\]\]\:text-xs [cmdk-group-heading]{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.\[\&_\[cmdk-group-heading\]\]\:font-medium [cmdk-group-heading]{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.\[\&_\[cmdk-group-heading\]\]\:text-muted-foreground [cmdk-group-heading]{color:var(--muted-foreground)}.\[\&_\[cmdk-group\]\]\:px-2 [cmdk-group]{padding-inline:calc(var(--spacing)*2)}.\[\&_\[cmdk-group\]\:not\(\[hidden\]\)_\~\[cmdk-group\]\]\:pt-0 [cmdk-group]:not([hidden])~[cmdk-group]{padding-top:calc(var(--spacing)*0)}.\[\&_\[cmdk-input-wrapper\]_svg\]\:h-5 [cmdk-input-wrapper] svg{height:calc(var(--spacing)*5)}.\[\&_\[cmdk-input-wrapper\]_svg\]\:w-5 [cmdk-input-wrapper] svg{width:calc(var(--spacing)*5)}.\[\&_\[cmdk-input\]\]\:h-12 [cmdk-input]{height:calc(var(--spacing)*12)}.\[\&_\[cmdk-item\]\]\:px-2 [cmdk-item]{padding-inline:calc(var(--spacing)*2)}.\[\&_\[cmdk-item\]\]\:py-3 [cmdk-item]{padding-block:calc(var(--spacing)*3)}.\[\&_\[cmdk-item\]_svg\]\:h-5 [cmdk-item] svg{height:calc(var(--spacing)*5)}.\[\&_\[cmdk-item\]_svg\]\:w-5 [cmdk-item] svg{width:calc(var(--spacing)*5)}.\[\&_p\]\:leading-relaxed p{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:invisible svg{visibility:hidden}.\[\&_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)}.\[\&_tr\]\:border-b tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.\[\&_tr\:last-child\]\:border-0 tr:last-child{border-style:var(--tw-border-style);border-width:0}.\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]){padding-right:calc(var(--spacing)*0)}.\[\.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}.\[\&\>\[role\=checkbox\]\]\:translate-y-\[2px\]>[role=checkbox]{--tw-translate-y:2px;translate:var(--tw-translate-x)var(--tw-translate-y)}.\[\&\>button\]\:hidden>button{display:none}.\[\&\>span\:last-child\]\:truncate>span:last-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.\[\&\>svg\]\:pointer-events-none>svg{pointer-events:none}.\[\&\>svg\]\:size-3>svg{width:calc(var(--spacing)*3);height:calc(var(--spacing)*3)}.\[\&\>svg\]\:size-4>svg{width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.\[\&\>svg\]\:shrink-0>svg{flex-shrink:0}.\[\&\>svg\]\:translate-y-0\.5>svg{--tw-translate-y:calc(var(--spacing)*.5);translate:var(--tw-translate-x)var(--tw-translate-y)}.\[\&\>svg\]\:text-current>svg{color:currentColor}.\[\&\>svg\]\:text-sidebar-accent-foreground>svg{color:var(--sidebar-accent-foreground)}.\[\&\>tr\]\:last\:border-b-0>tr:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.\[\&\[data-state\=open\]\>svg\]\:rotate-180[data-state=open]>svg{rotate:180deg}[data-side=left][data-collapsible=offcanvas] .\[\[data-side\=left\]\[data-collapsible\=offcanvas\]_\&\]\:-right-2{right:calc(var(--spacing)*-2)}[data-side=left][data-state=collapsed] .\[\[data-side\=left\]\[data-state\=collapsed\]_\&\]\:cursor-e-resize{cursor:e-resize}[data-side=right][data-collapsible=offcanvas] .\[\[data-side\=right\]\[data-collapsible\=offcanvas\]_\&\]\:-left-2{left:calc(var(--spacing)*-2)}[data-side=right][data-state=collapsed] .\[\[data-side\=right\]\[data-state\=collapsed\]_\&\]\:cursor-w-resize{cursor:w-resize}@media (hover:hover){a.\[a\&\]\:hover\:bg-accent:hover{background-color:var(--accent)}a.\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab,var(--destructive)90%,transparent)}}a.\[a\&\]\:hover\:bg-green-700\/90:hover{background-color:#008138e6}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-green-700\/90:hover{background-color:color-mix(in oklab,var(--color-green-700)90%,transparent)}}a.\[a\&\]\:hover\:bg-primary\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--primary)90%,transparent)}}a.\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){a.\[a\&\]\:hover\:bg-secondary\/90:hover{background-color:color-mix(in oklab,var(--secondary)90%,transparent)}}a.\[a\&\]\:hover\:text-accent-foreground:hover{color:var(--accent-foreground)}}}@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-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-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.1% .005 285.823);--card:oklch(100% 0 0);--card-foreground:oklch(14.1% .005 285.823);--popover:oklch(100% 0 0);--popover-foreground:oklch(14.1% .005 285.823);--primary:oklch(50.81% .1049 165.61);--primary-foreground:oklch(98.5% 0 0);--secondary:oklch(96.7% .001 286.375);--secondary-foreground:oklch(21% .006 285.885);--muted:oklch(96.7% .001 286.375);--muted-foreground:oklch(55.2% .016 285.938);--accent:oklch(96.7% .001 286.375);--accent-foreground:oklch(21% .006 285.885);--destructive:oklch(57.7% .245 27.325);--border:oklch(92% .004 286.32);--input:oklch(92% .004 286.32);--ring:oklch(70.5% .015 286.067);--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.1% .005 285.823);--sidebar-primary:oklch(21% .006 285.885);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(96.7% .001 286.375);--sidebar-accent-foreground:oklch(21% .006 285.885);--sidebar-border:oklch(92% .004 286.32);--sidebar-ring:oklch(70.5% .015 286.067)}.dark{--background:oklch(14.1% .005 285.823);--foreground:oklch(98.5% 0 0);--card:oklch(21% .006 285.885);--card-foreground:oklch(98.5% 0 0);--popover:oklch(21% .006 285.885);--popover-foreground:oklch(98.5% 0 0);--primary:oklch(92% .004 286.32);--primary-foreground:oklch(21% .006 285.885);--secondary:oklch(27.4% .006 286.033);--secondary-foreground:oklch(98.5% 0 0);--muted:oklch(27.4% .006 286.033);--muted-foreground:oklch(70.5% .015 286.067);--accent:oklch(27.4% .006 286.033);--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.2% .016 285.938);--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(21% .006 285.885);--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(27.4% .006 286.033);--sidebar-accent-foreground:oklch(98.5% 0 0);--sidebar-border:oklch(100% 0 0/.1);--sidebar-ring:oklch(55.2% .016 285.938)}@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-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"";inherits:false;initial-value:0}@property --tw-gradient-via-position{syntax:"";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{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-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes spin{to{transform:rotate(1turn)}}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}@keyframes pulse{50%{opacity:.5}}@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))}}@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))}}@keyframes accordion-down{0%{height:0}to{height:var(--radix-accordion-content-height,var(--bits-accordion-content-height,var(--reka-accordion-content-height,var(--kb-accordion-content-height,auto))))}}@keyframes accordion-up{0%{height:var(--radix-accordion-content-height,var(--bits-accordion-content-height,var(--reka-accordion-content-height,var(--kb-accordion-content-height,auto))))}to{height:0}}@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/8d697b304b401681-s.woff2) format("woff2");unicode-range:u+0301,u+0400-045f,u+0490-0491,u+04b0-04b1,u+2116}@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/ba015fad6dcf6784-s.woff2) format("woff2");unicode-range:u+0100-02ba,u+02bd-02c5,u+02c7-02cc,u+02ce-02d7,u+02dd-02ff,u+0304,u+0308,u+0329,u+1d00-1dbf,u+1e00-1e9f,u+1ef2-1eff,u+2020,u+20a0-20ab,u+20ad-20c0,u+2113,u+2c60-2c7f,u+a720-a7ff}@font-face{font-family:Geist;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/569ce4b8f30dc480-s.p.woff2) format("woff2");unicode-range:u+00??,u+0131,u+0152-0153,u+02bb-02bc,u+02c6,u+02da,u+02dc,u+0304,u+0308,u+0329,u+2000-206f,u+20ac,u+2122,u+2191,u+2193,u+2212,u+2215,u+feff,u+fffd}@font-face{font-family:Geist Fallback;src:local("Arial");ascent-override:95.94%;descent-override:28.16%;line-gap-override:0.00%;size-adjust:104.76%}.__className_5cfdac{font-family:Geist,Geist Fallback;font-style:normal}.__variable_5cfdac{--font-geist-sans:"Geist","Geist Fallback"}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/9610d9e46709d722-s.woff2) format("woff2");unicode-range:u+0301,u+0400-045f,u+0490-0491,u+04b0-04b1,u+2116}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/747892c23ea88013-s.woff2) format("woff2");unicode-range:u+0100-02ba,u+02bd-02c5,u+02c7-02cc,u+02ce-02d7,u+02dd-02ff,u+0304,u+0308,u+0329,u+1d00-1dbf,u+1e00-1e9f,u+1ef2-1eff,u+2020,u+20a0-20ab,u+20ad-20c0,u+2113,u+2c60-2c7f,u+a720-a7ff}@font-face{font-family:Geist Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(/_next/static/media/93f479601ee12b01-s.p.woff2) format("woff2");unicode-range:u+00??,u+0131,u+0152-0153,u+02bb-02bc,u+02c6,u+02da,u+02dc,u+0304,u+0308,u+0329,u+2000-206f,u+20ac,u+2122,u+2191,u+2193,u+2212,u+2215,u+feff,u+fffd}@font-face{font-family:Geist Mono Fallback;src:local("Arial");ascent-override:74.67%;descent-override:21.92%;line-gap-override:0.00%;size-adjust:134.59%}.__className_9a8899{font-family:Geist Mono,Geist Mono Fallback;font-style:normal}.__variable_9a8899{--font-geist-mono:"Geist Mono","Geist Mono Fallback"} \ No newline at end of file diff --git a/transports/bifrost-http/ui/config/index.html b/transports/bifrost-http/ui/config/index.html index 7232245f20..1eb1bb4834 100644 --- a/transports/bifrost-http/ui/config/index.html +++ b/transports/bifrost-http/ui/config/index.html @@ -1,4 +1,4 @@ -Bifrost - The fastest LLM gateway
\ No newline at end of file +
\ No newline at end of file diff --git a/transports/bifrost-http/ui/config/index.txt b/transports/bifrost-http/ui/config/index.txt index e171cea8e1..18169a72a9 100644 --- a/transports/bifrost-http/ui/config/index.txt +++ b/transports/bifrost-http/ui/config/index.txt @@ -16,8 +16,8 @@ e:I[9665,[],"OutletBoundary"] 17:I[6614,[],""] :HL["/_next/static/media/569ce4b8f30dc480-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] :HL["/_next/static/media/93f479601ee12b01-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] -:HL["/_next/static/css/215063fa9dc64c3d.css","style"] -0:{"P":null,"b":"build","p":"","c":["","config",""],"i":false,"f":[[["",{"children":["config",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/215063fa9dc64c3d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","suppressHydrationWarning":true,"children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"attribute":"class","defaultTheme":"system","enableSystem":true,"children":[["$","$L4",null,{}],["$","$L5",null,{"children":["$","$L6",null,{"children":[["$","$L7",null,{}],["$","main",null,{"className":"custom-scrollbar w-5xl relative mx-auto flex min-h-screen flex-col px-4 py-12","children":["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}]}]}]}]]}],{"children":["config",["$","$1","c",{"children":[null,["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$La",null,{"Component":"$b","searchParams":{},"params":{},"promises":["$@c","$@d"]}],null,["$","$Le",null,{"children":["$Lf","$L10",["$","$L11",null,{"promise":"$@12"}]]}]]}],{},null,false]},null,false]},null,false],["$","$1","h",{"children":[null,["$","$1","Se7ep-X2olt1FpjUwyBYlv",{"children":[["$","$L13",null,{"children":"$L14"}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],["$","$L15",null,{"children":"$L16"}]]}],false]],"m":"$undefined","G":["$17","$undefined"],"s":false,"S":true} +:HL["/_next/static/css/e2d791eafd0cc299.css","style"] +0:{"P":null,"b":"build","p":"","c":["","config",""],"i":false,"f":[[["",{"children":["config",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/e2d791eafd0cc299.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","suppressHydrationWarning":true,"children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"attribute":"class","defaultTheme":"system","enableSystem":true,"children":[["$","$L4",null,{}],["$","$L5",null,{"children":["$","$L6",null,{"children":[["$","$L7",null,{}],["$","main",null,{"className":"custom-scrollbar w-5xl relative mx-auto flex min-h-screen flex-col px-4 py-12","children":["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}]}]}]}]]}],{"children":["config",["$","$1","c",{"children":[null,["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$La",null,{"Component":"$b","searchParams":{},"params":{},"promises":["$@c","$@d"]}],null,["$","$Le",null,{"children":["$Lf","$L10",["$","$L11",null,{"promise":"$@12"}]]}]]}],{},null,false]},null,false]},null,false],["$","$1","h",{"children":[null,["$","$1","uZgTTLqvODVKwVMPrqm_Xv",{"children":[["$","$L13",null,{"children":"$L14"}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],["$","$L15",null,{"children":"$L16"}]]}],false]],"m":"$undefined","G":["$17","$undefined"],"s":false,"S":true} 18:"$Sreact.suspense" 19:I[4911,[],"AsyncMetadata"] c:{} diff --git a/transports/bifrost-http/ui/docs/index.html b/transports/bifrost-http/ui/docs/index.html index 4e7eb0ea69..b7882f6320 100644 --- a/transports/bifrost-http/ui/docs/index.html +++ b/transports/bifrost-http/ui/docs/index.html @@ -1,4 +1,4 @@ -Bifrost - The fastest LLM gateway
Documentation
Power Up Your Bifrost Stack

Everything you need to know about building production AI applications with Bifrost

Popular
Quick Start
Get Bifrost running in under 30 seconds
  • HTTP Transport Setup
  • Go Package Usage
  • Docker Guide
Read More
Architecture
Deep dive into Bifrost's design and performance
  • System Overview
  • Request Flow
  • Concurrency Model
  • Design Decisions
Read More
Comprehensive
Usage Guides
Complete API reference and configuration guides
  • Providers Setup
  • Key Management
  • Error Handling
  • Memory & Networking
Read More
Contributing
Help improve Bifrost for everyone
  • Contributing Guide
  • Adding Providers
  • Plugin Development
  • Code Conventions
Read More
Integration Examples
Practical examples and testing code
  • OpenAI Integration
  • Anthropic Integration
  • GenAI Integration
  • Migration Guides
Read More
Benchmarks
Performance metrics and guides
  • 5K RPS Test Results
  • Performance Metrics
  • Configuration Tuning
  • Hardware Comparisons
Read More
MCP Documentation
Comprehensive guide to Model Context Protocol integration

Learn how to build sophisticated AI agents with MCP support, tool calling, and external integrations.

View MCP Guide
Configuration Reference
Complete reference for all configuration options

Detailed documentation on provider setup, key management, and advanced configuration options.

Configuration Docs
\ No newline at end of file +
Documentation
Power Up Your Bifrost Stack

Everything you need to know about building production AI applications with Bifrost

Popular
Quick Start
Get Bifrost running in under 30 seconds
  • HTTP Transport Setup
  • Go Package Usage
  • Docker Guide
Read More
Architecture
Deep dive into Bifrost's design and performance
  • System Overview
  • Request Flow
  • Concurrency Model
  • Design Decisions
Read More
Comprehensive
Usage Guides
Complete API reference and configuration guides
  • Providers Setup
  • Key Management
  • Error Handling
  • Memory & Networking
Read More
Contributing
Help improve Bifrost for everyone
  • Contributing Guide
  • Adding Providers
  • Plugin Development
  • Code Conventions
Read More
Integration Examples
Practical examples and testing code
  • OpenAI Integration
  • Anthropic Integration
  • GenAI Integration
  • Migration Guides
Read More
Benchmarks
Performance metrics and guides
  • 5K RPS Test Results
  • Performance Metrics
  • Configuration Tuning
  • Hardware Comparisons
Read More
MCP Documentation
Comprehensive guide to Model Context Protocol integration

Learn how to build sophisticated AI agents with MCP support, tool calling, and external integrations.

View MCP Guide
Configuration Reference
Complete reference for all configuration options

Detailed documentation on provider setup, key management, and advanced configuration options.

Configuration Docs
\ No newline at end of file diff --git a/transports/bifrost-http/ui/docs/index.txt b/transports/bifrost-http/ui/docs/index.txt index cb66298d1d..14bb6f3521 100644 --- a/transports/bifrost-http/ui/docs/index.txt +++ b/transports/bifrost-http/ui/docs/index.txt @@ -15,8 +15,8 @@ e:I[4911,[],"AsyncMetadataOutlet"] 14:I[6614,[],""] :HL["/_next/static/media/569ce4b8f30dc480-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] :HL["/_next/static/media/93f479601ee12b01-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] -:HL["/_next/static/css/215063fa9dc64c3d.css","style"] -0:{"P":null,"b":"build","p":"","c":["","docs",""],"i":false,"f":[[["",{"children":["docs",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/215063fa9dc64c3d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","suppressHydrationWarning":true,"children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"attribute":"class","defaultTheme":"system","enableSystem":true,"children":[["$","$L4",null,{}],["$","$L5",null,{"children":["$","$L6",null,{"children":[["$","$L7",null,{}],["$","main",null,{"className":"custom-scrollbar w-5xl relative mx-auto flex min-h-screen flex-col px-4 py-12","children":["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}]}]}]}]]}],{"children":["docs",["$","$1","c",{"children":[null,["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","div",null,{"className":"bg-background","children":["$","div",null,{"className":"mx-auto max-w-7xl","children":["$","div",null,{"className":"space-y-8","children":[["$","div",null,{"className":"space-y-4 text-center","children":[["$","div",null,{"className":"bg-primary/10 text-primary inline-flex items-center gap-2 rounded-full px-4 py-2 text-sm","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-book-open h-4 w-4","aria-hidden":"true","children":[["$","path","1akyts",{"d":"M12 7v14"}],["$","path","ruj8y",{"d":"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"}],"$undefined"]}],["$","span",null,{"className":"font-semibold","children":"Documentation"}]]}],["$","div",null,{"className":"from-primary bg-gradient-to-r to-green-600 bg-clip-text pb-2 text-5xl font-bold text-transparent","children":"Power Up Your Bifrost Stack"}],["$","p",null,{"className":"text-muted-foreground mx-auto max-w-2xl text-lg","children":"Everything you need to know about building production AI applications with Bifrost"}],["$","div",null,{"className":"flex justify-center gap-4","children":[["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs","target":"_blank","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-external-link mr-2 h-4 w-4","aria-hidden":"true","children":[["$","path","1q9fwt",{"d":"M15 3h6v6"}],["$","path","gplh6r",{"d":"M10 14 21 3"}],["$","path","a6xqqp",{"d":"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}],"$undefined"]}],"View Full Documentation"],"data-slot":"button","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive bg-primary text-primary-foreground shadow-xs hover:bg-primary/90 h-9 px-4 py-2 has-[>svg]:px-3 cursor-pointer","ref":null}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs/quickstart","target":"_blank","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-play mr-2 h-4 w-4","aria-hidden":"true","children":[["$","polygon","1oa8hb",{"points":"6 3 20 12 6 21 6 3"}],"$undefined"]}],"Quick Start Guide"],"data-slot":"button","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 cursor-pointer","ref":null}]]}]]}],["$","div",null,{"className":"grid gap-6 md:grid-cols-2 lg:grid-cols-3","children":[["$","div","Quick Start",{"data-slot":"card","className":"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm group transition-all duration-200 hover:shadow-lg","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"className":"flex items-center justify-between","children":[["$","div",null,{"className":"bg-primary/10 group-hover:bg-primary/20 mb-4 flex h-12 w-12 items-center justify-center rounded-lg transition-colors","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-play text-primary h-6 w-6","aria-hidden":"true","children":[["$","polygon","1oa8hb",{"points":"6 3 20 12 6 21 6 3"}],"$undefined"]}]}],["$","span",null,{"data-slot":"badge","className":"inline-flex items-center justify-center rounded-md border px-2 py-0.5 font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 text-xs","children":"Popular"}]]}],["$","div",null,{"data-slot":"card-title","className":"font-semibold text-xl","children":"Quick Start"}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm leading-relaxed","children":"Get Bifrost running in under 30 seconds"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6 flex h-full flex-col justify-between gap-8","children":[["$","div",null,{"className":"space-y-4","children":["$","ul",null,{"className":"space-y-2","children":[["$","li","0",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"HTTP Transport Setup"]}],["$","li","1",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Go Package Usage"]}],["$","li","2",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Docker Guide"]}]]}]}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs/quickstart","target":"_blank","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer flex items-center justify-center gap-2","children":["Read More",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-external-link h-4 w-4","aria-hidden":"true","children":[["$","path","1q9fwt",{"d":"M15 3h6v6"}],["$","path","gplh6r",{"d":"M10 14 21 3"}],["$","path","a6xqqp",{"d":"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}],"$undefined"]}]],"data-slot":"button","ref":null}]]}]]}],["$","div","Architecture",{"data-slot":"card","className":"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm group transition-all duration-200 hover:shadow-lg","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"className":"flex items-center justify-between","children":[["$","div",null,{"className":"bg-primary/10 group-hover:bg-primary/20 mb-4 flex h-12 w-12 items-center justify-center rounded-lg transition-colors","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-git-branch text-primary h-6 w-6","aria-hidden":"true","children":[["$","line","17qcm7",{"x1":"6","x2":"6","y1":"3","y2":"15"}],["$","circle","1h7g24",{"cx":"18","cy":"6","r":"3"}],["$","circle","fqmcym",{"cx":"6","cy":"18","r":"3"}],["$","path","n2h4wq",{"d":"M18 9a9 9 0 0 1-9 9"}],"$undefined"]}]}],"$undefined"]}],["$","div",null,{"data-slot":"card-title","className":"font-semibold text-xl","children":"Architecture"}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm leading-relaxed","children":"Deep dive into Bifrost's design and performance"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6 flex h-full flex-col justify-between gap-8","children":[["$","div",null,{"className":"space-y-4","children":["$","ul",null,{"className":"space-y-2","children":[["$","li","0",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"System Overview"]}],["$","li","1",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Request Flow"]}],["$","li","2",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Concurrency Model"]}],["$","li","3",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Design Decisions"]}]]}]}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs/architecture","target":"_blank","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer flex items-center justify-center gap-2","children":["Read More",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-external-link h-4 w-4","aria-hidden":"true","children":[["$","path","1q9fwt",{"d":"M15 3h6v6"}],["$","path","gplh6r",{"d":"M10 14 21 3"}],["$","path","a6xqqp",{"d":"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}],"$undefined"]}]],"data-slot":"button","ref":null}]]}]]}],["$","div","Usage Guides",{"data-slot":"card","className":"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm group transition-all duration-200 hover:shadow-lg","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"className":"flex items-center justify-between","children":[["$","div",null,{"className":"bg-primary/10 group-hover:bg-primary/20 mb-4 flex h-12 w-12 items-center justify-center rounded-lg transition-colors","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-book-open text-primary h-6 w-6","aria-hidden":"true","children":[["$","path","1akyts",{"d":"M12 7v14"}],["$","path","ruj8y",{"d":"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"}],"$undefined"]}]}],["$","span",null,{"data-slot":"badge","className":"inline-flex items-center justify-center rounded-md border px-2 py-0.5 font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 text-xs","children":"Comprehensive"}]]}],["$","div",null,{"data-slot":"card-title","className":"font-semibold text-xl","children":"Usage Guides"}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm leading-relaxed","children":"Complete API reference and configuration guides"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6 flex h-full flex-col justify-between gap-8","children":[["$","div",null,{"className":"space-y-4","children":["$","ul",null,{"className":"space-y-2","children":[["$","li","0",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Providers Setup"]}],["$","li","1",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Key Management"]}],["$","li","2",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Error Handling"]}],["$","li","3",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Memory & Networking"]}]]}]}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs/usage","target":"_blank","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer flex items-center justify-center gap-2","children":["Read More",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-external-link h-4 w-4","aria-hidden":"true","children":[["$","path","1q9fwt",{"d":"M15 3h6v6"}],["$","path","gplh6r",{"d":"M10 14 21 3"}],["$","path","a6xqqp",{"d":"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}],"$undefined"]}]],"data-slot":"button","ref":null}]]}]]}],["$","div","Contributing",{"data-slot":"card","className":"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm group transition-all duration-200 hover:shadow-lg","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"className":"flex items-center justify-between","children":[["$","div",null,{"className":"bg-primary/10 group-hover:bg-primary/20 mb-4 flex h-12 w-12 items-center justify-center rounded-lg transition-colors","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-users text-primary h-6 w-6","aria-hidden":"true","children":[["$","path","1yyitq",{"d":"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"}],["$","path","16gr8j",{"d":"M16 3.128a4 4 0 0 1 0 7.744"}],["$","path","kshegd",{"d":"M22 21v-2a4 4 0 0 0-3-3.87"}],["$","circle","nufk8",{"cx":"9","cy":"7","r":"4"}],"$undefined"]}]}],"$undefined"]}],["$","div",null,{"data-slot":"card-title","className":"font-semibold text-xl","children":"Contributing"}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm leading-relaxed","children":"Help improve Bifrost for everyone"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6 flex h-full flex-col justify-between gap-8","children":[["$","div",null,{"className":"space-y-4","children":["$","ul",null,{"className":"space-y-2","children":[["$","li","0",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Contributing Guide"]}],["$","li","1",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Adding Providers"]}],["$","li","2",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Plugin Development"]}],["$","li","3",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Code Conventions"]}]]}]}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs/contributing","target":"_blank","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer flex items-center justify-center gap-2","children":["Read More",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-external-link h-4 w-4","aria-hidden":"true","children":[["$","path","1q9fwt",{"d":"M15 3h6v6"}],["$","path","gplh6r",{"d":"M10 14 21 3"}],["$","path","a6xqqp",{"d":"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}],"$undefined"]}]],"data-slot":"button","ref":null}]]}]]}],["$","div","Integration Examples",{"data-slot":"card","className":"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm group transition-all duration-200 hover:shadow-lg","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"className":"flex items-center justify-between","children":[["$","div",null,{"className":"bg-primary/10 group-hover:bg-primary/20 mb-4 flex h-12 w-12 items-center justify-center rounded-lg transition-colors","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-code text-primary h-6 w-6","aria-hidden":"true","children":[["$","path","eg8j8",{"d":"m16 18 6-6-6-6"}],["$","path","ppft3o",{"d":"m8 6-6 6 6 6"}],"$undefined"]}]}],"$undefined"]}],["$","div",null,{"data-slot":"card-title","className":"font-semibold text-xl","children":"Integration Examples"}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm leading-relaxed","children":"Practical examples and testing code"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6 flex h-full flex-col justify-between gap-8","children":[["$","div",null,{"className":"space-y-4","children":["$","ul",null,{"className":"space-y-2","children":[["$","li","0",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"OpenAI Integration"]}],["$","li","1",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Anthropic Integration"]}],["$","li","2",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"GenAI Integration"]}],["$","li","3",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Migration Guides"]}]]}]}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs/usage/http-transport/integrations","target":"_blank","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer flex items-center justify-center gap-2","children":["Read More",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-external-link h-4 w-4","aria-hidden":"true","children":[["$","path","1q9fwt",{"d":"M15 3h6v6"}],["$","path","gplh6r",{"d":"M10 14 21 3"}],["$","path","a6xqqp",{"d":"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}],"$undefined"]}]],"data-slot":"button","ref":null}]]}]]}],["$","div","Benchmarks",{"data-slot":"card","className":"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm group transition-all duration-200 hover:shadow-lg","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"className":"flex items-center justify-between","children":[["$","div",null,{"className":"bg-primary/10 group-hover:bg-primary/20 mb-4 flex h-12 w-12 items-center justify-center rounded-lg transition-colors","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-zap text-primary h-6 w-6","aria-hidden":"true","children":[["$","path","1xq2db",{"d":"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"}],"$undefined"]}]}],"$undefined"]}],["$","div",null,{"data-slot":"card-title","className":"font-semibold text-xl","children":"Benchmarks"}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm leading-relaxed","children":"Performance metrics and guides"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6 flex h-full flex-col justify-between gap-8","children":[["$","div",null,{"className":"space-y-4","children":["$","ul",null,{"className":"space-y-2","children":[["$","li","0",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"5K RPS Test Results"]}],["$","li","1",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Performance Metrics"]}],["$","li","2",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Configuration Tuning"]}],["$","li","3",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Hardware Comparisons"]}]]}]}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/blob/main/docs/benchmarks.md","target":"_blank","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer flex items-center justify-center gap-2","children":["Read More",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-external-link h-4 w-4","aria-hidden":"true","children":[["$","path","1q9fwt",{"d":"M15 3h6v6"}],["$","path","gplh6r",{"d":"M10 14 21 3"}],["$","path","a6xqqp",{"d":"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}],"$undefined"]}]],"data-slot":"button","ref":null}]]}]]}]]}],["$","div",null,{"className":"grid gap-6 pt-8 md:grid-cols-2","children":[["$","div",null,{"data-slot":"card","className":"text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm border-primary/20 bg-primary/5","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"data-slot":"card-title","className":"leading-none font-semibold flex items-center gap-2","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-file-text text-primary h-5 w-5","aria-hidden":"true","children":[["$","path","1rqfz7",{"d":"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"}],["$","path","tnqrlb",{"d":"M14 2v4a2 2 0 0 0 2 2h4"}],["$","path","b1mrlr",{"d":"M10 9H8"}],["$","path","t4e002",{"d":"M16 13H8"}],["$","path","z1uh3a",{"d":"M16 17H8"}],"$undefined"]}],"MCP Documentation"]}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm","children":"Comprehensive guide to Model Context Protocol integration"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6","children":[["$","p",null,{"className":"text-muted-foreground mb-4 text-sm","children":"Learn how to build sophisticated AI agents with MCP support, tool calling, and external integrations."}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/blob/main/docs/mcp.md","target":"_blank","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-book-open mr-2 h-4 w-4","aria-hidden":"true","children":[["$","path","1akyts",{"d":"M12 7v14"}],["$","path","ruj8y",{"d":"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"}],"$undefined"]}],"View MCP Guide"],"data-slot":"button","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive bg-primary text-primary-foreground shadow-xs hover:bg-primary/90 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer","ref":null}]]}]]}],["$","div",null,{"data-slot":"card","className":"text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm border-green-200 bg-green-50 dark:border-green-800 dark:bg-green-950/20","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"data-slot":"card-title","className":"leading-none font-semibold flex items-center gap-2","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-settings h-5 w-5 text-green-600","aria-hidden":"true","children":[["$","path","1qme2f",{"d":"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"}],["$","circle","1v7zrd",{"cx":"12","cy":"12","r":"3"}],"$undefined"]}],"Configuration Reference"]}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm","children":"Complete reference for all configuration options"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6","children":[["$","p",null,{"className":"text-muted-foreground mb-4 text-sm","children":"Detailed documentation on provider setup, key management, and advanced configuration options."}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs/usage/http-transport/configuration","target":"_blank","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-settings mr-2 h-4 w-4","aria-hidden":"true","children":[["$","path","1qme2f",{"d":"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"}],["$","circle","1v7zrd",{"cx":"12","cy":"12","r":"3"}],"$undefined"]}],"Configuration Docs"],"data-slot":"button","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer","ref":null}]]}]]}]]}]]}]}]}],null,["$","$Lb",null,{"children":["$Lc","$Ld",["$","$Le",null,{"promise":"$@f"}]]}]]}],{},null,false]},null,false]},null,false],["$","$1","h",{"children":[null,["$","$1","CG6exotWPr_RQ5bkW3Wx6v",{"children":[["$","$L10",null,{"children":"$L11"}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],["$","$L12",null,{"children":"$L13"}]]}],false]],"m":"$undefined","G":["$14","$undefined"],"s":false,"S":true} +:HL["/_next/static/css/e2d791eafd0cc299.css","style"] +0:{"P":null,"b":"build","p":"","c":["","docs",""],"i":false,"f":[[["",{"children":["docs",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/e2d791eafd0cc299.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","suppressHydrationWarning":true,"children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"attribute":"class","defaultTheme":"system","enableSystem":true,"children":[["$","$L4",null,{}],["$","$L5",null,{"children":["$","$L6",null,{"children":[["$","$L7",null,{}],["$","main",null,{"className":"custom-scrollbar w-5xl relative mx-auto flex min-h-screen flex-col px-4 py-12","children":["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}]}]}]}]]}],{"children":["docs",["$","$1","c",{"children":[null,["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","div",null,{"className":"bg-background","children":["$","div",null,{"className":"mx-auto max-w-7xl","children":["$","div",null,{"className":"space-y-8","children":[["$","div",null,{"className":"space-y-4 text-center","children":[["$","div",null,{"className":"bg-primary/10 text-primary inline-flex items-center gap-2 rounded-full px-4 py-2 text-sm","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-book-open h-4 w-4","aria-hidden":"true","children":[["$","path","1akyts",{"d":"M12 7v14"}],["$","path","ruj8y",{"d":"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"}],"$undefined"]}],["$","span",null,{"className":"font-semibold","children":"Documentation"}]]}],["$","div",null,{"className":"from-primary bg-gradient-to-r to-green-600 bg-clip-text pb-2 text-5xl font-bold text-transparent","children":"Power Up Your Bifrost Stack"}],["$","p",null,{"className":"text-muted-foreground mx-auto max-w-2xl text-lg","children":"Everything you need to know about building production AI applications with Bifrost"}],["$","div",null,{"className":"flex justify-center gap-4","children":[["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs","target":"_blank","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-external-link mr-2 h-4 w-4","aria-hidden":"true","children":[["$","path","1q9fwt",{"d":"M15 3h6v6"}],["$","path","gplh6r",{"d":"M10 14 21 3"}],["$","path","a6xqqp",{"d":"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}],"$undefined"]}],"View Full Documentation"],"data-slot":"button","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive bg-primary text-primary-foreground shadow-xs hover:bg-primary/90 h-9 px-4 py-2 has-[>svg]:px-3 cursor-pointer","ref":null}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs/quickstart","target":"_blank","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-play mr-2 h-4 w-4","aria-hidden":"true","children":[["$","polygon","1oa8hb",{"points":"6 3 20 12 6 21 6 3"}],"$undefined"]}],"Quick Start Guide"],"data-slot":"button","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 cursor-pointer","ref":null}]]}]]}],["$","div",null,{"className":"grid gap-6 md:grid-cols-2 lg:grid-cols-3","children":[["$","div","Quick Start",{"data-slot":"card","className":"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm group transition-all duration-200 hover:shadow-lg","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"className":"flex items-center justify-between","children":[["$","div",null,{"className":"bg-primary/10 group-hover:bg-primary/20 mb-4 flex h-12 w-12 items-center justify-center rounded-lg transition-colors","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-play text-primary h-6 w-6","aria-hidden":"true","children":[["$","polygon","1oa8hb",{"points":"6 3 20 12 6 21 6 3"}],"$undefined"]}]}],["$","span",null,{"data-slot":"badge","className":"inline-flex items-center justify-center rounded-md border px-2 py-0.5 font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 text-xs","children":"Popular"}]]}],["$","div",null,{"data-slot":"card-title","className":"font-semibold text-xl","children":"Quick Start"}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm leading-relaxed","children":"Get Bifrost running in under 30 seconds"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6 flex h-full flex-col justify-between gap-8","children":[["$","div",null,{"className":"space-y-4","children":["$","ul",null,{"className":"space-y-2","children":[["$","li","0",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"HTTP Transport Setup"]}],["$","li","1",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Go Package Usage"]}],["$","li","2",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Docker Guide"]}]]}]}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs/quickstart","target":"_blank","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer flex items-center justify-center gap-2","children":["Read More",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-external-link h-4 w-4","aria-hidden":"true","children":[["$","path","1q9fwt",{"d":"M15 3h6v6"}],["$","path","gplh6r",{"d":"M10 14 21 3"}],["$","path","a6xqqp",{"d":"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}],"$undefined"]}]],"data-slot":"button","ref":null}]]}]]}],["$","div","Architecture",{"data-slot":"card","className":"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm group transition-all duration-200 hover:shadow-lg","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"className":"flex items-center justify-between","children":[["$","div",null,{"className":"bg-primary/10 group-hover:bg-primary/20 mb-4 flex h-12 w-12 items-center justify-center rounded-lg transition-colors","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-git-branch text-primary h-6 w-6","aria-hidden":"true","children":[["$","line","17qcm7",{"x1":"6","x2":"6","y1":"3","y2":"15"}],["$","circle","1h7g24",{"cx":"18","cy":"6","r":"3"}],["$","circle","fqmcym",{"cx":"6","cy":"18","r":"3"}],["$","path","n2h4wq",{"d":"M18 9a9 9 0 0 1-9 9"}],"$undefined"]}]}],"$undefined"]}],["$","div",null,{"data-slot":"card-title","className":"font-semibold text-xl","children":"Architecture"}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm leading-relaxed","children":"Deep dive into Bifrost's design and performance"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6 flex h-full flex-col justify-between gap-8","children":[["$","div",null,{"className":"space-y-4","children":["$","ul",null,{"className":"space-y-2","children":[["$","li","0",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"System Overview"]}],["$","li","1",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Request Flow"]}],["$","li","2",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Concurrency Model"]}],["$","li","3",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Design Decisions"]}]]}]}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs/architecture","target":"_blank","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer flex items-center justify-center gap-2","children":["Read More",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-external-link h-4 w-4","aria-hidden":"true","children":[["$","path","1q9fwt",{"d":"M15 3h6v6"}],["$","path","gplh6r",{"d":"M10 14 21 3"}],["$","path","a6xqqp",{"d":"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}],"$undefined"]}]],"data-slot":"button","ref":null}]]}]]}],["$","div","Usage Guides",{"data-slot":"card","className":"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm group transition-all duration-200 hover:shadow-lg","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"className":"flex items-center justify-between","children":[["$","div",null,{"className":"bg-primary/10 group-hover:bg-primary/20 mb-4 flex h-12 w-12 items-center justify-center rounded-lg transition-colors","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-book-open text-primary h-6 w-6","aria-hidden":"true","children":[["$","path","1akyts",{"d":"M12 7v14"}],["$","path","ruj8y",{"d":"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"}],"$undefined"]}]}],["$","span",null,{"data-slot":"badge","className":"inline-flex items-center justify-center rounded-md border px-2 py-0.5 font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 text-xs","children":"Comprehensive"}]]}],["$","div",null,{"data-slot":"card-title","className":"font-semibold text-xl","children":"Usage Guides"}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm leading-relaxed","children":"Complete API reference and configuration guides"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6 flex h-full flex-col justify-between gap-8","children":[["$","div",null,{"className":"space-y-4","children":["$","ul",null,{"className":"space-y-2","children":[["$","li","0",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Providers Setup"]}],["$","li","1",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Key Management"]}],["$","li","2",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Error Handling"]}],["$","li","3",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Memory & Networking"]}]]}]}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs/usage","target":"_blank","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer flex items-center justify-center gap-2","children":["Read More",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-external-link h-4 w-4","aria-hidden":"true","children":[["$","path","1q9fwt",{"d":"M15 3h6v6"}],["$","path","gplh6r",{"d":"M10 14 21 3"}],["$","path","a6xqqp",{"d":"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}],"$undefined"]}]],"data-slot":"button","ref":null}]]}]]}],["$","div","Contributing",{"data-slot":"card","className":"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm group transition-all duration-200 hover:shadow-lg","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"className":"flex items-center justify-between","children":[["$","div",null,{"className":"bg-primary/10 group-hover:bg-primary/20 mb-4 flex h-12 w-12 items-center justify-center rounded-lg transition-colors","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-users text-primary h-6 w-6","aria-hidden":"true","children":[["$","path","1yyitq",{"d":"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"}],["$","path","16gr8j",{"d":"M16 3.128a4 4 0 0 1 0 7.744"}],["$","path","kshegd",{"d":"M22 21v-2a4 4 0 0 0-3-3.87"}],["$","circle","nufk8",{"cx":"9","cy":"7","r":"4"}],"$undefined"]}]}],"$undefined"]}],["$","div",null,{"data-slot":"card-title","className":"font-semibold text-xl","children":"Contributing"}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm leading-relaxed","children":"Help improve Bifrost for everyone"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6 flex h-full flex-col justify-between gap-8","children":[["$","div",null,{"className":"space-y-4","children":["$","ul",null,{"className":"space-y-2","children":[["$","li","0",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Contributing Guide"]}],["$","li","1",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Adding Providers"]}],["$","li","2",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Plugin Development"]}],["$","li","3",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Code Conventions"]}]]}]}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs/contributing","target":"_blank","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer flex items-center justify-center gap-2","children":["Read More",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-external-link h-4 w-4","aria-hidden":"true","children":[["$","path","1q9fwt",{"d":"M15 3h6v6"}],["$","path","gplh6r",{"d":"M10 14 21 3"}],["$","path","a6xqqp",{"d":"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}],"$undefined"]}]],"data-slot":"button","ref":null}]]}]]}],["$","div","Integration Examples",{"data-slot":"card","className":"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm group transition-all duration-200 hover:shadow-lg","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"className":"flex items-center justify-between","children":[["$","div",null,{"className":"bg-primary/10 group-hover:bg-primary/20 mb-4 flex h-12 w-12 items-center justify-center rounded-lg transition-colors","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-code text-primary h-6 w-6","aria-hidden":"true","children":[["$","path","eg8j8",{"d":"m16 18 6-6-6-6"}],["$","path","ppft3o",{"d":"m8 6-6 6 6 6"}],"$undefined"]}]}],"$undefined"]}],["$","div",null,{"data-slot":"card-title","className":"font-semibold text-xl","children":"Integration Examples"}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm leading-relaxed","children":"Practical examples and testing code"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6 flex h-full flex-col justify-between gap-8","children":[["$","div",null,{"className":"space-y-4","children":["$","ul",null,{"className":"space-y-2","children":[["$","li","0",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"OpenAI Integration"]}],["$","li","1",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Anthropic Integration"]}],["$","li","2",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"GenAI Integration"]}],["$","li","3",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Migration Guides"]}]]}]}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs/usage/http-transport/integrations","target":"_blank","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer flex items-center justify-center gap-2","children":["Read More",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-external-link h-4 w-4","aria-hidden":"true","children":[["$","path","1q9fwt",{"d":"M15 3h6v6"}],["$","path","gplh6r",{"d":"M10 14 21 3"}],["$","path","a6xqqp",{"d":"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}],"$undefined"]}]],"data-slot":"button","ref":null}]]}]]}],["$","div","Benchmarks",{"data-slot":"card","className":"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm group transition-all duration-200 hover:shadow-lg","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"className":"flex items-center justify-between","children":[["$","div",null,{"className":"bg-primary/10 group-hover:bg-primary/20 mb-4 flex h-12 w-12 items-center justify-center rounded-lg transition-colors","children":["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-zap text-primary h-6 w-6","aria-hidden":"true","children":[["$","path","1xq2db",{"d":"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"}],"$undefined"]}]}],"$undefined"]}],["$","div",null,{"data-slot":"card-title","className":"font-semibold text-xl","children":"Benchmarks"}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm leading-relaxed","children":"Performance metrics and guides"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6 flex h-full flex-col justify-between gap-8","children":[["$","div",null,{"className":"space-y-4","children":["$","ul",null,{"className":"space-y-2","children":[["$","li","0",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"5K RPS Test Results"]}],["$","li","1",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Performance Metrics"]}],["$","li","2",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Configuration Tuning"]}],["$","li","3",{"className":"text-muted-foreground flex items-center gap-2 text-sm","children":[["$","div",null,{"className":"bg-primary h-1.5 w-1.5 rounded-full"}],"Hardware Comparisons"]}]]}]}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/blob/main/docs/benchmarks.md","target":"_blank","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer flex items-center justify-center gap-2","children":["Read More",["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-external-link h-4 w-4","aria-hidden":"true","children":[["$","path","1q9fwt",{"d":"M15 3h6v6"}],["$","path","gplh6r",{"d":"M10 14 21 3"}],["$","path","a6xqqp",{"d":"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"}],"$undefined"]}]],"data-slot":"button","ref":null}]]}]]}]]}],["$","div",null,{"className":"grid gap-6 pt-8 md:grid-cols-2","children":[["$","div",null,{"data-slot":"card","className":"text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm border-primary/20 bg-primary/5","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"data-slot":"card-title","className":"leading-none font-semibold flex items-center gap-2","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-file-text text-primary h-5 w-5","aria-hidden":"true","children":[["$","path","1rqfz7",{"d":"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"}],["$","path","tnqrlb",{"d":"M14 2v4a2 2 0 0 0 2 2h4"}],["$","path","b1mrlr",{"d":"M10 9H8"}],["$","path","t4e002",{"d":"M16 13H8"}],["$","path","z1uh3a",{"d":"M16 17H8"}],"$undefined"]}],"MCP Documentation"]}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm","children":"Comprehensive guide to Model Context Protocol integration"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6","children":[["$","p",null,{"className":"text-muted-foreground mb-4 text-sm","children":"Learn how to build sophisticated AI agents with MCP support, tool calling, and external integrations."}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/blob/main/docs/mcp.md","target":"_blank","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-book-open mr-2 h-4 w-4","aria-hidden":"true","children":[["$","path","1akyts",{"d":"M12 7v14"}],["$","path","ruj8y",{"d":"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"}],"$undefined"]}],"View MCP Guide"],"data-slot":"button","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive bg-primary text-primary-foreground shadow-xs hover:bg-primary/90 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer","ref":null}]]}]]}],["$","div",null,{"data-slot":"card","className":"text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm border-green-200 bg-green-50 dark:border-green-800 dark:bg-green-950/20","children":[["$","div",null,{"data-slot":"card-header","className":"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6","children":[["$","div",null,{"data-slot":"card-title","className":"leading-none font-semibold flex items-center gap-2","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-settings h-5 w-5 text-green-600","aria-hidden":"true","children":[["$","path","1qme2f",{"d":"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"}],["$","circle","1v7zrd",{"cx":"12","cy":"12","r":"3"}],"$undefined"]}],"Configuration Reference"]}],["$","div",null,{"data-slot":"card-description","className":"text-muted-foreground text-sm","children":"Complete reference for all configuration options"}]]}],["$","div",null,{"data-slot":"card-content","className":"px-6","children":[["$","p",null,{"className":"text-muted-foreground mb-4 text-sm","children":"Detailed documentation on provider setup, key management, and advanced configuration options."}],["$","$La",null,{"href":"https://github.com/maximhq/bifrost/tree/main/docs/usage/http-transport/configuration","target":"_blank","children":[["$","svg",null,{"ref":"$undefined","xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-settings mr-2 h-4 w-4","aria-hidden":"true","children":[["$","path","1qme2f",{"d":"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"}],["$","circle","1v7zrd",{"cx":"12","cy":"12","r":"3"}],"$undefined"]}],"Configuration Docs"],"data-slot":"button","className":"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[>svg]:px-3 w-full cursor-pointer","ref":null}]]}]]}]]}]]}]}]}],null,["$","$Lb",null,{"children":["$Lc","$Ld",["$","$Le",null,{"promise":"$@f"}]]}]]}],{},null,false]},null,false]},null,false],["$","$1","h",{"children":[null,["$","$1","jytZBPYyjYXHLiYEjD9DFv",{"children":[["$","$L10",null,{"children":"$L11"}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],["$","$L12",null,{"children":"$L13"}]]}],false]],"m":"$undefined","G":["$14","$undefined"],"s":false,"S":true} 15:"$Sreact.suspense" 16:I[4911,[],"AsyncMetadata"] 13:["$","div",null,{"hidden":true,"children":["$","$15",null,{"fallback":null,"children":["$","$L16",null,{"promise":"$@17"}]}]}] diff --git a/transports/bifrost-http/ui/index.html b/transports/bifrost-http/ui/index.html index 0b281127fa..b73c020436 100644 --- a/transports/bifrost-http/ui/index.html +++ b/transports/bifrost-http/ui/index.html @@ -1,4 +1,4 @@ -Bifrost - The fastest LLM gateway
\ No newline at end of file +
\ No newline at end of file diff --git a/transports/bifrost-http/ui/index.txt b/transports/bifrost-http/ui/index.txt index 4d1ecd86e2..93ff7a5367 100644 --- a/transports/bifrost-http/ui/index.txt +++ b/transports/bifrost-http/ui/index.txt @@ -16,8 +16,8 @@ e:I[9665,[],"OutletBoundary"] 17:I[6614,[],""] :HL["/_next/static/media/569ce4b8f30dc480-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] :HL["/_next/static/media/93f479601ee12b01-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] -:HL["/_next/static/css/215063fa9dc64c3d.css","style"] -0:{"P":null,"b":"build","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/215063fa9dc64c3d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","suppressHydrationWarning":true,"children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"attribute":"class","defaultTheme":"system","enableSystem":true,"children":[["$","$L4",null,{}],["$","$L5",null,{"children":["$","$L6",null,{"children":[["$","$L7",null,{}],["$","main",null,{"className":"custom-scrollbar w-5xl relative mx-auto flex min-h-screen flex-col px-4 py-12","children":["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}]}]}]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$La",null,{"Component":"$b","searchParams":{},"params":{},"promises":["$@c","$@d"]}],null,["$","$Le",null,{"children":["$Lf","$L10",["$","$L11",null,{"promise":"$@12"}]]}]]}],{},null,false]},null,false],["$","$1","h",{"children":[null,["$","$1","71v20r63JFBj8GaiDCnhrv",{"children":[["$","$L13",null,{"children":"$L14"}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],["$","$L15",null,{"children":"$L16"}]]}],false]],"m":"$undefined","G":["$17","$undefined"],"s":false,"S":true} +:HL["/_next/static/css/e2d791eafd0cc299.css","style"] +0:{"P":null,"b":"build","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/e2d791eafd0cc299.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","suppressHydrationWarning":true,"children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"attribute":"class","defaultTheme":"system","enableSystem":true,"children":[["$","$L4",null,{}],["$","$L5",null,{"children":["$","$L6",null,{"children":[["$","$L7",null,{}],["$","main",null,{"className":"custom-scrollbar w-5xl relative mx-auto flex min-h-screen flex-col px-4 py-12","children":["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}]}]}]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$La",null,{"Component":"$b","searchParams":{},"params":{},"promises":["$@c","$@d"]}],null,["$","$Le",null,{"children":["$Lf","$L10",["$","$L11",null,{"promise":"$@12"}]]}]]}],{},null,false]},null,false],["$","$1","h",{"children":[null,["$","$1","7smvqxSqCPdhGZevyUq9nv",{"children":[["$","$L13",null,{"children":"$L14"}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],["$","$L15",null,{"children":"$L16"}]]}],false]],"m":"$undefined","G":["$17","$undefined"],"s":false,"S":true} 18:"$Sreact.suspense" 19:I[4911,[],"AsyncMetadata"] c:{} diff --git a/transports/bifrost-http/ui/mcp-clients/index.html b/transports/bifrost-http/ui/mcp-clients/index.html index f069dc3dd6..5f756c5812 100644 --- a/transports/bifrost-http/ui/mcp-clients/index.html +++ b/transports/bifrost-http/ui/mcp-clients/index.html @@ -1,4 +1,4 @@ -Bifrost - The fastest LLM gateway
\ No newline at end of file +
\ No newline at end of file diff --git a/transports/bifrost-http/ui/mcp-clients/index.txt b/transports/bifrost-http/ui/mcp-clients/index.txt index 0525a93b25..e776161607 100644 --- a/transports/bifrost-http/ui/mcp-clients/index.txt +++ b/transports/bifrost-http/ui/mcp-clients/index.txt @@ -8,7 +8,7 @@ 8:I[7555,[],""] 9:I[1295,[],""] a:I[894,[],"ClientPageRoot"] -b:I[7765,["106","static/chunks/106-5f7ab4dfb56f2b8d.js","189","static/chunks/189-34927d98e5a18740.js","944","static/chunks/944-f01f5d77bd313da6.js","231","static/chunks/231-650d1d0c1739f9f4.js","412","static/chunks/412-1cad36ab64c91342.js","62","static/chunks/62-2bb67bacb7d964db.js","613","static/chunks/613-5b9d5c2343ec7cb3.js","915","static/chunks/915-36ceadf34271615d.js","364","static/chunks/app/mcp-clients/page-b2d03b2155b1dfee.js"],"default"] +b:I[7765,["106","static/chunks/106-5f7ab4dfb56f2b8d.js","189","static/chunks/189-34927d98e5a18740.js","944","static/chunks/944-f01f5d77bd313da6.js","231","static/chunks/231-650d1d0c1739f9f4.js","412","static/chunks/412-1cad36ab64c91342.js","62","static/chunks/62-2bb67bacb7d964db.js","613","static/chunks/613-5b9d5c2343ec7cb3.js","915","static/chunks/915-36ceadf34271615d.js","364","static/chunks/app/mcp-clients/page-b787aebb95ad9490.js"],"default"] e:I[9665,[],"OutletBoundary"] 11:I[4911,[],"AsyncMetadataOutlet"] 13:I[9665,[],"ViewportBoundary"] @@ -16,8 +16,8 @@ e:I[9665,[],"OutletBoundary"] 17:I[6614,[],""] :HL["/_next/static/media/569ce4b8f30dc480-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] :HL["/_next/static/media/93f479601ee12b01-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] -:HL["/_next/static/css/215063fa9dc64c3d.css","style"] -0:{"P":null,"b":"build","p":"","c":["","mcp-clients",""],"i":false,"f":[[["",{"children":["mcp-clients",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/215063fa9dc64c3d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","suppressHydrationWarning":true,"children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"attribute":"class","defaultTheme":"system","enableSystem":true,"children":[["$","$L4",null,{}],["$","$L5",null,{"children":["$","$L6",null,{"children":[["$","$L7",null,{}],["$","main",null,{"className":"custom-scrollbar w-5xl relative mx-auto flex min-h-screen flex-col px-4 py-12","children":["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}]}]}]}]]}],{"children":["mcp-clients",["$","$1","c",{"children":[null,["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$La",null,{"Component":"$b","searchParams":{},"params":{},"promises":["$@c","$@d"]}],null,["$","$Le",null,{"children":["$Lf","$L10",["$","$L11",null,{"promise":"$@12"}]]}]]}],{},null,false]},null,false]},null,false],["$","$1","h",{"children":[null,["$","$1","5ktnMBbTUE3xHXNz2uQqGv",{"children":[["$","$L13",null,{"children":"$L14"}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],["$","$L15",null,{"children":"$L16"}]]}],false]],"m":"$undefined","G":["$17","$undefined"],"s":false,"S":true} +:HL["/_next/static/css/e2d791eafd0cc299.css","style"] +0:{"P":null,"b":"build","p":"","c":["","mcp-clients",""],"i":false,"f":[[["",{"children":["mcp-clients",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/e2d791eafd0cc299.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","suppressHydrationWarning":true,"children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"attribute":"class","defaultTheme":"system","enableSystem":true,"children":[["$","$L4",null,{}],["$","$L5",null,{"children":["$","$L6",null,{"children":[["$","$L7",null,{}],["$","main",null,{"className":"custom-scrollbar w-5xl relative mx-auto flex min-h-screen flex-col px-4 py-12","children":["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}]}]}]}]]}],{"children":["mcp-clients",["$","$1","c",{"children":[null,["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$La",null,{"Component":"$b","searchParams":{},"params":{},"promises":["$@c","$@d"]}],null,["$","$Le",null,{"children":["$Lf","$L10",["$","$L11",null,{"promise":"$@12"}]]}]]}],{},null,false]},null,false]},null,false],["$","$1","h",{"children":[null,["$","$1","o2eaDdDUmYTi6VnysRjm6v",{"children":[["$","$L13",null,{"children":"$L14"}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],["$","$L15",null,{"children":"$L16"}]]}],false]],"m":"$undefined","G":["$17","$undefined"],"s":false,"S":true} 18:"$Sreact.suspense" 19:I[4911,[],"AsyncMetadata"] c:{} diff --git a/transports/bifrost-http/ui/plugins/index.html b/transports/bifrost-http/ui/plugins/index.html index 017d1856e7..0d979b814d 100644 --- a/transports/bifrost-http/ui/plugins/index.html +++ b/transports/bifrost-http/ui/plugins/index.html @@ -1,4 +1,4 @@ -Bifrost - The fastest LLM gateway
Plugin EcosystemBeta
Supercharge Bifrost

Extend Bifrost with powerful plugins for observability, testing, security, and custom business logic. Full support in Go SDK, with HTTP transport integration in active development.

Featured Plugins

Production-ready plugins with varying levels of HTTP transport support

Maximproduction
Maxim Logger
Observability
Advanced LLM observability, tracing, and analytics platform integration

Key Features

Real-time LLM tracing
Performance analytics
Cost tracking
Command Line
bifrost-http --plugins maxim
production
Response Mocker
Development
Mock AI responses for testing, development, and cost-effective prototyping

Key Features

Configurable mock responses
Request pattern matching
Development environment support
HTTP transport support coming soon
enterprise
Circuit Breaker
Reliability
Resilience patterns for handling provider failures and preventing cascade errors

Key Features

Automatic failure detection
Fallback mechanisms
Rate limiting
HTTP transport support coming soon

Usage Patterns

Multiple ways to integrate plugins into your workflow

HTTP Transport
Maxim plugin only (for now)
bifrost-http --plugins maxim

Additional plugins coming soon

Docker Deployment
Environment variables
docker run -e APP_PLUGINS=maxim

Additional plugins coming soon

Go SDK
Full plugin ecosystem
Plugins: []schemas.Plugin{...}

All plugins available

Coming Soon

Exciting plugins currently in development

Redis Cache
Coming Soon
High-performance caching layer with Redis backend
Auth Guard
Coming Soon
Enterprise authentication and authorization middleware
Rate Limiter
Coming Soon
Advanced rate limiting with multiple strategies

Join the Plugin Ecosystem

Contribute to the growing collection of Bifrost plugins or build your own custom solutions

\ No newline at end of file +
Plugin EcosystemBeta
Supercharge Bifrost

Extend Bifrost with powerful plugins for observability, testing, security, and custom business logic. Full support in Go SDK, with HTTP transport integration in active development.

Featured Plugins

Production-ready plugins with varying levels of HTTP transport support

Maximproduction
Maxim Logger
Observability
Advanced LLM observability, tracing, and analytics platform integration

Key Features

Real-time LLM tracing
Performance analytics
Cost tracking
Command Line
bifrost-http --plugins maxim
production
Response Mocker
Development
Mock AI responses for testing, development, and cost-effective prototyping

Key Features

Configurable mock responses
Request pattern matching
Development environment support
HTTP transport support coming soon
enterprise
Circuit Breaker
Reliability
Resilience patterns for handling provider failures and preventing cascade errors

Key Features

Automatic failure detection
Fallback mechanisms
Rate limiting
HTTP transport support coming soon

Usage Patterns

Multiple ways to integrate plugins into your workflow

HTTP Transport
Maxim plugin only (for now)
bifrost-http --plugins maxim

Additional plugins coming soon

Docker Deployment
Environment variables
docker run -e APP_PLUGINS=maxim

Additional plugins coming soon

Go SDK
Full plugin ecosystem
Plugins: []schemas.Plugin{...}

All plugins available

Coming Soon

Exciting plugins currently in development

Redis Cache
Coming Soon
High-performance caching layer with Redis backend
Auth Guard
Coming Soon
Enterprise authentication and authorization middleware
Rate Limiter
Coming Soon
Advanced rate limiting with multiple strategies

Join the Plugin Ecosystem

Contribute to the growing collection of Bifrost plugins or build your own custom solutions

\ No newline at end of file diff --git a/transports/bifrost-http/ui/plugins/index.txt b/transports/bifrost-http/ui/plugins/index.txt index 7b463aa53a..89bcb44f20 100644 --- a/transports/bifrost-http/ui/plugins/index.txt +++ b/transports/bifrost-http/ui/plugins/index.txt @@ -16,8 +16,8 @@ e:I[9665,[],"OutletBoundary"] 17:I[6614,[],""] :HL["/_next/static/media/569ce4b8f30dc480-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] :HL["/_next/static/media/93f479601ee12b01-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] -:HL["/_next/static/css/215063fa9dc64c3d.css","style"] -0:{"P":null,"b":"build","p":"","c":["","plugins",""],"i":false,"f":[[["",{"children":["plugins",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/215063fa9dc64c3d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","suppressHydrationWarning":true,"children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"attribute":"class","defaultTheme":"system","enableSystem":true,"children":[["$","$L4",null,{}],["$","$L5",null,{"children":["$","$L6",null,{"children":[["$","$L7",null,{}],["$","main",null,{"className":"custom-scrollbar w-5xl relative mx-auto flex min-h-screen flex-col px-4 py-12","children":["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}]}]}]}]]}],{"children":["plugins",["$","$1","c",{"children":[null,["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$La",null,{"Component":"$b","searchParams":{},"params":{},"promises":["$@c","$@d"]}],null,["$","$Le",null,{"children":["$Lf","$L10",["$","$L11",null,{"promise":"$@12"}]]}]]}],{},null,false]},null,false]},null,false],["$","$1","h",{"children":[null,["$","$1","OumpsqGtvxIomOieQa4R0v",{"children":[["$","$L13",null,{"children":"$L14"}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],["$","$L15",null,{"children":"$L16"}]]}],false]],"m":"$undefined","G":["$17","$undefined"],"s":false,"S":true} +:HL["/_next/static/css/e2d791eafd0cc299.css","style"] +0:{"P":null,"b":"build","p":"","c":["","plugins",""],"i":false,"f":[[["",{"children":["plugins",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/e2d791eafd0cc299.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","suppressHydrationWarning":true,"children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"attribute":"class","defaultTheme":"system","enableSystem":true,"children":[["$","$L4",null,{}],["$","$L5",null,{"children":["$","$L6",null,{"children":[["$","$L7",null,{}],["$","main",null,{"className":"custom-scrollbar w-5xl relative mx-auto flex min-h-screen flex-col px-4 py-12","children":["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}]}]}]}]]}],{"children":["plugins",["$","$1","c",{"children":[null,["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$La",null,{"Component":"$b","searchParams":{},"params":{},"promises":["$@c","$@d"]}],null,["$","$Le",null,{"children":["$Lf","$L10",["$","$L11",null,{"promise":"$@12"}]]}]]}],{},null,false]},null,false]},null,false],["$","$1","h",{"children":[null,["$","$1","5_xUMikC2fpCftpfhbg71v",{"children":[["$","$L13",null,{"children":"$L14"}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],["$","$L15",null,{"children":"$L16"}]]}],false]],"m":"$undefined","G":["$17","$undefined"],"s":false,"S":true} 18:"$Sreact.suspense" 19:I[4911,[],"AsyncMetadata"] c:{} diff --git a/transports/bifrost-http/ui/providers/index.html b/transports/bifrost-http/ui/providers/index.html index 52afd5ecfc..d516157eb2 100644 --- a/transports/bifrost-http/ui/providers/index.html +++ b/transports/bifrost-http/ui/providers/index.html @@ -1,4 +1,4 @@ -Bifrost - The fastest LLM gateway
\ No newline at end of file +
\ No newline at end of file diff --git a/transports/bifrost-http/ui/providers/index.txt b/transports/bifrost-http/ui/providers/index.txt index 46e2f84006..4f6f6020c7 100644 --- a/transports/bifrost-http/ui/providers/index.txt +++ b/transports/bifrost-http/ui/providers/index.txt @@ -8,7 +8,7 @@ 8:I[7555,[],""] 9:I[1295,[],""] a:I[894,[],"ClientPageRoot"] -b:I[3621,["106","static/chunks/106-5f7ab4dfb56f2b8d.js","189","static/chunks/189-34927d98e5a18740.js","944","static/chunks/944-f01f5d77bd313da6.js","231","static/chunks/231-650d1d0c1739f9f4.js","412","static/chunks/412-1cad36ab64c91342.js","62","static/chunks/62-2bb67bacb7d964db.js","822","static/chunks/822-002f831ce65c92b1.js","613","static/chunks/613-5b9d5c2343ec7cb3.js","925","static/chunks/925-c10f273e99bd2248.js","915","static/chunks/915-36ceadf34271615d.js","830","static/chunks/830-ff62672a2ba99b18.js","387","static/chunks/app/providers/page-b25fa0ce16423da1.js"],"default"] +b:I[3621,["106","static/chunks/106-5f7ab4dfb56f2b8d.js","189","static/chunks/189-34927d98e5a18740.js","944","static/chunks/944-f01f5d77bd313da6.js","231","static/chunks/231-650d1d0c1739f9f4.js","412","static/chunks/412-1cad36ab64c91342.js","62","static/chunks/62-2bb67bacb7d964db.js","822","static/chunks/822-002f831ce65c92b1.js","613","static/chunks/613-5b9d5c2343ec7cb3.js","925","static/chunks/925-c10f273e99bd2248.js","915","static/chunks/915-36ceadf34271615d.js","830","static/chunks/830-ff62672a2ba99b18.js","387","static/chunks/app/providers/page-d68ad7e5de246698.js"],"default"] e:I[9665,[],"OutletBoundary"] 11:I[4911,[],"AsyncMetadataOutlet"] 13:I[9665,[],"ViewportBoundary"] @@ -16,8 +16,8 @@ e:I[9665,[],"OutletBoundary"] 17:I[6614,[],""] :HL["/_next/static/media/569ce4b8f30dc480-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] :HL["/_next/static/media/93f479601ee12b01-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] -:HL["/_next/static/css/215063fa9dc64c3d.css","style"] -0:{"P":null,"b":"build","p":"","c":["","providers",""],"i":false,"f":[[["",{"children":["providers",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/215063fa9dc64c3d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","suppressHydrationWarning":true,"children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"attribute":"class","defaultTheme":"system","enableSystem":true,"children":[["$","$L4",null,{}],["$","$L5",null,{"children":["$","$L6",null,{"children":[["$","$L7",null,{}],["$","main",null,{"className":"custom-scrollbar w-5xl relative mx-auto flex min-h-screen flex-col px-4 py-12","children":["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}]}]}]}]]}],{"children":["providers",["$","$1","c",{"children":[null,["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$La",null,{"Component":"$b","searchParams":{},"params":{},"promises":["$@c","$@d"]}],null,["$","$Le",null,{"children":["$Lf","$L10",["$","$L11",null,{"promise":"$@12"}]]}]]}],{},null,false]},null,false]},null,false],["$","$1","h",{"children":[null,["$","$1","TJMrJl_xLqHRC35sJm-CNv",{"children":[["$","$L13",null,{"children":"$L14"}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],["$","$L15",null,{"children":"$L16"}]]}],false]],"m":"$undefined","G":["$17","$undefined"],"s":false,"S":true} +:HL["/_next/static/css/e2d791eafd0cc299.css","style"] +0:{"P":null,"b":"build","p":"","c":["","providers",""],"i":false,"f":[[["",{"children":["providers",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/e2d791eafd0cc299.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","suppressHydrationWarning":true,"children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"attribute":"class","defaultTheme":"system","enableSystem":true,"children":[["$","$L4",null,{}],["$","$L5",null,{"children":["$","$L6",null,{"children":[["$","$L7",null,{}],["$","main",null,{"className":"custom-scrollbar w-5xl relative mx-auto flex min-h-screen flex-col px-4 py-12","children":["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]}]]}]}]}]}]]}],{"children":["providers",["$","$1","c",{"children":[null,["$","$L8",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$La",null,{"Component":"$b","searchParams":{},"params":{},"promises":["$@c","$@d"]}],null,["$","$Le",null,{"children":["$Lf","$L10",["$","$L11",null,{"promise":"$@12"}]]}]]}],{},null,false]},null,false]},null,false],["$","$1","h",{"children":[null,["$","$1","4_VSlrg_FocfEGhhclPw-v",{"children":[["$","$L13",null,{"children":"$L14"}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],["$","$L15",null,{"children":"$L16"}]]}],false]],"m":"$undefined","G":["$17","$undefined"],"s":false,"S":true} 18:"$Sreact.suspense" 19:I[4911,[],"AsyncMetadata"] c:{} diff --git a/transports/config.example.json b/transports/config.example.json index 1c17c62529..9ef78aff5b 100644 --- a/transports/config.example.json +++ b/transports/config.example.json @@ -115,7 +115,14 @@ { "value": "env.AZURE_API_KEY", "models": ["gpt-4o"], - "weight": 1.0 + "weight": 1.0, + "azure_key_config": { + "endpoint": "env.AZURE_ENDPOINT", + "deployments": { + "gpt-4o": "gpt-4o-aug" + }, + "api_version": "2024-08-01-preview" + } } ], "network_config": { @@ -124,25 +131,23 @@ "retry_backoff_initial_ms": 100, "retry_backoff_max_ms": 2000 }, - "meta_config": { - "endpoint": "env.AZURE_ENDPOINT", - "deployments": { - "gpt-4o": "gpt-4o-aug" - }, - "api_version": "2024-08-01-preview" - }, "concurrency_and_buffer_size": { "concurrency": 3, "buffer_size": 10 } }, "vertex": { - "keys": [], - "meta_config": { - "project_id": "env.VERTEX_PROJECT_ID", - "region": "us-central1", - "auth_credentials": "env.VERTEX_CREDENTIALS" - }, + "keys": [ + { + "models": ["gemini-2.0-flash-001"], + "weight": 1.0, + "vertex_key_config": { + "project_id": "env.VERTEX_PROJECT_ID", + "region": "us-central1", + "auth_credentials": "env.VERTEX_CREDENTIALS" + } + } + ], "concurrency_and_buffer_size": { "concurrency": 3, "buffer_size": 10 diff --git a/ui/components/config/meta-config-renderer.tsx b/ui/components/config/meta-config-renderer.tsx index 644b472b52..4c33e12ebc 100644 --- a/ui/components/config/meta-config-renderer.tsx +++ b/ui/components/config/meta-config-renderer.tsx @@ -17,30 +17,6 @@ interface MetaField { } const providerMetaFields: Record = { - azure: { - title: 'Azure OpenAI Meta Config', - fields: [ - { - name: 'endpoint', - label: 'Endpoint', - type: 'text', - placeholder: 'https://your-resource.openai.azure.com or env.AZURE_ENDPOINT', - }, - { - name: 'api_version', - label: 'API Version (Optional)', - type: 'text', - placeholder: 'YYYY-MM-DD or env.AZURE_VERSION', - }, - { - name: 'deployments', - label: 'Deployments (JSON format)', - type: 'textarea', - placeholder: '{ "gpt-4": "my-deployment" }', - isJson: true, - }, - ], - }, bedrock: { title: 'AWS Bedrock Meta Config', fields: [ @@ -50,28 +26,30 @@ const providerMetaFields: Record type: 'text', placeholder: 'us-east-1 or env.AWS_REGION', }, - ], - }, - vertex: { - title: 'Google Vertex AI Meta Config', - fields: [ { - name: 'project_id', - label: 'Project ID', + name: 'secret_access_key', + label: 'Secret Access Key (Optional)', type: 'text', - placeholder: 'gcp-project-id or env.GCP_PROJECT', + placeholder: 'Your AWS secret access key or env.AWS_SECRET_ACCESS_KEY', }, { - name: 'region', - label: 'Region', + name: 'session_token', + label: 'Session Token (Optional)', type: 'text', - placeholder: 'us-central1 or env.GCP_REGION', + placeholder: 'Your AWS session token or env.AWS_SESSION_TOKEN', }, { - name: 'auth_credentials', - label: 'Auth Credentials (JSON key)', + name: 'arn', + label: 'ARN (Optional)', + type: 'text', + placeholder: 'AWS ARN or env.AWS_ARN', + }, + { + name: 'inference_profiles', + label: 'Inference Profiles (JSON format, Optional)', type: 'textarea', - placeholder: 'JSON key or env.GCP_CREDS', + placeholder: '{ "model-id": "profile-name" }', + isJson: true, }, ], }, diff --git a/ui/components/config/provider-form.tsx b/ui/components/config/provider-form.tsx index 50847deaad..6d58508414 100644 --- a/ui/components/config/provider-form.tsx +++ b/ui/components/config/provider-form.tsx @@ -13,6 +13,7 @@ import { ProviderIconType, renderProviderIcon } from '@/lib/constants/icons' import { PROVIDER_LABELS, PROVIDERS as Providers } from '@/lib/constants/logs' import { AddProviderRequest, + AzureKeyConfig, ConcurrencyAndBufferSize, Key as KeyType, MetaConfig, @@ -22,15 +23,18 @@ import { ProxyConfig, ProxyType, UpdateProviderRequest, + VertexKeyConfig, } from '@/lib/types/config' import { cn } from '@/lib/utils' import { Validator } from '@/lib/utils/validation' +import { isRedacted, isValidVertexAuthCredentials, isValidAzureDeployments } from '@/lib/utils/validation' import isEqual from 'lodash.isequal' import { AlertTriangle, Globe, Info, Plus, Save, X, Zap } from 'lucide-react' import { useEffect, useMemo, useState } from 'react' import { toast } from 'sonner' import { Alert, AlertDescription } from '../ui/alert' import MetaConfigRenderer from './meta-config-renderer' +import { Textarea } from '../ui/textarea' interface ProviderFormProps { provider?: ProviderResponse | null @@ -43,22 +47,37 @@ interface ProviderFormProps { const createInitialState = (provider?: ProviderResponse | null, defaultProvider?: string): Omit => { const isNewProvider = !provider const providerName = provider?.name || defaultProvider || '' - const keysRequired = !['vertex', 'ollama', 'sgl'].includes(providerName) + const keysRequired = !['ollama', 'sgl'].includes(providerName) // Vertex needs keys for config + + // Create default key based on provider type + const createDefaultKey = (): KeyType => { + const baseKey: KeyType = { id: '', value: '', models: [], weight: 1.0 } + + if (providerName === 'azure') { + baseKey.azure_key_config = { + endpoint: '', + deployments: {}, + api_version: '2024-02-01', + } + } else if (providerName === 'vertex') { + baseKey.vertex_key_config = { + project_id: '', + region: '', + auth_credentials: '', + } + } + + return baseKey + } return { selectedProvider: providerName, - keys: - isNewProvider && keysRequired - ? [{ value: '', models: [], weight: 1.0 }] - : !isNewProvider && keysRequired && provider?.keys - ? provider.keys - : [], + keys: isNewProvider && keysRequired ? [createDefaultKey()] : !isNewProvider && keysRequired && provider?.keys ? provider.keys : [], networkConfig: provider?.network_config || DEFAULT_NETWORK_CONFIG, performanceConfig: provider?.concurrency_and_buffer_size || DEFAULT_PERFORMANCE_CONFIG, metaConfig: provider?.meta_config || { - endpoint: '', - deployments: {}, - api_version: '', + region: '', + secret_access_key: '', }, proxyConfig: provider?.proxy_config || { type: 'none', @@ -92,8 +111,8 @@ export default function ProviderForm({ provider, onSave, onCancel, existingProvi const { selectedProvider, keys, networkConfig, performanceConfig, metaConfig, proxyConfig, isDirty } = formData const baseURLRequired = selectedProvider === 'ollama' || selectedProvider === 'sgl' - const keysRequired = !['vertex', 'ollama', 'sgl'].includes(selectedProvider) - const keysValid = !keysRequired || keys.every((k) => k.value.trim() !== '') + const keysRequired = !['ollama', 'sgl'].includes(selectedProvider) // Vertex needs keys for config + const keysValid = !keysRequired || keys.every((k) => selectedProvider === 'vertex' || k.value.trim() !== '') // Vertex can have empty API key const keysPresent = !keysRequired || keys.length > 0 const performanceValid = @@ -104,41 +123,58 @@ export default function ProviderForm({ provider, onSave, onCancel, existingProvi performanceConfig.concurrency !== initialState.performanceConfig.concurrency || performanceConfig.buffer_size !== initialState.performanceConfig.buffer_size - /* Meta configuration validation based on provider requirements */ - const getMetaValidation = () => { + /* Key-level configuration validation for Azure and Vertex */ + const getKeyValidation = () => { let valid = true let message = '' - if (selectedProvider === 'azure') { - const endpointValid = !!metaConfig.endpoint && (metaConfig.endpoint as string).trim() !== '' - const deploymentsValid = !!( - metaConfig.deployments && - typeof metaConfig.deployments === 'object' && - Object.keys(metaConfig.deployments as Record).length > 0 - ) - valid = endpointValid && deploymentsValid - if (!valid) { - message = 'Endpoint and at least one Deployment are required for Azure' + for (const key of keys) { + if (selectedProvider === 'azure' && key.azure_key_config) { + const endpointValid = !!key.azure_key_config.endpoint && key.azure_key_config.endpoint.trim() !== '' + + // Validate deployments using utility function + const deploymentsValid = isValidAzureDeployments(key.azure_key_config.deployments) + + if (!endpointValid || !deploymentsValid) { + valid = false + message = 'Endpoint and valid Deployments (JSON object) are required for Azure keys' + break + } + } else if (selectedProvider === 'vertex' && key.vertex_key_config) { + const projectValid = !!key.vertex_key_config.project_id && key.vertex_key_config.project_id.trim() !== '' + const regionValid = !!key.vertex_key_config.region && key.vertex_key_config.region.trim() !== '' + + // Validate auth credentials using utility function + const credsValid = isValidVertexAuthCredentials(key.vertex_key_config.auth_credentials) + + if (!projectValid || !credsValid || !regionValid) { + valid = false + message = 'Project ID, valid Auth Credentials (JSON object or env.VAR), and Region are required for Vertex AI keys' + break + } } - } else if (selectedProvider === 'bedrock') { + } + + return { valid, message } + } + + /* Meta configuration validation based on provider requirements (Bedrock only) */ + const getMetaValidation = () => { + let valid = true + let message = '' + + if (selectedProvider === 'bedrock') { const regionValid = !!metaConfig.region && (metaConfig.region as string).trim() !== '' valid = regionValid if (!valid) { message = 'Region is required for AWS Bedrock' } - } else if (selectedProvider === 'vertex') { - const projectValid = !!metaConfig.project_id && (metaConfig.project_id as string).trim() !== '' - const credsValid = !!metaConfig.auth_credentials && (metaConfig.auth_credentials as string).trim() !== '' - const regionValid = !!metaConfig.region && (metaConfig.region as string).trim() !== '' - valid = projectValid && credsValid && regionValid - if (!valid) { - message = 'Project ID, Auth Credentials, and Region are required for Vertex AI' - } } return { valid, message } } + const { valid: keyValid, message: keyErrorMessage } = getKeyValidation() const { valid: metaValid, message: metaErrorMessage } = getMetaValidation() useEffect(() => { @@ -179,7 +215,13 @@ export default function ProviderForm({ provider, onSave, onCancel, existingProvi if (provider) { const data: UpdateProviderRequest = { - keys: keysRequired ? keys.filter((k) => k.value.trim() !== '') : [], + keys: keysRequired + ? keys.filter((k) => + selectedProvider === 'vertex' + ? true // Include all Vertex keys (API key can be empty) + : k.value.trim() !== '', + ) + : [], network_config: networkConfig, concurrency_and_buffer_size: performanceConfig, meta_config: metaConfig, @@ -189,7 +231,13 @@ export default function ProviderForm({ provider, onSave, onCancel, existingProvi } else { const data: AddProviderRequest = { provider: selectedProvider as ModelProvider, - keys: keysRequired ? keys.filter((k) => k.value.trim() !== '') : [], + keys: keysRequired + ? keys.filter((k) => + selectedProvider === 'vertex' + ? true // Include all Vertex keys (API key can be empty) + : k.value.trim() !== '', + ) + : [], network_config: networkConfig, concurrency_and_buffer_size: performanceConfig, meta_config: metaConfig, @@ -228,9 +276,13 @@ export default function ProviderForm({ provider, onSave, onCancel, existingProvi ? [ Validator.minValue(keys.length, 1, 'At least one API key is required'), Validator.custom( - keys.every((k) => k.value.trim() !== ''), + keys.every((k) => selectedProvider === 'vertex' || k.value.trim() !== ''), 'API key value cannot be empty', ), + Validator.custom( + keys.every((k) => k.weight >= 0 && k.weight <= 1), + 'Key weights must be between 0 and 1', + ), ] : []), @@ -243,36 +295,34 @@ export default function ProviderForm({ provider, onSave, onCancel, existingProvi Validator.minValue(performanceConfig.buffer_size, 1, 'Buffer size must be greater than 0'), Validator.custom(performanceConfig.concurrency < performanceConfig.buffer_size, 'Buffer size must be greater than concurrency'), - // Meta config validation - Validator.custom(metaValid, metaErrorMessage), + // Key-level config validation + Validator.custom(keyValid, keyErrorMessage), - // Meta config validation for Azure - ...(selectedProvider === 'azure' - ? [ - Validator.required(metaConfig.endpoint, 'Azure endpoint is required'), - Validator.minValue( - Object.keys((metaConfig.deployments as Record) || {}).length, - 1, - 'At least one Azure deployment is required', - ), - ] - : []), + // Meta config validation (Bedrock only) + Validator.custom(metaValid, metaErrorMessage), // Meta config validation for Bedrock ...(selectedProvider === 'bedrock' ? [Validator.required(metaConfig.region, 'AWS region is required')] : []), - - // Meta config validation for Vertex - ...(selectedProvider === 'vertex' - ? [ - Validator.required(metaConfig.project_id, 'Project ID is required for Vertex AI'), - Validator.required(metaConfig.auth_credentials, 'Auth credentials are required for Vertex AI'), - Validator.required(metaConfig.region, 'Region is required for Vertex AI'), - ] - : []), ]) const addKey = () => { - updateField('keys', [...keys, { value: '', models: [], weight: 1.0 }]) + const newKey: KeyType = { id: '', value: '', models: [], weight: 1.0 } + + if (selectedProvider === 'azure') { + newKey.azure_key_config = { + endpoint: '', + deployments: {}, + api_version: '2024-02-01', + } + } else if (selectedProvider === 'vertex') { + newKey.vertex_key_config = { + project_id: '', + region: '', + auth_credentials: '', + } + } + + updateField('keys', [...keys, newKey]) } const removeKey = (index: number) => { @@ -298,6 +348,48 @@ export default function ProviderForm({ provider, onSave, onCancel, existingProvi updateField('keys', newKeys) } + const updateKeyAzureConfig = (index: number, field: keyof AzureKeyConfig, value: string | Record) => { + const newKeys = [...keys] + const keyToUpdate = { ...newKeys[index] } + + if (!keyToUpdate.azure_key_config) { + keyToUpdate.azure_key_config = { + endpoint: '', + deployments: {}, + api_version: '2024-02-01', + } + } + + keyToUpdate.azure_key_config = { + ...keyToUpdate.azure_key_config, + [field]: value, + } + + newKeys[index] = keyToUpdate + updateField('keys', newKeys) + } + + const updateKeyVertexConfig = (index: number, field: keyof VertexKeyConfig, value: string) => { + const newKeys = [...keys] + const keyToUpdate = { ...newKeys[index] } + + if (!keyToUpdate.vertex_key_config) { + keyToUpdate.vertex_key_config = { + project_id: '', + region: '', + auth_credentials: '', + } + } + + keyToUpdate.vertex_key_config = { + ...keyToUpdate.vertex_key_config, + [field]: value, + } + + newKeys[index] = keyToUpdate + updateField('keys', newKeys) + } + const handleMetaConfigChange = (field: keyof MetaConfig, value: string | Record) => { updateField('metaConfig', { ...metaConfig, [field]: value }) } @@ -313,8 +405,8 @@ export default function ProviderForm({ provider, onSave, onCancel, existingProvi }) } - // Add Meta Config tab for providers that need it - if (selectedProvider === 'azure' || selectedProvider === 'bedrock' || selectedProvider === 'vertex') { + // Add Meta Config tab only for Bedrock + if (selectedProvider === 'bedrock') { availableTabs.push({ id: 'meta-config', label: 'Meta Config', @@ -447,20 +539,23 @@ export default function ProviderForm({ provider, onSave, onCancel, existingProvi {keys.map((key, index) => (
-
-
API Key
- updateKey(index, 'value', e.target.value)} - type="text" - className={`flex-1 transition-all duration-200 ease-in-out ${keysRequired && key.value.trim() === '' ? 'border-destructive' : ''}`} - /> -
+ {selectedProvider !== 'vertex' && ( +
+
API Key
+ updateKey(index, 'value', e.target.value)} + type="text" + className={`flex-1 transition-all duration-200 ease-in-out ${keysRequired && selectedProvider !== 'vertex' && key.value.trim() === '' ? 'border-destructive' : ''}`} + /> +
+ )} +
@@ -482,10 +577,11 @@ export default function ProviderForm({ provider, onSave, onCancel, existingProvi value={key.weight} onChange={(e) => updateKey(index, 'weight', e.target.value)} type="number" - step="0.1" + step="0.01" min="0" - max="1.0" - className="w-20 transition-all duration-200 ease-in-out" + className={`w-20 transition-all duration-200 ease-in-out ${ + keysRequired && (key.weight < 0 || key.weight > 1) ? 'border-destructive' : '' + }`} />
@@ -511,6 +607,112 @@ export default function ProviderForm({ provider, onSave, onCancel, existingProvi onValueChange={(newModels) => updateKey(index, 'models', newModels)} />
+ + {/* Azure Key Configuration */} + {selectedProvider === 'azure' && ( +
+
+ + updateKeyAzureConfig(index, 'endpoint', e.target.value)} + className={`transition-all duration-200 ease-in-out ${!key.azure_key_config?.endpoint?.trim() ? 'border-destructive' : ''}`} + /> +
+
+ + updateKeyAzureConfig(index, 'api_version', e.target.value)} + /> +
+
+ +
+ JSON object mapping model names to deployment names +
+