diff --git a/alembic/versions/c4d6e8f0a2b4_add_cache_pricing_columns.py b/alembic/versions/c4d6e8f0a2b4_add_cache_pricing_columns.py new file mode 100644 index 00000000..5b9001f3 --- /dev/null +++ b/alembic/versions/c4d6e8f0a2b4_add_cache_pricing_columns.py @@ -0,0 +1,39 @@ +"""Add cache pricing columns to model_pricing. + +Revision ID: c4d6e8f0a2b4 +Revises: f3b6d8a1c4e7 +Create Date: 2026-07-23 12:00:00.000000 + +""" + +from collections.abc import Sequence + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "c4d6e8f0a2b4" +down_revision: str | Sequence[str] | None = "f3b6d8a1c4e7" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + """Upgrade schema.""" + # Nullable: providers/models without prompt caching leave these unset. + # The cost calculation in log_usage only prices cache tokens when a rate + # is configured, so existing rows are unaffected. + op.add_column( + "model_pricing", + sa.Column("cache_read_price_per_million", sa.Float(), nullable=True), + ) + op.add_column( + "model_pricing", + sa.Column("cache_write_price_per_million", sa.Float(), nullable=True), + ) + + +def downgrade() -> None: + """Downgrade schema.""" + op.drop_column("model_pricing", "cache_write_price_per_million") + op.drop_column("model_pricing", "cache_read_price_per_million") diff --git a/docs/configuration.md b/docs/configuration.md index dc33e5a5..70d25e92 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -269,6 +269,30 @@ Config pricing sets initial values. Pricing set via the `/v1/pricing` API takes A pricing entry whose provider is not listed in the `providers` section is skipped at startup with a warning, not treated as a fatal error: the provider may still be reachable through environment credentials (any-llm reads keys like `OPENAI_API_KEY`), so a pricing/provider mismatch should not abort the gateway. To have such an entry take effect, add the provider to the `providers` section. +#### Cache token pricing + +Providers that support prompt caching (OpenAI, Gemini, Anthropic) report cached-token counts that are captured in `cache_read_tokens` and `cache_write_tokens` on the usage log. Without a configured cache rate these tokens are still billed, at `input_price_per_million` (they are ordinary input tokens), just not at a discounted cache rate. To bill them at the provider's cache price instead, add the optional `cache_read_price_per_million` and `cache_write_price_per_million` rates (USD per million tokens): + +```yaml +pricing: + openai:gpt-4o: + input_price_per_million: 2.50 + output_price_per_million: 10.00 + cache_read_price_per_million: 1.25 # discounted rate for cached input + anthropic:claude-sonnet-4-6: + input_price_per_million: 3.00 + output_price_per_million: 15.00 + cache_read_price_per_million: 0.30 # cache-read rate + cache_write_price_per_million: 3.75 # cache-creation (write) rate +``` + +Every physical input token is charged once. The input/prompt count is treated as the grand total that *includes* cache reads and writes; the uncached remainder is billed at `input_price_per_million`, and cache tokens are re-priced at their own rate when one is configured. Providers report cache counts two ways, and the gateway normalizes both onto that single model: + +- **OpenAI / Gemini**: `cache_read_tokens` is already a subset of `prompt_tokens`, so setting `cache_read_price_per_million` discounts the cached portion (re-priced at the cache rate instead of the full `input_price_per_million`) rather than double-counting it. `cache_write_tokens` is always 0 for these providers. +- **Anthropic**: `cache_read_tokens` and `cache_write_tokens` are reported separately from `prompt_tokens`, so they are added on top and billed at `cache_read_price_per_million` / `cache_write_price_per_million`. This holds on every turn, including warm-cache reads that create no new cache (`cache_write_tokens` is 0). + +When a cache rate is left unset (null), those cache tokens are not dropped or billed at $0: they remain part of the input total and are billed at `input_price_per_million`, since they are still real input tokens. Set the cache rate to bill them at the provider's discounted cache price. The same fields are available on the `/v1/pricing` API (`SetPricingRequest` and `PricingResponse`). + ### Default pricing Default pricing is **off by default**. When you enable it (`default_pricing: true` in `config.yml`, or diff --git a/docs/public/openapi.json b/docs/public/openapi.json index 2d9c3e72..209daa4f 100644 --- a/docs/public/openapi.json +++ b/docs/public/openapi.json @@ -2445,6 +2445,28 @@ "ModelPricingInfo": { "description": "Pricing information for a model.", "properties": { + "cache_read_price_per_million": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Cache Read Price Per Million" + }, + "cache_write_price_per_million": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Cache Write Price Per Million" + }, "input_price_per_million": { "title": "Input Price Per Million", "type": "number" @@ -2596,6 +2618,28 @@ "PricingResponse": { "description": "Response model for model pricing.", "properties": { + "cache_read_price_per_million": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Cache Read Price Per Million" + }, + "cache_write_price_per_million": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Cache Write Price Per Million" + }, "created_at": { "title": "Created At", "type": "string" @@ -2626,6 +2670,8 @@ "effective_at", "input_price_per_million", "output_price_per_million", + "cache_read_price_per_million", + "cache_write_price_per_million", "created_at", "updated_at" ], @@ -3369,6 +3415,32 @@ "SetPricingRequest": { "description": "Request model for setting model pricing.", "properties": { + "cache_read_price_per_million": { + "anyOf": [ + { + "minimum": 0.0, + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Price per 1M cached-input tokens", + "title": "Cache Read Price Per Million" + }, + "cache_write_price_per_million": { + "anyOf": [ + { + "minimum": 0.0, + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Price per 1M cache-write (creation) tokens", + "title": "Cache Write Price Per Million" + }, "effective_at": { "anyOf": [ { diff --git a/src/gateway/api/routes/_pipeline.py b/src/gateway/api/routes/_pipeline.py index 440daf95..3ff98126 100644 --- a/src/gateway/api/routes/_pipeline.py +++ b/src/gateway/api/routes/_pipeline.py @@ -81,11 +81,11 @@ ) from gateway.core.config import GatewayConfig from gateway.core.env import otari_env -from gateway.core.usage import cache_read_tokens_of, cache_write_tokens_of +from gateway.core.usage import cache_read_tokens_of, cache_tokens_in_prompt_of, cache_write_tokens_of from gateway.log_config import logger from gateway.metrics import record_abandoned_attempt, record_cost, record_tokens from gateway.model_labeling import relabel_model -from gateway.models.entities import UsageLog +from gateway.models.entities import ModelPricing, UsageLog from gateway.models.guardrails import GuardrailConfig from gateway.models.mcp import McpServerConfig from gateway.rate_limit import RateLimitInfo, check_rate_limit @@ -957,6 +957,71 @@ async def prepare_gateway_tools( # --------------------------------------------------------------------------- +def _compute_cost(pricing: ModelPricing, usage_data: CompletionUsage) -> float: + """Compute standalone-mode cost from provider usage and model pricing. + + Prices prompt, completion, and (when configured) cache tokens on a single + convention borrowed from the genai-prices dataset this project already uses: + the input/prompt token count is the grand total that *includes* cache reads + and writes, and each physical token is charged once. + + Providers report cache tokens two different ways, so usage is normalized + first (see :attr:`GatewayUsage.cache_tokens_in_prompt`): + + * OpenAI / Gemini report cache reads as a subset already inside + ``prompt_tokens`` (``cache_tokens_in_prompt`` is ``True``). + * Anthropic reports ``input_tokens`` *exclusive* of cache reads/writes and + lists them as separate buckets (``cache_tokens_in_prompt`` is ``False``), + so they are folded back into the total here. + + Uncached input is then ``total - cache_read - cache_write``, priced at the + input rate. Cache reads/writes are charged at their own rate when one is + configured; when a cache rate is absent, those tokens stay in the uncached + bucket and are billed at the full input rate rather than being dropped or + double counted. This mirrors ``ModelPrice.calc_price`` in genai-prices. + + A malformed provider payload (e.g. a cached count larger than + ``prompt_tokens`` on the OpenAI shape) is clamped so the uncached remainder + can never go negative and produce a negative cost that would credit the + user's budget. Cost is therefore always non-negative. + """ + prompt_tokens = usage_data.prompt_tokens or 0 + completion_tokens = usage_data.completion_tokens or 0 + cache_read = cache_read_tokens_of(usage_data) + cache_write = cache_write_tokens_of(usage_data) + + if cache_tokens_in_prompt_of(usage_data): + total_input_tokens = prompt_tokens + else: + total_input_tokens = prompt_tokens + cache_read + cache_write + + # Defensive clamp: keep the cache buckets inside the input total so the + # uncached remainder stays >= 0 even if a provider/adapter reports an + # inconsistent payload. For the Anthropic shape the total is built from + # these counts, so this is a no-op there; it only bites a broken OpenAI-shape + # payload where cached_tokens exceeds prompt_tokens. + cache_read = min(cache_read, total_input_tokens) + cache_write = min(cache_write, total_input_tokens - cache_read) + + read_rate = pricing.cache_read_price_per_million + write_rate = pricing.cache_write_price_per_million + + # Only pull a cache bucket out of the uncached-input total when it has its + # own price; otherwise it stays billed at the input rate. + priced_cache_read = cache_read if read_rate is not None else 0 + priced_cache_write = cache_write if write_rate is not None else 0 + uncached_input_tokens = total_input_tokens - priced_cache_read - priced_cache_write + + cost = (uncached_input_tokens / 1_000_000) * pricing.input_price_per_million + cost += (completion_tokens / 1_000_000) * pricing.output_price_per_million + if read_rate is not None and cache_read > 0: + cost += (cache_read / 1_000_000) * read_rate + if write_rate is not None and cache_write > 0: + cost += (cache_write / 1_000_000) * write_rate + + return cost + + def _elapsed_ms(started_at: float | None) -> int | None: """Milliseconds elapsed since a monotonic ``started_at`` reading. @@ -1041,9 +1106,7 @@ async def log_usage( pricing = await find_model_pricing(db, provider, model, as_of=usage_log.timestamp) if pricing: - cost = (usage_data.prompt_tokens / 1_000_000) * pricing.input_price_per_million + ( - usage_data.completion_tokens / 1_000_000 - ) * pricing.output_price_per_million + cost = _compute_cost(pricing, usage_data) usage_log.cost = cost record_cost(str(provider or ""), model, cost) else: diff --git a/src/gateway/api/routes/messages.py b/src/gateway/api/routes/messages.py index 41880e8d..476f63da 100644 --- a/src/gateway/api/routes/messages.py +++ b/src/gateway/api/routes/messages.py @@ -181,6 +181,7 @@ def _messages_stream_usage(event: MessageStreamEvent) -> CompletionUsage | None: total_tokens=input_tokens + output_tokens, cache_read_tokens=event.usage.cache_read_input_tokens or 0, cache_write_tokens=event.usage.cache_creation_input_tokens or 0, + cache_tokens_in_prompt=False, ) if isinstance(event, MessageStartEvent): usage = event.message.usage @@ -194,6 +195,7 @@ def _messages_stream_usage(event: MessageStreamEvent) -> CompletionUsage | None: total_tokens=input_tokens, cache_read_tokens=cache_read, cache_write_tokens=cache_write, + cache_tokens_in_prompt=False, ) return None @@ -239,6 +241,7 @@ def extract_usage(self, result: MessageResponse) -> CompletionUsage | None: total_tokens=result.usage.input_tokens + result.usage.output_tokens, cache_read_tokens=result.usage.cache_read_input_tokens or 0, cache_write_tokens=result.usage.cache_creation_input_tokens or 0, + cache_tokens_in_prompt=False, ) async def call_provider(self, kwargs: dict[str, Any]) -> MessageResponse: diff --git a/src/gateway/api/routes/models.py b/src/gateway/api/routes/models.py index 8dadc2fe..73cd3827 100644 --- a/src/gateway/api/routes/models.py +++ b/src/gateway/api/routes/models.py @@ -46,6 +46,8 @@ class ModelPricingInfo(BaseModel): input_price_per_million: float output_price_per_million: float + cache_read_price_per_million: float | None = None + cache_write_price_per_million: float | None = None class ModelObject(BaseModel): @@ -183,6 +185,8 @@ def _model_from_pricing(pricing: ModelPricing) -> ModelObject: pricing=ModelPricingInfo( input_price_per_million=pricing.input_price_per_million, output_price_per_million=pricing.output_price_per_million, + cache_read_price_per_million=pricing.cache_read_price_per_million, + cache_write_price_per_million=pricing.cache_write_price_per_million, ), pricing_source="configured", context_window=_context_window_for_key(pricing.model_key), @@ -209,6 +213,8 @@ def _alias_model( pricing=ModelPricingInfo( input_price_per_million=pricing.input_price_per_million, output_price_per_million=pricing.output_price_per_million, + cache_read_price_per_million=pricing.cache_read_price_per_million, + cache_write_price_per_million=pricing.cache_write_price_per_million, ) if pricing else None, @@ -265,6 +271,8 @@ def _apply_default_pricing(obj: ModelObject, pricing_selector: str | None = None obj.pricing = ModelPricingInfo( input_price_per_million=default.input_price_per_million, output_price_per_million=default.output_price_per_million, + cache_read_price_per_million=default.cache_read_price_per_million, + cache_write_price_per_million=default.cache_write_price_per_million, ) obj.pricing_source = "default" @@ -354,6 +362,8 @@ async def list_models( pricing=ModelPricingInfo( input_price_per_million=pricing.input_price_per_million, output_price_per_million=pricing.output_price_per_million, + cache_read_price_per_million=pricing.cache_read_price_per_million, + cache_write_price_per_million=pricing.cache_write_price_per_million, ) if pricing else None, @@ -550,6 +560,8 @@ async def get_model( pricing=ModelPricingInfo( input_price_per_million=pricing.input_price_per_million, output_price_per_million=pricing.output_price_per_million, + cache_read_price_per_million=pricing.cache_read_price_per_million, + cache_write_price_per_million=pricing.cache_write_price_per_million, ) if pricing else None, diff --git a/src/gateway/api/routes/pricing.py b/src/gateway/api/routes/pricing.py index c24cf1d2..d4d7fa18 100644 --- a/src/gateway/api/routes/pricing.py +++ b/src/gateway/api/routes/pricing.py @@ -25,6 +25,12 @@ class SetPricingRequest(BaseModel): model_key: str = Field(description="Model identifier in format 'provider:model'") input_price_per_million: float = Field(ge=0, description="Price per 1M input tokens") output_price_per_million: float = Field(ge=0, description="Price per 1M output tokens") + cache_read_price_per_million: float | None = Field( + default=None, ge=0, description="Price per 1M cached-input tokens" + ) + cache_write_price_per_million: float | None = Field( + default=None, ge=0, description="Price per 1M cache-write (creation) tokens" + ) effective_at: datetime | None = Field( default=None, description="ISO 8601 datetime from which this price applies. Defaults to now if omitted.", @@ -38,6 +44,8 @@ class PricingResponse(BaseModel): effective_at: str input_price_per_million: float output_price_per_million: float + cache_read_price_per_million: float | None + cache_write_price_per_million: float | None created_at: str updated_at: str @@ -49,6 +57,8 @@ def from_model(cls, pricing: "ModelPricing") -> "PricingResponse": effective_at=pricing.effective_at.isoformat(), input_price_per_million=pricing.input_price_per_million, output_price_per_million=pricing.output_price_per_million, + cache_read_price_per_million=pricing.cache_read_price_per_million, + cache_write_price_per_million=pricing.cache_write_price_per_million, created_at=pricing.created_at.isoformat(), updated_at=pricing.updated_at.isoformat(), ) @@ -144,6 +154,35 @@ async def set_pricing( normalized_key = normalize_pricing_key(config, request.model_key) effective_at = normalize_effective_at(request.effective_at) + + # Resolve the cache rates to persist. A field the client omits inherits the + # model's most recent stored value, so a partial input/output update never + # silently wipes cache pricing (each POST without an explicit effective_at + # creates a new version, so the inherited value must carry forward). An + # explicit null still clears the rate. + cache_read_set = "cache_read_price_per_million" in request.model_fields_set + cache_write_set = "cache_write_price_per_million" in request.model_fields_set + latest: ModelPricing | None = None + if not (cache_read_set and cache_write_set): + latest = ( + await db.execute( + select(ModelPricing) + .where(ModelPricing.model_key == normalized_key) + .order_by(ModelPricing.effective_at.desc()) + .limit(1) + ) + ).scalar_one_or_none() + cache_read = ( + request.cache_read_price_per_million + if cache_read_set + else (latest.cache_read_price_per_million if latest else None) + ) + cache_write = ( + request.cache_write_price_per_million + if cache_write_set + else (latest.cache_write_price_per_million if latest else None) + ) + result = await db.execute( select(ModelPricing).where( ModelPricing.model_key == normalized_key, @@ -155,12 +194,16 @@ async def set_pricing( if pricing: pricing.input_price_per_million = request.input_price_per_million pricing.output_price_per_million = request.output_price_per_million + pricing.cache_read_price_per_million = cache_read + pricing.cache_write_price_per_million = cache_write else: pricing = ModelPricing( model_key=normalized_key, effective_at=effective_at, input_price_per_million=request.input_price_per_million, output_price_per_million=request.output_price_per_million, + cache_read_price_per_million=cache_read, + cache_write_price_per_million=cache_write, ) db.add(pricing) diff --git a/src/gateway/core/config.py b/src/gateway/core/config.py index de536775..3e79fe7c 100644 --- a/src/gateway/core/config.py +++ b/src/gateway/core/config.py @@ -80,6 +80,16 @@ class PricingConfig(BaseModel): input_price_per_million: float = Field(ge=0) output_price_per_million: float = Field(ge=0) + cache_read_price_per_million: float | None = Field( + default=None, + ge=0, + description="Price per 1M cached-input tokens (OpenAI/Gemini discount rate or Anthropic cache-read rate).", + ) + cache_write_price_per_million: float | None = Field( + default=None, + ge=0, + description="Price per 1M cache-write (creation) tokens. Anthropic only.", + ) effective_at: datetime | None = Field( default=None, description="ISO 8601 datetime from which this price applies. Defaults to now if omitted.", diff --git a/src/gateway/core/usage.py b/src/gateway/core/usage.py index bdc140e7..a9dfd987 100644 --- a/src/gateway/core/usage.py +++ b/src/gateway/core/usage.py @@ -24,6 +24,18 @@ class GatewayUsage(CompletionUsage): cache_read_tokens: int = 0 cache_write_tokens: int = 0 + cache_tokens_in_prompt: bool = True + """Whether ``cache_read_tokens`` / ``cache_write_tokens`` are already counted + within ``prompt_tokens``. + + ``True`` for OpenAI-shaped usage, where cached tokens are a subset of + ``prompt_tokens`` (the whole prompt is billed and the cached slice is merely a + re-priced discount). ``False`` for the Anthropic path, where ``input_tokens`` + excludes cache reads/writes and they are reported as separate additive buckets. + The cost calculation reads this to normalize both shapes onto a single + convention (see ``_compute_cost`` in ``_pipeline.py``). Defaults to ``True`` so a + plain ``CompletionUsage`` and every OpenAI-style path need no change. + """ @classmethod def from_completion_usage( @@ -32,6 +44,7 @@ def from_completion_usage( *, cache_read_tokens: int | None = None, cache_write_tokens: int | None = None, + cache_tokens_in_prompt: bool | None = None, ) -> "GatewayUsage": """Build a ``GatewayUsage`` from a base ``CompletionUsage`` plus cache counts. @@ -40,12 +53,15 @@ def from_completion_usage( ``usage`` is itself a :class:`GatewayUsage`, otherwise the OpenAI-style ``prompt_tokens_details.cached_tokens`` (a subset of ``prompt_tokens``, purely informational for re-pricing). An explicit ``0`` is honored and does not - trigger the fallback. + trigger the fallback. ``cache_tokens_in_prompt`` defaults to the source's + value (``True`` for a plain ``CompletionUsage``). """ if cache_read_tokens is None: cache_read_tokens = cache_read_tokens_of(usage) if cache_write_tokens is None: cache_write_tokens = cache_write_tokens_of(usage) + if cache_tokens_in_prompt is None: + cache_tokens_in_prompt = cache_tokens_in_prompt_of(usage) return cls( prompt_tokens=usage.prompt_tokens, completion_tokens=usage.completion_tokens, @@ -54,6 +70,7 @@ def from_completion_usage( prompt_tokens_details=usage.prompt_tokens_details, cache_read_tokens=cache_read_tokens, cache_write_tokens=cache_write_tokens, + cache_tokens_in_prompt=cache_tokens_in_prompt, ) @@ -75,3 +92,15 @@ def cache_write_tokens_of(usage: CompletionUsage) -> int: if isinstance(usage, GatewayUsage): return usage.cache_write_tokens return 0 + + +def cache_tokens_in_prompt_of(usage: CompletionUsage) -> bool: + """Whether the usage's cache counts are already included in ``prompt_tokens``. + + A plain ``CompletionUsage`` is always OpenAI-shaped (``cached_tokens`` is a + subset of ``prompt_tokens``), so it returns ``True``. A :class:`GatewayUsage` + reports its explicit flag; the Anthropic path sets it ``False``. + """ + if isinstance(usage, GatewayUsage): + return usage.cache_tokens_in_prompt + return True diff --git a/src/gateway/models/entities.py b/src/gateway/models/entities.py index 31d28d47..6ec767d2 100644 --- a/src/gateway/models/entities.py +++ b/src/gateway/models/entities.py @@ -255,6 +255,13 @@ class ModelPricing(Base): ) input_price_per_million: Mapped[float] = mapped_column() output_price_per_million: Mapped[float] = mapped_column() + # Nullable: providers without prompt caching (or models without a + # discounted cache rate) leave these unset. When set, the cost + # calculation prices cache_read_tokens / cache_write_tokens at these + # per-million-token rates, following the provider inclusion convention + # (see log_usage in _pipeline.py). + cache_read_price_per_million: Mapped[float | None] = mapped_column(nullable=True) + cache_write_price_per_million: Mapped[float | None] = mapped_column(nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(UTC)) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), @@ -269,6 +276,8 @@ def to_dict(self) -> dict[str, Any]: "effective_at": self.effective_at.isoformat() if self.effective_at else None, "input_price_per_million": self.input_price_per_million, "output_price_per_million": self.output_price_per_million, + "cache_read_price_per_million": self.cache_read_price_per_million, + "cache_write_price_per_million": self.cache_write_price_per_million, "created_at": self.created_at.isoformat() if self.created_at else None, "updated_at": self.updated_at.isoformat() if self.updated_at else None, } diff --git a/src/gateway/services/pricing_init_service.py b/src/gateway/services/pricing_init_service.py index 8e7ce93a..dd4f73e0 100644 --- a/src/gateway/services/pricing_init_service.py +++ b/src/gateway/services/pricing_init_service.py @@ -67,6 +67,8 @@ async def initialize_pricing_from_config(config: GatewayConfig, db: AsyncSession input_price = pricing_config.input_price_per_million output_price = pricing_config.output_price_per_million + cache_read_price = pricing_config.cache_read_price_per_million + cache_write_price = pricing_config.cache_write_price_per_million effective_at = normalize_effective_at(pricing_config.effective_at) existing_pricing = ( @@ -92,6 +94,8 @@ async def initialize_pricing_from_config(config: GatewayConfig, db: AsyncSession effective_at=effective_at, input_price_per_million=input_price, output_price_per_million=output_price, + cache_read_price_per_million=cache_read_price, + cache_write_price_per_million=cache_write_price, ) db.add(new_pricing) logger.info("Added pricing for '%s': input=$%s/M, output=$%s/M", model_key, input_price, output_price) diff --git a/src/gateway/services/pricing_service.py b/src/gateway/services/pricing_service.py index 46bcd3dd..db97e6f2 100644 --- a/src/gateway/services/pricing_service.py +++ b/src/gateway/services/pricing_service.py @@ -194,6 +194,8 @@ def default_model_pricing(provider: str | None, model: str, as_of: datetime) -> # Input-only models (embeddings, rerank) legitimately have no output rate; # price output at 0 rather than rejecting the whole model. output_rate = _flat_rate(price.output_mtok) if price.output_mtok is not None else 0.0 + cache_read_rate = _flat_rate(price.cache_read_mtok) if price.cache_read_mtok is not None else None + cache_write_rate = _flat_rate(price.cache_write_mtok) if price.cache_write_mtok is not None else None model_key = f"{provider}:{model}" if provider else model logger.debug( @@ -207,6 +209,8 @@ def default_model_pricing(provider: str | None, model: str, as_of: datetime) -> effective_at=as_of, input_price_per_million=_flat_rate(price.input_mtok), output_price_per_million=output_rate, + cache_read_price_per_million=cache_read_rate, + cache_write_price_per_million=cache_write_rate, ) diff --git a/src/gateway/static/dashboard/assets/index-BnmiwDLj.js b/src/gateway/static/dashboard/assets/index-BnmiwDLj.js deleted file mode 100644 index 63dcf028..00000000 --- a/src/gateway/static/dashboard/assets/index-BnmiwDLj.js +++ /dev/null @@ -1,82 +0,0 @@ -var w5=Object.defineProperty;var Hx=e=>{throw TypeError(e)};var T5=(e,t,a)=>t in e?w5(e,t,{enumerable:!0,configurable:!0,writable:!0,value:a}):e[t]=a;var Vx=(e,t,a)=>T5(e,typeof t!="symbol"?t+"":t,a),Jh=(e,t,a)=>t.has(e)||Hx("Cannot "+a);var L=(e,t,a)=>(Jh(e,t,"read from private field"),a?a.call(e):t.get(e)),xe=(e,t,a)=>t.has(e)?Hx("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,a),fe=(e,t,a,i)=>(Jh(e,t,"write to private field"),i?i.call(e,a):t.set(e,a),a),Re=(e,t,a)=>(Jh(e,t,"access private method"),a);var Sc=(e,t,a,i)=>({set _(s){fe(e,t,s,a)},get _(){return L(e,t,i)}});(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))i(s);new MutationObserver(s=>{for(const c of s)if(c.type==="childList")for(const d of c.addedNodes)d.tagName==="LINK"&&d.rel==="modulepreload"&&i(d)}).observe(document,{childList:!0,subtree:!0});function a(s){const c={};return s.integrity&&(c.integrity=s.integrity),s.referrerPolicy&&(c.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?c.credentials="include":s.crossOrigin==="anonymous"?c.credentials="omit":c.credentials="same-origin",c}function i(s){if(s.ep)return;s.ep=!0;const c=a(s);fetch(s.href,c)}})();function pS(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var em={exports:{}},uo={};/** - * @license React - * react-jsx-runtime.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Ix;function N5(){if(Ix)return uo;Ix=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.fragment");function a(i,s,c){var d=null;if(c!==void 0&&(d=""+c),s.key!==void 0&&(d=""+s.key),"key"in s){c={};for(var h in s)h!=="key"&&(c[h]=s[h])}else c=s;return s=c.ref,{$$typeof:e,type:i,key:d,ref:s!==void 0?s:null,props:c}}return uo.Fragment=t,uo.jsx=a,uo.jsxs=a,uo}var qx;function A5(){return qx||(qx=1,em.exports=N5()),em.exports}var u=A5(),tm={exports:{}},Ne={};/** - * @license React - * react.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Gx;function D5(){if(Gx)return Ne;Gx=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.portal"),a=Symbol.for("react.fragment"),i=Symbol.for("react.strict_mode"),s=Symbol.for("react.profiler"),c=Symbol.for("react.consumer"),d=Symbol.for("react.context"),h=Symbol.for("react.forward_ref"),p=Symbol.for("react.suspense"),b=Symbol.for("react.memo"),v=Symbol.for("react.lazy"),y=Symbol.for("react.activity"),C=Symbol.iterator;function x(A){return A===null||typeof A!="object"?null:(A=C&&A[C]||A["@@iterator"],typeof A=="function"?A:null)}var $={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},w=Object.assign,T={};function E(A,I,V){this.props=A,this.context=I,this.refs=T,this.updater=V||$}E.prototype.isReactComponent={},E.prototype.setState=function(A,I){if(typeof A!="object"&&typeof A!="function"&&A!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,A,I,"setState")},E.prototype.forceUpdate=function(A){this.updater.enqueueForceUpdate(this,A,"forceUpdate")};function N(){}N.prototype=E.prototype;function O(A,I,V){this.props=A,this.context=I,this.refs=T,this.updater=V||$}var M=O.prototype=new N;M.constructor=O,w(M,E.prototype),M.isPureReactComponent=!0;var K=Array.isArray;function F(){}var q={H:null,A:null,T:null,S:null},P=Object.prototype.hasOwnProperty;function te(A,I,V){var G=V.ref;return{$$typeof:e,type:A,key:I,ref:G!==void 0?G:null,props:V}}function ee(A,I){return te(A.type,I,A.props)}function re(A){return typeof A=="object"&&A!==null&&A.$$typeof===e}function ce(A){var I={"=":"=0",":":"=2"};return"$"+A.replace(/[=:]/g,function(V){return I[V]})}var de=/\/+/g;function D(A,I){return typeof A=="object"&&A!==null&&A.key!=null?ce(""+A.key):I.toString(36)}function H(A){switch(A.status){case"fulfilled":return A.value;case"rejected":throw A.reason;default:switch(typeof A.status=="string"?A.then(F,F):(A.status="pending",A.then(function(I){A.status==="pending"&&(A.status="fulfilled",A.value=I)},function(I){A.status==="pending"&&(A.status="rejected",A.reason=I)})),A.status){case"fulfilled":return A.value;case"rejected":throw A.reason}}throw A}function R(A,I,V,G,ue){var me=typeof A;(me==="undefined"||me==="boolean")&&(A=null);var he=!1;if(A===null)he=!0;else switch(me){case"bigint":case"string":case"number":he=!0;break;case"object":switch(A.$$typeof){case e:case t:he=!0;break;case v:return he=A._init,R(he(A._payload),I,V,G,ue)}}if(he)return ue=ue(A),he=G===""?"."+D(A,0):G,K(ue)?(V="",he!=null&&(V=he.replace(de,"$&/")+"/"),R(ue,I,V,"",function(se){return se})):ue!=null&&(re(ue)&&(ue=ee(ue,V+(ue.key==null||A&&A.key===ue.key?"":(""+ue.key).replace(de,"$&/")+"/")+he)),I.push(ue)),1;he=0;var _e=G===""?".":G+":";if(K(A))for(var Oe=0;Oe>>1,Q=R[Z];if(0>>1;Zs(V,B))Gs(ue,V)?(R[Z]=ue,R[G]=B,Z=G):(R[Z]=V,R[I]=B,Z=I);else if(Gs(ue,B))R[Z]=ue,R[G]=B,Z=G;else break e}}return k}function s(R,k){var B=R.sortIndex-k.sortIndex;return B!==0?B:R.id-k.id}if(e.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var c=performance;e.unstable_now=function(){return c.now()}}else{var d=Date,h=d.now();e.unstable_now=function(){return d.now()-h}}var p=[],b=[],v=1,y=null,C=3,x=!1,$=!1,w=!1,T=!1,E=typeof setTimeout=="function"?setTimeout:null,N=typeof clearTimeout=="function"?clearTimeout:null,O=typeof setImmediate<"u"?setImmediate:null;function M(R){for(var k=a(b);k!==null;){if(k.callback===null)i(b);else if(k.startTime<=R)i(b),k.sortIndex=k.expirationTime,t(p,k);else break;k=a(b)}}function K(R){if(w=!1,M(R),!$)if(a(p)!==null)$=!0,F||(F=!0,ce());else{var k=a(b);k!==null&&H(K,k.startTime-R)}}var F=!1,q=-1,P=5,te=-1;function ee(){return T?!0:!(e.unstable_now()-teR&&ee());){var Z=y.callback;if(typeof Z=="function"){y.callback=null,C=y.priorityLevel;var Q=Z(y.expirationTime<=R);if(R=e.unstable_now(),typeof Q=="function"){y.callback=Q,M(R),k=!0;break t}y===a(p)&&i(p),M(R)}else i(p);y=a(p)}if(y!==null)k=!0;else{var A=a(b);A!==null&&H(K,A.startTime-R),k=!1}}break e}finally{y=null,C=B,x=!1}k=void 0}}finally{k?ce():F=!1}}}var ce;if(typeof O=="function")ce=function(){O(re)};else if(typeof MessageChannel<"u"){var de=new MessageChannel,D=de.port2;de.port1.onmessage=re,ce=function(){D.postMessage(null)}}else ce=function(){E(re,0)};function H(R,k){q=E(function(){R(e.unstable_now())},k)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(R){R.callback=null},e.unstable_forceFrameRate=function(R){0>R||125Z?(R.sortIndex=B,t(b,R),a(p)===null&&R===a(b)&&(w?(N(q),q=-1):w=!0,H(K,B-Z))):(R.sortIndex=Q,t(p,R),$||x||($=!0,F||(F=!0,ce()))),R},e.unstable_shouldYield=ee,e.unstable_wrapCallback=function(R){var k=C;return function(){var B=C;C=k;try{return R.apply(this,arguments)}finally{C=B}}}})(am)),am}var Xx;function R5(){return Xx||(Xx=1,lm.exports=M5()),lm.exports}var im={exports:{}},Zt={};/** - * @license React - * react-dom.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Zx;function k5(){if(Zx)return Zt;Zx=1;var e=cd();function t(p){var b="https://react.dev/errors/"+p;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}return e(),im.exports=k5(),im.exports}/** - * @license React - * react-dom-client.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Jx;function _5(){if(Jx)return co;Jx=1;var e=R5(),t=cd(),a=gS();function i(n){var l="https://react.dev/errors/"+n;if(1Q||(n.current=Z[Q],Z[Q]=null,Q--)}function V(n,l){Q++,Z[Q]=n.current,n.current=l}var G=A(null),ue=A(null),me=A(null),he=A(null);function _e(n,l){switch(V(me,l),V(ue,n),V(G,null),l.nodeType){case 9:case 11:n=(n=l.documentElement)&&(n=n.namespaceURI)?fx(n):0;break;default:if(n=l.tagName,l=l.namespaceURI)l=fx(l),n=hx(l,n);else switch(n){case"svg":n=1;break;case"math":n=2;break;default:n=0}}I(G),V(G,n)}function Oe(){I(G),I(ue),I(me)}function se(n){n.memoizedState!==null&&V(he,n);var l=G.current,r=hx(l,n.type);l!==r&&(V(ue,n),V(G,r))}function ve(n){ue.current===n&&(I(G),I(ue)),he.current===n&&(I(he),io._currentValue=B)}var Ue,Ot;function nn(n){if(Ue===void 0)try{throw Error()}catch(r){var l=r.stack.trim().match(/\n( *(at )?)/);Ue=l&&l[1]||"",Ot=-1)":-1f||_[o]!==X[f]){var le=` -`+_[o].replace(" at new "," at ");return n.displayName&&le.includes("")&&(le=le.replace("",n.displayName)),le}while(1<=o&&0<=f);break}}}finally{xn=!1,Error.prepareStackTrace=r}return(r=n?n.displayName||n.name:"")?nn(r):""}function ps(n,l){switch(n.tag){case 26:case 27:case 5:return nn(n.type);case 16:return nn("Lazy");case 13:return n.child!==l&&l!==null?nn("Suspense Fallback"):nn("Suspense");case 19:return nn("SuspenseList");case 0:case 15:return Xn(n.type,!1);case 11:return Xn(n.type.render,!1);case 1:return Xn(n.type,!0);case 31:return nn("Activity");default:return""}}function Za(n){try{var l="",r=null;do l+=ps(n,r),r=n,n=n.return;while(n);return l}catch(o){return` -Error generating stack: `+o.message+` -`+o.stack}}var Wa=Object.prototype.hasOwnProperty,$e=e.unstable_scheduleCallback,He=e.unstable_cancelCallback,st=e.unstable_shouldYield,Vi=e.unstable_requestPaint,St=e.unstable_now,iu=e.unstable_getCurrentPriorityLevel,gs=e.unstable_ImmediatePriority,bs=e.unstable_UserBlockingPriority,Ja=e.unstable_NormalPriority,ru=e.unstable_LowPriority,Te=e.unstable_IdlePriority,Ct=e.log,Be=e.unstable_setDisableYieldValue,ft=null,ut=null;function ct(n){if(typeof Ct=="function"&&Be(n),ut&&typeof ut.setStrictMode=="function")try{ut.setStrictMode(ft,n)}catch{}}var et=Math.clz32?Math.clz32:ca,Ve=Math.log,ln=Math.LN2;function ca(n){return n>>>=0,n===0?32:31-(Ve(n)/ln|0)|0}var Ht=256,hl=262144,su=4194304;function ei(n){var l=n&42;if(l!==0)return l;switch(n&-n){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return n&261888;case 262144:case 524288:case 1048576:case 2097152:return n&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return n&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return n}}function ou(n,l,r){var o=n.pendingLanes;if(o===0)return 0;var f=0,m=n.suspendedLanes,S=n.pingedLanes;n=n.warmLanes;var j=o&134217727;return j!==0?(o=j&~m,o!==0?f=ei(o):(S&=j,S!==0?f=ei(S):r||(r=j&~n,r!==0&&(f=ei(r))))):(j=o&~m,j!==0?f=ei(j):S!==0?f=ei(S):r||(r=o&~n,r!==0&&(f=ei(r)))),f===0?0:l!==0&&l!==f&&(l&m)===0&&(m=f&-f,r=l&-l,m>=r||m===32&&(r&4194048)!==0)?l:f}function vs(n,l){return(n.pendingLanes&~(n.suspendedLanes&~n.pingedLanes)&l)===0}function h$(n,l){switch(n){case 1:case 2:case 4:case 8:case 64:return l+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return l+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Gg(){var n=su;return su<<=1,(su&62914560)===0&&(su=4194304),n}function Kd(n){for(var l=[],r=0;31>r;r++)l.push(n);return l}function ys(n,l){n.pendingLanes|=l,l!==268435456&&(n.suspendedLanes=0,n.pingedLanes=0,n.warmLanes=0)}function m$(n,l,r,o,f,m){var S=n.pendingLanes;n.pendingLanes=r,n.suspendedLanes=0,n.pingedLanes=0,n.warmLanes=0,n.expiredLanes&=r,n.entangledLanes&=r,n.errorRecoveryDisabledLanes&=r,n.shellSuspendCounter=0;var j=n.entanglements,_=n.expirationTimes,X=n.hiddenUpdates;for(r=S&~r;0"u")return null;try{return n.activeElement||n.body}catch{return n.body}}var x$=/[\n"\\]/g;function Ln(n){return n.replace(x$,function(l){return"\\"+l.charCodeAt(0).toString(16)+" "})}function qd(n,l,r,o,f,m,S,j){n.name="",S!=null&&typeof S!="function"&&typeof S!="symbol"&&typeof S!="boolean"?n.type=S:n.removeAttribute("type"),l!=null?S==="number"?(l===0&&n.value===""||n.value!=l)&&(n.value=""+On(l)):n.value!==""+On(l)&&(n.value=""+On(l)):S!=="submit"&&S!=="reset"||n.removeAttribute("value"),l!=null?Gd(n,S,On(l)):r!=null?Gd(n,S,On(r)):o!=null&&n.removeAttribute("value"),f==null&&m!=null&&(n.defaultChecked=!!m),f!=null&&(n.checked=f&&typeof f!="function"&&typeof f!="symbol"),j!=null&&typeof j!="function"&&typeof j!="symbol"&&typeof j!="boolean"?n.name=""+On(j):n.removeAttribute("name")}function rb(n,l,r,o,f,m,S,j){if(m!=null&&typeof m!="function"&&typeof m!="symbol"&&typeof m!="boolean"&&(n.type=m),l!=null||r!=null){if(!(m!=="submit"&&m!=="reset"||l!=null)){Id(n);return}r=r!=null?""+On(r):"",l=l!=null?""+On(l):r,j||l===n.value||(n.value=l),n.defaultValue=l}o=o??f,o=typeof o!="function"&&typeof o!="symbol"&&!!o,n.checked=j?n.checked:!!o,n.defaultChecked=!!o,S!=null&&typeof S!="function"&&typeof S!="symbol"&&typeof S!="boolean"&&(n.name=S),Id(n)}function Gd(n,l,r){l==="number"&&du(n.ownerDocument)===n||n.defaultValue===""+r||(n.defaultValue=""+r)}function Xi(n,l,r,o){if(n=n.options,l){l={};for(var f=0;f"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Wd=!1;if(Nl)try{var $s={};Object.defineProperty($s,"passive",{get:function(){Wd=!0}}),window.addEventListener("test",$s,$s),window.removeEventListener("test",$s,$s)}catch{Wd=!1}var fa=null,Jd=null,hu=null;function hb(){if(hu)return hu;var n,l=Jd,r=l.length,o,f="value"in fa?fa.value:fa.textContent,m=f.length;for(n=0;n=ws),yb=" ",xb=!1;function Sb(n,l){switch(n){case"keyup":return Q$.indexOf(l.keyCode)!==-1;case"keydown":return l.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Cb(n){return n=n.detail,typeof n=="object"&&"data"in n?n.data:null}var er=!1;function X$(n,l){switch(n){case"compositionend":return Cb(l);case"keypress":return l.which!==32?null:(xb=!0,yb);case"textInput":return n=l.data,n===yb&&xb?null:n;default:return null}}function Z$(n,l){if(er)return n==="compositionend"||!af&&Sb(n,l)?(n=hb(),hu=Jd=fa=null,er=!1,n):null;switch(n){case"paste":return null;case"keypress":if(!(l.ctrlKey||l.altKey||l.metaKey)||l.ctrlKey&&l.altKey){if(l.char&&1=l)return{node:r,offset:l-n};n=o}e:{for(;r;){if(r.nextSibling){r=r.nextSibling;break e}r=r.parentNode}r=void 0}r=Db(r)}}function Rb(n,l){return n&&l?n===l?!0:n&&n.nodeType===3?!1:l&&l.nodeType===3?Rb(n,l.parentNode):"contains"in n?n.contains(l):n.compareDocumentPosition?!!(n.compareDocumentPosition(l)&16):!1:!1}function kb(n){n=n!=null&&n.ownerDocument!=null&&n.ownerDocument.defaultView!=null?n.ownerDocument.defaultView:window;for(var l=du(n.document);l instanceof n.HTMLIFrameElement;){try{var r=typeof l.contentWindow.location.href=="string"}catch{r=!1}if(r)n=l.contentWindow;else break;l=du(n.document)}return l}function of(n){var l=n&&n.nodeName&&n.nodeName.toLowerCase();return l&&(l==="input"&&(n.type==="text"||n.type==="search"||n.type==="tel"||n.type==="url"||n.type==="password")||l==="textarea"||n.contentEditable==="true")}var iE=Nl&&"documentMode"in document&&11>=document.documentMode,tr=null,uf=null,Ds=null,cf=!1;function _b(n,l,r){var o=r.window===r?r.document:r.nodeType===9?r:r.ownerDocument;cf||tr==null||tr!==du(o)||(o=tr,"selectionStart"in o&&of(o)?o={start:o.selectionStart,end:o.selectionEnd}:(o=(o.ownerDocument&&o.ownerDocument.defaultView||window).getSelection(),o={anchorNode:o.anchorNode,anchorOffset:o.anchorOffset,focusNode:o.focusNode,focusOffset:o.focusOffset}),Ds&&As(Ds,o)||(Ds=o,o=rc(uf,"onSelect"),0>=S,f-=S,ml=1<<32-et(l)+f|r<Me?(Fe=ye,ye=null):Fe=ye.sibling;var Xe=W(U,ye,Y[Me],ae);if(Xe===null){ye===null&&(ye=Fe);break}n&&ye&&Xe.alternate===null&&l(U,ye),z=m(Xe,z,Me),Ye===null?Se=Xe:Ye.sibling=Xe,Ye=Xe,ye=Fe}if(Me===Y.length)return r(U,ye),Ie&&Dl(U,Me),Se;if(ye===null){for(;MeMe?(Fe=ye,ye=null):Fe=ye.sibling;var _a=W(U,ye,Xe.value,ae);if(_a===null){ye===null&&(ye=Fe);break}n&&ye&&_a.alternate===null&&l(U,ye),z=m(_a,z,Me),Ye===null?Se=_a:Ye.sibling=_a,Ye=_a,ye=Fe}if(Xe.done)return r(U,ye),Ie&&Dl(U,Me),Se;if(ye===null){for(;!Xe.done;Me++,Xe=Y.next())Xe=ie(U,Xe.value,ae),Xe!==null&&(z=m(Xe,z,Me),Ye===null?Se=Xe:Ye.sibling=Xe,Ye=Xe);return Ie&&Dl(U,Me),Se}for(ye=o(ye);!Xe.done;Me++,Xe=Y.next())Xe=J(ye,U,Me,Xe.value,ae),Xe!==null&&(n&&Xe.alternate!==null&&ye.delete(Xe.key===null?Me:Xe.key),z=m(Xe,z,Me),Ye===null?Se=Xe:Ye.sibling=Xe,Ye=Xe);return n&&ye.forEach(function(j5){return l(U,j5)}),Ie&&Dl(U,Me),Se}function rt(U,z,Y,ae){if(typeof Y=="object"&&Y!==null&&Y.type===w&&Y.key===null&&(Y=Y.props.children),typeof Y=="object"&&Y!==null){switch(Y.$$typeof){case x:e:{for(var Se=Y.key;z!==null;){if(z.key===Se){if(Se=Y.type,Se===w){if(z.tag===7){r(U,z.sibling),ae=f(z,Y.props.children),ae.return=U,U=ae;break e}}else if(z.elementType===Se||typeof Se=="object"&&Se!==null&&Se.$$typeof===P&&di(Se)===z.type){r(U,z.sibling),ae=f(z,Y.props),Ls(ae,Y),ae.return=U,U=ae;break e}r(U,z);break}else l(U,z);z=z.sibling}Y.type===w?(ae=ri(Y.props.children,U.mode,ae,Y.key),ae.return=U,U=ae):(ae=$u(Y.type,Y.key,Y.props,null,U.mode,ae),Ls(ae,Y),ae.return=U,U=ae)}return S(U);case $:e:{for(Se=Y.key;z!==null;){if(z.key===Se)if(z.tag===4&&z.stateNode.containerInfo===Y.containerInfo&&z.stateNode.implementation===Y.implementation){r(U,z.sibling),ae=f(z,Y.children||[]),ae.return=U,U=ae;break e}else{r(U,z);break}else l(U,z);z=z.sibling}ae=bf(Y,U.mode,ae),ae.return=U,U=ae}return S(U);case P:return Y=di(Y),rt(U,z,Y,ae)}if(H(Y))return pe(U,z,Y,ae);if(ce(Y)){if(Se=ce(Y),typeof Se!="function")throw Error(i(150));return Y=Se.call(Y),Ee(U,z,Y,ae)}if(typeof Y.then=="function")return rt(U,z,Du(Y),ae);if(Y.$$typeof===O)return rt(U,z,wu(U,Y),ae);Mu(U,Y)}return typeof Y=="string"&&Y!==""||typeof Y=="number"||typeof Y=="bigint"?(Y=""+Y,z!==null&&z.tag===6?(r(U,z.sibling),ae=f(z,Y),ae.return=U,U=ae):(r(U,z),ae=gf(Y,U.mode,ae),ae.return=U,U=ae),S(U)):r(U,z)}return function(U,z,Y,ae){try{Os=0;var Se=rt(U,z,Y,ae);return fr=null,Se}catch(ye){if(ye===dr||ye===Nu)throw ye;var Ye=Cn(29,ye,null,U.mode);return Ye.lanes=ae,Ye.return=U,Ye}finally{}}}var hi=lv(!0),av=lv(!1),ba=!1;function Af(n){n.updateQueue={baseState:n.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Df(n,l){n=n.updateQueue,l.updateQueue===n&&(l.updateQueue={baseState:n.baseState,firstBaseUpdate:n.firstBaseUpdate,lastBaseUpdate:n.lastBaseUpdate,shared:n.shared,callbacks:null})}function va(n){return{lane:n,tag:0,payload:null,callback:null,next:null}}function ya(n,l,r){var o=n.updateQueue;if(o===null)return null;if(o=o.shared,(We&2)!==0){var f=o.pending;return f===null?l.next=l:(l.next=f.next,f.next=l),o.pending=l,l=Cu(n),Fb(n,null,r),l}return Su(n,o,l,r),Cu(n)}function Ps(n,l,r){if(l=l.updateQueue,l!==null&&(l=l.shared,(r&4194048)!==0)){var o=l.lanes;o&=n.pendingLanes,r|=o,l.lanes=r,Yg(n,r)}}function Mf(n,l){var r=n.updateQueue,o=n.alternate;if(o!==null&&(o=o.updateQueue,r===o)){var f=null,m=null;if(r=r.firstBaseUpdate,r!==null){do{var S={lane:r.lane,tag:r.tag,payload:r.payload,callback:null,next:null};m===null?f=m=S:m=m.next=S,r=r.next}while(r!==null);m===null?f=m=l:m=m.next=l}else f=m=l;r={baseState:o.baseState,firstBaseUpdate:f,lastBaseUpdate:m,shared:o.shared,callbacks:o.callbacks},n.updateQueue=r;return}n=r.lastBaseUpdate,n===null?r.firstBaseUpdate=l:n.next=l,r.lastBaseUpdate=l}var Rf=!1;function zs(){if(Rf){var n=cr;if(n!==null)throw n}}function Bs(n,l,r,o){Rf=!1;var f=n.updateQueue;ba=!1;var m=f.firstBaseUpdate,S=f.lastBaseUpdate,j=f.shared.pending;if(j!==null){f.shared.pending=null;var _=j,X=_.next;_.next=null,S===null?m=X:S.next=X,S=_;var le=n.alternate;le!==null&&(le=le.updateQueue,j=le.lastBaseUpdate,j!==S&&(j===null?le.firstBaseUpdate=X:j.next=X,le.lastBaseUpdate=_))}if(m!==null){var ie=f.baseState;S=0,le=X=_=null,j=m;do{var W=j.lane&-536870913,J=W!==j.lane;if(J?(Ke&W)===W:(o&W)===W){W!==0&&W===ur&&(Rf=!0),le!==null&&(le=le.next={lane:0,tag:j.tag,payload:j.payload,callback:null,next:null});e:{var pe=n,Ee=j;W=l;var rt=r;switch(Ee.tag){case 1:if(pe=Ee.payload,typeof pe=="function"){ie=pe.call(rt,ie,W);break e}ie=pe;break e;case 3:pe.flags=pe.flags&-65537|128;case 0:if(pe=Ee.payload,W=typeof pe=="function"?pe.call(rt,ie,W):pe,W==null)break e;ie=y({},ie,W);break e;case 2:ba=!0}}W=j.callback,W!==null&&(n.flags|=64,J&&(n.flags|=8192),J=f.callbacks,J===null?f.callbacks=[W]:J.push(W))}else J={lane:W,tag:j.tag,payload:j.payload,callback:j.callback,next:null},le===null?(X=le=J,_=ie):le=le.next=J,S|=W;if(j=j.next,j===null){if(j=f.shared.pending,j===null)break;J=j,j=J.next,J.next=null,f.lastBaseUpdate=J,f.shared.pending=null}}while(!0);le===null&&(_=ie),f.baseState=_,f.firstBaseUpdate=X,f.lastBaseUpdate=le,m===null&&(f.shared.lanes=0),Ea|=S,n.lanes=S,n.memoizedState=ie}}function iv(n,l){if(typeof n!="function")throw Error(i(191,n));n.call(l)}function rv(n,l){var r=n.callbacks;if(r!==null)for(n.callbacks=null,n=0;nm?m:8;var S=R.T,j={};R.T=j,Zf(n,!1,l,r);try{var _=f(),X=R.S;if(X!==null&&X(j,_),_!==null&&typeof _=="object"&&typeof _.then=="function"){var le=mE(_,o);Us(n,l,le,Tn(n))}else Us(n,l,o,Tn(n))}catch(ie){Us(n,l,{then:function(){},status:"rejected",reason:ie},Tn())}finally{k.p=m,S!==null&&j.types!==null&&(S.types=j.types),R.T=S}}function xE(){}function Yf(n,l,r,o){if(n.tag!==5)throw Error(i(476));var f=zv(n).queue;Pv(n,f,l,B,r===null?xE:function(){return Bv(n),r(o)})}function zv(n){var l=n.memoizedState;if(l!==null)return l;l={memoizedState:B,baseState:B,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:_l,lastRenderedState:B},next:null};var r={};return l.next={memoizedState:r,baseState:r,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:_l,lastRenderedState:r},next:null},n.memoizedState=l,n=n.alternate,n!==null&&(n.memoizedState=l),l}function Bv(n){var l=zv(n);l.next===null&&(l=n.alternate.memoizedState),Us(n,l.next.queue,{},Tn())}function Xf(){return qt(io)}function Kv(){return Nt().memoizedState}function Fv(){return Nt().memoizedState}function SE(n){for(var l=n.return;l!==null;){switch(l.tag){case 24:case 3:var r=Tn();n=va(r);var o=ya(l,n,r);o!==null&&(dn(o,l,r),Ps(o,l,r)),l={cache:jf()},n.payload=l;return}l=l.return}}function CE(n,l,r){var o=Tn();r={lane:o,revertLane:0,gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},Fu(n)?Hv(l,r):(r=mf(n,l,r,o),r!==null&&(dn(r,n,o),Vv(r,l,o)))}function Uv(n,l,r){var o=Tn();Us(n,l,r,o)}function Us(n,l,r,o){var f={lane:o,revertLane:0,gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null};if(Fu(n))Hv(l,f);else{var m=n.alternate;if(n.lanes===0&&(m===null||m.lanes===0)&&(m=l.lastRenderedReducer,m!==null))try{var S=l.lastRenderedState,j=m(S,r);if(f.hasEagerState=!0,f.eagerState=j,Sn(j,S))return Su(n,l,f,0),ot===null&&xu(),!1}catch{}finally{}if(r=mf(n,l,f,o),r!==null)return dn(r,n,o),Vv(r,l,o),!0}return!1}function Zf(n,l,r,o){if(o={lane:2,revertLane:Ah(),gesture:null,action:o,hasEagerState:!1,eagerState:null,next:null},Fu(n)){if(l)throw Error(i(479))}else l=mf(n,r,o,2),l!==null&&dn(l,n,2)}function Fu(n){var l=n.alternate;return n===Ae||l!==null&&l===Ae}function Hv(n,l){mr=_u=!0;var r=n.pending;r===null?l.next=l:(l.next=r.next,r.next=l),n.pending=l}function Vv(n,l,r){if((r&4194048)!==0){var o=l.lanes;o&=n.pendingLanes,r|=o,l.lanes=r,Yg(n,r)}}var Hs={readContext:qt,use:Pu,useCallback:Et,useContext:Et,useEffect:Et,useImperativeHandle:Et,useLayoutEffect:Et,useInsertionEffect:Et,useMemo:Et,useReducer:Et,useRef:Et,useState:Et,useDebugValue:Et,useDeferredValue:Et,useTransition:Et,useSyncExternalStore:Et,useId:Et,useHostTransitionStatus:Et,useFormState:Et,useActionState:Et,useOptimistic:Et,useMemoCache:Et,useCacheRefresh:Et};Hs.useEffectEvent=Et;var Iv={readContext:qt,use:Pu,useCallback:function(n,l){return Jt().memoizedState=[n,l===void 0?null:l],n},useContext:qt,useEffect:Nv,useImperativeHandle:function(n,l,r){r=r!=null?r.concat([n]):null,Bu(4194308,4,Rv.bind(null,l,n),r)},useLayoutEffect:function(n,l){return Bu(4194308,4,n,l)},useInsertionEffect:function(n,l){Bu(4,2,n,l)},useMemo:function(n,l){var r=Jt();l=l===void 0?null:l;var o=n();if(mi){ct(!0);try{n()}finally{ct(!1)}}return r.memoizedState=[o,l],o},useReducer:function(n,l,r){var o=Jt();if(r!==void 0){var f=r(l);if(mi){ct(!0);try{r(l)}finally{ct(!1)}}}else f=l;return o.memoizedState=o.baseState=f,n={pending:null,lanes:0,dispatch:null,lastRenderedReducer:n,lastRenderedState:f},o.queue=n,n=n.dispatch=CE.bind(null,Ae,n),[o.memoizedState,n]},useRef:function(n){var l=Jt();return n={current:n},l.memoizedState=n},useState:function(n){n=Vf(n);var l=n.queue,r=Uv.bind(null,Ae,l);return l.dispatch=r,[n.memoizedState,r]},useDebugValue:Gf,useDeferredValue:function(n,l){var r=Jt();return Qf(r,n,l)},useTransition:function(){var n=Vf(!1);return n=Pv.bind(null,Ae,n.queue,!0,!1),Jt().memoizedState=n,[!1,n]},useSyncExternalStore:function(n,l,r){var o=Ae,f=Jt();if(Ie){if(r===void 0)throw Error(i(407));r=r()}else{if(r=l(),ot===null)throw Error(i(349));(Ke&127)!==0||fv(o,l,r)}f.memoizedState=r;var m={value:r,getSnapshot:l};return f.queue=m,Nv(mv.bind(null,o,m,n),[n]),o.flags|=2048,gr(9,{destroy:void 0},hv.bind(null,o,m,r,l),null),r},useId:function(){var n=Jt(),l=ot.identifierPrefix;if(Ie){var r=pl,o=ml;r=(o&~(1<<32-et(o)-1)).toString(32)+r,l="_"+l+"R_"+r,r=Ou++,0<\/script>",m=m.removeChild(m.firstChild);break;case"select":m=typeof o.is=="string"?S.createElement("select",{is:o.is}):S.createElement("select"),o.multiple?m.multiple=!0:o.size&&(m.size=o.size);break;default:m=typeof o.is=="string"?S.createElement(f,{is:o.is}):S.createElement(f)}}m[Vt]=l,m[an]=o;e:for(S=l.child;S!==null;){if(S.tag===5||S.tag===6)m.appendChild(S.stateNode);else if(S.tag!==4&&S.tag!==27&&S.child!==null){S.child.return=S,S=S.child;continue}if(S===l)break e;for(;S.sibling===null;){if(S.return===null||S.return===l)break e;S=S.return}S.sibling.return=S.return,S=S.sibling}l.stateNode=m;e:switch(Qt(m,f,o),f){case"button":case"input":case"select":case"textarea":o=!!o.autoFocus;break e;case"img":o=!0;break e;default:o=!1}o&&Ll(l)}}return mt(l),dh(l,l.type,n===null?null:n.memoizedProps,l.pendingProps,r),null;case 6:if(n&&l.stateNode!=null)n.memoizedProps!==o&&Ll(l);else{if(typeof o!="string"&&l.stateNode===null)throw Error(i(166));if(n=me.current,sr(l)){if(n=l.stateNode,r=l.memoizedProps,o=null,f=It,f!==null)switch(f.tag){case 27:case 5:o=f.memoizedProps}n[Vt]=l,n=!!(n.nodeValue===r||o!==null&&o.suppressHydrationWarning===!0||cx(n.nodeValue,r)),n||pa(l,!0)}else n=sc(n).createTextNode(o),n[Vt]=l,l.stateNode=n}return mt(l),null;case 31:if(r=l.memoizedState,n===null||n.memoizedState!==null){if(o=sr(l),r!==null){if(n===null){if(!o)throw Error(i(318));if(n=l.memoizedState,n=n!==null?n.dehydrated:null,!n)throw Error(i(557));n[Vt]=l}else si(),(l.flags&128)===0&&(l.memoizedState=null),l.flags|=4;mt(l),n=!1}else r=Sf(),n!==null&&n.memoizedState!==null&&(n.memoizedState.hydrationErrors=r),n=!0;if(!n)return l.flags&256?(En(l),l):(En(l),null);if((l.flags&128)!==0)throw Error(i(558))}return mt(l),null;case 13:if(o=l.memoizedState,n===null||n.memoizedState!==null&&n.memoizedState.dehydrated!==null){if(f=sr(l),o!==null&&o.dehydrated!==null){if(n===null){if(!f)throw Error(i(318));if(f=l.memoizedState,f=f!==null?f.dehydrated:null,!f)throw Error(i(317));f[Vt]=l}else si(),(l.flags&128)===0&&(l.memoizedState=null),l.flags|=4;mt(l),f=!1}else f=Sf(),n!==null&&n.memoizedState!==null&&(n.memoizedState.hydrationErrors=f),f=!0;if(!f)return l.flags&256?(En(l),l):(En(l),null)}return En(l),(l.flags&128)!==0?(l.lanes=r,l):(r=o!==null,n=n!==null&&n.memoizedState!==null,r&&(o=l.child,f=null,o.alternate!==null&&o.alternate.memoizedState!==null&&o.alternate.memoizedState.cachePool!==null&&(f=o.alternate.memoizedState.cachePool.pool),m=null,o.memoizedState!==null&&o.memoizedState.cachePool!==null&&(m=o.memoizedState.cachePool.pool),m!==f&&(o.flags|=2048)),r!==n&&r&&(l.child.flags|=8192),qu(l,l.updateQueue),mt(l),null);case 4:return Oe(),n===null&&kh(l.stateNode.containerInfo),mt(l),null;case 10:return Rl(l.type),mt(l),null;case 19:if(I(Tt),o=l.memoizedState,o===null)return mt(l),null;if(f=(l.flags&128)!==0,m=o.rendering,m===null)if(f)Is(o,!1);else{if(jt!==0||n!==null&&(n.flags&128)!==0)for(n=l.child;n!==null;){if(m=ku(n),m!==null){for(l.flags|=128,Is(o,!1),n=m.updateQueue,l.updateQueue=n,qu(l,n),l.subtreeFlags=0,n=r,r=l.child;r!==null;)Ub(r,n),r=r.sibling;return V(Tt,Tt.current&1|2),Ie&&Dl(l,o.treeForkCount),l.child}n=n.sibling}o.tail!==null&&St()>Zu&&(l.flags|=128,f=!0,Is(o,!1),l.lanes=4194304)}else{if(!f)if(n=ku(m),n!==null){if(l.flags|=128,f=!0,n=n.updateQueue,l.updateQueue=n,qu(l,n),Is(o,!0),o.tail===null&&o.tailMode==="hidden"&&!m.alternate&&!Ie)return mt(l),null}else 2*St()-o.renderingStartTime>Zu&&r!==536870912&&(l.flags|=128,f=!0,Is(o,!1),l.lanes=4194304);o.isBackwards?(m.sibling=l.child,l.child=m):(n=o.last,n!==null?n.sibling=m:l.child=m,o.last=m)}return o.tail!==null?(n=o.tail,o.rendering=n,o.tail=n.sibling,o.renderingStartTime=St(),n.sibling=null,r=Tt.current,V(Tt,f?r&1|2:r&1),Ie&&Dl(l,o.treeForkCount),n):(mt(l),null);case 22:case 23:return En(l),_f(),o=l.memoizedState!==null,n!==null?n.memoizedState!==null!==o&&(l.flags|=8192):o&&(l.flags|=8192),o?(r&536870912)!==0&&(l.flags&128)===0&&(mt(l),l.subtreeFlags&6&&(l.flags|=8192)):mt(l),r=l.updateQueue,r!==null&&qu(l,r.retryQueue),r=null,n!==null&&n.memoizedState!==null&&n.memoizedState.cachePool!==null&&(r=n.memoizedState.cachePool.pool),o=null,l.memoizedState!==null&&l.memoizedState.cachePool!==null&&(o=l.memoizedState.cachePool.pool),o!==r&&(l.flags|=2048),n!==null&&I(ci),null;case 24:return r=null,n!==null&&(r=n.memoizedState.cache),l.memoizedState.cache!==r&&(l.flags|=2048),Rl(Dt),mt(l),null;case 25:return null;case 30:return null}throw Error(i(156,l.tag))}function TE(n,l){switch(yf(l),l.tag){case 1:return n=l.flags,n&65536?(l.flags=n&-65537|128,l):null;case 3:return Rl(Dt),Oe(),n=l.flags,(n&65536)!==0&&(n&128)===0?(l.flags=n&-65537|128,l):null;case 26:case 27:case 5:return ve(l),null;case 31:if(l.memoizedState!==null){if(En(l),l.alternate===null)throw Error(i(340));si()}return n=l.flags,n&65536?(l.flags=n&-65537|128,l):null;case 13:if(En(l),n=l.memoizedState,n!==null&&n.dehydrated!==null){if(l.alternate===null)throw Error(i(340));si()}return n=l.flags,n&65536?(l.flags=n&-65537|128,l):null;case 19:return I(Tt),null;case 4:return Oe(),null;case 10:return Rl(l.type),null;case 22:case 23:return En(l),_f(),n!==null&&I(ci),n=l.flags,n&65536?(l.flags=n&-65537|128,l):null;case 24:return Rl(Dt),null;case 25:return null;default:return null}}function py(n,l){switch(yf(l),l.tag){case 3:Rl(Dt),Oe();break;case 26:case 27:case 5:ve(l);break;case 4:Oe();break;case 31:l.memoizedState!==null&&En(l);break;case 13:En(l);break;case 19:I(Tt);break;case 10:Rl(l.type);break;case 22:case 23:En(l),_f(),n!==null&&I(ci);break;case 24:Rl(Dt)}}function qs(n,l){try{var r=l.updateQueue,o=r!==null?r.lastEffect:null;if(o!==null){var f=o.next;r=f;do{if((r.tag&n)===n){o=void 0;var m=r.create,S=r.inst;o=m(),S.destroy=o}r=r.next}while(r!==f)}}catch(j){lt(l,l.return,j)}}function Ca(n,l,r){try{var o=l.updateQueue,f=o!==null?o.lastEffect:null;if(f!==null){var m=f.next;o=m;do{if((o.tag&n)===n){var S=o.inst,j=S.destroy;if(j!==void 0){S.destroy=void 0,f=l;var _=r,X=j;try{X()}catch(le){lt(f,_,le)}}}o=o.next}while(o!==m)}}catch(le){lt(l,l.return,le)}}function gy(n){var l=n.updateQueue;if(l!==null){var r=n.stateNode;try{rv(l,r)}catch(o){lt(n,n.return,o)}}}function by(n,l,r){r.props=pi(n.type,n.memoizedProps),r.state=n.memoizedState;try{r.componentWillUnmount()}catch(o){lt(n,l,o)}}function Gs(n,l){try{var r=n.ref;if(r!==null){switch(n.tag){case 26:case 27:case 5:var o=n.stateNode;break;case 30:o=n.stateNode;break;default:o=n.stateNode}typeof r=="function"?n.refCleanup=r(o):r.current=o}}catch(f){lt(n,l,f)}}function gl(n,l){var r=n.ref,o=n.refCleanup;if(r!==null)if(typeof o=="function")try{o()}catch(f){lt(n,l,f)}finally{n.refCleanup=null,n=n.alternate,n!=null&&(n.refCleanup=null)}else if(typeof r=="function")try{r(null)}catch(f){lt(n,l,f)}else r.current=null}function vy(n){var l=n.type,r=n.memoizedProps,o=n.stateNode;try{e:switch(l){case"button":case"input":case"select":case"textarea":r.autoFocus&&o.focus();break e;case"img":r.src?o.src=r.src:r.srcSet&&(o.srcset=r.srcSet)}}catch(f){lt(n,n.return,f)}}function fh(n,l,r){try{var o=n.stateNode;YE(o,n.type,r,l),o[an]=l}catch(f){lt(n,n.return,f)}}function yy(n){return n.tag===5||n.tag===3||n.tag===26||n.tag===27&&Aa(n.type)||n.tag===4}function hh(n){e:for(;;){for(;n.sibling===null;){if(n.return===null||yy(n.return))return null;n=n.return}for(n.sibling.return=n.return,n=n.sibling;n.tag!==5&&n.tag!==6&&n.tag!==18;){if(n.tag===27&&Aa(n.type)||n.flags&2||n.child===null||n.tag===4)continue e;n.child.return=n,n=n.child}if(!(n.flags&2))return n.stateNode}}function mh(n,l,r){var o=n.tag;if(o===5||o===6)n=n.stateNode,l?(r.nodeType===9?r.body:r.nodeName==="HTML"?r.ownerDocument.body:r).insertBefore(n,l):(l=r.nodeType===9?r.body:r.nodeName==="HTML"?r.ownerDocument.body:r,l.appendChild(n),r=r._reactRootContainer,r!=null||l.onclick!==null||(l.onclick=Tl));else if(o!==4&&(o===27&&Aa(n.type)&&(r=n.stateNode,l=null),n=n.child,n!==null))for(mh(n,l,r),n=n.sibling;n!==null;)mh(n,l,r),n=n.sibling}function Gu(n,l,r){var o=n.tag;if(o===5||o===6)n=n.stateNode,l?r.insertBefore(n,l):r.appendChild(n);else if(o!==4&&(o===27&&Aa(n.type)&&(r=n.stateNode),n=n.child,n!==null))for(Gu(n,l,r),n=n.sibling;n!==null;)Gu(n,l,r),n=n.sibling}function xy(n){var l=n.stateNode,r=n.memoizedProps;try{for(var o=n.type,f=l.attributes;f.length;)l.removeAttributeNode(f[0]);Qt(l,o,r),l[Vt]=n,l[an]=r}catch(m){lt(n,n.return,m)}}var Pl=!1,kt=!1,ph=!1,Sy=typeof WeakSet=="function"?WeakSet:Set,Ft=null;function NE(n,l){if(n=n.containerInfo,Lh=mc,n=kb(n),of(n)){if("selectionStart"in n)var r={start:n.selectionStart,end:n.selectionEnd};else e:{r=(r=n.ownerDocument)&&r.defaultView||window;var o=r.getSelection&&r.getSelection();if(o&&o.rangeCount!==0){r=o.anchorNode;var f=o.anchorOffset,m=o.focusNode;o=o.focusOffset;try{r.nodeType,m.nodeType}catch{r=null;break e}var S=0,j=-1,_=-1,X=0,le=0,ie=n,W=null;t:for(;;){for(var J;ie!==r||f!==0&&ie.nodeType!==3||(j=S+f),ie!==m||o!==0&&ie.nodeType!==3||(_=S+o),ie.nodeType===3&&(S+=ie.nodeValue.length),(J=ie.firstChild)!==null;)W=ie,ie=J;for(;;){if(ie===n)break t;if(W===r&&++X===f&&(j=S),W===m&&++le===o&&(_=S),(J=ie.nextSibling)!==null)break;ie=W,W=ie.parentNode}ie=J}r=j===-1||_===-1?null:{start:j,end:_}}else r=null}r=r||{start:0,end:0}}else r=null;for(Ph={focusedElem:n,selectionRange:r},mc=!1,Ft=l;Ft!==null;)if(l=Ft,n=l.child,(l.subtreeFlags&1028)!==0&&n!==null)n.return=l,Ft=n;else for(;Ft!==null;){switch(l=Ft,m=l.alternate,n=l.flags,l.tag){case 0:if((n&4)!==0&&(n=l.updateQueue,n=n!==null?n.events:null,n!==null))for(r=0;r title"))),Qt(m,o,r),m[Vt]=n,Kt(m),o=m;break e;case"link":var S=Tx("link","href",f).get(o+(r.href||""));if(S){for(var j=0;jrt&&(S=rt,rt=Ee,Ee=S);var U=Mb(j,Ee),z=Mb(j,rt);if(U&&z&&(J.rangeCount!==1||J.anchorNode!==U.node||J.anchorOffset!==U.offset||J.focusNode!==z.node||J.focusOffset!==z.offset)){var Y=ie.createRange();Y.setStart(U.node,U.offset),J.removeAllRanges(),Ee>rt?(J.addRange(Y),J.extend(z.node,z.offset)):(Y.setEnd(z.node,z.offset),J.addRange(Y))}}}}for(ie=[],J=j;J=J.parentNode;)J.nodeType===1&&ie.push({element:J,left:J.scrollLeft,top:J.scrollTop});for(typeof j.focus=="function"&&j.focus(),j=0;jr?32:r,R.T=null,r=Ch,Ch=null;var m=wa,S=Ul;if(Lt=0,Sr=wa=null,Ul=0,(We&6)!==0)throw Error(i(331));var j=We;if(We|=4,Ry(m.current),Ay(m,m.current,S,r),We=j,Js(0,!1),ut&&typeof ut.onPostCommitFiberRoot=="function")try{ut.onPostCommitFiberRoot(ft,m)}catch{}return!0}finally{k.p=f,R.T=o,Xy(n,l)}}function Wy(n,l,r){l=zn(r,l),l=th(n.stateNode,l,2),n=ya(n,l,2),n!==null&&(ys(n,2),bl(n))}function lt(n,l,r){if(n.tag===3)Wy(n,n,r);else for(;l!==null;){if(l.tag===3){Wy(l,n,r);break}else if(l.tag===1){var o=l.stateNode;if(typeof l.type.getDerivedStateFromError=="function"||typeof o.componentDidCatch=="function"&&(ja===null||!ja.has(o))){n=zn(r,n),r=Jv(2),o=ya(l,r,2),o!==null&&(ey(r,o,l,n),ys(o,2),bl(o));break}}l=l.return}}function wh(n,l,r){var o=n.pingCache;if(o===null){o=n.pingCache=new ME;var f=new Set;o.set(l,f)}else f=o.get(l),f===void 0&&(f=new Set,o.set(l,f));f.has(r)||(vh=!0,f.add(r),n=LE.bind(null,n,l,r),l.then(n,n))}function LE(n,l,r){var o=n.pingCache;o!==null&&o.delete(l),n.pingedLanes|=n.suspendedLanes&r,n.warmLanes&=~r,ot===n&&(Ke&r)===r&&(jt===4||jt===3&&(Ke&62914560)===Ke&&300>St()-Xu?(We&2)===0&&Cr(n,0):yh|=r,xr===Ke&&(xr=0)),bl(n)}function Jy(n,l){l===0&&(l=Gg()),n=ii(n,l),n!==null&&(ys(n,l),bl(n))}function PE(n){var l=n.memoizedState,r=0;l!==null&&(r=l.retryLane),Jy(n,r)}function zE(n,l){var r=0;switch(n.tag){case 31:case 13:var o=n.stateNode,f=n.memoizedState;f!==null&&(r=f.retryLane);break;case 19:o=n.stateNode;break;case 22:o=n.stateNode._retryCache;break;default:throw Error(i(314))}o!==null&&o.delete(l),Jy(n,r)}function BE(n,l){return $e(n,l)}var lc=null,Er=null,Th=!1,ac=!1,Nh=!1,Na=0;function bl(n){n!==Er&&n.next===null&&(Er===null?lc=Er=n:Er=Er.next=n),ac=!0,Th||(Th=!0,FE())}function Js(n,l){if(!Nh&&ac){Nh=!0;do for(var r=!1,o=lc;o!==null;){if(n!==0){var f=o.pendingLanes;if(f===0)var m=0;else{var S=o.suspendedLanes,j=o.pingedLanes;m=(1<<31-et(42|n)+1)-1,m&=f&~(S&~j),m=m&201326741?m&201326741|1:m?m|2:0}m!==0&&(r=!0,lx(o,m))}else m=Ke,m=ou(o,o===ot?m:0,o.cancelPendingCommit!==null||o.timeoutHandle!==-1),(m&3)===0||vs(o,m)||(r=!0,lx(o,m));o=o.next}while(r);Nh=!1}}function KE(){ex()}function ex(){ac=Th=!1;var n=0;Na!==0&&ZE()&&(n=Na);for(var l=St(),r=null,o=lc;o!==null;){var f=o.next,m=tx(o,l);m===0?(o.next=null,r===null?lc=f:r.next=f,f===null&&(Er=r)):(r=o,(n!==0||(m&3)!==0)&&(ac=!0)),o=f}Lt!==0&&Lt!==5||Js(n),Na!==0&&(Na=0)}function tx(n,l){for(var r=n.suspendedLanes,o=n.pingedLanes,f=n.expirationTimes,m=n.pendingLanes&-62914561;0j)break;var le=_.transferSize,ie=_.initiatorType;le&&dx(ie)&&(_=_.responseEnd,S+=le*(_"u"?null:document;function $x(n,l,r){var o=jr;if(o&&typeof l=="string"&&l){var f=Ln(l);f='link[rel="'+n+'"][href="'+f+'"]',typeof r=="string"&&(f+='[crossorigin="'+r+'"]'),Cx.has(f)||(Cx.add(f),n={rel:n,crossOrigin:r,href:l},o.querySelector(f)===null&&(l=o.createElement("link"),Qt(l,"link",n),Kt(l),o.head.appendChild(l)))}}function r5(n){Hl.D(n),$x("dns-prefetch",n,null)}function s5(n,l){Hl.C(n,l),$x("preconnect",n,l)}function o5(n,l,r){Hl.L(n,l,r);var o=jr;if(o&&n&&l){var f='link[rel="preload"][as="'+Ln(l)+'"]';l==="image"&&r&&r.imageSrcSet?(f+='[imagesrcset="'+Ln(r.imageSrcSet)+'"]',typeof r.imageSizes=="string"&&(f+='[imagesizes="'+Ln(r.imageSizes)+'"]')):f+='[href="'+Ln(n)+'"]';var m=f;switch(l){case"style":m=wr(n);break;case"script":m=Tr(n)}Vn.has(m)||(n=y({rel:"preload",href:l==="image"&&r&&r.imageSrcSet?void 0:n,as:l},r),Vn.set(m,n),o.querySelector(f)!==null||l==="style"&&o.querySelector(lo(m))||l==="script"&&o.querySelector(ao(m))||(l=o.createElement("link"),Qt(l,"link",n),Kt(l),o.head.appendChild(l)))}}function u5(n,l){Hl.m(n,l);var r=jr;if(r&&n){var o=l&&typeof l.as=="string"?l.as:"script",f='link[rel="modulepreload"][as="'+Ln(o)+'"][href="'+Ln(n)+'"]',m=f;switch(o){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":m=Tr(n)}if(!Vn.has(m)&&(n=y({rel:"modulepreload",href:n},l),Vn.set(m,n),r.querySelector(f)===null)){switch(o){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(r.querySelector(ao(m)))return}o=r.createElement("link"),Qt(o,"link",n),Kt(o),r.head.appendChild(o)}}}function c5(n,l,r){Hl.S(n,l,r);var o=jr;if(o&&n){var f=Qi(o).hoistableStyles,m=wr(n);l=l||"default";var S=f.get(m);if(!S){var j={loading:0,preload:null};if(S=o.querySelector(lo(m)))j.loading=5;else{n=y({rel:"stylesheet",href:n,"data-precedence":l},r),(r=Vn.get(m))&&Vh(n,r);var _=S=o.createElement("link");Kt(_),Qt(_,"link",n),_._p=new Promise(function(X,le){_.onload=X,_.onerror=le}),_.addEventListener("load",function(){j.loading|=1}),_.addEventListener("error",function(){j.loading|=2}),j.loading|=4,uc(S,l,o)}S={type:"stylesheet",instance:S,count:1,state:j},f.set(m,S)}}}function d5(n,l){Hl.X(n,l);var r=jr;if(r&&n){var o=Qi(r).hoistableScripts,f=Tr(n),m=o.get(f);m||(m=r.querySelector(ao(f)),m||(n=y({src:n,async:!0},l),(l=Vn.get(f))&&Ih(n,l),m=r.createElement("script"),Kt(m),Qt(m,"link",n),r.head.appendChild(m)),m={type:"script",instance:m,count:1,state:null},o.set(f,m))}}function f5(n,l){Hl.M(n,l);var r=jr;if(r&&n){var o=Qi(r).hoistableScripts,f=Tr(n),m=o.get(f);m||(m=r.querySelector(ao(f)),m||(n=y({src:n,async:!0,type:"module"},l),(l=Vn.get(f))&&Ih(n,l),m=r.createElement("script"),Kt(m),Qt(m,"link",n),r.head.appendChild(m)),m={type:"script",instance:m,count:1,state:null},o.set(f,m))}}function Ex(n,l,r,o){var f=(f=me.current)?oc(f):null;if(!f)throw Error(i(446));switch(n){case"meta":case"title":return null;case"style":return typeof r.precedence=="string"&&typeof r.href=="string"?(l=wr(r.href),r=Qi(f).hoistableStyles,o=r.get(l),o||(o={type:"style",instance:null,count:0,state:null},r.set(l,o)),o):{type:"void",instance:null,count:0,state:null};case"link":if(r.rel==="stylesheet"&&typeof r.href=="string"&&typeof r.precedence=="string"){n=wr(r.href);var m=Qi(f).hoistableStyles,S=m.get(n);if(S||(f=f.ownerDocument||f,S={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},m.set(n,S),(m=f.querySelector(lo(n)))&&!m._p&&(S.instance=m,S.state.loading=5),Vn.has(n)||(r={rel:"preload",as:"style",href:r.href,crossOrigin:r.crossOrigin,integrity:r.integrity,media:r.media,hrefLang:r.hrefLang,referrerPolicy:r.referrerPolicy},Vn.set(n,r),m||h5(f,n,r,S.state))),l&&o===null)throw Error(i(528,""));return S}if(l&&o!==null)throw Error(i(529,""));return null;case"script":return l=r.async,r=r.src,typeof r=="string"&&l&&typeof l!="function"&&typeof l!="symbol"?(l=Tr(r),r=Qi(f).hoistableScripts,o=r.get(l),o||(o={type:"script",instance:null,count:0,state:null},r.set(l,o)),o):{type:"void",instance:null,count:0,state:null};default:throw Error(i(444,n))}}function wr(n){return'href="'+Ln(n)+'"'}function lo(n){return'link[rel="stylesheet"]['+n+"]"}function jx(n){return y({},n,{"data-precedence":n.precedence,precedence:null})}function h5(n,l,r,o){n.querySelector('link[rel="preload"][as="style"]['+l+"]")?o.loading=1:(l=n.createElement("link"),o.preload=l,l.addEventListener("load",function(){return o.loading|=1}),l.addEventListener("error",function(){return o.loading|=2}),Qt(l,"link",r),Kt(l),n.head.appendChild(l))}function Tr(n){return'[src="'+Ln(n)+'"]'}function ao(n){return"script[async]"+n}function wx(n,l,r){if(l.count++,l.instance===null)switch(l.type){case"style":var o=n.querySelector('style[data-href~="'+Ln(r.href)+'"]');if(o)return l.instance=o,Kt(o),o;var f=y({},r,{"data-href":r.href,"data-precedence":r.precedence,href:null,precedence:null});return o=(n.ownerDocument||n).createElement("style"),Kt(o),Qt(o,"style",f),uc(o,r.precedence,n),l.instance=o;case"stylesheet":f=wr(r.href);var m=n.querySelector(lo(f));if(m)return l.state.loading|=4,l.instance=m,Kt(m),m;o=jx(r),(f=Vn.get(f))&&Vh(o,f),m=(n.ownerDocument||n).createElement("link"),Kt(m);var S=m;return S._p=new Promise(function(j,_){S.onload=j,S.onerror=_}),Qt(m,"link",o),l.state.loading|=4,uc(m,r.precedence,n),l.instance=m;case"script":return m=Tr(r.src),(f=n.querySelector(ao(m)))?(l.instance=f,Kt(f),f):(o=r,(f=Vn.get(m))&&(o=y({},r),Ih(o,f)),n=n.ownerDocument||n,f=n.createElement("script"),Kt(f),Qt(f,"link",o),n.head.appendChild(f),l.instance=f);case"void":return null;default:throw Error(i(443,l.type))}else l.type==="stylesheet"&&(l.state.loading&4)===0&&(o=l.instance,l.state.loading|=4,uc(o,r.precedence,n));return l.instance}function uc(n,l,r){for(var o=r.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),f=o.length?o[o.length-1]:null,m=f,S=0;S title"):null)}function m5(n,l,r){if(r===1||l.itemProp!=null)return!1;switch(n){case"meta":case"title":return!0;case"style":if(typeof l.precedence!="string"||typeof l.href!="string"||l.href==="")break;return!0;case"link":if(typeof l.rel!="string"||typeof l.href!="string"||l.href===""||l.onLoad||l.onError)break;switch(l.rel){case"stylesheet":return n=l.disabled,typeof l.precedence=="string"&&n==null;default:return!0}case"script":if(l.async&&typeof l.async!="function"&&typeof l.async!="symbol"&&!l.onLoad&&!l.onError&&l.src&&typeof l.src=="string")return!0}return!1}function Ax(n){return!(n.type==="stylesheet"&&(n.state.loading&3)===0)}function p5(n,l,r,o){if(r.type==="stylesheet"&&(typeof o.media!="string"||matchMedia(o.media).matches!==!1)&&(r.state.loading&4)===0){if(r.instance===null){var f=wr(o.href),m=l.querySelector(lo(f));if(m){l=m._p,l!==null&&typeof l=="object"&&typeof l.then=="function"&&(n.count++,n=dc.bind(n),l.then(n,n)),r.state.loading|=4,r.instance=m,Kt(m);return}m=l.ownerDocument||l,o=jx(o),(f=Vn.get(f))&&Vh(o,f),m=m.createElement("link"),Kt(m);var S=m;S._p=new Promise(function(j,_){S.onload=j,S.onerror=_}),Qt(m,"link",o),r.instance=m}n.stylesheets===null&&(n.stylesheets=new Map),n.stylesheets.set(r,l),(l=r.state.preload)&&(r.state.loading&3)===0&&(n.count++,r=dc.bind(n),l.addEventListener("load",r),l.addEventListener("error",r))}}var qh=0;function g5(n,l){return n.stylesheets&&n.count===0&&hc(n,n.stylesheets),0qh?50:800)+l);return n.unsuspend=r,function(){n.unsuspend=null,clearTimeout(o),clearTimeout(f)}}:null}function dc(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)hc(this,this.stylesheets);else if(this.unsuspend){var n=this.unsuspend;this.unsuspend=null,n()}}}var fc=null;function hc(n,l){n.stylesheets=null,n.unsuspend!==null&&(n.count++,fc=new Map,l.forEach(b5,n),fc=null,dc.call(n))}function b5(n,l){if(!(l.state.loading&4)){var r=fc.get(n);if(r)var o=r.get(null);else{r=new Map,fc.set(n,r);for(var f=n.querySelectorAll("link[data-precedence],style[data-precedence]"),m=0;m"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}return e(),nm.exports=_5(),nm.exports}var L5=O5();/** - * react-router v7.18.1 - * - * Copyright (c) Remix Software Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE.md file in the root directory of this source tree. - * - * @license MIT - */var Bp=/^(?:[a-z][a-z0-9+.-]*:|[\\/]{2})/i,bS=/^[\\/]{2}/;function P5(e,t){return t+e.replace(/\\/g,"/")}var t0="popstate";function n0(e){return typeof e=="object"&&e!=null&&"pathname"in e&&"search"in e&&"hash"in e&&"state"in e&&"key"in e}function z5(e={}){function t(s,c){let{pathname:d="/",search:h="",hash:p=""}=Pi(s.location.hash.substring(1));return!d.startsWith("/")&&!d.startsWith(".")&&(d="/"+d),Im("",{pathname:d,search:h,hash:p},c.state&&c.state.usr||null,c.state&&c.state.key||"default")}function a(s,c){let d=s.document.querySelector("base"),h="";if(d&&d.getAttribute("href")){let p=s.location.href,b=p.indexOf("#");h=b===-1?p:p.slice(0,b)}return h+"#"+(typeof c=="string"?c:Do(c))}function i(s,c){Mn(s.pathname.charAt(0)==="/",`relative pathnames are not supported in hash history.push(${JSON.stringify(c)})`)}return K5(t,a,i,e)}function yt(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function Mn(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function B5(){return Math.random().toString(36).substring(2,10)}function l0(e,t){return{usr:e.state,key:e.key,idx:t,masked:e.mask?{pathname:e.pathname,search:e.search,hash:e.hash}:void 0}}function Im(e,t,a=null,i,s){return{pathname:typeof e=="string"?e:e.pathname,search:"",hash:"",...typeof t=="string"?Pi(t):t,state:a,key:t&&t.key||i||B5(),mask:s}}function Do({pathname:e="/",search:t="",hash:a=""}){return t&&t!=="?"&&(e+=t.charAt(0)==="?"?t:"?"+t),a&&a!=="#"&&(e+=a.charAt(0)==="#"?a:"#"+a),e}function Pi(e){let t={};if(e){let a=e.indexOf("#");a>=0&&(t.hash=e.substring(a),e=e.substring(0,a));let i=e.indexOf("?");i>=0&&(t.search=e.substring(i),e=e.substring(0,i)),e&&(t.pathname=e)}return t}function K5(e,t,a,i={}){let{window:s=document.defaultView,v5Compat:c=!1}=i,d=s.history,h="POP",p=null,b=v();b==null&&(b=0,d.replaceState({...d.state,idx:b},""));function v(){return(d.state||{idx:null}).idx}function y(){h="POP";let T=v(),E=T==null?null:T-b;b=T,p&&p({action:h,location:w.location,delta:E})}function C(T,E){h="PUSH";let N=n0(T)?T:Im(w.location,T,E);a&&a(N,T),b=v()+1;let O=l0(N,b),M=w.createHref(N.mask||N);try{d.pushState(O,"",M)}catch(K){if(K instanceof DOMException&&K.name==="DataCloneError")throw K;s.location.assign(M)}c&&p&&p({action:h,location:w.location,delta:1})}function x(T,E){h="REPLACE";let N=n0(T)?T:Im(w.location,T,E);a&&a(N,T),b=v();let O=l0(N,b),M=w.createHref(N.mask||N);d.replaceState(O,"",M),c&&p&&p({action:h,location:w.location,delta:0})}function $(T){return F5(s,T)}let w={get action(){return h},get location(){return e(s,d)},listen(T){if(p)throw new Error("A history only accepts one active listener");return s.addEventListener(t0,y),p=T,()=>{s.removeEventListener(t0,y),p=null}},createHref(T){return t(s,T)},createURL:$,encodeLocation(T){let E=$(T);return{pathname:E.pathname,search:E.search,hash:E.hash}},push:C,replace:x,go(T){return d.go(T)}};return w}function F5(e,t,a=!1){let i="http://localhost";e&&(i=e.location.origin!=="null"?e.location.origin:e.location.href),yt(i,"No window.location.(origin|href) available to create URL");let s=typeof t=="string"?t:Do(t);return s=s.replace(/ $/,"%20"),!a&&bS.test(s)&&(s=i+s),new URL(s,i)}function vS(e,t,a="/"){return U5(e,t,a,!1)}function U5(e,t,a,i,s){let c=typeof t=="string"?Pi(t):t,d=ta(c.pathname||"/",a);if(d==null)return null;let h=H5(e),p=null,b=ej(d);for(let v=0;p==null&&v{let v={relativePath:b===void 0?d.path||"":b,caseSensitive:d.caseSensitive===!0,childrenIndex:h,route:d};if(v.relativePath.startsWith("/")){if(!v.relativePath.startsWith(i)&&p)return;yt(v.relativePath.startsWith(i),`Absolute route path "${v.relativePath}" nested under path "${i}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),v.relativePath=v.relativePath.slice(i.length)}let y=ll([i,v.relativePath]),C=a.concat(v);d.children&&d.children.length>0&&(yt(d.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${y}".`),yS(d.children,t,C,y,p)),!(d.path==null&&!d.index)&&t.push({path:y,score:Z5(y,d.index),routesMeta:C.map((x,$)=>{let[w,T]=CS(x.relativePath,x.caseSensitive,$===C.length-1);return{...x,matcher:w,compiledParams:T}})})};return e.forEach((d,h)=>{var p;if(d.path===""||!((p=d.path)!=null&&p.includes("?")))c(d,h);else for(let b of xS(d.path))c(d,h,!0,b)}),t}function xS(e){let t=e.split("/");if(t.length===0)return[];let[a,...i]=t,s=a.endsWith("?"),c=a.replace(/\?$/,"");if(i.length===0)return s?[c,""]:[c];let d=xS(i.join("/")),h=[];return h.push(...d.map(p=>p===""?c:[c,p].join("/"))),s&&h.push(...d),h.map(p=>e.startsWith("/")&&p===""?"/":p)}function V5(e){e.sort((t,a)=>t.score!==a.score?a.score-t.score:W5(t.routesMeta.map(i=>i.childrenIndex),a.routesMeta.map(i=>i.childrenIndex)))}var I5=/^:[\w-]+$/,q5=3,G5=2,Q5=1,Y5=10,X5=-2,a0=e=>e==="*";function Z5(e,t){let a=e.split("/"),i=a.length;return a.some(a0)&&(i+=X5),t&&(i+=G5),a.filter(s=>!a0(s)).reduce((s,c)=>s+(I5.test(c)?q5:c===""?Q5:Y5),i)}function W5(e,t){return e.length===t.length&&e.slice(0,-1).every((i,s)=>i===t[s])?e[e.length-1]-t[t.length-1]:0}function J5(e,t,a=!1){let{routesMeta:i}=e,s={},c="/",d=[];for(let h=0;h{if(v==="*"){let $=h[C]||"";d=c.slice(0,c.length-$.length).replace(/(.)\/+$/,"$1")}const x=h[C];return y&&!x?b[v]=void 0:b[v]=(x||"").replace(/%2F/g,"/"),b},{}),pathname:c,pathnameBase:d,pattern:e}}function CS(e,t=!1,a=!0){Mn(e==="*"||!e.endsWith("*")||e.endsWith("/*"),`Route path "${e}" will be treated as if it were "${e.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${e.replace(/\*$/,"/*")}".`);let i=[],s="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(d,h,p,b,v)=>{if(i.push({paramName:h,isOptional:p!=null}),p){let y=v.charAt(b+d.length);return y&&y!=="/"?"/([^\\/]*)":"(?:/([^\\/]*))?"}return"/([^\\/]+)"}).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return e.endsWith("*")?(i.push({paramName:"*"}),s+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):a?s+="\\/*$":e!==""&&e!=="/"&&(s+="(?:(?=\\/|$))"),[new RegExp(s,t?void 0:"i"),i]}function ej(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return Mn(!1,`The URL path "${e}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${t}).`),e}}function ta(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let a=t.endsWith("/")?t.length-1:t.length,i=e.charAt(a);return i&&i!=="/"?null:e.slice(a)||"/"}function tj(e,t="/"){let{pathname:a,search:i="",hash:s=""}=typeof e=="string"?Pi(e):e,c;return a?(a=$S(a),a.startsWith("/")?c=i0(a.substring(1),"/"):c=i0(a,t)):c=t,{pathname:c,search:aj(i),hash:ij(s)}}function i0(e,t){let a=qc(t).split("/");return e.split("/").forEach(s=>{s===".."?a.length>1&&a.pop():s!=="."&&a.push(s)}),a.length>1?a.join("/"):"/"}function rm(e,t,a,i){return`Cannot include a '${e}' character in a manually specified \`to.${t}\` field [${JSON.stringify(i)}]. Please separate it out to the \`to.${a}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function nj(e){return e.filter((t,a)=>a===0||t.route.path&&t.route.path.length>0)}function Kp(e){let t=nj(e);return t.map((a,i)=>i===t.length-1?a.pathname:a.pathnameBase)}function dd(e,t,a,i=!1){let s;typeof e=="string"?s=Pi(e):(s={...e},yt(!s.pathname||!s.pathname.includes("?"),rm("?","pathname","search",s)),yt(!s.pathname||!s.pathname.includes("#"),rm("#","pathname","hash",s)),yt(!s.search||!s.search.includes("#"),rm("#","search","hash",s)));let c=e===""||s.pathname==="",d=c?"/":s.pathname,h;if(d==null)h=a;else{let y=t.length-1;if(!i&&d.startsWith("..")){let C=d.split("/");for(;C[0]==="..";)C.shift(),y-=1;s.pathname=C.join("/")}h=y>=0?t[y]:"/"}let p=tj(s,h),b=d&&d!=="/"&&d.endsWith("/"),v=(c||d===".")&&a.endsWith("/");return!p.pathname.endsWith("/")&&(b||v)&&(p.pathname+="/"),p}var $S=e=>e.replace(/[\\/]{2,}/g,"/"),ll=e=>$S(e.join("/")),qc=e=>e.replace(/\/+$/,""),lj=e=>qc(e).replace(/^\/*/,"/"),aj=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,ij=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e,rj=class{constructor(e,t,a,i=!1){this.status=e,this.statusText=t||"",this.internal=i,a instanceof Error?(this.data=a.toString(),this.error=a):this.data=a}};function sj(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}function oj(e){let t=e.map(a=>a.route.path).filter(Boolean);return ll(t)||"/"}var ES=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function jS(e,t){let a=e;if(typeof a!="string"||!Bp.test(a))return{absoluteURL:void 0,isExternal:!1,to:a};let i=a,s=!1;if(ES)try{let c=new URL(window.location.href),d=bS.test(a)?new URL(P5(a,c.protocol)):new URL(a),h=ta(d.pathname,t);d.origin===c.origin&&h!=null?a=h+d.search+d.hash:s=!0}catch{Mn(!1,` contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}return{absoluteURL:i,isExternal:s,to:a}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var wS=["POST","PUT","PATCH","DELETE"];new Set(wS);var uj=["GET",...wS];new Set(uj);var cj=["about:","blob:","chrome:","chrome-untrusted:","content:","data:","devtools:","file:","filesystem:","javascript:"];function dj(e){try{return cj.includes(new URL(e).protocol)}catch{return!1}}var as=g.createContext(null);as.displayName="DataRouter";var fd=g.createContext(null);fd.displayName="DataRouterState";var TS=g.createContext(!1);function fj(){return g.useContext(TS)}var NS=g.createContext({isTransitioning:!1});NS.displayName="ViewTransition";var hj=g.createContext(new Map);hj.displayName="Fetchers";var mj=g.createContext(null);mj.displayName="Await";var kn=g.createContext(null);kn.displayName="Navigation";var qo=g.createContext(null);qo.displayName="Location";var ol=g.createContext({outlet:null,matches:[],isDataRoute:!1});ol.displayName="Route";var Fp=g.createContext(null);Fp.displayName="RouteError";var AS="REACT_ROUTER_ERROR",pj="REDIRECT",gj="ROUTE_ERROR_RESPONSE";function bj(e){if(e.startsWith(`${AS}:${pj}:{`))try{let t=JSON.parse(e.slice(28));if(typeof t=="object"&&t&&typeof t.status=="number"&&typeof t.statusText=="string"&&typeof t.location=="string"&&typeof t.reloadDocument=="boolean"&&typeof t.replace=="boolean")return t}catch{}}function vj(e){if(e.startsWith(`${AS}:${gj}:{`))try{let t=JSON.parse(e.slice(40));if(typeof t=="object"&&t&&typeof t.status=="number"&&typeof t.statusText=="string")return new rj(t.status,t.statusText,t.data)}catch{}}function yj(e,{relative:t}={}){yt(is(),"useHref() may be used only in the context of a component.");let{basename:a,navigator:i}=g.useContext(kn),{hash:s,pathname:c,search:d}=Qo(e,{relative:t}),h=c;return a!=="/"&&(h=c==="/"?a:ll([a,c])),i.createHref({pathname:h,search:d,hash:s})}function is(){return g.useContext(qo)!=null}function ul(){return yt(is(),"useLocation() may be used only in the context of a component."),g.useContext(qo).location}var DS="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function MS(e){g.useContext(kn).static||g.useLayoutEffect(e)}function Go(){let{isDataRoute:e}=g.useContext(ol);return e?_j():xj()}function xj(){yt(is(),"useNavigate() may be used only in the context of a component.");let e=g.useContext(as),{basename:t,navigator:a}=g.useContext(kn),{matches:i}=g.useContext(ol),{pathname:s}=ul(),c=JSON.stringify(Kp(i)),d=g.useRef(!1);return MS(()=>{d.current=!0}),g.useCallback((p,b={})=>{if(Mn(d.current,DS),!d.current)return;if(typeof p=="number"){a.go(p);return}let v=dd(p,JSON.parse(c),s,b.relative==="path");e==null&&t!=="/"&&(v.pathname=v.pathname==="/"?t:ll([t,v.pathname])),(b.replace?a.replace:a.push)(v,b.state,b)},[t,a,c,s,e])}var Sj=g.createContext(null);function Cj(e){let t=g.useContext(ol).outlet;return g.useMemo(()=>t&&g.createElement(Sj.Provider,{value:e},t),[t,e])}function Qo(e,{relative:t}={}){let{matches:a}=g.useContext(ol),{pathname:i}=ul(),s=JSON.stringify(Kp(a));return g.useMemo(()=>dd(e,JSON.parse(s),i,t==="path"),[e,s,i,t])}function $j(e,t){return RS(e,t)}function RS(e,t,a){var T;yt(is(),"useRoutes() may be used only in the context of a component.");let{navigator:i}=g.useContext(kn),{matches:s}=g.useContext(ol),c=s[s.length-1],d=c?c.params:{},h=c?c.pathname:"/",p=c?c.pathnameBase:"/",b=c&&c.route;{let E=b&&b.path||"";_S(h,!b||E.endsWith("*")||E.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${h}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. - -Please change the parent to .`)}let v=ul(),y;if(t){let E=typeof t=="string"?Pi(t):t;yt(p==="/"||((T=E.pathname)==null?void 0:T.startsWith(p)),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${p}" but pathname "${E.pathname}" was given in the \`location\` prop.`),y=E}else y=v;let C=y.pathname||"/",x=C;if(p!=="/"){let E=p.replace(/^\//,"").split("/");x="/"+C.replace(/^\//,"").split("/").slice(E.length).join("/")}let $=a&&a.state.matches.length?a.state.matches.map(E=>Object.assign(E,{route:a.manifest[E.route.id]||E.route})):vS(e,{pathname:x});Mn(b||$!=null,`No routes matched location "${y.pathname}${y.search}${y.hash}" `),Mn($==null||$[$.length-1].route.element!==void 0||$[$.length-1].route.Component!==void 0||$[$.length-1].route.lazy!==void 0,`Matched leaf route at location "${y.pathname}${y.search}${y.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let w=Nj($&&$.map(E=>Object.assign({},E,{params:Object.assign({},d,E.params),pathname:ll([p,i.encodeLocation?i.encodeLocation(E.pathname.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:E.pathname]),pathnameBase:E.pathnameBase==="/"?p:ll([p,i.encodeLocation?i.encodeLocation(E.pathnameBase.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:E.pathnameBase])})),s,a);return t&&w?g.createElement(qo.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",mask:void 0,...y},navigationType:"POP"}},w):w}function Ej(){let e=kj(),t=sj(e)?`${e.status} ${e.statusText}`:e instanceof Error?e.message:JSON.stringify(e),a=e instanceof Error?e.stack:null,i="rgba(200,200,200, 0.5)",s={padding:"0.5rem",backgroundColor:i},c={padding:"2px 4px",backgroundColor:i},d=null;return console.error("Error handled by React Router default ErrorBoundary:",e),d=g.createElement(g.Fragment,null,g.createElement("p",null,"💿 Hey developer 👋"),g.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",g.createElement("code",{style:c},"ErrorBoundary")," or"," ",g.createElement("code",{style:c},"errorElement")," prop on your route.")),g.createElement(g.Fragment,null,g.createElement("h2",null,"Unexpected Application Error!"),g.createElement("h3",{style:{fontStyle:"italic"}},t),a?g.createElement("pre",{style:s},a):null,d)}var jj=g.createElement(Ej,null),kS=class extends g.Component{constructor(e){super(e),this.state={location:e.location,revalidation:e.revalidation,error:e.error}}static getDerivedStateFromError(e){return{error:e}}static getDerivedStateFromProps(e,t){return t.location!==e.location||t.revalidation!=="idle"&&e.revalidation==="idle"?{error:e.error,location:e.location,revalidation:e.revalidation}:{error:e.error!==void 0?e.error:t.error,location:t.location,revalidation:e.revalidation||t.revalidation}}componentDidCatch(e,t){this.props.onError?this.props.onError(e,t):console.error("React Router caught the following error during render",e)}render(){let e=this.state.error;if(this.context&&typeof e=="object"&&e&&"digest"in e&&typeof e.digest=="string"){const a=vj(e.digest);a&&(e=a)}let t=e!==void 0?g.createElement(ol.Provider,{value:this.props.routeContext},g.createElement(Fp.Provider,{value:e,children:this.props.component})):this.props.children;return this.context?g.createElement(wj,{error:e},t):t}};kS.contextType=TS;var sm=new WeakMap;function wj({children:e,error:t}){let{basename:a}=g.useContext(kn);if(typeof t=="object"&&t&&"digest"in t&&typeof t.digest=="string"){let i=bj(t.digest);if(i){let s=sm.get(t);if(s)throw s;let c=jS(i.location,a),d=c.absoluteURL||c.to;if(dj(d))throw new Error("Invalid redirect location");if(ES&&!sm.get(t))if(c.isExternal||i.reloadDocument)window.location.href=d;else{const h=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(c.to,{replace:i.replace}));throw sm.set(t,h),h}return g.createElement("meta",{httpEquiv:"refresh",content:`0;url=${d}`})}}return e}function Tj({routeContext:e,match:t,children:a}){let i=g.useContext(as);return i&&i.static&&i.staticContext&&(t.route.errorElement||t.route.ErrorBoundary)&&(i.staticContext._deepestRenderedBoundaryId=t.route.id),g.createElement(ol.Provider,{value:e},a)}function Nj(e,t=[],a){let i=a==null?void 0:a.state;if(e==null){if(!i)return null;if(i.errors)e=i.matches;else if(t.length===0&&!i.initialized&&i.matches.length>0)e=i.matches;else return null}let s=e,c=i==null?void 0:i.errors;if(c!=null){let v=s.findIndex(y=>y.route.id&&(c==null?void 0:c[y.route.id])!==void 0);yt(v>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(c).join(",")}`),s=s.slice(0,Math.min(s.length,v+1))}let d=!1,h=-1;if(a&&i){d=i.renderFallback;for(let v=0;v=0?s=s.slice(0,h+1):s=[s[0]];break}}}}let p=a==null?void 0:a.onError,b=i&&p?(v,y)=>{var C,x;p(v,{location:i.location,params:((x=(C=i.matches)==null?void 0:C[0])==null?void 0:x.params)??{},pattern:oj(i.matches),errorInfo:y})}:void 0;return s.reduceRight((v,y,C)=>{let x,$=!1,w=null,T=null;i&&(x=c&&y.route.id?c[y.route.id]:void 0,w=y.route.errorElement||jj,d&&(h<0&&C===0?(_S("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),$=!0,T=null):h===C&&($=!0,T=y.route.hydrateFallbackElement||null)));let E=t.concat(s.slice(0,C+1)),N=()=>{let O;return x?O=w:$?O=T:y.route.Component?O=g.createElement(y.route.Component,null):y.route.element?O=y.route.element:O=v,g.createElement(Tj,{match:y,routeContext:{outlet:v,matches:E,isDataRoute:i!=null},children:O})};return i&&(y.route.ErrorBoundary||y.route.errorElement||C===0)?g.createElement(kS,{location:i.location,revalidation:i.revalidation,component:w,error:x,children:N(),routeContext:{outlet:null,matches:E,isDataRoute:!0},onError:b}):N()},null)}function Up(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function Aj(e){let t=g.useContext(as);return yt(t,Up(e)),t}function Dj(e){let t=g.useContext(fd);return yt(t,Up(e)),t}function Mj(e){let t=g.useContext(ol);return yt(t,Up(e)),t}function Hp(e){let t=Mj(e),a=t.matches[t.matches.length-1];return yt(a.route.id,`${e} can only be used on routes that contain a unique "id"`),a.route.id}function Rj(){return Hp("useRouteId")}function kj(){var i;let e=g.useContext(Fp),t=Dj("useRouteError"),a=Hp("useRouteError");return e!==void 0?e:(i=t.errors)==null?void 0:i[a]}function _j(){let{router:e}=Aj("useNavigate"),t=Hp("useNavigate"),a=g.useRef(!1);return MS(()=>{a.current=!0}),g.useCallback(async(s,c={})=>{Mn(a.current,DS),a.current&&(typeof s=="number"?await e.navigate(s):await e.navigate(s,{fromRouteId:t,...c}))},[e,t])}var r0={};function _S(e,t,a){!t&&!r0[e]&&(r0[e]=!0,Mn(!1,a))}g.memo(Oj);function Oj({routes:e,manifest:t,future:a,state:i,isStatic:s,onError:c}){return RS(e,void 0,{manifest:t,state:i,isStatic:s,onError:c})}function OS({to:e,replace:t,state:a,relative:i}){yt(is()," may be used only in the context of a component.");let{static:s}=g.useContext(kn);Mn(!s," must not be used on the initial render in a . This is a no-op, but you should modify your code so the is only ever rendered in response to some user interaction or state change.");let{matches:c}=g.useContext(ol),{pathname:d}=ul(),h=Go(),p=dd(e,Kp(c),d,i==="path"),b=JSON.stringify(p);return g.useEffect(()=>{h(JSON.parse(b),{replace:t,state:a,relative:i})},[h,b,i,t,a]),null}function Lj(e){return Cj(e.context)}function fn(e){yt(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function Pj({basename:e="/",children:t=null,location:a,navigationType:i="POP",navigator:s,static:c=!1,useTransitions:d}){yt(!is(),"You cannot render a inside another . You should never have more than one in your app.");let h=e.replace(/^\/*/,"/"),p=g.useMemo(()=>({basename:h,navigator:s,static:c,useTransitions:d,future:{}}),[h,s,c,d]);typeof a=="string"&&(a=Pi(a));let{pathname:b="/",search:v="",hash:y="",state:C=null,key:x="default",mask:$}=a,w=g.useMemo(()=>{let T=ta(b,h);return T==null?null:{location:{pathname:T,search:v,hash:y,state:C,key:x,mask:$},navigationType:i}},[h,b,v,y,C,x,i,$]);return Mn(w!=null,` is not able to match the URL "${b}${v}${y}" because it does not start with the basename, so the won't render anything.`),w==null?null:g.createElement(kn.Provider,{value:p},g.createElement(qo.Provider,{children:t,value:w}))}function zj({children:e,location:t}){return $j(qm(e),t)}function qm(e,t=[]){let a=[];return g.Children.forEach(e,(i,s)=>{if(!g.isValidElement(i))return;let c=[...t,s];if(i.type===g.Fragment){a.push.apply(a,qm(i.props.children,c));return}yt(i.type===fn,`[${typeof i.type=="string"?i.type:i.type.name}] is not a component. All component children of must be a or `),yt(!i.props.index||!i.props.children,"An index route cannot have child routes.");let d={id:i.props.id||c.join("-"),caseSensitive:i.props.caseSensitive,element:i.props.element,Component:i.props.Component,index:i.props.index,path:i.props.path,middleware:i.props.middleware,loader:i.props.loader,action:i.props.action,hydrateFallbackElement:i.props.hydrateFallbackElement,HydrateFallback:i.props.HydrateFallback,errorElement:i.props.errorElement,ErrorBoundary:i.props.ErrorBoundary,hasErrorBoundary:i.props.hasErrorBoundary===!0||i.props.ErrorBoundary!=null||i.props.errorElement!=null,shouldRevalidate:i.props.shouldRevalidate,handle:i.props.handle,lazy:i.props.lazy};i.props.children&&(d.children=qm(i.props.children,c)),a.push(d)}),a}var Oc="get",Lc="application/x-www-form-urlencoded";function hd(e){return typeof HTMLElement<"u"&&e instanceof HTMLElement}function Bj(e){return hd(e)&&e.tagName.toLowerCase()==="button"}function Kj(e){return hd(e)&&e.tagName.toLowerCase()==="form"}function Fj(e){return hd(e)&&e.tagName.toLowerCase()==="input"}function Uj(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function Hj(e,t){return e.button===0&&(!t||t==="_self")&&!Uj(e)}function Gm(e=""){return new URLSearchParams(typeof e=="string"||Array.isArray(e)||e instanceof URLSearchParams?e:Object.keys(e).reduce((t,a)=>{let i=e[a];return t.concat(Array.isArray(i)?i.map(s=>[a,s]):[[a,i]])},[]))}function Vj(e,t){let a=Gm(e);return t&&t.forEach((i,s)=>{a.has(s)||t.getAll(s).forEach(c=>{a.append(s,c)})}),a}var Cc=null;function Ij(){if(Cc===null)try{new FormData(document.createElement("form"),0),Cc=!1}catch{Cc=!0}return Cc}var qj=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function om(e){return e!=null&&!qj.has(e)?(Mn(!1,`"${e}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${Lc}"`),null):e}function Gj(e,t){let a,i,s,c,d;if(Kj(e)){let h=e.getAttribute("action");i=h?ta(h,t):null,a=e.getAttribute("method")||Oc,s=om(e.getAttribute("enctype"))||Lc,c=new FormData(e)}else if(Bj(e)||Fj(e)&&(e.type==="submit"||e.type==="image")){let h=e.form;if(h==null)throw new Error('Cannot submit a {metadata?.description ?

{metadata.description}

: null} @@ -702,11 +752,21 @@ function InlinePriceForm({ row, onClose }: { row: ModelRow; onClose: () => void const deletePricing = useDeletePricing(); const [input, setInput] = useState(row.inputPrice == null ? "" : String(row.inputPrice)); const [output, setOutput] = useState(row.outputPrice == null ? "" : String(row.outputPrice)); + const [cacheRead, setCacheRead] = useState(row.cacheReadPrice == null ? "" : String(row.cacheReadPrice)); + const [cacheWrite, setCacheWrite] = useState(row.cacheWritePrice == null ? "" : String(row.cacheWritePrice)); + + const canSave = isValidPrice(input) && isValidPrice(output) && isValidOptionalPrice(cacheRead) && isValidOptionalPrice(cacheWrite); const save = () => { - if (!isValidPrice(input) || !isValidPrice(output)) return; + if (!canSave) return; setPricing.mutate( - { model_key: row.key, input_price_per_million: Number(input), output_price_per_million: Number(output) }, + { + model_key: row.key, + input_price_per_million: Number(input), + output_price_per_million: Number(output), + cache_read_price_per_million: optionalPrice(cacheRead), + cache_write_price_per_million: optionalPrice(cacheWrite), + }, { onSuccess: onClose }, ); }; @@ -722,12 +782,15 @@ function InlinePriceForm({ row, onClose }: { row: ModelRow; onClose: () => void Output $ / 1M - + ); + return ( + + {number(primary, primaryLabel)} + / + {number(secondary, secondaryLabel)} + + ); +} + function ModelTable({ rows, isLoading, @@ -784,59 +888,63 @@ function ModelTable({ Provider } /> - - Price + Cache r / w $ / 1M {isLoading ? ( - + ) : rows.length > 0 ? ( - rows.map((row) => ( - - onSelect(row.key)} selected={row.key === selectedKey}> - {row.model} - {row.provider} - - {formatContext(row.contextWindow)} - - - {row.inputPrice == null ? "—" : formatCost(row.inputPrice)} - - - {row.outputPrice == null ? "—" : formatCost(row.outputPrice)} - - - - - - {pricingKey === row.key ? ( - - - onSetPricingKey(null)} /> - - - ) : null} - - )) + rows.map((row) => { + const edit = () => onSetPricingKey(pricingKey === row.key ? null : row.key); + return ( + + onSelect(row.key)} selected={row.key === selectedKey}> + {row.model} + {row.provider} + + {formatContext(row.contextWindow)} + + + + + + + + + {pricingKey === row.key ? ( + + + onSetPricingKey(null)} /> + + + ) : null} + + ); + }) ) : ( - {empty} + {empty} )} @@ -935,6 +1043,8 @@ export function ModelsPage() { contextWindow: catalogRow?.contextWindow ?? null, inputPrice: priced ? priced.input_price_per_million : (catalogRow?.inputPrice ?? null), outputPrice: priced ? priced.output_price_per_million : (catalogRow?.outputPrice ?? null), + cacheReadPrice: priced ? priced.cache_read_price_per_million : (catalogRow?.cacheReadPrice ?? null), + cacheWritePrice: priced ? priced.cache_write_price_per_million : (catalogRow?.cacheWritePrice ?? null), source: priced ? "configured" : (catalogRow?.source ?? "none"), }); }; @@ -953,6 +1063,8 @@ export function ModelsPage() { contextWindow: model.context_window, inputPrice: model.pricing?.input_price_per_million ?? null, outputPrice: model.pricing?.output_price_per_million ?? null, + cacheReadPrice: model.pricing?.cache_read_price_per_million ?? null, + cacheWritePrice: model.pricing?.cache_write_price_per_million ?? null, source: priceStatus, }); } @@ -989,6 +1101,8 @@ export function ModelsPage() { releaseDate: metadataByKey[model.key]?.release_date ?? null, inputPrice: null, outputPrice: null, + cacheReadPrice: null, + cacheWritePrice: null, source: "none", }); } @@ -1164,7 +1278,11 @@ export function ModelsPage() { error={models.error ?? pricing.error ?? discoverable.error ?? providers.error ?? metadata.error} /> -
+
@@ -1260,19 +1378,22 @@ export function ModelsPage() { ) : null}
- + {selectedRow ? ( + + ) : null}
); diff --git a/web/src/pages/OverviewPage.test.tsx b/web/src/pages/OverviewPage.test.tsx index aa778fd0..ce488b1c 100644 --- a/web/src/pages/OverviewPage.test.tsx +++ b/web/src/pages/OverviewPage.test.tsx @@ -112,6 +112,18 @@ describe("OverviewPage", () => { expect(screen.getByText("Elevated")).toBeInTheDocument(); // error-rate status word (non-hue) }); + it("shows 30-day cache reads as a tile", async () => { + mockApi({ + today: { cost: 5 }, + period: { cost: 200, cache_read_tokens: 5_100_000 }, + prev: { cost: 100, cache_read_tokens: 1_000_000 }, + }); + renderPage(); + + expect(await screen.findByText("5.1M")).toBeInTheDocument(); + expect(screen.getByText("Cache reads, last 30 days")).toBeInTheDocument(); + }); + it("shows a dash for error rate when there are no requests", async () => { mockApi({ period: { request_count: 0, error_count: 0 } }); renderPage(); diff --git a/web/src/pages/OverviewPage.tsx b/web/src/pages/OverviewPage.tsx index 405e620f..ffd792ac 100644 --- a/web/src/pages/OverviewPage.tsx +++ b/web/src/pages/OverviewPage.tsx @@ -13,7 +13,7 @@ import { import type { UsageEntry } from "@/api/types"; import { LoadingRow, Table, TableMessage, Td, Th, THead, Tr } from "@/components/Table"; import { DeltaHint, ErrorBanner, PageHeader, StatCard } from "@/components/ui"; -import { deltaFraction, formatNumber, formatPct, formatRelative, formatUsd } from "@/lib/format"; +import { deltaFraction, formatNumber, formatPct, formatRelative, formatTokens, formatUsd } from "@/lib/format"; import { budgetHealth, errorRateHealth, providerHealthStatus, toStatStatus } from "@/lib/overview"; const DAY_MS = 86_400_000; @@ -165,6 +165,16 @@ export function OverviewPage() { ) : null } /> + + ) : null + } + to="/usage" + /> { expect(screen.getByText(/2\.1% errors/)).toBeInTheDocument(); }); + it("shows cache read and write totals as tiles", async () => { + const base = summary(); + mockApi(summary({ totals: { ...base.totals, cache_read_tokens: 5_100_000, cache_write_tokens: 2_700_000 } })); + renderPage(); + + // Await a value (loads after the query resolves), not the static label. + expect(await screen.findByText("5.1M")).toBeInTheDocument(); + expect(screen.getByText("2.7M")).toBeInTheDocument(); + expect(screen.getByText("Cache read")).toBeInTheDocument(); + expect(screen.getByText("Cache write")).toBeInTheDocument(); + }); + it("queries the previous period with a bounded end_date for deltas", async () => { const fetchMock = mockApi(summary()); renderPage(); diff --git a/web/src/pages/UsagePage.tsx b/web/src/pages/UsagePage.tsx index c59c0d14..4e44f046 100644 --- a/web/src/pages/UsagePage.tsx +++ b/web/src/pages/UsagePage.tsx @@ -450,6 +450,24 @@ export function UsagePage() { value={totals ? formatTokens(totals.total_tokens) : "—"} hint={totals ? : null} /> + + ) : null + } + /> + + ) : null + } + />